我创建了一个简单的游戏,其中用户需要猜测使用Random()随机生成的0-9之间的4位数字,这4位数字彼此不同,没有重复的数字。用户尝试了5次猜测,每一次失败(几乎正确)的猜测都会告诉用户正确的部分。

您认为这对用户来说太难还是太容易猜测?我还需要对此代码进行一些审查。也许其中一些可以简化?

import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;

public class Game {

    public static void main(String[] args) {      
        System.out.println("The computer has generate a unique 4 digit number.\n"
                + "You can try to guess the 4 digits number in 5 attempts.\n");
        System.out.println("_______________________________________________________\n");
        int[] random=numberGenerator();
        int maxTry=5;
        int indexMatch=0;
        int match=0;
        while(maxTry>0 && indexMatch!=4){
            int[] guess=getGuess();
            indexMatch=0;
            match=0;
            for(int i=0;i<guess.length;i++){
                if(guess[i]==random[i]){
                    indexMatch++;
                }
                else if(guess[i]==random[0] || guess[i]==random[1] || guess[i]==random[2] || guess[i]==random[3]){
                    match++;
                }
            }
            if(indexMatch==4){
                System.out.print("Well done! Your guess is Correct! The number is: ");
                for(int i=0;i<guess.length;i++){
                    System.out.print(guess[i]);
                }
            }
            else{
                maxTry--;
                if(maxTry>1){
                    System.out.println("You have guess "+indexMatch+" correct number in correct position,"+
                    " and "+match+" correct number in incorrect position. \n"+maxTry+" attempt remaining.");
                }
                else if(maxTry==1){
                    System.out.println("You have guess "+indexMatch+" correct number in correct position,"+
                    " and "+match+" correct number in incorrect position. \nLast attempt!. Good luck");
                }
                else{
                    System.out.println("Sorry, you failed to guess the number in 5 attempts.");
                    System.out.print("The number is: ");
                    for(int i=0;i<random.length;i++){
                        System.out.print(random[i]);
                }
            }
        }
    }   
}

public static int[] getGuess(){
    Scanner keyboard = new Scanner(System.in);
    System.out.println("Please enter your guess: ");
    String input = keyboard.nextLine();
        if(input.length()!=4 || input.replaceAll("\D","").length()!=4){
            System.out.println("Invalid number. You must enter 4 digits between 0-9 only.");
            return getGuess();
    }
    int[] guess = new int[4];
    for (int i = 0; i < 4; i++) {
        guess[i] = Integer.parseInt(String.valueOf(input.charAt(i)));
    }
    return guess;
}

public static int[] numberGenerator() {
    Random randy = new Random();
    int[] randArray = {10,10,10,10};

    for(int i=0;i<randArray.length;i++){
        int temp = randy.nextInt(9);
        while(temp == randArray[0] || temp == randArray[1] || temp == randArray[2] || temp == randArray[3]){
            temp=randy.nextInt(9);
        }
        randArray[i]=temp;      
    }
    return randArray;
}
}


#1 楼

我不会评论随机数的生成,变量命名或一般的代码结构-您在上面的内容中提供了足够的信息。

但是,这是我的两分钱,“这太难了吗?”您的问题的一部分。

您描述的游戏曾经被称为“牛和牛”,并且


事实证明,任何数字都可以解决多达七个回合。
最小平均游戏时间为26274/5040 = 5.2131转。


参考文献:http://fourdigits.sourceforge.net,http://www.cs.nccu。 edu.tw/~chaolin/papers/science3203.pdf

所以是的,只有四个猜测很难。五个猜测给您带来更少的机会,六个比给您带来更好的机会。

如果您尝试使用计算机解决游戏问题,通常希望找到使最大有效数最小化的猜测剩下的组合。原来,如果您从猜测开始

1234


您可以得到的“最差答案”是

1 white peg (one number correct, in the wrong location)


这将为您提供1440种可能的组合(4个数字中的任何一个都可能是带有白色钉的数字,但位置不正确。这使您在三个位置中的每一个中都有4个可能的数字,乘以6x5x4其他三个未结头寸(必须用一个尚未使用的数字来填补)。

您实际上可以列出所有可能的组合,并“划掉”那些组合。轮流放进去;将其与“如果我猜为abcd,且响应为[x白色,y黑色],还剩下多少组合?”相结合,然后再次选择使该数字最小化的猜测。一台计算机。

代码中的另一个错误

在某些情况下,最好的猜测是“非法”的-也就是说,您要求的组合是与您所掌握的信息不一致 至今。它甚至可以包含加倍的数字-例如

1123


您必须确保您的代码正确处理了该输入。这样做的方法是稍微更改测试循环-而不是

for(int i=0;i<guess.length;i++){
  if(guess[i]==random[i]){
    indexMatch++;
  }
  else if(guess[i]==random[0] || guess[i]==random[1] || guess[i]==random[2] || guess[i]==random[3]){
    match++;
  }
}


您需要

for(int i=0;i<guess.length;i++)
  if(guess[i]==random[i]){
    indexMatch++;
  }
  else if( random[i] == guess[0] || random[i] == guess[1] || random[i] == guess[2] || random[i] == guess[3]){
     match++;
   }
 }


看看如果随机代码是

1234


而猜测是

3132


正确的答案会发生什么?将是“一个黑色,两个白色”。但是您的代码将给出“一个黑色,三个白色”,因为猜测中重复的三个被计算了两次。通过切换测试,随机数中的每个值都只会被查看一次-而这一切都不同。

#2 楼

由于问题的这一部分到目前为止已被跳过,我将接受它:

您认为这太难了吗?

我认为是。与您的游戏类似,在经典的Mastermind中,玩家必须猜测4个非唯一彩色钉子的组合,并且每次都会给正确位置和颜色组合的数量打上标记。

64 = 1,296种可能的组合

但是,在您的游戏中(借用术语),有10种可能的钉,并且由于它们是唯一的,组合是:

10 x 9 x 8 x 7 = 5,040种可能的组合

增加288.8%,此外,您只给用户5次尝试猜测的机会,而与Mastermind的6、8或12相反。策划大师至少要打六局,就是要测试每种组合,例如:



但是,当您将可能性增加到10并减少时,对5的猜测,您将无法再执行此操作,因此永远不会猜对正确数字的机会急剧增加,例如:



因此,我认为您需要要么增加猜测数量至少为10(或至少10个),或者选择较小的一组猜测对象,例如A-F,1-6或基于Mastermind的GUI。

评论


\ $ \ begingroup \ $
很好。以0.001的获胜概率,也许我应该把它作为一个赌博游戏。
\ $ \ endgroup \ $
–婴儿
2014年1月24日,11:51

\ $ \ begingroup \ $
-1 Mastermind与您刚才描述的完全不同。在Mastermind中,会告诉玩家正确的颜色有多少个钉子,但位置错误,正确的颜色和位置有多少个钉子。因此,在您的第一个示例中,将为1234分配3个白色的钉子,为玩家提供比此处显示的信息更多的信息。而且,策划者总是可以在5个步骤中被击败。
\ $ \ endgroup \ $
– BlueRaja-Danny Pflughoeft
2014年1月24日19:56



#3 楼

现在仅关注一个特定点:

int temp = randy.nextInt(9);


这将永远不会生成数字9

如果要生成0到9之间的数字(包括10个不同的数字),则需要执行以下操作:

int temp = randy.nextInt(10);


如果想要排除0(在这种情况下可能是合理的...。):

int temp = randy.nextInt(9) + 1;


评论


\ $ \ begingroup \ $
ouch,我真的很想念这个!谢谢。包含0,所以我将使用nextInt(10)
\ $ \ endgroup \ $
–婴儿
2014年1月24日7:12



\ $ \ begingroup \ $
您也不需要循环。您可以将数字0-9放入列表中,将列表随机排序,然后从列表中获取前四个条目。
\ $ \ endgroup \ $
–大卫·康拉德(David Conrad)
2014年1月27日在16:49

#4 楼

较少关注功能,而更多关注术语:我不会将您的new Random()称为randy。尽管名称很清楚,但randy一词的含义还不成熟。您永远都不知道谁会看到您的源代码,因此,如果您的任何客户看到的代码中的变量名或注释都有些不成熟,他们可能会认为您的不成熟扩展到了功能,即使它没有。

因此而不是

Random randy = new Random();




Random rand = new Random();


通常,避免使用不成熟的词,即同事,公司或产品或外语单词,除非您要使用的单词与该变量的用途相关。 EPAComplianceCheck()是可以接受的。 ScrewYouGinaMcCarthy()少一些。

评论


\ $ \ begingroup \ $
哈哈。好吧,实际上我也觉得这很有趣。我只是忘了在发布之前更改所有这些变量的名称。不管怎么说,还是要谢谢你!
\ $ \ endgroup \ $
–婴儿
2014年1月24日上午11:45

\ $ \ begingroup \ $
令人惊讶的是,人们在代码中放了多少个小玩笑然后忘记了……直到不该看到的人看到了它们。
\ $ \ endgroup \ $
– Tim B
2014年1月24日13:17

#5 楼

getGuess()中,

Integer.parseInt(String.valueOf(input.charAt(i)))


可以写为Character.digit(input.charAt(i), 10)

getGuess()中,验证失败时重新进行是不适当的。 (按住Enter键会使堆栈溢出!)请改用循环:

public static int[] getGuess(){
    Scanner keyboard = new Scanner(System.in);
    do {
        System.out.print("Please enter your guess: ");
        String input = keyboard.nextLine();
        if (input.matches("\d{4}")) break;
        System.out.println("Invalid number. You must enter 4 digits between 0-9 only.");
    } while (true);
    int[] guess = new int[4];
    for (int i = 0; i < guess.length; i++) {
        guess[i] = Character.digit(input.charAt(i), 10);
    }
    return guess;
}

numberGenerator()中,将while循环更改为do-while。 />

评论


\ $ \ begingroup \ $
酷。 Character.digit()对我来说是新事物。大约一会儿,嗯,是的,我想你是对的
\ $ \ endgroup \ $
–婴儿
2014年1月24日在7:34

\ $ \ begingroup \ $
如何使用do-while验证用户输入?我以为递归很简单。
\ $ \ endgroup \ $
–婴儿
2014年1月24日8:30

\ $ \ begingroup \ $
@RafaEl从概念上讲是{prompt(); input = getInput(); while(!isValid(input));
\ $ \ endgroup \ $
–棘轮怪胎
2014年1月24日在8:44

\ $ \ begingroup \ $
好的,那确实很有意义。并感谢您使用的正则表达式(“ \\ d {4}”)更加简短。
\ $ \ endgroup \ $
–婴儿
2014年1月24日8:53

\ $ \ begingroup \ $
@Tibos我不知道执行尾部调用优化的任何Java实现。
\ $ \ endgroup \ $
– 200_success
2014年1月24日15:47

#6 楼

首先出发:

public class Game {


真的吗?这是什么游戏?这是俄罗斯方块之类的东西吗?使其类似于CodeGuessGame,并向该类添加javadoc注释,以准确解释其含义和作用。

System.out.println("The computer has generate a unique 4 digit number.\n"
                 + "You can try to guess the 4 digits number in 5 attempts.\n");


除了拼写错误之外,您不应该这样做该行的末尾有\n,因为您使用的是println。就我个人而言,我总是将我的\n放在每条连续行的开头,但这并不是完全必要。

        System.out.println("_______________________________________________________\n");


再次,额外的\n。我个人建议使用连字符(-)或等号(=),因为连字符在创建水平规则时更为常见。下划线表示用户应键入的空格。

        int[] random = numberGenerator();
        int maxTry = 5;
        int indexMatch = 0;
        int match = 0;


为了简洁起见,应将maxTryindexMatchmatch的声明合并为一个语句。

        while(maxTry>0 && indexMatch!=4){
            int[] guess=getGuess();
            indexMatch=0;
            match=0;
            for(int i=0;i<guess.length;i++){
                if(guess[i]==random[i]){
                    indexMatch++;
                }
                else if(guess[i]==random[0] || guess[i]==random[1] || guess[i]==random[2] || guess[i]==random[3]){
                    match++;
                }
            }
            if(indexMatch==4){
                System.out.print("Well done! Your guess is Correct! The number is: ");
                for(int i=0;i<guess.length;i++){
                    System.out.print(guess[i]);
                }
            }
            else{
                maxTry--;
                if(maxTry>1){
                    System.out.println("You have guess "+indexMatch+" correct number in correct position,"+
                    " and "+match+" correct number in incorrect position. \n"+maxTry+" attempt remaining.");
                }
                else if(maxTry==1){
                    System.out.println("You have guess "+indexMatch+" correct number in correct position,"+
                    " and "+match+" correct number in incorrect position. \nLast attempt!. Good luck");
                }
                else{
                    System.out.println("Sorry, you failed to guess the number in 5 attempts.");
                    System.out.print("The number is: ");
                    for(int i=0;i<random.length;i++){
                        System.out.print(random[i]);
                }
            }
        }
    }   
}


该给我们这个shpaghetti吗?请学习使用System.out.format和三元运算(condition?valueIfTrue:valueIfFalse)。

至此,我不再忍受阅读您的代码了。请编写一个新程序,并在编写时考虑到可配置性和可重用性。

游戏应作为单独的实例运行。 main方法不应包含任何与实际游戏有关的逻辑。它要做的就是(1)初始化Game,(2)设置可配置选项,(3)调用Game的实例方法(称为类似play)来运行游戏。

理想情况下,您的主要方法最终应如下所示:

public static void main(String [] args){
    new MastermindGame()
        .setLanes(4)
        .setColors("0", "1", "2", "3", "4", "5", "6", "7", "8", "9")
        .setGuesses(5)
        .setStartFlavor("The computer has generated a 4 digit number."
                   + "\nYou must guess this 4 digit number in 5 attempt(s).")
        .setPrompt("Enter guess:")
        .setResponse("Correct digit in correct position count = %1"
                 + "\nCorrect digit in incorrect position count = %2"
                 + "\nGuesses remaining = %3")
        .setLose("Sorry, you failed to guess the number (%1) in 5 attempts.");
        .setWin("Congratulations, you win!");
        .play();
}


评论


\ $ \ begingroup \ $
我绝对不会推荐这种主要方法。这就要求所有返回void的方法都将其返回。再加上调试将是一件繁琐的事情。我认为这是非常不好的做法。
\ $ \ endgroup \ $
–sixtyfootersdude
2014年1月24日19:34

\ $ \ begingroup \ $
再想一想,也许还可以。该术语似乎是“流利接口”或“方法链”。处理此类事情的堆栈跟踪会很麻烦,但是有些有趣。请参阅此问题以进一步讨论它们:stackoverflow.com/questions/1345001/…
\ $ \ endgroup \ $
–sixtyfootersdude
2014年1月24日19:47

\ $ \ begingroup \ $
这不会影响堆栈跟踪,并且唯一使调试混乱的是,您需要在子表达式上设置断点(或仅将它们放在函数本身而不是调用站点上)。
\ $ \ endgroup \ $
– Ben Voigt
2014年1月24日19:54