设计步骤
当设计一个三子棋游戏时,我们需要设计具体步骤来为写代码做铺垫
1、设计游戏菜单
我们可以一个设计菜单来决定开始游戏和退出游戏。
2、创建一个棋盘数据结构
使用一个二维数组来表示棋盘。你可以选择一个固定大小的数组,例如3x3的棋盘。
3、初始化棋盘
编写一个函数来初始化棋盘,将所有位置设置为空。
4、打印棋盘
编写一个函数来打印当前的棋盘状态,以便玩家可以看到棋盘。
5、玩家落子
编写一个函数来接收玩家的输入,并在合法的位置落下玩家的棋子。
6、检查胜利条件
编写一个函数来检查当前棋盘状态是否有玩家获胜。这个函数可以检查所有可能的胜利组合,例如横线、竖线和对角线。
7、检查平局
编写一个函数来检查当前棋盘是否已满并且没有玩家获胜,即判断是否为平局。
8、游戏循环
在主函数中,使用一个循环来不断进行玩家的回合,直到有玩家获胜或者平局。
以下是具体操作的详细解释
一、设计游戏菜单
设计菜单我们可以用do…while()语句和switch语句来实现
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("游戏开始\n");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
} while (input);
其中的case 1:
部分就是开始游戏的地方,我们之后所写的代码都会放在其中。
这里为了主函数简洁自定义了一个menu()函数。
//菜单
void menu()
{
printf("**************************\n");
printf("********三子棋游戏********\n");
printf("******** 1.play ********\n");
printf("******** 0.exit ********\n");
printf("**************************\n");
}
二、创建一个棋盘数据结构
#define SIZE 3
char board[SIZE][SIZE];
使用一个二维数组来表示棋盘。你可以选择一个固定大小的数组,例如3x3的棋盘。为了方便改变棋盘大小,所以用了#define
。
三、初始化棋盘
这里我们用自定义函数InitializeBoard()
创建了一个用于初始化棋盘的函数。
initializeBoard()
:这个函数用于初始化棋盘,将所有位置设置为空。它通过遍历二维数组 board
,将每个元素设置为一个空格字符 ’ ',表示该位置当前没有棋子。
// 初始化游戏棋盘
void InitializeBoard()
{
int i, j;
for (i = 0; i < SIZE; i++)
{
for (j = 0; j < SIZE; j++)
{
board[i][j] = ' ';
}
}
}
该函数使用两个嵌套的循环来遍历二维数组 board 的所有元素。循环变量 i 和 j 分别表示行和列的索引,取值范围是 0 到 2。在循环体内部,将每个元素 board[i][j] 设置为一个空格字符 ’ '。
四、打印棋盘
// 打印游戏棋盘
void PrintBoard()
{
int i, j;
for (i = 0; i < SIZE; i++)
{
for (j = 0; j < SIZE; j++)
{
printf(" %c ", board[i][j]);
if (j != SIZE - 1)
{
printf("|");
}
}
printf("\n");
if (i != SIZE - 1) {
printf("---+---+---\n");
}
}
}
函数通过两个嵌套的循环遍历二维数组 board
,其中 SIZE
表示棋盘的大小。在每个位置上,使用 printf
函数打印对应的棋子标识,并在每个棋子标识之间打印竖直的分隔符 "|"
。
在内层循环结束后,通过判断 j
是否为 SIZE - 1
,来确定是否打印竖直的分隔符 "|"
。如果 j
不等于 SIZE - 1
,表示当前位置不是最后一列,需要打印分隔符。
在外层循环结束后,通过判断 i
是否为 SIZE - 1
,来确定是否打印水平的分隔线 "---+---+---"
。如果 i
不等于 SIZE - 1
,表示当前行不是最后一行,需要打印分隔线。
通过以上的打印方式,函数可以将棋盘的状态以可读性较好的形式输出到控制台。每个棋子标识被打印在一个方格内,方格之间使用分隔符进行分隔,使得棋盘的布局清晰可见。水平和竖直的分隔线进一步增强了棋盘的可读性,使得玩家可以更容易地理解棋盘的结构和当前的棋局。
五、玩家落子
int row, col;
char currentPlayer = 'X';
//打印玩家下棋
void PrintPlayer()
{
printf("玩家 %c,请输入下棋位置(行 列):", currentPlayer);
scanf("%d %d", &row, &col);
}
这段代码是用于打印当前玩家下棋的信息,并获取玩家输入的下棋位置。
首先定义了三个变量:row
和 col
用于存储玩家输入的行和列,currentPlayer
表示当前轮到的玩家,初始为 'X'
。
然后定义了函数 PrintPlayer()
,该函数用于打印提示信息,让当前玩家输入下棋位置。使用 printf
函数输出提示信息,显示当前玩家的标识符 currentPlayer,并要求玩家输入下棋位置的行和列。
接下来使用 scanf
函数接收玩家的输入,并将输入的值存储到 row 和 col 变量中,以便后续使用。
通过调用 PrintPlayer()
函数,可以在每次轮到玩家下棋时,打印提示信息并获取玩家输入的下棋位置。这样玩家就可以根据提示进行输入,并将输入的位置用于后续的操作,例如在棋盘上落子等。
检查位置是否合规\是否已被占据
//game.c
// 检查输入是否合法
int CheckLegality()
{
if (row - 1 < 0 || row - 1 > SIZE || col - 1 < 0 || col - 1 > SIZE)
{
return 1;
}
else
{
return 0;
}
}
// 检查位置是否已经被占据
int CheckOccupation()
{
if (board[row - 1][col - 1] != ' ')
{
return 1;
}
else
{
return 0;
}
}
//主函数
// 检查输入是否合法
if (CheckLegality() == 1)
{
printf("无效的位置,请重新输入。\n");
continue;
}
// 检查位置是否已经被占据
if (CheckOccupation() == 1)
{
printf("该位置已经被占据,请重新输入。\n");
continue;
}
这段代码实现了两个函数:CheckLegality()
和 CheckOccupation()
,用于检查输入位置的合法性和位置是否已经被占据。
CheckLegality()
函数首先检查输入的行数 row
和列数 col
是否超出了合法的范围。如果满足以下任一条件:row - 1 < 0、row - 1 > SIZE、col - 1 < 0、col - 1 > SIZE
,则返回值为1,表示位置无效。否则,返回值为0,表示位置合法。
CheckOccupation()
函数检查指定位置上的棋盘元素是否已经被占据。它通过检查 board[row - 1][col - 1]
是否等于空格字符 ’ ’ 来判断该位置是否已经被占据。如果不等于空格字符,表示该位置已经被占据,返回值为1;否则,返回值为0,表示位置未被占据。
在主程序中,通过调用这两个函数来进行输入检查。如果 CheckLegality()
返回值为1,表示位置无效,输出提示信息 “无效的位置,请重新输入。”,然后使用 continue
语句跳过当前循环,等待用户重新输入合法的位置。如果 CheckOccupation()
返回值为1,表示位置已经被占据,输出提示信息 “该位置已经被占据,请重新输入。”,然后同样使用 continue
语句跳过当前循环,等待用户重新输入未被占据的位置。
通过这两个检查函数,可以确保用户输入的位置既在合法范围内,又未被占据,以保证下棋操作的有效性和正确性。
//下棋
void Play()
{
board[row - 1][col - 1] = currentPlayer;
}
当检查位置完成之后,就可以通过调用 Play()
函数,可以将当前玩家的标识符写入棋盘的指定位置,实现了玩家在棋盘上落子的功能。
切换玩家
// 切换玩家
void CheckPlayer()
{
currentPlayer = (currentPlayer == 'X') ? 'O' : 'X';
}
在函数内部,使用了条件运算符 (currentPlayer == 'X') ? 'O' : 'X'
来进行判断和切换。该条件运算符的作用是,如果 currentPlayer
的值等于字符 'X'
,则将 currentPlayer
的值设为字符 'O'
,否则将其设为字符 'X'
。
通过调用这个函数,可以在每次玩家下完一步棋后,调用 CheckPlayer()
来切换当前玩家,使得下一次的提示和操作针对另一个玩家。
例如,如果当前玩家是 'X'
,调用 CheckPlayer()
后,currentPlayer
的值将被设置为 'O'
,下一次调用 PrintPlayer()
函数时,提示信息会显示为 “玩家 O,请输入下棋位置(行 列):”,以此类推。这样就实现了玩家轮流下棋的功能。
六、检查胜利条件
// 检查是否有玩家获胜
int CheckWin()
{
int i, j;
// 检查行
for (i = 0; i < SIZE; i++)
{
if (board[i][0] == currentPlayer && board[i][1] == currentPlayer && board[i][2] == currentPlayer)
{
printf("玩家 %c 获胜!\n", currentPlayer);
return 1;
break;
}
}
// 检查列
for (j = 0; j < SIZE; j++)
{
if (board[0][j] == currentPlayer && board[1][j] == currentPlayer && board[2][j] == currentPlayer)
{
printf("玩家 %c 获胜!\n", currentPlayer);
return 1;
break;
}
}
// 检查对角线
if (board[0][0] == currentPlayer && board[1][1] == currentPlayer && board[2][2] == currentPlayer)
{
printf("玩家 %c 获胜!\n", currentPlayer);
return 1;
}
if (board[0][2] == currentPlayer && board[1][1] == currentPlayer && board[2][0] == currentPlayer)
{
printf("玩家 %c 获胜!\n", currentPlayer);
return 1;
}
return 0;
}
这段代码实现了一个名为 CheckWin()
的函数,用于检查是否有玩家获胜。
该函数首先使用两个循环分别检查行和列,通过比较棋盘上的三个连续位置是否都属于当前玩家来判断是否获胜。循环变量 i
控制行的索引,循环变量 j
控制列的索引。如果某一行或某一列上的三个位置都属于当前玩家,即 board[i][0]
、board[i][1]
、board[i][2]
或 board[0][j]
、board[1][j]
、board[2][j]
均等于 currentPlayer
,则会输出提示信息 “玩家 X(或 O)获胜!”,其中 currentPlayer
是当前玩家的标识(可能是 ‘X’ 或 ‘O’),然后返回1,表示有玩家获胜,并结束函数的执行。
接下来,通过检查两条对角线上的三个位置是否都属于当前玩家,来判断是否获胜。第一条对角线为 board[0][0]
、board[1][1]
、board[2][2]
,第二条对角线为 board[0][2]
、board[1][1]
、board[2][0]
。如果其中任一对角线上的三个位置都属于当前玩家,同样会输出相应的提示信息,并返回1,表示有玩家获胜。
如果以上检查都未满足,则返回0,表示没有玩家获胜。
这个函数的目的是在每次玩家下棋后调用,检查是否有玩家已经连成三个棋子,从而判断游戏是否结束。
七、检查平局
这段代码实现了一个名为 CheckDraw()
的函数,用于判断是否是平局(即棋盘已满但没有玩家获胜)。
该函数通过遍历整个棋盘,检查是否还存在空位(即棋盘上的元素是否为字符 ' '
)。如果在遍历过程中找到了空位,说明游戏尚未结束,函数会立即返回0,表示不是平局。
如果遍历完成后没有找到任何空位,即棋盘已满,函数会返回1,表示是平局。
这个函数的目的是在每次玩家下棋后调用,用于判断游戏是否达到平局状态,即所有位置都已被占据但没有玩家获胜。
八、游戏循环
上述代码结束后就会执行while(input)
进入下次循环。
下面我们将代码结合一下看看
//game.h
#ifndef GAME_H
#define GAME_H
void InitializeBoard();
void PrintBoard();
int CheckWin();
int CheckDraw();
int CheckLegality();
int CheckOccupation();
void CheckPlayer();
void Play();
void menu();
#endif
//game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define SIZE 3
char board[SIZE][SIZE];
//菜单
void menu()
{
printf("**************************\n");
printf("********三子棋游戏********\n");
printf("******** 1.play ********\n");
printf("******** 0.exit ********\n");
printf("**************************\n");
}
// 初始化游戏棋盘
void InitializeBoard()
{
int i, j;
for (i = 0; i < SIZE; i++)
{
for (j = 0; j < SIZE; j++)
{
board[i][j] = ' ';
}
}
}
// 打印游戏棋盘
void PrintBoard()
{
int i, j;
for (i = 0; i < SIZE; i++)
{
for (j = 0; j < SIZE; j++)
{
printf(" %c ", board[i][j]);
if (j != SIZE - 1)
{
printf("|");
}
}
printf("\n");
if (i != SIZE - 1) {
printf("---+---+---\n");
}
}
}
int row, col;
char currentPlayer = 'X';
//打印玩家下棋
void PrintPlayer()
{
printf("玩家 %c,请输入下棋位置(行 列):", currentPlayer);
scanf("%d %d", &row, &col);
}
// 切换玩家
void CheckPlayer()
{
currentPlayer = (currentPlayer == 'X') ? 'O' : 'X';
}
//下棋
void Play()
{
board[row - 1][col - 1] = currentPlayer;
}
// 检查输入是否合法
int CheckLegality()
{
if (row - 1 < 0 || row - 1 > SIZE || col - 1 < 0 || col - 1 > SIZE)
{
return 1;
}
else
{
return 0;
}
}
// 检查位置是否已经被占据
int CheckOccupation()
{
if (board[row - 1][col - 1] != ' ')
{
return 1;
}
else
{
return 0;
}
}
// 检查是否有玩家获胜
int CheckWin()
{
int i, j;
// 检查行
for (i = 0; i < SIZE; i++)
{
if (board[i][0] == currentPlayer && board[i][1] == currentPlayer && board[i][2] == currentPlayer)
{
printf("玩家 %c 获胜!\n", currentPlayer);
return 1;
break;
}
}
// 检查列
for (j = 0; j < SIZE; j++)
{
if (board[0][j] == currentPlayer && board[1][j] == currentPlayer && board[2][j] == currentPlayer)
{
printf("玩家 %c 获胜!\n", currentPlayer);
return 1;
break;
}
}
// 检查对角线
if (board[0][0] == currentPlayer && board[1][1] == currentPlayer && board[2][2] == currentPlayer)
{
printf("玩家 %c 获胜!\n", currentPlayer);
return 1;
}
if (board[0][2] == currentPlayer && board[1][1] == currentPlayer && board[2][0] == currentPlayer)
{
printf("玩家 %c 获胜!\n", currentPlayer);
return 1;
}
return 0;
}
// 判断是否平局
int CheckDraw()
{
int i, j;
for (i = 0; i < SIZE; i++)
{
for (j = 0; j < SIZE; j++)
{
if (board[i][j] == ' ')
{
return 0; // 棋盘还有空位,游戏未结束
}
}
}
return 1; // 棋盘已满,平局
}
//test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include "game.h"
int main()
{
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("游戏开始\n");
InitializeBoard();
printf("欢迎来到三子棋游戏!\n");
while (1)
{
printf("当前棋盘状态:\n");
PrintBoard();
//打印玩家下棋
PrintPlayer();
// 检查输入是否合法
if (CheckLegality() == 1)
{
printf("无效的位置,请重新输入。\n");
continue;
}
// 检查位置是否已经被占据
if (CheckOccupation() == 1)
{
printf("该位置已经被占据,请重新输入。\n");
continue;
}
//下棋
Play();
// 检查游戏是否结束
if (CheckWin() == 1)
{
printf("当前棋盘状态:\n");
PrintBoard();
break;
}
if (CheckDraw())
{
printf("平局!\n");
printf("当前棋盘状态:\n");
PrintBoard();
break;
}
//切换玩家
CheckPlayer();
}
printf("游戏结束。\n");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
} while (input);
return 0;
}
以下是运行结果
以上是我对我近期学习的一个总结,如果有不对的地方或者有什么建议的话,都可以提出来哦。