介绍
在这个项目中,你将学习如何用 C 语言创建一个简单的井字棋游戏。游戏将由两名玩家轮流在一个 3x3 的网格中标记空格。第一个在一行、一列或一条对角线上有三个标记的玩家就是赢家。如果所有空格都被填满,且没有玩家在一行中有三个标记,则游戏平局。
👀 预览

🎯 任务
在这个项目中,你将学习:
- 如何创建一个游戏棋盘并用空格初始化它。
- 如何实现清除屏幕、显示游戏棋盘以及检查游戏是否结束的函数。
- 如何确定游戏的赢家。
- 如何实现主游戏循环,让玩家轮流与游戏进行交互。
🏆 成果
完成这个项目后,你将能够:
- 在 C 语言中创建和操作数组。
- 使用循环和条件语句来实现游戏逻辑。
- 通过命令行界面与用户进行交互。
- 将代码组织成函数,以提高模块化和可读性。
创建项目文件
首先,创建一个名为tictactoe.c的新文件,并在你喜欢的代码编辑器中打开它。
cd ~/project
touch tictactoe.c
定义常量
现在,我们需要编写 C 代码。第一步是包含头文件:
#include <stdio.h>
#include <stdbool.h>
声明棋盘大小:
char board[3][3];
初始化游戏棋盘
用空格初始化游戏棋盘。
void initializeBoard() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
board[i][j] = ' ';
}
}
}
第一个for循环是外层循环,它使用变量i从0迭代到2,表示游戏棋盘上的行数。第二个for循环是内层循环,它使用变量j从0迭代到2,依次表示游戏棋盘上的列数。
board[i][j] = ' '在内层循环中,将游戏棋盘第i行第j列的方格内容设置为空格字符(' ')。这意味着整个游戏棋盘被初始化为空状态,棋盘上没有玩家落子。
每一轮结束后清屏
实现一个在每回合后清空屏幕的函数。
void clearScreen() {
printf("\033[H\033[J");
}
clearScreen函数主要用于清除终端或控制台屏幕的内容,以清除上一回合的游戏状态和输出。这是通过向标准输出流(通常是终端窗口)打印一个特殊的转义序列来完成的,该序列会清除屏幕上的文本内容。
printf("\033[H\033[J")使用了ANSI转义序列:
\033是ASCII转义字符的八进制表示,代表序列的开始。[H表示将光标移动到屏幕的左上角,相当于将光标定位到屏幕的第一行第一列。[J表示清除屏幕。它会清除光标位置之后的所有文本内容,包括之前的游戏状态和输出。
这意味着每次调用clearScreen函数时,它都会清除当前终端或控制台屏幕的内容,以便在下一回合游戏之前显示新的游戏状态,从而得到一个更简洁的界面。
显示游戏棋盘
创建printBoard函数,以便在屏幕上直观地显示当前游戏棋盘的状态,让玩家了解棋盘上棋子的位置。这是井字棋游戏中用于展示游戏状态的一部分。
void printBoard() {
printf("\n");
printf(" 1 2 3\n");
printf("1 %c | %c | %c\n", board[0][0], board[0][1], board[0][2]);
printf(" ---------\n");
printf("2 %c | %c | %c\n", board[1][0], board[1][1], board[1][2]);
printf(" ---------\n");
printf("3 %c | %c | %c\n", board[2][0], board[2][1], board[2][2]);
}
检查游戏是否结束
实现一个名为isGameOver的函数,用于判断井字棋游戏是否结束,并返回一个布尔值(true或false)。
bool isGameOver() {
// 检查行
for (int i = 0; i < 3; i++) {
if (board[i][0]!= ' ' && board[i][0] == board[i][1] && board[i][0] == board[i][2]) {
return true;
}
}
// 检查列
for (int j = 0; j < 3; j++) {
if (board[0][j]!= ' ' && board[0][j] == board[1][j] && board[0][j] == board[2][j]) {
return true;
}
}
// 检查对角线
if (board[0][0]!= ' ' && board[0][0] == board[1][1] && board[0][0] == board[2][2]) {
return true;
}
if (board[0][2]!= ' ' && board[0][2] == board[1][1] && board[0][2] == board[2][0]) {
return true;
}
// 检查是否平局
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (board[i][j] == ' ') {
return false;
}
}
}
return true;
}
检查某一行是否有获胜者:如果一行中的三个单元格都被同一个玩家占据,则有获胜者,函数返回true。
检查某一列是否有获胜者:如果一列中的三个单元格都被同一个玩家占据,则有获胜者,函数返回true。
检查对角线上是否有获胜者:如果任何一条对角线上的三个方格都被同一个玩家占据,则有获胜者,函数返回true。
检查是否平局:最后,函数检查所有单元格是否都已被占据,但没有获胜者。如果是这种情况,则游戏平局,函数返回true。
确定获胜者
定义了一个名为getWinner的函数,其目的是确定井字棋游戏中是否有获胜者,并返回获胜者的标记(X或O),如果没有获胜者,则返回一个空格(' ')。
char getWinner() {
// 检查行
for (int i = 0; i < 3; i++) {
if (board[i][0]!= ' ' && board[i][0] == board[i][1] && board[i][0] == board[i][2]) {
return board[i][0];
}
}
// 检查列
for (int j = 0; j < 3; j++) {
if (board[0][j]!= ' ' && board[0][j] == board[1][j] && board[0][j] == board[2][j]) {
return board[0][j];
}
}
// 检查对角线
if (board[0][0]!= ' ' && board[0][0] == board[1][1] && board[0][0] == board[2][2]) {
return board[0][0];
}
if (board[0][2]!= ' ' && board[0][2] == board[1][1] && board[0][2] == board[2][0]) {
return board[0][2];
}
return ' '; // 没有获胜者
}
实现主游戏循环
编写主游戏循环,让玩家轮流进行游戏并与游戏进行交互。
int main() {
initializeBoard();
int currentPlayer = 1;
while (1) {
clearScreen();
printf("Current board state:\n");
printBoard();
int row, col;
printf("Player %d, please enter a row and column (e.g., 1 2):", currentPlayer);
while (scanf("%d %d", &row, &col)!= 2) {
printf("Invalid input, please try again: ");
while (getchar()!= '\n');
}
if (row < 1 || row > 3 || col < 1 || col > 3 || board[row - 1][col - 1]!= ' ') {
printf("Invalid move, please try again.\n");
} else {
if (currentPlayer == 1) {
board[row - 1][col - 1] = 'X';
currentPlayer = 2;
} else {
board[row - 1][col - 1] = 'O';
currentPlayer = 1;
}
}
if (isGameOver()) {
clearScreen();
printf("Game over!\n");
printBoard();
char winner = getWinner();
if (winner!= ' ') {
printf("Player %c wins!\n", winner);
} else {
printf("It's a draw!\n");
}
break;
}
}
return 0;
}
调用initializeBoard函数来初始化游戏棋盘,将所有方格设置为空格。
创建一个整数变量currentPlayer,用于跟踪轮到谁下棋。初始设置为 1,表示玩家 1。
在while (1)主循环中:
- 调用
clearScreen函数清除屏幕,以便在每回合后刷新显示。 - 调用
printBoard函数打印当前游戏棋盘。
玩家输入:通过scanf函数提示当前玩家输入行和列坐标,例如Player 1, please enter a row and column (e.g., 1 2):。如果输入无效(不是两个整数),则显示消息Invalid input, please try again:并清除输入缓冲区,直到获得有效输入。
输入验证:然后,检查输入的行和列坐标是否有效(范围为 1 到 3),并且所选位置是否为空格。如果输入无效或所选位置已被占用,则显示消息Invalid move, please try again.并要求玩家重新输入。
落子:如果输入有效,代码会根据当前玩家(1 或 2)在游戏棋盘的相应位置放置一个X或一个O。
游戏结束检测:然后,代码调用isGameOver()函数检查游戏是否结束。如果游戏结束,屏幕会清除并显示游戏结束的消息,包括获胜者(如果有)或平局。
游戏结果:如果有获胜者,代码会根据获胜者是哪个玩家显示Player X wins!或Player O wins!。如果没有获胜者,代码会显示It's a draw!表示平局。
编译并运行项目
执行gcc命令进行编译:
cd ~/project
gcc -o tictactoe tictactoe.c
./tictactoe

总结
恭喜你!你已经成功地用 C 语言创建了一个简单的井字棋游戏。玩家可以轮流在 3x3 的网格上标记他们的位置。游戏在每一轮后检查是否有获胜者或平局,并相应地显示结果。这个简单的项目展示了 C 编程的基本概念,可以作为更复杂游戏开发的起点。



