C++ 讲解井字棋
井字棋是一种直观的双人游戏,只要两个玩家都采用最佳策略,游戏总会以平局结束。这个游戏也被称为”圈圈叉叉”或”零和叉”。
可以使用计算机或其他设备来玩井字棋游戏,通常是在纸上画出游戏棋盘。这个经典游戏也为其他游戏(如连四)奠定了基础。
井字棋的历史
公元前一世纪左右,在罗马帝国就有一种原始版本的井字棋游戏。 “terni lapilli” 这个名字意味着每次出三个小石头。在游戏中,罗马废墟上留下了用粉笔画的网格图案。古埃及的废墟也发现了该游戏的证据。
这个游戏在英国被称为 “noughts and crosses”,首次在1864年印刷物中出现。术语 “tick-tack-toe” 首次在1884年的文学作品中出现,尽管它指的是在一个小黑板上玩的儿童游戏。
游戏规则
- 游戏必须由两名玩家(在这个程序中分别是人类和电脑)进行。
- 两名玩家都使用字母 “O” 和 “X” 标记自己的格子。
- 游戏结束当一名玩家用自己的字符(’O’ 或 ‘X’)填满整行、整列或对角线时。
- 如果没有人获胜,比赛被视为平局。
程序分解
# define COMPUTER 1
# define HUMAN 2
# define SIDE 3
# define COMPUTERMOVE 'O'
# define HUMANMOVE 'X'
在这里,我们预先定义了游戏中要执行的一些移动。我们将棋盘的长度设定为3,计算机移动将用’O’表示,人类或用户用’X’表示。
void showBoard ( char board [ ] [ SIDE ] )
{
printf ( " \ n \ n " ) ;
printf ( " \ t \ t \ t % c | % c | % c \ n " , board [ 0 ] [ 0 ] , board [ 0 ] [ 1 ] , board [ 0 ] [ 2 ] ) ;
printf ( " \ t \ t \ t -------------- \ n " ) ;
printf ( " \ t \ t \ t % c | % c | % c \ n " , board [ 1 ] [ 0 ] , board [ 1 ] [ 1 ] , board [ 1 ] [ 2 ] ) ;
printf ( " \ t \ t \ t -------------- \ n " ) ;
printf ( " \ t \ t \ t % c | % c | % c \ n \ n " , board [ 2 ] [ 0 ] , board [ 2 ] [ 1 ] , board [ 2 ] [ 2 ] ) ;
return ;
}
上述的函数即 void showBoard 将展示我们的一个大小为 3X3 的棋盘的当前状态,并且我们已经初始化它为数字 1 到 9 来表示。
void showInstructions ( )
{
printf ( " \ t \ t \ t Tic-Tac-Toe \ n \ n " ) ;
printf ( " Choose a cell numbered from 1 to 9 as below and play \ n \ n " ) ;
printf ( " \ t \ t \ t 1 | 2 | 3 \ n " ) ;
printf ( " \ t \ t \ t -------------- \ n " ) ;
printf ( " \ t \ t \ t 4 | 5 | 6 \ n " ) ;
printf ( " \ t \ t \ t -------------- \ n " ) ;
printf ( " \ t \ t \ t 7 | 8 | 9 \ n \ n " ) ;
printf ( " - \ t - \ t - \ t - \ t - \ t- \ t - \ t - \ t - \ t - \ n \ n " ) ;
return ;
}
上述功能即void showInstruction将展示如下的指令,并进行游戏:选择一个从1到9编号的单元格。
void initialize ( char board [ ] [ SIDE ] , int moves [ ] )
{
// Initiate the random number generator so that
// the same configuration doesn't arises
srand ( time ( NULL ) ) ;
// Initially the board is empty
for ( int i = 0 ; i < SIDE ; i + + )
{
for (int j = 0 ; j < SIDE ; j + + )
board [ i ] [ j ] = ' ' ;
}
// Fill the moves with numbers
for ( int i = 0 ; i < SIDE * SIDE ; i + + )
moves [ i ] = I ;
// randomise the moves
random_shuffle ( moves , moves + SIDE * SIDE ) ;
return ;
}
上述的函数即void initialize是用于初始化随机数,以避免出现相同的配置,同时初始棋盘为空。
void declareWinner ( int whoseTurn )
{
if ( whoseTurn == COMPUTER )
printf ( " COMPUTER has won \ n " ) ;
else
printf ( " HUMAN has won \ n " ) ;
return ;
}
这个函数,即void declareWinner,将宣布游戏的赢家,无论是电脑赢还是人类赢得游戏。
bool rowCrossed ( char board [ ] [ SIDE ] )
{
for ( int i=0 ; i < SIDE ; i + + )
{
if (board [ i ] [ 0 ] == board [ i ] [ 1 ] &&
board [ i ] [ 1 ] == board [ i ] [ 2 ] &&
board [ i ] [ 0 ] != ' ' )
return ( true ) ;
}
return ( false ) ;
}
上面的函数即bool rowCrossed,如果任何一行与玩家的移动交叉,将返回true或false。
bool columnCrossed ( char board [ ] [ SIDE ] )
{
for (int i = 0 ; i < SIDE ; i + + )
{
if (board [ 0 ] [ i ] == board [ 1 ] [ i ] &&
board [ 1 ] [ i ] == board [ 2 ] [ i ] &&
board [ 0 ] [ i ] != ' ' )
return ( true ) ;
}
return ( false ) ;
}
以上函数即bool columnCrossed,如果任何一列与玩家的移动交叉,将返回true或false。
bool diagonalCrossed(char board[][SIDE])
{
if (board[0][0] == board[1][1] &&
board[1][1] == board[2][2] &&
board[0][0] != ' ')
return(true);
if (board[0][2] == board[1][1] &&
board[1][1] == board[2][0] &&
board[0][2] != ' ')
return(true);
return(false);
}
上述函数即bool diagonalCrossed将根据玩家的移动是否与任何对角线相交来返回true或false。
bool gameOver ( char board [ ] [ SIDE ] )
{
return ( rowCrossed ( board ) || columnCrossed ( board ) || diagonalCrossed ( board ) ) ;
}
上述函数即bool gameOver会在游戏结束时返回true或false。
void playTicTacToe ( int whoseTurn )
{
// A 3 * 3 Tic-Tac-Toe board for playing
char board [ SIDE ] [ SIDE ] ;
int moves [ SIDE * SIDE ] ;
// Initialise the game
initialise ( board , moves ) ;
// Show the instructions before playing
showInstructions ( ) ;
int moveIndex = 0 , x , y ;
// Keep playing till the game is over or it is a draw
while ( gameOver ( board ) == false &&
moveIndex != SIDE * SIDE )
{
if ( whoseTurn == COMPUTER )
{
x = moves [ moveIndex ] / SIDE ;
y = moves [ moveIndex ] % SIDE ;
board [ x ] [ y ] = COMPUTERMOVE ;
printf ( " COMPUTER has put a % c in cell % d \ n " , COMPUTERMOVE , moves [ moveIndex ] + 1 ) ;
showBoard ( board ) ;
moveIndex + + ;
whoseTurn = HUMAN ;
}
else if ( whoseTurn == HUMAN )
{
x = moves [ moveIndex ] / SIDE ;
y = moves [ moveIndex ] % SIDE ;
board [ x ] [ y ] = HUMANMOVE ;
printf ( " HUMAN has put a % c in cell % d \ n " , HUMANMOVE , moves [ moveIndex] + 1 ) ;
showBoard ( board ) ;
moveIndex + + ;
whoseTurn = COMPUTER ;
}
}
// If the game has drawn
if ( gameOver ( board ) == false && moveIndex == SIDE * SIDE )
printf ( " It's a draw \ n " ) ;
else
{
// Toggling the user to declare the actual
// winner
if ( whoseTurn == COMPUTER )
whoseTurn = HUMAN ;
else if ( whoseTurn == HUMAN )
whoseTurn = COMPUTER ;
// Declare the winner
declareWinner ( whoseTurn ) ;
}
上述函数即void playTictacToe将是玩井字游戏的主要函数。
C++中的井字游戏程序
// A C++ Program to play tic-tac-toe
#include
using namespace std;
#define COMPUTER 1
#define HUMAN 2
#define SIDE 3 // Length of the board
// Computer will move with 'O'
// and human with 'X'
#define COMPUTERMOVE 'O'
#define HUMANMOVE 'X'
// A function to show the current board status
void showBoard(char board[][SIDE])
{
printf("\n\n");
printf("\t\t\t %c | %c | %c \n", board[0][0],
board[0][1], board[0][2]);
printf("\t\t\t--------------\n");
printf("\t\t\t %c | %c | %c \n", board[1][0],
board[1][1], board[1][2]);
printf("\t\t\t--------------\n");
printf("\t\t\t %c | %c | %c \n\n", board[2][0],
board[2][1], board[2][2]);
return;
}
// A function to show the instructions
void showInstructions()
{
printf("\t\t\t Tic-Tac-Toe\n\n");
printf("Choose a cell numbered from 1 to 9 as below"
" and play\n\n");
printf("\t\t\t 1 | 2 | 3 \n");
printf("\t\t\t--------------\n");
printf("\t\t\t 4 | 5 | 6 \n");
printf("\t\t\t--------------\n");
printf("\t\t\t 7 | 8 | 9 \n\n");
printf("-\t-\t-\t-\t-\t-\t-\t-\t-\t-\n\n");
return;
}
// A function to initialise the game
void initialise(char board[][SIDE], int moves[])
{
// Initiate the random number generator so that
// the same configuration doesn't arises
srand(time(NULL));
// Initially the board is empty
for (int i = 0; i < SIDE; i++)
{
for (int j = 0; j < SIDE; j++)
board[i][j] = ' ';
}
// Fill the moves with numbers
for (int i = 0; i < SIDE * SIDE; i++)
moves[i] = i;
// randomise the moves
random_shuffle(moves, moves + SIDE * SIDE);
return;
}
// A function to declare the winner of the game
void declareWinner(int whoseTurn)
{
if (whoseTurn == COMPUTER)
printf("COMPUTER has won\n");
else
printf("HUMAN has won\n");
return;
}
// A function that returns true if any of the row
// is crossed with the same player's move
bool rowCrossed(char board[][SIDE])
{
for (int i = 0; i < SIDE; i++)
{
if (board[i][0] == board[i][1] &&
board[i][1] == board[i][2] &&
board[i][0] != ' ')
return (true);
}
return (false);
}
// A function that returns true if any of the column
// is crossed with the same player's move
bool columnCrossed(char board[][SIDE])
{
for (int i = 0; i < SIDE; i++)
{
if (board[0][i] == board[1][i] &&
board[1][i] == board[2][i] &&
board[0][i] != ' ')
return (true);
}
return (false);
}
// A function that returns true if any of the diagonal
// is crossed with the same player's move
bool diagonalCrossed(char board[][SIDE])
{
if (board[0][0] == board[1][1] &&
board[1][1] == board[2][2] &&
board[0][0] != ' ')
return (true);
if (board[0][2] == board[1][1] &&
board[1][1] == board[2][0] &&
board[0][2] != ' ')
return (true);
return (false);
}
// A function that returns true if the game is over
// else it returns a false
bool gameOver(char board[][SIDE])
{
return (rowCrossed(board) || columnCrossed(board) || diagonalCrossed(board));
}
// A function to play Tic-Tac-Toe
void playTicTacToe(int whoseTurn)
{
// A 3*3 Tic-Tac-Toe board for playing
char board[SIDE][SIDE];
int moves[SIDE * SIDE];
// Initialise the game
initialise(board, moves);
// Show the instructions before playing
showInstructions();
int moveIndex = 0, x, y;
// Keep playing till the game is over or it is a draw
while (gameOver(board) == false &&
moveIndex != SIDE * SIDE)
{
if (whoseTurn == COMPUTER)
{
x = moves[moveIndex] / SIDE;
y = moves[moveIndex] % SIDE;
board[x][y] = COMPUTERMOVE;
printf("COMPUTER has put a %c in cell %d\n",
COMPUTERMOVE, moves[moveIndex] + 1);
showBoard(board);
moveIndex++;
whoseTurn = HUMAN;
}
else if (whoseTurn == HUMAN)
{
x = moves[moveIndex] / SIDE;
y = moves[moveIndex] % SIDE;
board[x][y] = HUMANMOVE;
printf("HUMAN has put a %c in cell %d\n",
HUMANMOVE, moves[moveIndex] + 1);
showBoard(board);
moveIndex++;
whoseTurn = COMPUTER;
}
}
// If the game has drawn
if (gameOver(board) == false &&
moveIndex == SIDE * SIDE)
printf("It's a draw\n");
else
{
// Toggling the user to declare the actual
// winner
if (whoseTurn == COMPUTER)
whoseTurn = HUMAN;
else if (whoseTurn == HUMAN)
whoseTurn = COMPUTER;
// Declare the winner
declareWinner(whoseTurn);
}
return;
}
// Driver program
int main()
{
// Let us play the game with COMPUTER starting first
playTicTacToe(COMPUTER);
return (0);
}
输出:
Tic-Tac-Toe
Choose a cell numbered from 1 to 9 as below and play
1 | 2 | 3
- - - - - - - -
4 | 5 | 6
- - - - - - - -
7 | 8 | 9
- - - - - - - - - -
COMPUTER has put a O in cell 6
| |
- - - - - - - -
| | O
- - - - - - - -
| |
HUMAN has put a X in cell 7
| |
- - - - - - - -
| | O
- - - - - - - -
X | |
COMPUTER has put a O in cell 5
| |
- - - - - - - -
| O | O
- - - - - - - -
X | |
HUMAN has put a X in cell 1
X | |
- - - - - - - -
| O | O
- - - - - - - -
X | |
COMPUTER has put a O in cell 9
X | |
- - - - - - - -
| O | O
- - - - - - - -
X | | O
HUMAN has put a X in cell 8
X | |
- - - - - - - -
| O | O
- - - - - - - -
X | X | O
COMPUTER has put a O in cell 4
X | |
- - - - - - - -
O | O | O
- - - - - - - -
X | X | O
COMPUTER has won