Open
Description
基本功能
使用我们已有的知识,最优的代码应该在 20 - 30 行左右。大家可以对比一下自己的代码和下面的示例代码,看看你有哪些冗余的代码是可以精简的。
字符串实现
字符串实现完全使用了我们已学的知识,示例如下:
import java.util.Scanner;
public class TicTacToe {
public static void main(String[] args) {
String board = ".........";
boolean player = true;
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println(board.substring(0, 3));
System.out.println(board.substring(3, 6));
System.out.println(board.substring(6, 9));
System.out.print("Next move: ");
int nextPosition = scanner.nextInt();
board = board.substring(0, nextPosition)
+ ((player) ? 'O' : 'X')
+ board.substring(nextPosition + 1, board.length());
player = !player;
}
}
}
有以下地方需要同学们注意:
- 使用
while (true)
循环:基本功能不要求实现终局判断,因此我们只需要一个死循环来实现用户的交替输入。for
循环的等价实现是for (;;)
,显然没有while (true)
来得直观。 - 使用一个
boolean
变量来记录当前玩家:游戏中只有两个玩家,而布尔值恰好只提供两个值(true
和false
),因此我们可以用true
来代表玩家X
,而false
代表玩家O
。 while () {}
循环的意思是当()
中的表达式值为true
时会执行{}
中的语句。while(true)
因为括号中的布尔表达式恒为true
,所以会无限循环下去。请明确布尔变量player
的意义,这个布尔变量与循环控制条件没有任何关系。- 更新棋盘状态:假设我们要更新第
nextPosition
个位置,其实是把前几位、更新的这一位及后几位拼起来,再重新赋值给board
。这里还用到了三目运算符,你也可以用if...else...
替换 。
一维数组实现
上次 lab 最后让大家自学了一维数组。如果使用这种方法,下面的实现应该是最优的:
import java.util.Scanner;
public class TicTacToe {
public static void main(String[] args) {
char[] board = {'.', '.', '.', '.', '.', '.', '.', '.', '.'};
boolean player = true;
Scanner scanner = new Scanner(System.in);
while (true) {
for (int i = 0; i < 3; i++) {
String row = "" + board[i * 3] + board[i * 3 + 1] + board[i * 3 + 2];
System.out.println(row);
}
System.out.print("Next move: ");
int nextPosition = scanner.nextInt();
board[nextPosition] = (player) ? 'O' : 'X';
player = !player;
}
}
}
有以下地方需要同学们注意:
- 数组的输出需要借助循环来完成。思考里面的
for
循环,它为什么能输出整个棋盘? - 思考
row
的赋值语句中,为什么要在最左边加上""
?
二维数组实现
二维数组还没有学到,仅供学有余力的同学了解。
import java.util.Scanner;
public class TicTacToe {
public static void main(String[] args) {
char[][] board = {{'.', '.', '.'}, {'.', '.', '.'}, {'.', '.', '.'}};
boolean player = true;
Scanner scanner = new Scanner(System.in);
while (true) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
System.out.print(board[i][j]);
}
System.out.println();
}
System.out.print("Next move: ");
int nextPosition = scanner.nextInt();
board[nextPosition / 3][nextPosition % 3] = (player) ? 'O' : 'X';
player = !player;
}
}
}
方法调用实现
一些同学使用了方法调用,因此这里也给出了一份在一维数组基础上的方法调用实现。当然,你也可以用其他方式组织你的方法。
import java.util.Scanner;
public class TicTacToe {
public static void main(String[] args) {
char[] board = {'.', '.', '.', '.', '.', '.', '.', '.', '.'};
boolean player = false;
Scanner scanner = new Scanner(System.in);
while (true) {
printBoard(board);
board = setBoard(board, getNextPosition(scanner), player = !player);
}
}
private static int getNextPosition(Scanner scanner) {
System.out.print("Next move: ");
return scanner.nextInt();
}
private static void printBoard(char[] board) {
for (int i = 0; i < 3; i++) {
String row = "" + board[i * 3] + board[i * 3 + 1] + board[i * 3 + 2];
System.out.println(row);
}
}
private static char[] setBoard(char[] board, int position, boolean player) {
board[position] = (player) ? 'O' : 'X';
return board;
}
}
附加功能
提供一份在一维数组基础上实现的附加功能示例代码。
import java.util.Scanner;
public class TicTacToe {
public static void main(String[] args) {
char[] board = {'.', '.', '.', '.', '.', '.', '.', '.', '.'};
int availablePositions = 9;
Scanner scanner = new Scanner(System.in);
printBoard(board);
while (availablePositions > 0) {
int nextPosition = getNextPosition(scanner, board);
board[nextPosition] = (availablePositions % 2 == 1) ? 'O' : 'X';
availablePositions -= 1;
printBoard(board);
if (checkGameOver(board, nextPosition, availablePositions)) return;
}
}
/**
* Read next position from stdin.
*/
private static int getNextPosition(Scanner scanner, char[] board) {
System.out.print("Next move: ");
int nextPosition = scanner.nextInt();
while (!checkIsValidInput(nextPosition, board)) {
System.out.println("Invalid input. Try again with an available position by an integer between [0, 8].");
nextPosition = scanner.nextInt();
}
return nextPosition;
}
/**
* Check if the game has ended.
*/
private static boolean checkGameOver(char[] board, int nextPosition, int availablePositions) {
boolean hasWon = checkHasWon(board, nextPosition);
// Check if the game is over
if (hasWon) {
System.out.println(((availablePositions % 2 == 0) ? 'O' : 'X') + " has won. Exit.");
return true;
} else if (availablePositions == 0) {
System.out.println("Ended in a draw. Exit.");
return true;
}
return false;
}
/**
* Check if a player has won.
*/
private static boolean checkHasWon(char[] board, int nextPosition) {
int row = nextPosition / 3, col = nextPosition % 3;
boolean hasWon = ((board[row * 3] == board[row * 3 + 1]) && (board[row * 3 + 1] == board[row * 3 + 2]))
|| ((board[col] == board[col + 3]) && (board[col + 3] == board[col + 6]))
|| ((row == col) && (board[0] == board[4]) && (board[4] == board[8]))
|| ((row == 2 - col) && (board[2] == board[4]) && (board[4] == board[6]));
return hasWon;
}
/**
* Check if input is valid.
*/
private static boolean checkIsValidInput(int nextPosition, char[] board) {
return nextPosition >= 0 && nextPosition <= 8 && board[nextPosition] == '.';
}
/**
* Print board.
*/
private static void printBoard(char[] board) {
for (int i = 0; i < 3; i++) {
String row = "" + board[i * 3] + board[i * 3 + 1] + board[i * 3 + 2];
System.out.println(row);
}
}
}
有以下地方需要同学们注意:
-
思考终局判断中下列四行的作用分别是什么?
boolean hasWon = ((board[row * 3] == board[row * 3 + 1]) && (board[row * 3 + 1] == board[row * 3 + 2])) || ((board[col] == board[col + 3]) && (board[col + 3] == board[col + 6])) || ((row == col) && (board[0] == board[4]) && (board[4] == board[8])) || ((row == 2 - col) && (board[2] == board[4]) && (board[4] == board[6]));
-
程序是如何判断能否落子的?
Metadata
Metadata
Assignees
Labels
No labels