规则:
您只能说连续于前一个所说数字的数字。
您一次可以说1、2或3个数字。
例如:
播放器1说1
播放器2说2 ,3
播放器1表示4、5、6
播放器2表示7、8
依此类推...
您将在玩AI,您将开始。尽力而为,但是无论努力如何,都会输!
我将亲自给能够找到一种以50 rep赏金击败AI(即程序中的错误)的人。
Main.java
public class Main {
private static final int END = 21;
public static void main(String[] args) {
Game game = new Game(END);
game.startGame();
}
}
Game.java
import java.util.Scanner;
import java.util.regex.Pattern;
public class Game {
private static final Pattern NUMBER_LIST_PATTERN = Pattern.compile("(\d+,)*\d+");
private static final String HELP = "help";
private static final int MAX_NUMS_PER_TURN = 3;
private final int end;
public Game(int end) {
if (end % 4 != 1) {
throw new IllegalArgumentException("Nice try, this number will fail the AI.");
}
this.end = end;
}
public void startGame() {
Scanner scanner = new Scanner(System.in);
AI impossibleComputer = new AI();
displayHelp();
do {
boolean hasLost = false;
int previousNumber = 0;
do {
int[] numbers = getNumbers(scanner, previousNumber);
if (contains(numbers, end)) {
System.out.println("You lose!");
hasLost = true;
} else {
previousNumber = printAINumbers(impossibleComputer.getNextNumbers(numbers[numbers.length - 1]));
}
} while (!hasLost);
} while (doAgain(scanner));
System.out.println("Thanks for playing!");
}
private void displayHelp() {
System.out.println("The goal of this game is to not say the number 21.\n\n" + "Rules:\n"
+ "1. You must only say numbers consecutive to the previous said number.\n"
+ "2. You may say 1, 2, or 3 numbers at a time.\n\n" + "For example:\n\n" + "\tPlayer 1 says 1\n"
+ "\tPlayer 2 says 2, 3\n" + "\tPlayer 1 says 4, 5, 6\n" + "\tPlayer 2 says 7, 8\n"
+ "\tAnd so on...\n\n"
+ "You will be playing AI, and you will start. Try your best, but no matter how hard you try, you will lose!\n");
}
private int[] getNumbers(Scanner scanner, int previousNumber) {
System.out
.println("Enter your numbers, separated by commas only (e.g. \"1,2,3\"), or enter \"help\" for help.\n"
+ "Note that if you enter more than three numbers, this program will ignore the ones after the third one.");
while (true) {
String input = scanner.nextLine().trim();
if (input.equals(HELP)) {
displayHelp();
return null;
} else {
if (NUMBER_LIST_PATTERN.matcher(input).matches()) {
int[] result = toIntArray(input);
if (result != null && result[0] == previousNumber + 1) {
return result;
}
}
System.out.println("Oops, illegal input. Try again:");
}
}
}
private int[] toIntArray(String input) {
String[] array = input.split(",");
int length = array.length;
int[] result = new int[length > MAX_NUMS_PER_TURN ? MAX_NUMS_PER_TURN : length];
for (int i = 0; i < length && i < MAX_NUMS_PER_TURN; i++) {
result[i] = Integer.parseInt(array[i]);
if (i != 0 && result[i] != result[i - 1] + 1) {
return null;
}
}
return result;
}
private boolean contains(int[] numbers, int number) {
for (int num : numbers) {
if (num == number) {
return true;
}
}
return false;
}
private int printAINumbers(int[] nextNumbers) {
System.out.print("The AI says: ");
for (int number : nextNumbers) {
System.out.print(number + " ");
}
System.out.println();
return nextNumbers[nextNumbers.length - 1];
}
private boolean doAgain(Scanner scanner) {
System.out.println("Do you want to play again?");
while (true) {
char in = Character.toUpperCase(scanner.next().charAt(0));
if (in == 'Y') {
return true;
} else if (in == 'N') {
return false;
}
System.out.println("Oops, that was not valid input. Try again: ");
}
}
}
现在是揭示秘密的部分...
AI.java
public class AI {
public int[] getNextNumbers(int lastNumber) {
if (lastNumber % 4 == 0) {
return new int[] {lastNumber + 1};
}
return list(lastNumber + 1, 4 * ((int) (lastNumber / 4) + 1));
}
private int[] list(int x, int y) {
int[] result = new int[y - x + 1];
for (int i = 0; x + i <= y; i++) {
result[i] = x + i;
}
return result;
}
}
如果您不明白为什么会这样,那么AI所做的就是算到4的下一个倍数。为什么这项工作?好吧,这就是发生的事情:
Player: 1, [2], [3]
AI: [2], [3], 4
Player: 5, [6], [7]
AI: [6], [7], 8
Player: 9, [10], [11]
AI: [10], [11], 12
Player: 13, [14], [15]
AI: [14], [15], 16
Player: 17, [18], [19]
AI: [18], [19], 20
Player: 21
方括号中的任何内容,如果玩家未说,将由AI讲。无论您如何玩,AI都会赢(只要它开始就可以)。
关注点:
我在面向对象操作方面做得好吗?
我的变量名好吗?
和往常一样,还有其他吗?
#1 楼
这是一个两人游戏。 AI和人类玩家都应实现一个通用的Player
接口。例如,这会使将其转换成人类游戏变得容易。输入验证部分发生在
getNumbers()
(正则表达式匹配和连续数字检查)中,部分发生在toIntArray()
(另一个连续数字)中检查)。如果验证都发生在同一个地方,那就太好了。等效的游戏只会要求每个玩家选择1、2或3来添加到当前总和中。这样可以简化解析过程,并且无需传递
int[]
数组。如果您想恢复原始行为,可以在遵循以下界面的前提下进行恢复。我不喜欢
startGame()
可以玩多个游戏的方式。我会考虑“再次播放?”循环并提示。使用
Scanner
时,最好在遇到EOF的情况下捕获NoSuchElementException
。CursedNumberGame.java
import java.io.PrintStream;
import java.util.NoSuchElementException;
import java.util.Scanner;
public class CursedNumberGame {
public interface Player {
/**
* Given the parameters of the game (max and avoid), and the
* current sum, chooses a number between 1 and max inclusive.
*/
int play(int currentSum, int max, int avoid);
}
private final int maxPerTurn, avoid;
public CursedNumberGame(int maxPerTurn, int avoid) {
this.maxPerTurn = maxPerTurn;
this.avoid = avoid;
}
public static void displayHelp(PrintStream out) {
out.println("The goal of this game is to stay below the number 21.\n\n"
+ "At each turn, a player chooses either \"1\", \"2\", or \"3\".\n"
+ "That number will be added to the current number.\n\n"
+ "You will be playing AI, and you will start. Try your best, but no matter how\n"
+ "hard you try, you will lose!");
}
public void run(Scanner scanner, PrintStream out) {
displayHelp(out);
Player[] players = new Player[] { new HumanPlayer(scanner, out), new AI() };
int p, sum;
for (p = 0, sum = 0; sum < this.avoid; p = 1 - p) {
int choice = players[p].play(sum, this.maxPerTurn, this.avoid);
out.printf("%s played %d. The sum is now %d.\n", players[p], choice, sum + choice);
sum += choice;
}
out.printf("%s lost!\n", players[1 - p]);
}
private static boolean doAgain(Scanner scanner, PrintStream out) {
out.print("Do you want to play again? ");
while (true) {
char in = Character.toUpperCase(scanner.nextLine().charAt(0));
if (in == 'Y') {
return true;
} else if (in == 'N') {
return false;
}
out.print("Oops, that was not valid input. Try again: ");
}
}
public static void main(String[] args) {
CursedNumberGame game = new CursedNumberGame(3, 21);
try (Scanner scanner = new Scanner(System.in)) {
do {
game.run(scanner, System.out);
} while (doAgain(scanner, System.out));
} catch (NoSuchElementException eof) {
}
System.out.println("Thanks for playing!");
}
}
AI.java
public class AI implements CursedNumberGame.Player {
@Override
public int play(int currentSum, int max, int avoid) {
assert(max == 3);
assert(avoid == 21);
assert(4 - (currentSum % 4) < max);
return 4 - (currentSum % 4);
}
@Override
public String toString() {
return "The AI";
}
}
HumanPlayer.java
import java.util.Scanner;
import java.io.PrintStream;
public class HumanPlayer implements CursedNumberGame.Player {
private static final String HELP = "help";
private final Scanner in;
private final PrintStream out;
public HumanPlayer(Scanner in, PrintStream out) {
this.in = in;
this.out = out;
}
@Override
public int play(int currentSum, int max, int avoid) {
out.printf("\nEnter a number from 1 to %d inclusive: ", max);
do {
String input = in.nextLine();
if (HELP.equals(input)) {
CursedNumberGame.displayHelp(out);
continue;
} else try {
int n = Integer.parseInt(input);
if (0 < n && n <= max) {
return n;
}
} catch (NumberFormatException notAnInt) {
}
out.print("Oops, illegal input. Try again: ");
} while (true);
}
@Override
public String toString() {
return "You";
}
}