目录
前言
三子棋规则
代码实现
代码剖析
大噶好,今天整点三子棋。这个小游戏的代码全部用C语言写出,涉及多维数组、函数以及一些零碎的知识点,为了增加可读性并深化函数的理解,代码由三个文件构成。
1.text.c(游戏的测试)
2.game.c(游戏实现、主体代码)
3.game.h (函数声明、头文件)
代码总计200+行,本文篇幅较长,需要对函数和数组,有一定理解。
下面只进行讲解电脑随机下棋的版本,如果学有余力想让电脑堵棋,代码我放到最后,大家可以找我私聊或者评论区交流
三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、井字棋等。将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了。但是,有很多时候会出现和棋的情况。——源自百度百科,CSGO白给小镇匪家下棋!!
可以类比五子棋的规则理解
test.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()//打印菜单
{
printf("******************\n");
printf("******1.game******\n");
printf("******0.exit******\n");
printf("******************\n");
}
void game()
{
//初始化棋盘元素全部为0
char board[ROW][COL] = { 0 };
//将棋盘每一个元素变为空格
InitBoard(board, ROW, COL);
//打印棋盘
DisplayBoard(board, ROW, COL);
char win = 0;
while (1)
{
//玩家开始下棋
player_move(board, ROW, COL);
DisplayBoard(board, ROW, COL);
win = IsWin(board, ROW, COL);
if (win != 'C')
break;
//电脑开始下棋
computer_move(board, ROW, COL);
DisplayBoard(board, ROW, COL);
win = IsWin(board, ROW, COL);
if (win != 'C')
break;
//判断输赢,返回为*玩家胜,#电脑胜,'C'继续,'E'平局
//char win = IsWin(board, ROW, COL);
}
if (win == '#')
printf("很遗憾,您连人工智障都没下过\n");
else if (win == '*')
printf("恭喜,您把人工智障吊起来锤\n");
else
printf("很遗憾,您和人工智障五五开\n");
}
void test_game()//测试游戏面板
{
int input = 0;
srand((unsigned int)time(NULL));//随机数
do
{
menu();
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误请重新输入\n");
break;
}
} while (input);
}
int main()
{
test_game();
return 0;
}
game.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
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] = ' ';
}
}
}
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
if (i < row)//打印分隔行
{
for (j = 0; j < row; j++)
{
printf("---");
if (j < col - 1)
printf("+");
}
}
printf("\n"); //换行
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if (j < col - 1)
printf("|");//打印棋格之间的间隔
}
printf("\n");//换行
}
for (j = 0; j < row; j++)
{
printf("---");
if (j < col - 1)
printf("+");
}
printf("\n");
}
void player_move(char board[ROW][COL], int row, int col)
{
printf("玩家下棋,请输入坐标->\n");
int x = 0;
int y = 0;
while (1)
{
scanf("%d %d", &x, &y);
if (x >= 1 && y >= 1 && x <= row && y <= col)
{
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] ='*';
break;
}
else
{
printf("该坐标已有棋子,请重下\n");
}
}
else
{
printf("该坐标不存在,请重下\n");
}
}
}
void computer_move(char board[ROW][COL], int row, int col)
{
printf("电脑下棋->\n");
int x = 0;
int y = 0;
while (1)
{
x = rand() % row;//x属于0到2
y = rand() % col;//y属于0到2
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '#';
break;
}
}
}
char IsWin(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
//判断行
for (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 (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
{
return board[1][i];
}
}
//判断对角线
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 'E';
}
else
return 'C';
}
int IsFull(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++)
{
if (board[i][j] == ' ')
{
return 0;//有空格棋盘没满,继续下
}
}
}
return 1;//没找到空格返回1,平局
}
game.h:
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#define ROW 3
#define COL 3
void InitBoard(char board[ROW][COL],int row,int col);
void DisplayBoard(char board[ROW][COL], int row, int col);
void player_move(char board[ROW][COL], int row, int col);
void computer_move(char board[ROW][COL], int row, int col);
char IsWin(char board[ROW][COL], int row, int col);
头文件只是一些引用头文件和对常量的定义以及函数的声明,不再解释。
下面对两个源文件中涉及的所有代码按游戏实现的思考顺序开始解释,注意以下ROW为3,COL为3
为了简便,主函数中调用test_game函数
这里我们先看do while语句里面的内容,进入游戏看到的第一个东西肯定是游戏面板啊,不然怎么选择,所以先打印面板,也就是menu函数。
哈哈哈哈哈这里懒得美化了,可以更好看的,输入1就是开始游戏,输入0就是退出游戏(代码结束) 至于为啥
再看回来,Switch是不是和我们输入1或者0或者其他数字的结果一一对应,这里我们输入1,开始进入游戏
现在我们进入游戏了,第一件事是啥,肯定是生成棋盘,不然在哪下啊,我们首先先定义一个二维数组并将元素全部初始化为0(在后面数组总结会再次说明)
好了,现在进入初始化棋盘的函数,也就是把每一个元素变成空格
这一步比较简单,两个for循环嵌套遍历二位数组,把每一个元素赋值成空格即可
回到game函数
下一步就是打印棋盘,我们要把棋盘打印出来,不然玩家怎么能看到呢?进入该函数
这里每个人有自己美化的风格,我比较喜欢的就是我这种
每一个小格子里面有两个空格,两个空格之间夹着对应行、列的元素(因为这里元素也是空格所以不太好分辨,可以去上面看代码运行结果)
再回到game函数
好了,准备工作做完了,该开始下棋了,注意这里需要放到循环里面,一直一直到分出胜负或者平局(棋盘满了),先不看代码函数怎么实现,我们先剖析底层逻辑,当玩家下完棋以后,我们要打印出现在的棋盘表示玩家下在了哪里,再判断玩家是否获胜,如果获胜了那电脑就不需要下了。如果没获胜,那电脑再接着下,同理,也需要判断是否获胜,以此循环。
下面看玩家下棋的代码
这里输入x和y,x和y属于1到3,所以要进行判断只有输入的x、y符合标准再下棋,但是我们知道数组的下标是从0开始的(后面数组总结会说明),所以外if语句内部的if要-1。这里还要进行判断,如果这个地方没有棋,也就是x、y所对应元素为空格时再下,这里标记玩家下的棋是*。如果有棋了,就需要重新下。
再来看电脑下棋的逻辑:
这里可以进行优化,但是因为我实在是太菜了,还写不出来相关的AI算法,只能随机生成x和y,这里用到了rand函数(随机生成数字),需要配合rand函数的定义
具体如何使用大家可以参考MSDN上的定义
下完棋当然要判断输赢,然后再来看判断输赢的函数
分别判断每一行、每一列、两条对角线的元素是否出现全为*或者#的情况,如果出现则分别返回*或者#。还有一种情况就是平局,也就是棋盘下满了,这里再来看如何判断棋盘满
这里一样,遍历二维数组,看能不能找到空格,如果能找到就说明没下完,继续下,如果下完了就没找到空格,平局。
这里注意如果平局了IsFull函数就会返回 1,执行if语句,返回字符E,没平局就继续下,IsFull返回0,执行else语句返回字符C
主要不是继续下,就一直循环,直到出现赢家或者平局跳出循环
然后再分别打印对应结果
至此所有代码完成
下面来进行优化,来看一下堵棋版本,只需要把电脑下棋的函数替换掉就可以
void computer_move(char board[ROW][COL], int row, int col)
{
printf("电脑下棋->\n");
int x = 0;
int y = 0;
int i = 0;
int j = 0;
int count1 = 0;//对角线
int count2 = 0;//对角线
for (i = 0; i < row; i++)//遍历每一行看需不需要堵棋
{
int count = 0;
for (j = 0; j < col; j++)
{
if (board[i][j] == '*')
count++;
}
if (count == 2)//这一行有两个棋了,要堵棋
{
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
{
board[i][j] = '#';
return;//结束函数
}
}
}
}
for (j = 0; j < col; j++)//遍历每一列看需不需要堵棋
{
int count = 0;
for (i = 0; i < row; i++)
{
if (board[i][j] == '*')
count++;
}
if (count == 2)//这一列有两个棋了,要堵棋
{
for (i = 0; i < row; i++)
{
if (board[i][j] == ' ')
{
board[i][j] = '#';
return;//结束函数
}
}
}
}
for (i = 0; i = 0; i++, j--)//遍历副对角线
{
if (board[i][j] == '*')
count2++;
}
if (count2 == 2)
{
for (i = 0, j = col - 1; i < row && j >= 0; i++, j--)
{
if (board[i][j] == ' ')
{
board[i][j] = '#';
return;
}
}
}
//当不需要堵棋的时候,随便下。
while (1)
{
x = rand() % row;//x属于0到2
y = rand() % col;//y属于0到2
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '#';
return;
}
}
}
想要源代码的也可以找我私聊把整个工程发给你
其实发现只要思路清晰,按部就班一步一步来,代码还是很好理解很好写出来的,重在耐心与调试,中了继续肝博客了,下次整个扫雷。