代码思路:
1.多文件的使用(test.c game.c game.h)
2.宏定义的使用
3.打印菜单
4.创建二维数组
5.初始化数组
6.打印棋盘
7.玩家下棋
8.电脑下棋
9.判断输赢
10.代码全析总结
(1)多文件的使用:
在路径下面创建一个头文件和两个源文件
test.c 里面是 主函数 调用的函数 菜单的打印
game.c 这里面是 游戏主体 的实现
game.h 除了 主函数 之外的 所有函数 以及 宏 所存放的地方
(2)宏定义的使用:
在我的头文件里面使用宏定义:将每一行,每一列都定义为3
如果想要修改棋盘的行和列,只需要在头文件的ROW COL后面修改数字即可,这样很方便,提高了效率。
(3)打印菜单:
为了保证游戏能够多次进行,或者说我们玩玩一把不过瘾,需要再玩一把。我们可以直接把菜单的打印和游戏函数的实现放到一个循环里面。
因为上来直接可以让你选择是否玩游戏,所以说不管三七二十一直接用do while循环,代码的实现如下:
int input = 0;
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("三子棋游戏开始\n");
game();//整个游戏的实现主体在这个game函数里面。
break;
case 0:
printf("退出游戏!\n");
break;
default:
printf("选择错误,请重新选择:\n");
break;
}
} while (input);
void menu()
{
printf("*********************************\n");
printf("******** 1.play *********\n");
printf("******** 0.exit *********\n");
printf("*********************************\n");
}
这个循环的控制条件放的是input,为什么要放input呢?初始化input为0,当你选择非零的时候,就会进入case1或者default,循环的结果为真,所以进入循环让你继续游戏或者重新选择。input是零的话就为假,直接跳出循环。
(4)创建二维数组:
char board[ROW][COL] = { 0 };//咱们暂且将二维数组初始化为0。
(5)初始化数组:
我们现在要充分利用函数的思想,所以要创建一个初始化数组的函数
函数的调用:
InitBoard(board, ROW, COL);//初始化数组
函数的声明:
//初始化数组
void InitBoard(char board[ROW][COL],int row,int col);
函数的实现:
初始化二维数组为空格的话,需要用两个for循环来遍历
//初始化棋盘为空格
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
(6)打印棋盘:(重难点!)
函数的调用:
PrintBoard(board, ROW, COL);//打印数组
函数的声明:
//打印数组
void PrintBoard(char board[ROW][COL], int row, int col);
函数的实现:
棋盘的打印需要用分割线来分隔开
1.打印数据(包括:“空格+%c+空格” +“|”)
2.打印分割线(包括 :“---”+“|”)
见下图:
红 蓝总共为一组,每一行需要打印这么多,只不过在最后那一列不用打印,到时候加一个限制条件即可。
//版本2---打印棋盘
void PrintBoard(char board[ROW][COL], int row, int col)
{
//1.打印数据
int i = 0;//这个i相当于作用域内的全局变量
//所以下面的第2个条件照样使用。
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if (j < col - 1)//限制最后一个,不用打印。
printf("|");
}
printf("\n");//打印完一行后要换行
//2.打印分割线
if (i < row - 1)//如果是三子棋的话,只需要打印两行分割线即可,这里的row是3,所以说这需要i=0,1
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)//限制最后一个不用打印。
printf("|");
}
printf("\n");
}
}
}
值得注意的是:这个 打印棋盘 是重难点,需要多次反复思考
(7)玩家下棋:(重难点!)
函数的调用:
PlayerMove(board, ROW, COL);//玩家下棋
函数的声明:
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);
函数的实现:
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)//这一步仔细想想!!!!
{
int x = 0;
int y = 0;
printf("玩家下棋:>");
while (1)
{
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (board[x - 1][y - 1] == ' ')//因为数组的下边从0开始,所以输的数要-1
{
board[x-1][y-1] = '*';//符合坐标范围而且是空,才能落子
break;//当你成功落完子之后,需要赶紧跳出循环,等待电脑下棋。
}
else//这个else的对立面,就是空格处有*或#,不能再落子了
printf("坐标被占用,请重新输入:");
}
else//超出范围无法落子
{
printf("坐标非法,请重新输入:");
}
}
}
代码的每一步解析,在注释中需要读者仔细阅读反复理解
(8)电脑下棋:(重难点!)
函数的调用:
ComputerMove(board, ROW, COL);//电脑下棋
函数的声明:
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);
函数的实现:
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋\n");
while (1)//这里我们为什么要用一个死循环?如果电脑所下的地方被占用,那么就让它一直循环产生一个随机数,直到产生一个没有被占用的地方。
{
// X和Y要放循环内部,让他每一次循环都会产生随机数,不然就会电脑不下棋,形成死循环。
x = rand() % row;//产生0~row-1的数字
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;//这里的break意思是指电脑下棋成功后直接跳出循环。
}
}
}
这里电脑下棋需要运用,我们之前在猜数字游戏学过的随机数的产生。
rand---srand---time函数
同样,每一步的解析和疑问都在代码的注释中,需要读者反复阅读理解。
(9)判断输赢:(重难点!)
游戏所进行的状态无非就这几种:
玩家赢---'*'
电脑赢---'#'
平局---'Q'
继续游戏---'C'
后面的字母代表,如果某种情况成立,那么就返回后面所对应的字符:
那么我们就需要创建一个判断输赢的函数(另外在判断平局的时候比较特殊,需要创建一个判断平局的函数:IsFull):
函数的调用:
ret = IsWin(board, ROW, COL);//ret是创建的变量用来接收它的返回值,数据类型:char
IsFull(board, ROW, COL);
函数的声明:
//判断输赢
char IsWin(char board[ROW][COL], int row, int col);
//判断平局
int IsFull(char board[ROW][COL], int row, int col);
函数的实现:
char IsWin(char board[ROW][COL], int row, int col)//因为返回的是字符,所以要用char
{
//赢
//行
int i = 0;
for (i = 0; i < row; i++)
{
//这种判断方法也是很有局限的,也是确定了只能用三子棋,不能用5×5 6×6 10×10的棋盘。
if (board[i][0] == board[i][1]&& board[i][1] == board[i][2]&& board[i][0] != ' ')//记得这里两个条件判断的时候一定要两两分开!!!
return board[i][0];
}
//列
for (i = 0; i < row; i++)
{
if (board[0][i] == board[1][i]&& board[1][i] == board[2][i]&&board[1][i] != ' ')
return board[0][i];
}
//对角线
if (board[0][0] == board[1][1]&& board[1][1] == board[2][2]&& board[1][1] != ' ')
return board[0][0];
if (board[2][0] == board[1][1]&& board[1][1] == board[0][2]&& board[1][1] != ' ')
return board[0][2];
//判断平局
if (IsFull(board, ROW, COL) == 1)
return 'Q';
else
return 'C';
}
int IsFull(char board[ROW][COL], int row, int col)//判断是否平局的函数。
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0;//如果存在空格,游戏继续。
}
}
return 1;//如果返回1那么是平局,平局对应上方的第130行,正好返回Q
}
以上内容是所有三子棋的一步一步的分析,不理解的地方,需要反复阅读认真理解
(10)代码全析总结:
1.test.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include "game.h"
void menu()
{
printf("*********************************\n");
printf("******** 1.play *********\n");
printf("******** 0.exit *********\n");
printf("*********************************\n");
}
void game()
{
//设置一个变量来接收是否平局
char ret = 0;//这里因为返回一个字符,所以要用char类型
char board[ROW][COL] = { 0 };
InitBoard(board, ROW, COL);//初始化数组
PrintBoard(board, ROW, COL);//打印数组
while (1)
{
PlayerMove(board, ROW, COL);//玩家下棋
PrintBoard(board, ROW, COL);//打印数组
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
ComputerMove(board, ROW, COL);//电脑下棋
PrintBoard(board, ROW, COL);//打印数组
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
}
if (ret == '*')
printf("玩家赢\n");
if (ret == '#')
printf("电脑赢\n");
if (ret == 'Q')
printf("平局\n");
}
int main()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("三子棋游戏开始\n");
game();//整个游戏的实现主体在这个game函数里面。
break;
case 0:
printf("退出游戏!\n");
break;
default:
printf("选择错误,请重新选择:\n");
break;
}
} while (input);
return 0;
}
2.game.c :
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//初始化棋盘为空格
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
//版本1
//void PrintBoard(char board[ROW][COL], int row, int col)
//{
// int i = 0;
// for (i = 0; i < row; i++)
// {
// int j = 0;
// for (j = 0; j < col; j++)
// {
// printf("%c", board[i][j]);
// }
// printf("\n");
// }
//}
//版本2---打印棋盘
void PrintBoard(char board[ROW][COL], int row, int col)
{
//1.打印数据
int i = 0;//这个i相当于作用域内的全局变量
//所以下面的第2个条件照样使用。
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if (j < col - 1)//限制最后一个,不用打印。
printf("|");
}
printf("\n");//打印完一行后要换行
//2.打印分割线
if (i < row - 1)//如果是三子棋的话,只需要打印两行分割线即可,这里的row是3,所以说这需要i=0,1
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)//限制最后一个不用打印。
printf("|");
}
printf("\n");
}
}
}
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)//这一步仔细想想!!!!
{
int x = 0;
int y = 0;
printf("玩家下棋:>");
while (1)
{
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (board[x - 1][y - 1] == ' ')//因为数组的下边从0开始,所以输的数要-1
{
board[x-1][y-1] = '*';//符合坐标范围而且是空,才能落子
break;//当你成功落完子之后,需要赶紧跳出循环,等待电脑下棋。
}
else//这个else的对立面,就是空格处有*或#,不能再落子了
printf("坐标被占用,请重新输入:");
}
else//超出范围无法落子
{
printf("坐标非法,请重新输入:");
}
}
}
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋\n");
while (1)//这里我们为什么要用一个死循环?如果电脑所下的地方被占用,那么就让它一直循环产生一个随机数,直到产生一个没有被占用的地方。
{
// X和Y要放循环内部,让他每一次循环都会产生随机数,不然就会电脑不下棋,形成死循环。
x = rand() % row;//产生0~row-1的数字
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;//这里的break意思是指电脑下棋成功后直接跳出循环。
}
}
}
char IsWin(char board[ROW][COL], int row, int col)//因为返回的是字符,所以要用char
{
//赢
//行
int i = 0;
for (i = 0; i < row; i++)
{
//这种判断方法也是很有局限的,也是确定了只能用三子棋,不能用5×5 6×6 10×10的棋盘。
if (board[i][0] == board[i][1]&& board[i][1] == board[i][2]&& board[i][0] != ' ')//记得这里两个条件判断的时候一定要两两分开!!!
return board[i][0];
}
//列
for (i = 0; i < row; i++)
{
if (board[0][i] == board[1][i]&& board[1][i] == board[2][i]&&board[1][i] != ' ')
return board[0][i];
}
//对角线
if (board[0][0] == board[1][1]&& board[1][1] == board[2][2]&& board[1][1] != ' ')
return board[0][0];
if (board[2][0] == board[1][1]&& board[1][1] == board[0][2]&& board[1][1] != ' ')
return board[0][2];
//判断平局
if (IsFull(board, ROW, COL) == 1)
return 'Q';
else
return 'C';
}
int IsFull(char board[ROW][COL], int row, int col)//判断是否平局的函数。
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0;//如果存在空格,游戏继续。
}
}
return 1;//如果返回1那么是平局,平局对应上方的第130行,正好返回Q
}
3.game.h :
#pragma once
#define ROW 3
#define COL 3
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//初始化数组
void InitBoard(char board[ROW][COL],int row,int col);
//打印数组
void PrintBoard(char board[ROW][COL], int row, int col);
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);
//判断输赢
char IsWin(char board[ROW][COL], int row, int col);
//玩家赢--'*'
//电脑赢--'#'
//平局---'Q'
//int IsFull(char board[ROW][COL], int row, int col);
//继续游戏---'C'
以上就是三子棋的全分析
如果对你有帮助,记得点赞👍+关注哦!
我的主页还有其他文章,欢迎学习指点。关注我,让我们一起学习,一起成长吧!