[英]Tic Tac Toe winning condition change when scalable board is larger than 4x4
因此,我已经制作了Tic Tac Toe程序已有一段时间了。 这是一款基本的Tic Tac Toe游戏,但游戏板可扩展。 该程序几乎完成,但是缺少一个小功能。
如果玩家在游戏板大于4x4时连续获得五个或更多标记,则必须结束游戏。
FE如果游戏板为9x9,则当玩家或计算机连续获得5个标记时,游戏必须结束。
(标记=“ O”或“ X”)。
现在,当某人连续获得等于棋盘大小的标记时,游戏结束(如果为9x9,则您需要连续获得9个标记才能获胜)。
我必须在playerHasWon
实现一项功能,而在playerHasWon
一直遇到很多麻烦。 我认为这很容易实现,但是我还没有找到实现方法。
希望我的解释很容易理解。 这是代码:
package tictac;
import java.util.Scanner;
import java.util.Random;
public class Tictac {
public static final int DRAW = 0; // game ends as a draw
public static final int COMPUTER = 1; // computer wins
public static final int PLAYER = 2; // player wins
public static final char PLAYER_MARK = 'X'; // The "X"
public static final char COMPUTER_MARK = 'O'; // The "O"
public static int size; // size of the board
public static String[][] board; // the board itself
public static int score = 0; // game win score
public static Scanner scan = new Scanner(System.in); // scanner
/**
* Builds the board with the integer size and user input.
*
* Displays game win message and switches play turns.
*
* @param args the command line parameters. Not used.
*/
public static void main(String[] args) {
while (true) {
System.out.println("Select board size");
System.out.print("[int]: ");
try {
size = Integer.parseInt(scan.nextLine());
} catch (Exception e) {
System.out.println("You can't do that.");
continue; // after message, give player new try
}
break;
}
int[] move = {};
board = new String[size][size];
setupBoard();
int i = 1;
loop: // creates the loop
while (true) {
if (i % 2 == 1) {
displayBoard();
move = getMove();
} else {
computerTurn();
}
switch (isGameFinished(move)) {
case PLAYER:
System.err.println("YOU WIN!");
displayBoard();
break loop;
case COMPUTER:
System.err.println("COMPUTER WINS!");
displayBoard();
break loop;
case DRAW:
System.err.println("IT'S A DRAW");
displayBoard();
break loop;
}
i++;
}
}
/**
* Checks for game finish.
*
* @param args command line parameters. Not used.
*
* @return DRAW the game ends as draw.
* @return COMPUTER the game ends as computer win.
* @return PLAYERE the game ends as player win.
*/
private static int isGameFinished(int[] move) {
if (isDraw()) {
return DRAW;
} else if (playerHasWon(board, move,
Character.toString(COMPUTER_MARK))) {
return COMPUTER;
} else if (playerHasWon(board, move,
Character.toString(PLAYER_MARK))) {
return PLAYER;
}
return -1; // can't be 0 || 1 || 2
}
/**
* Checks for win for every direction on the board.
*
* @param board the game board.
* @param move move on the board.
* @param playerMark mark on the board "X" or "O".
* @return the game is won.
*/
public static boolean playerHasWon(String[][] board, int[] move,
String playerMark) { //playermark x || o
// horizontal check
for (int i = 0; i < size; i++) {
if (board[i][0].equals(playerMark)) {
int j;
for (j = 1; j < size; j++) {
if (!board[i][j].equals(playerMark)) {
break;
}
}
if (j == size) {
return true;
}
}
}
// vertical check
for (int i = 0; i < size; i++) {
if (board[0][i].equals(playerMark)) {
int j;
for (j = 1; j < size; j++) {
if (!board[j][i].equals(playerMark)) {
break;
}
}
if (j == size) {
return true;
}
}
}
// diagonals check
int i;
for (i = 0; i < size; i++) {
if (!board[i][i].equals(playerMark)) {
break;
}
}
if (i == size) {
return true;
}
for (i = 0; i < size; i++) {
if (!board[i][(size - 1) - i].equals(playerMark)) {
break;
}
}
return i == size;
}
/**
* Checks for draws.
*
* @return if this game is a draw.
*/
public static boolean isDraw() {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (board[i][j] == " ") {
return false;
}
}
}
return true;
}
/**
* Displays the board.
*
*
*/
public static void displayBoard() {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
System.out.printf("[%s]", board[i][j]);
}
System.out.println();
}
}
/**
* Displays the board.
*
*
*/
public static void setupBoard() {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
board[i][j] = " ";
}
}
}
/**
* Takes in user input and sends it to isValidPlay.
*
* @return null.
*/
public static int[] getMove() {
Scanner sc = new Scanner(System.in);
System.out.println("Your turn:");
while (true) {
try {
System.out.printf("ROW: [0-%d]: ", size - 1);
int x = Integer.parseInt(sc.nextLine());
System.out.printf("COL: [0-%d]: ", size - 1);
int y = Integer.parseInt(sc.nextLine());
if (isValidPlay(x, y)) {
board[x][y] = "" + PLAYER_MARK;
return new int[]{x, y};
} else { // if input is unallowed
System.out.println("You can't do that");
continue; // after message, give player new try
}
} catch (Exception e) {
System.out.println("You can't do that.");
}
return null;
}
}
/*
* Randomizes computer's turn, where it inputs the mark 'O'.
*
*
*/
public static void computerTurn() {
Random rgen = new Random(); // Random number generator
while (true) {
int x = (int) (Math.random() * size);
int y = (int) (Math.random() * size);
if (isValidPlay(x, y)) {
board[x][y] = "" + COMPUTER_MARK;
break;
}
}
}
/**
* Checks if a move is possible.
*
* @param inX x-move is out of bounds.
* @param inY y-move is out of bounds.
* @return false
*/
public static boolean isValidPlay(int inX, int inY) {
// Play is out of bounds and thus not valid.
if ((inX >= size) || (inY >= size)) {
return false;
}
// Checks if a play have already been made at the location,
// and the location is thus invalid.
return (board[inX][inY] == " ");
}
}
// End of file
快速浏览一下,发现了问题并提出了快速解决方案:
public static boolean checkDiagonal(String markToLook) {
// how many marks are we looking for in row?
int sizeToWin = Math.min(size, 5);
// running down and right
// don't need to iterate rows that can't be the starting point
// of a winning diagonal formation, thus can exlude some with
// row < (size - (sizeToWin - 1))
for (int row = 0; row < (size - (sizeToWin - 1)); row++) {
for (int col = 0; col < size; col++) {
int countOfMarks = 0;
// down and right
for (int i = row; i < size; i++) {
if (board[i][i] == null ? markToLook == null :
board[i][i].equals(markToLook)) {
countOfMarks++;
if (countOfMarks >= sizeToWin) {
return true;
}
}
}
countOfMarks = 0;
// down and left
for (int i = row; i < size; i++) {
if (board[i][size - 1 - i] == null ? markToLook == null :
board[i][size - 1 - i].equals(markToLook)) {
countOfMarks++;
if (countOfMarks >= sizeToWin) {
return true;
}
}
}
}
}
return false;
}
然后从PlayerHasWon方法调用它,而不是在那里执行检查。 基本上,我们在板上对每个可能的起始正方形进行迭代以形成对角线获胜的阵型,然后对每个正方形向下/向左和向下/向右进行检查。
我很着急,没有进行太多测试,但是将在几个小时后返回以改进此解决方案。 似乎可以工作。
编辑:我发现以前的解决方案缺少进一步的测试,我已经将上面的代码更新为所需的功能。
首先,我认为playerMark
应该是char
而不是String
。 就是说,让我们去寻找答案。 “水平”情况将是:
// This is the number of marks in a row required to win
// Adjust formula if necessary
final int required = size > 4 ? 5 : 3;
for (int i = 0; i < size; i++) {
int currentScore = 0;
for (j = 0; j < size; j++) {
if (board[i][j].equals(playerMark)) {
currentScore++;
if (currentScore >= required)
return true;
}
else {
currentScore = 0;
}
}
}
}
纵向情况将是类似的。 对角线一个比较棘手,因为现在它的主对角线需要board[i][i+k]
,而第二个对角线则需要board[i][ki]
。 并且可能要遍历的k
和i
值并不明显。 这是我的尝试(水平情况下required
变量):
注意:从现在到2015年12月16日,所有内容已被完全重写。 以前的版本不起作用,并且未解释该算法。
经过两次失败的尝试后,我决定做作业并实际上整理一下事情,而不是脑子里想着可以跟踪所有变量。 结果是这样的图片:
主对角线涂成蓝色,次对角线涂成绿色。 每个对角线由k
值标识,其中k=0
始终是每个集合中最长的对角线。 k
值随着对角线的向下移动而增加,因此最长的一个对角线的对角线为负k
而最长的一个对角线的对角线为正k
。
两个对角线都成立的东西:
size-abs(k)
元素。 size-abs(k)
小于required
对角线无需搜索。 这意味着,对于电路板尺寸size
和所需的长度required
,我们将搜索的价值k
从required-size
,以size-required
。 请注意,它们具有相同的绝对值,第一个是<=0
,第二个是>=0
。 仅当required==size
,即当我们需要整个对角线来要求胜利时,即当我们仅需要搜索k=0
时,这些值都为零。 k<=0
, i
(行)的可能值从0
变为size+k
。 大于或等于size+k
穿过板的右边缘,因此在板的外部。 k>=0
, i
(行)的可能值从k
变为size
。 低于k
值穿过板的左边缘,因此在板外部。 仅适用于主要(蓝色)对角线:
j
(列)的值为k+i
。 仅对于次要(绿色)对角线:
j
(列)的值为size-1+ki
。 如果这不太明显,请选择右上角(k=0,i=0)
并注意j=size-1
。 然后注意,在k
上加1
(保持i
不变)总是将j
向右移动1
(如果从k=0,i=0
完成,它将出局,只考虑水平线i=0
与对角线的交点) k=1
),然后在i
加1
(保持k
不变),则j
总是向左移动1
。 修饰代码为://主对角线
for (int k = required - size; k < size - required; k++)
{
int currentScore = 0;
startI = Math.max (0, k);
endI = Math.min (size, size+k);
for (int i = startI, i < endI; i++)
{
if (board[i][k+i].equals (playerMark))
{
currentScore++;
if (currentScore >= required)
return true;
}
else
currentScore = 0;
}
}
// Secondary diagonal
for (int k = required - size; k < size - required; k++)
{
int currentScore = 0;
startI = Math.max (0, k);
endI = Math.min (size, size+k);
for (int i = startI, i < endI; i++)
{
if (board[i][size-1+k-i].equals (playerMark))
{
currentScore++;
if (currentScore >= required)
return true;
}
else
currentScore = 0;
}
}
此时,两种情况下的代码几乎相同,仅更改board[i][j]
的j
索引。 实际上,两个循环可以合并,只需要保留两个currentScore
变量即可,一个变量用于主(蓝色)对角线,另一个变量用于辅助(绿色)对角线。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.