个人认为该小游戏的准备前提为熟系for循环、while()循环、数组、函数定义使用以及声明和if语句的使用等。
OK!在学会这些之后就可以开始复刻这个小游戏了,首先要了解三字棋的规则,三子棋:在3*3的网格之中,每人执不同的棋子,将棋子放入到网格之中,率先在棋盘上形成一行、一列或一条对角线上有 3 个自己的棋子连成一线的玩家获胜!(类似简易版的五子棋)
接下来我们就使用C语言来做一个实现(最终效果参考,个人认为在学习时有效果参考会更有利于我们理解代码)

第一步 做一个菜单,让玩家进行简易的选择或退出,在玩家选择“1”时,进入游戏输出棋盘,在这里我们将其封装成了menu()这个函数,函数内容是选择菜单,就是一个简单的输入输出,
void menu()
{
printf("***************************\n");
printf("****** 1.play 0.exit ******\n");
printf("***************************\n");
}
接下来我们要输出棋盘格,在输出棋盘格的时候我们要考虑如何将玩家和电脑下棋的数据记录下来,由于棋盘是一个3*3的,所以我们想到将数据使用二维数组存储起来,方便操作,所以我们定义了一个数组board[][];在鹏哥的想法下因为我们想要后面可以实现更多操作,包括扩大棋盘格或者说不止于三字棋,可做成五子棋,当然也能够让我们能更好的运用所学知识,所以我们定义了两个宏分别是ROW和COL,方便后期修改数据
#define ROW 3
#define COL 3
char board[ROW][COL] = { 0 };//定义的数组
在打印出棋盘格之前,我们知道后面要将玩家和电脑的输入数组存储并打印出来,所以我们需要和数组联系起来,因此打印棋盘格之前,我们要将数组中的内容进行初始化,避免数组元素可能是随机的垃圾值
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for ( i = 0; i <row ; i++)
{
for ( j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
接下来就是打印棋盘格,在鹏哥的视频中讲了两个版本,第一个版本中棋盘格不能随ROW和COL改变大小。
//版本一,只能显示n列3行不能随ROW,COL改变
//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
// int i = 0;
// for ( i = 0; i < row; i++)
// {
// //打印数据
// printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
// //打印分格信息
// if(i<row-1)
// {
// printf("---|---|---\n");
// }
//
// }
//}
//版本二
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for ( i = 0; i < row; i++)
{
//打印数据
//printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
for (int j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if (j < col - 1)
printf("|");
}
printf("\n");
//打印分格信息
if(i<row-1)
{
//printf("---|---|---\n");
for (int j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
}
printf("\n");
}
}
游戏过程中,玩家和电脑轮流下棋,我们分别实现了PlayerMove()和ComputerMove()两个函数来处理双方的下棋逻辑。在PlayerMove()函数的实现中,考虑到普通玩家习惯从1开始计数(而非计算机的0起始编号),我们对输入的坐标进行了减一处理。此外,在获取玩家输入时,我们还需要检查坐标是否在有效范围内,并确保该位置未被占用,最后根据输入的坐标,赋值给数组中对应的位置。
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("玩家下棋:>\n");
while (1)
{
printf("请输入坐标:>");
scanf("%d %d", &x, &y);
//判断坐标范围是否合法
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//判断坐标是否为空
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '$'; // 赋值运算
break;
}
else
{
printf("坐标被占用,请重新输入\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
在ComputerMove()函数中,我们采用随机数生成的方式实现了计算机的基本落子功能。未来计划进一步优化算法,因为计算机的棋力水平很大程度上取决于编写者的编程能力。
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋:>\n");
int x = 0;
int y = 0;
while (1)
{
x = rand() % row;//0~2
y = rand() % col;//0~2
if (board[x][y]==' ')
{
board[x][y] = '#';
break;
}
}
}
在实现了落子功能之后就是判断输赢,在判断输赢上设计了Iswin()和IsFull()两个函数,分别判断落子之后落子方的胜负情况和继续下棋情况,以及根据IsFull()判断是否棋盘已满,平局状态,通过 IsWin() 返回值('C'/'Q'/'$'/'#')表示游戏进行中、平局、玩家胜、电脑胜。
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋:>\n");
int x = 0;
int y = 0;
while (1)
{
x = rand() % row;//0~2
y = rand() % col;//0~2
if (board[x][y]==' ')
{
board[x][y] = '#';
break;
}
}
}
char IsWin(char board[ROW][COL], int row, int col)
{
//行的判定
for (int i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
{
return board[i][1];
}
}
//列的判断
for (int j= 0; j< col; j++)
{
if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ')
{
return board[1][j];
}
}
//对角线的判断
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
{
return board[1][1];
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
{
return board[1][1];
}
//程序走到此出说明没有人赢,判断棋盘是否满了
if (IsFull(board, row, col))
{
return 'Q';
}
//游戏继续
return 'C';
}
//棋盘满了返回1
//棋盘未满返回0
int IsFull(char board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (board[i][j] == ' ')
{
return 0;
}
}
//return 1;
}
return 1;
}
在最后将这些函数都封装在了game()这个函数中 ,根据不同的返回值来判断输赢情况
void game()
{
char ret = 'a';
char board[ROW][COL] = { 0 };
//初始化棋盘
InitBoard(board, ROW, COL);
//打印棋盘
DisplayBoard(board,ROW,COL);
while (1)
{
//玩家下棋
PlayerMove(board, ROW, COL);
//判断输赢
ret=IsWin(board,ROW,COL);
if (ret !='C')
{
break;
}
DisplayBoard(board, ROW, COL);
ComputerMove(board, ROW, COL);
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C')
{
break;
}
DisplayBoard(board, ROW, COL);
}
if (ret=='$')
{
printf("玩家获胜\n");
}
else if (ret=='#')
{
printf("电脑获胜\n");
}
else
{
printf("平局");
}
DisplayBoard(board, ROW, COL);
}
最后来看一下main()函数
int main()
{
srand((unsigned int)time(NULL));//设置随机数的生成起点
int input = 0;
do
{
menu();//打印菜单
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("三字棋开始游戏\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
总结,在这次程序复刻中,深刻认识了动手实践的重要性,因为不想完全照着老师的代码敲,所以都是听完之后自己敲,在自己敲的过程中出现了很多错误中,其中最让我头疼的是在PlayerMove()函数中进行,board[x - 1][y - 1] = '$'; // 赋值运算,操作时,受if (board[x - 1][y - 1] == ' ')该判断语句的影响竟然将赋值运算中的“=”错误敲成“==”导致程序运行错误,这是很严重的一个错误(实在是资质愚钝,新手操作,大佬勿喷),不过也让我更加注意其中的细节之处,在复刻过程中还出了每次玩家写入 1 1的下棋操作时不显示的问题(其他坐标都能正常显示),好在最后一一解决,不枉我一片苦心。
此文章仅做学习记录,若有错误,望指出,拜谢。

被折叠的 条评论
为什么被折叠?



