저는 Java로 tic-tac-toe 게임을 작성했으며 현재 게임의 끝을 결정하는 방법은 게임이 끝날 때 다음과 같은 가능한 시나리오를 설명합니다.
- 보드가 꽉 찼고 아직 승자가 선언되지 않았습니다. 게임은 무승부입니다.
- 크로스가 이겼습니다.
- 서클이 이겼습니다.
안타깝게도이를 위해 테이블에서 이러한 시나리오의 미리 정의 된 집합을 읽습니다. 보드에 9 개의 공간 만 있고 따라서 테이블이 다소 작다는 점을 고려하면 이것이 반드시 나쁘지는 않지만 게임이 끝났는지 확인하는 더 나은 알고리즘 방식이 있습니까? 누군가가 이겼는지 여부를 결정하는 것은 문제의 핵심입니다. 9 칸이 꽉 찼는 지 확인하는 것은 사소하기 때문입니다.
테이블 방법이 해결책이 될 수 있지만 그렇지 않은 경우 무엇입니까? 또한 보드가 크기가 아니라면 n=9
어떻게 될까요? 그것은 무엇 말하자면 훨씬 더 큰 보드된다면 n=16
, n=25
등 연속적으로 배치 항목의 수를 원인에로 승리 x=4
, x=5
등? 모두를 위해 사용하는 일반적인 알고리즘 n = { 9, 16, 25, 36 ... }
?
답변
이기는 이동은 X 또는 O가 가장 최근에 이동 한 후에 만 발생할 수 있으므로 해당 이동에 포함 된 선택적 진단을 사용하여 행 / 열만 검색하여 승리 한 보드를 결정하려고 할 때 검색 공간을 제한 할 수 있습니다. 또한 무승부 틱택 토 게임에는 고정 된 수의 무브가 있기 때문에 마지막 무브가 이루어지면이기는 무브가 아니라면 기본적으로 무승부 게임입니다.
편집 :이 코드는 n x n 보드를위한 것입니다 (3×3 보드는 연속 3을 필요로 함).
편집 : 안티 진단을 확인하는 코드 추가, 포인트가 안티 진단에 있는지 확인하는 비 루프 방법을 알아낼 수 없으므로 그 단계가 누락되었습니다.
public class TripleT {
enum State{Blank, X, O};
int n = 3;
State[][] board = new State[n][n];
int moveCount;
void Move(int x, int y, State s){
if(board[x][y] == State.Blank){
board[x][y] = s;
}
moveCount++;
//check end conditions
//check col
for(int i = 0; i < n; i++){
if(board[x][i] != s)
break;
if(i == n-1){
//report win for s
}
}
//check row
for(int i = 0; i < n; i++){
if(board[i][y] != s)
break;
if(i == n-1){
//report win for s
}
}
//check diag
if(x == y){
//we're on a diagonal
for(int i = 0; i < n; i++){
if(board[i][i] != s)
break;
if(i == n-1){
//report win for s
}
}
}
//check anti diag (thanks rampion)
if(x + y == n - 1){
for(int i = 0; i < n; i++){
if(board[i][(n-1)-i] != s)
break;
if(i == n-1){
//report win for s
}
}
}
//check draw
if(moveCount == (Math.pow(n, 2) - 1)){
//report draw
}
}
}
답변
행, 열 또는 diag의 합이 15가되고 플레이어가이긴 경우 매직 스퀘어 http://mathworld.wolfram.com/MagicSquare.html을 사용할 수 있습니다 .
답변
이 의사 코드는 어떻습니까?
플레이어가 (x, y) 위치에 말을 내려 놓은 후 :
col=row=diag=rdiag=0
winner=false
for i=1 to n
if cell[x,i]=player then col++
if cell[i,y]=player then row++
if cell[i,i]=player then diag++
if cell[i,n-i+1]=player then rdiag++
if row=n or col=n or diag=n or rdiag=n then winner=true
나는 문자 [n, n]의 배열을 사용하고, O, X와 빈 공간을 사용합니다.
- 단순한.
- 하나의 루프.
- 5 개의 간단한 변수 : 4 개의 정수와 1 개의 부울.
- n의 모든 크기로 확장됩니다.
- 현재 조각 만 확인합니다.
- 마법이 아닙니다. 🙂
답변
이것은 Osama ALASSIRY의 답변 과 유사 하지만 선형 공간 및 상수 시간에 대해 상수 공간 및 선형 시간을 교환합니다. 즉, 초기화 후 루핑이 없습니다.
(0,0)
각 행, 각 열 및 두 개의 대각선 (대각선 및 대각선)에 대한 쌍 을 초기화합니다 . 이 쌍은 (sum,sum)
해당 행, 열 또는 대각선에있는 조각 의 누적 을 나타냅니다 .
플레이어 A의 조각은 (1,0) 값을가집니다. 플레이어 B의 조각은 (0,1) 값을가집니다.
플레이어가 말을 놓을 때 해당 행 쌍, 열 쌍 및 대각선 쌍을 업데이트합니다 (대각선에있는 경우). 새로 업데이트 된 행, 열, 대각선 쌍 중 하나에 해당하는 경우 (n,0)
또는 (0,n)
다음 A 또는 B 중 각각 받았다.
점근 분석 :
O (1) 시간 (이동 당) O (n) 공간 (전체)
메모리 사용을 위해 4*(n+1)
정수 를 사용 합니다.
two_elements * n_rows + two_elements * n_columns + two_elements * two_diagonals = 4 * n + 4 정수 = 4 (n + 1) 정수
운동 : 이동 당 O (1) 시간으로 무승부를 테스트하는 방법을 알 수 있습니까? 그렇다면 무승부에서 게임을 일찍 끝낼 수 있습니다.
답변
내가 자바 스크립트에서 작업중 인 프로젝트를 위해 작성한 솔루션입니다. 몇 가지 어레이의 메모리 비용에 신경 쓰지 않는다면 아마도 가장 빠르고 간단한 솔루션 일 것입니다. 마지막 움직임의 위치를 알고 있다고 가정합니다.
/*
* Determines if the last move resulted in a win for either player
* board: is an array representing the board
* lastMove: is the boardIndex of the last (most recent) move
* these are the boardIndexes:
*
* 0 | 1 | 2
* ---+---+---
* 3 | 4 | 5
* ---+---+---
* 6 | 7 | 8
*
* returns true if there was a win
*/
var winLines = [
[[1, 2], [4, 8], [3, 6]],
[[0, 2], [4, 7]],
[[0, 1], [4, 6], [5, 8]],
[[4, 5], [0, 6]],
[[3, 5], [0, 8], [2, 6], [1, 7]],
[[3, 4], [2, 8]],
[[7, 8], [2, 4], [0, 3]],
[[6, 8], [1, 4]],
[[6, 7], [0, 4], [2, 5]]
];
function isWinningMove(board, lastMove) {
var player = board[lastMove];
for (var i = 0; i < winLines[lastMove].length; i++) {
var line = winLines[lastMove][i];
if(player === board[line[0]] && player === board[line[1]]) {
return true;
}
}
return false;
}
답변
방금 C 프로그래밍 수업을 위해 작성했습니다.
여기에있는 다른 예는 어떤 크기의 직사각형 그리드 에서도 작동하지 않기 때문에 게시하고 있으며 , N -in-a-row 연속 표시를 이길 수 있습니다.
checkWinner()
함수 에서 내 알고리즘을 찾을 수 있습니다. 승자를 확인하기 위해 매직 넘버 나 멋진 것을 사용하지 않고, 단순히 4 개의 for 루프를 사용합니다. 코드는 잘 주석 처리되어 있으므로 스스로 말하도록하겠습니다.
// This program will work with any whole number sized rectangular gameBoard.
// It checks for N marks in straight lines (rows, columns, and diagonals).
// It is prettiest when ROWS and COLS are single digit numbers.
// Try altering the constants for ROWS, COLS, and N for great fun!
// PPDs come first
#include <stdio.h>
#define ROWS 9 // The number of rows our gameBoard array will have
#define COLS 9 // The number of columns of the same - Single digit numbers will be prettier!
#define N 3 // This is the number of contiguous marks a player must have to win
#define INITCHAR ' ' // This changes the character displayed (a ' ' here probably looks the best)
#define PLAYER1CHAR 'X' // Some marks are more aesthetically pleasing than others
#define PLAYER2CHAR 'O' // Change these lines if you care to experiment with them
// Function prototypes are next
int playGame (char gameBoard[ROWS][COLS]); // This function allows the game to be replayed easily, as desired
void initBoard (char gameBoard[ROWS][COLS]); // Fills the ROWSxCOLS character array with the INITCHAR character
void printBoard (char gameBoard[ROWS][COLS]); // Prints out the current board, now with pretty formatting and #s!
void makeMove (char gameBoard[ROWS][COLS], int player); // Prompts for (and validates!) a move and stores it into the array
int checkWinner (char gameBoard[ROWS][COLS], int player); // Checks the current state of the board to see if anyone has won
// The starting line
int main (void)
{
// Inits
char gameBoard[ROWS][COLS]; // Our gameBoard is declared as a character array, ROWS x COLS in size
int winner = 0;
char replay;
//Code
do // This loop plays through the game until the user elects not to
{
winner = playGame(gameBoard);
printf("\nWould you like to play again? Y for yes, anything else exits: ");
scanf("%c",&replay); // I have to use both a scanf() and a getchar() in
replay = getchar(); // order to clear the input buffer of a newline char
// (http://cboard.cprogramming.com/c-programming/121190-problem-do-while-loop-char.html)
} while ( replay == 'y' || replay == 'Y' );
// Housekeeping
printf("\n");
return winner;
}
int playGame(char gameBoard[ROWS][COLS])
{
int turn = 0, player = 0, winner = 0, i = 0;
initBoard(gameBoard);
do
{
turn++; // Every time this loop executes, a unique turn is about to be made
player = (turn+1)%2+1; // This mod function alternates the player variable between 1 & 2 each turn
makeMove(gameBoard,player);
printBoard(gameBoard);
winner = checkWinner(gameBoard,player);
if (winner != 0)
{
printBoard(gameBoard);
for (i=0;i<19-2*ROWS;i++) // Formatting - works with the default shell height on my machine
printf("\n"); // Hopefully I can replace these with something that clears the screen for me
printf("\n\nCongratulations Player %i, you've won with %i in a row!\n\n",winner,N);
return winner;
}
} while ( turn < ROWS*COLS ); // Once ROWS*COLS turns have elapsed
printf("\n\nGame Over!\n\nThere was no Winner :-(\n"); // The board is full and the game is over
return winner;
}
void initBoard (char gameBoard[ROWS][COLS])
{
int row = 0, col = 0;
for (row=0;row<ROWS;row++)
{
for (col=0;col<COLS;col++)
{
gameBoard[row][col] = INITCHAR; // Fill the gameBoard with INITCHAR characters
}
}
printBoard(gameBoard); // Having this here prints out the board before
return; // the playGame function asks for the first move
}
void printBoard (char gameBoard[ROWS][COLS]) // There is a ton of formatting in here
{ // That I don't feel like commenting :P
int row = 0, col = 0, i=0; // It took a while to fine tune
// But now the output is something like:
printf("\n"); //
// 1 2 3
for (row=0;row<ROWS;row++) // 1 | |
{ // -----------
if (row == 0) // 2 | |
{ // -----------
printf(" "); // 3 | |
for (i=0;i<COLS;i++)
{
printf(" %i ",i+1);
}
printf("\n\n");
}
for (col=0;col<COLS;col++)
{
if (col==0)
printf("%i ",row+1);
printf(" %c ",gameBoard[row][col]);
if (col<COLS-1)
printf("|");
}
printf("\n");
if (row < ROWS-1)
{
for(i=0;i<COLS-1;i++)
{
if(i==0)
printf(" ----");
else
printf("----");
}
printf("---\n");
}
}
return;
}
void makeMove (char gameBoard[ROWS][COLS],int player)
{
int row = 0, col = 0, i=0;
char currentChar;
if (player == 1) // This gets the correct player's mark
currentChar = PLAYER1CHAR;
else
currentChar = PLAYER2CHAR;
for (i=0;i<21-2*ROWS;i++) // Newline formatting again :-(
printf("\n");
printf("\nPlayer %i, please enter the column of your move: ",player);
scanf("%i",&col);
printf("Please enter the row of your move: ");
scanf("%i",&row);
row--; // These lines translate the user's rows and columns numbering
col--; // (starting with 1) to the computer's (starting with 0)
while(gameBoard[row][col] != INITCHAR || row > ROWS-1 || col > COLS-1) // We are not using a do... while because
{ // I wanted the prompt to change
printBoard(gameBoard);
for (i=0;i<20-2*ROWS;i++)
printf("\n");
printf("\nPlayer %i, please enter a valid move! Column first, then row.\n",player);
scanf("%i %i",&col,&row);
row--; // See above ^^^
col--;
}
gameBoard[row][col] = currentChar; // Finally, we store the correct mark into the given location
return; // And pop back out of this function
}
int checkWinner(char gameBoard[ROWS][COLS], int player) // I've commented the last (and the hardest, for me anyway)
{ // check, which checks for backwards diagonal runs below >>>
int row = 0, col = 0, i = 0;
char currentChar;
if (player == 1)
currentChar = PLAYER1CHAR;
else
currentChar = PLAYER2CHAR;
for ( row = 0; row < ROWS; row++) // This first for loop checks every row
{
for ( col = 0; col < (COLS-(N-1)); col++) // And all columns until N away from the end
{
while (gameBoard[row][col] == currentChar) // For consecutive rows of the current player's mark
{
col++;
i++;
if (i == N)
{
return player;
}
}
i = 0;
}
}
for ( col = 0; col < COLS; col++) // This one checks for columns of consecutive marks
{
for ( row = 0; row < (ROWS-(N-1)); row++)
{
while (gameBoard[row][col] == currentChar)
{
row++;
i++;
if (i == N)
{
return player;
}
}
i = 0;
}
}
for ( col = 0; col < (COLS - (N-1)); col++) // This one checks for "forwards" diagonal runs
{
for ( row = 0; row < (ROWS-(N-1)); row++)
{
while (gameBoard[row][col] == currentChar)
{
row++;
col++;
i++;
if (i == N)
{
return player;
}
}
i = 0;
}
}
// Finally, the backwards diagonals:
for ( col = COLS-1; col > 0+(N-2); col--) // Start from the last column and go until N columns from the first
{ // The math seems strange here but the numbers work out when you trace them
for ( row = 0; row < (ROWS-(N-1)); row++) // Start from the first row and go until N rows from the last
{
while (gameBoard[row][col] == currentChar) // If the current player's character is there
{
row++; // Go down a row
col--; // And back a column
i++; // The i variable tracks how many consecutive marks have been found
if (i == N) // Once i == N
{
return player; // Return the current player number to the
} // winnner variable in the playGame function
} // If it breaks out of the while loop, there weren't N consecutive marks
i = 0; // So make i = 0 again
} // And go back into the for loop, incrementing the row to check from
}
return 0; // If we got to here, no winner has been detected,
} // so we pop back up into the playGame function
// The end!
// Well, almost.
// Eventually I hope to get this thing going
// with a dynamically sized array. I'll make
// the CONSTANTS into variables in an initGame
// function and allow the user to define them.
답변
보드가 n × n 이면 n 개의 행, n 개의 열, 2 개의 대각선이 있습니다. 승자를 찾기 위해 각각의 모든 X 또는 모두 O를 확인하십시오.
승리하는 데 x < n 연속 사각형 만 필요 하면 조금 더 복잡합니다. 가장 확실한 해결책은 각 x x x 사각형을 확인하는 것입니다. 이를 보여주는 코드가 있습니다.
(사실이 * 기침 *을 테스트하지 못했지만, 그것은 않았다 첫 번째 시도에서 컴파일, 나를 야호!)
public class TicTacToe
{
public enum Square { X, O, NONE }
/**
* Returns the winning player, or NONE if the game has
* finished without a winner, or null if the game is unfinished.
*/
public Square findWinner(Square[][] board, int lengthToWin) {
// Check each lengthToWin x lengthToWin board for a winner.
for (int top = 0; top <= board.length - lengthToWin; ++top) {
int bottom = top + lengthToWin - 1;
for (int left = 0; left <= board.length - lengthToWin; ++left) {
int right = left + lengthToWin - 1;
// Check each row.
nextRow: for (int row = top; row <= bottom; ++row) {
if (board[row][left] == Square.NONE) {
continue;
}
for (int col = left; col <= right; ++col) {
if (board[row][col] != board[row][left]) {
continue nextRow;
}
}
return board[row][left];
}
// Check each column.
nextCol: for (int col = left; col <= right; ++col) {
if (board[top][col] == Square.NONE) {
continue;
}
for (int row = top; row <= bottom; ++row) {
if (board[row][col] != board[top][col]) {
continue nextCol;
}
}
return board[top][col];
}
// Check top-left to bottom-right diagonal.
diag1: if (board[top][left] != Square.NONE) {
for (int i = 1; i < lengthToWin; ++i) {
if (board[top+i][left+i] != board[top][left]) {
break diag1;
}
}
return board[top][left];
}
// Check top-right to bottom-left diagonal.
diag2: if (board[top][right] != Square.NONE) {
for (int i = 1; i < lengthToWin; ++i) {
if (board[top+i][right-i] != board[top][right]) {
break diag2;
}
}
return board[top][right];
}
}
}
// Check for a completely full board.
boolean isFull = true;
full: for (int row = 0; row < board.length; ++row) {
for (int col = 0; col < board.length; ++col) {
if (board[row][col] == Square.NONE) {
isFull = false;
break full;
}
}
}
// The board is full.
if (isFull) {
return Square.NONE;
}
// The board is not full and we didn't find a solution.
else {
return null;
}
}
}