三子棋
三子棋规则介绍
如何用c语言实现三子棋
基本思路
分部代码实现
游戏菜单
初始化棋盘
打印棋盘
玩家下棋
电脑下棋
判定胜负关系
game()函数
总代码实现
game.h
game.c
test.c
玩游戏
总结
三子棋算法有很多,这里仅是我个人的介绍,这种方法简单实现易于理解,特别适合新手实操。
三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、井字棋等。将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了。但是,有很多时候会出现和棋的情况。
对于游戏实现的代码,我们最好分三个板块来写,而不是在一个源文件里面写。这里应该分类创建三个文件,test.c:游戏整体的逻辑。game.h:关于游戏相关函数的声明,头文件的包含。game.c:游戏相关函数的实现。
创建游戏菜单,供用户选择玩游戏,还是退出游戏。
创建一个棋盘数组,并将它初始化。
打印一个棋盘。
玩家下棋(通过用户输入的坐标落子)
判定胜负关系。
电脑下棋(通过随机函数让它落子)
判定胜负关系。
游戏结束。
void menu()
{
printf("********************\n");
printf("****** 1.play ******\n");
printf("****** 0.exit ******\n");
printf("********************\n");
}
int main()
{
int input = 0;
printf("请选择:");
srand((unsigned int)time(NULL)); //使用rand函数要调用srand
do
{
menu();
scanf_s("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default :
printf("输入错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
这里涉及到用户的选择,所以需要把主函数部分展现给大家看,利用do while语句实现游戏的重复,使用switch语句接收用户的选择,选择1则进入game()函数开始玩游戏,选择0则退出游戏,如果未按要求输入就重新选择,这里的条件设置为input非常巧妙,只有输入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] = ' '; //初始化棋盘为空格
}
}
}
棋盘可以看成一个数组,将棋盘初始化即利用for循环将棋盘数组每个元素初始化为空格。
void Displayboard(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++)
{ //打印一行
printf(" %c ",board[i][j]); //将字符加竖线作为一个整体,分开打印
if (j < col-1)
{
printf("|"); //最后一列不用打印竖线
}
}
printf("\n");//记得每打印一行就换行
//打印多行
if (i < row-1) //最后一行不打印下划线,不是最后一行都要打印
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("___");
if (j < col-1)
{
printf("|");
}
}
printf("\n");
}
}
}
下面我们来看看打印出来的棋盘:
这里有什么不懂得可以看我代码里的注释,解释的非常详细,我就不另外解释了。
void Playerboard(char board[ROW][COL], int row, int col) //玩家下棋
{
int x = 0;
int y = 0;
while (1)
{ //每次输入坐标下棋是一个循环,所以用while
printf("玩家下棋,请输入坐标:\n");
scanf_s("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) //判断坐标合法性
{
if (board[x - 1][y - 1] == ' ') //如果坐标没被占用,玩家输入的坐标与数组元素下标相差1
{
board[x - 1][y - 1] = '*';
break; //玩家每下完一次要退出循环
}
else
{
printf("坐标被占用请重新输入!\n"); //输入错误,没有break,就返回循环
}
}
else
{
printf("坐标不合法请重新输入!\n");
}
}
}
这里有两个关键点:
(1)确定坐标的合法性,用户输入的坐标不能超过数组下标。
(2)判断坐标有没有被占用,如果没有被占用则玩家下棋,我把这里玩家下的棋为'*'号。
void Computerboard(char board[ROW][COL], int row, int col) //电脑下棋
{
printf("电脑下棋:\n");
while (1)
{
//要使电脑下棋合法即让随机数模坐标,其范围肯定不超过行和列
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ') //这里x和y不用减1,因为x和y的值不会等于行和列的数值
{
board[x][y] = '#';
break; //电脑下完棋要退出循环,让玩家下棋
}
}
}
让电脑下棋则比较简单,使用rand随机函数即可,但这里要注意的是使用rand函数必须在主函数调用srand。
如果玩家赢则返回‘*’号,如果电脑赢则返回‘#’号,平局返回‘Q’。
判定是否平局:
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; //没满,返回0,与后面的字符ret对应,字符ret就返回c;
}
}
}
return 1; //当所有棋盘都遍历完之后没有空格则表示满了,返回1;与后面Iswin函数里面的ret返回值对应
}
判定胜负:
char Iswin(char board[ROW][COL], int row, int col) //判断胜负函数
{
int i = 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[2][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[2][0] != ' ')
{
return board[1][1];
}
//Isfull函数判断平局,这里设计整型ret返回值与game()里的字符ret(用于接收iswin字符返回值判断输赢)对应
int ret = Isfull(board, ROW, COL); //这里的ret用来判断是否平局,如果返回1则返回Q,与前面的char ret对应,表示平局,不是返回1则返回c,游戏继续;
if (ret == 1)
{
return 'Q';
}
return 'C';
}
void game()
{
char board[ROW][COL];
Initboard(board, ROW, COL);
Displayboard(board, ROW, COL);
char ret = 0; //接收游戏返回的值
while (1)
{
Playerboard(board, ROW, COL);//玩家下棋
Displayboard(board, ROW, COL); //打印棋盘
ret = Iswin(board, ROW, COL); //判断是否赢
if (ret != 'C')
{
break; //不是c则退出循环
}
Computerboard(board, ROW, COL); //电脑下棋
Displayboard(board, ROW, COL); //打印棋盘
ret = Iswin(board, ROW, COL); //判断电脑赢
if (ret != 'C')
{
break; //跳出循环,不下了
}
}
if (ret == '*')
{
printf("玩家赢了!\n");
}
else if (ret == '#')
{
printf("电脑赢了!\n");
}
else
{
printf("平局!\n");
}
Displayboard(board, ROW, COL);
}
game()函数是游戏的整体模型,包含了菜单打印,棋盘初始化,打印棋盘,玩家下棋,电脑下棋以及判定胜负这些函数的调用,这些函数一步一步实现以保证整个游戏的进行。这里要注意的是玩家和电脑下完棋后都要一一判定胜负。
这里写在一起的话不便于理解和修改,所以我分了三个部分:
#pragma once
#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 Playerboard(char board[ROW][COL], int row, int col);
void Computerboard(char board[ROW][COL], int row, int col);
char Iswin(char board[ROW][COL], int row, int col);
#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++)
{
for (j = 0; j < col; j++)
{ //打印一行
printf(" %c ",board[i][j]); //将字符加竖线作为一个整体,分开打印
if (j < col-1)
{
printf("|"); //最后一列不用打印竖线
}
}
printf("\n");//记得每打印一行就换行
//打印多行
if (i < row-1) //最后一行不打印下划线,不是最后一行都要打印
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("___");
if (j < col-1)
{
printf("|");
}
}
printf("\n");
}
}
}
void Playerboard(char board[ROW][COL], int row, int col) //玩家下棋
{
int x = 0;
int y = 0;
while (1)
{ //每次输入坐标下棋是一个循环,所以用while
printf("玩家下棋,请输入坐标:\n");
scanf_s("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) //判断坐标合法性
{
if (board[x - 1][y - 1] == ' ') //如果坐标没被占用,玩家输入的坐标与数组元素下标相差1
{
board[x - 1][y - 1] = '*';
break; //玩家每下完一次要退出循环
}
else
{
printf("坐标被占用请重新输入!\n"); //输入错误,没有break,就返回循环
}
}
else
{
printf("坐标不合法请重新输入!\n");
}
}
}
void Computerboard(char board[ROW][COL], int row, int col) //电脑下棋
{
printf("电脑下棋:\n");
while (1)
{
//要使电脑下棋合法即让随机数模坐标,其范围肯定不超过行和列
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ') //这里x和y不用减1,因为x和y的值不会等于行和列的数值
{
board[x][y] = '#';
break; //电脑下完棋要退出循环,让玩家下棋
}
}
}
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; //没满,返回0,与后面的字符ret对应,字符ret就返回c;
}
}
}
return 1; //当所有棋盘都遍历完之后没有空格则表示满了,返回1;与后面Iswin函数里面的ret返回值对应
}
char Iswin(char board[ROW][COL], int row, int col) //判断胜负函数
{
int i = 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[2][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[2][0] != ' ')
{
return board[1][1];
}
//Isfull函数判断平局,这里设计整型ret返回值与game()里的字符ret(用于接收iswin字符返回值判断输赢)对应
int ret = Isfull(board, ROW, COL); //这里的ret用来判断是否平局,如果返回1则返回Q,与前面的char ret对应,表示平局,不是返回1则返回c,游戏继续;
if (ret == 1)
{
return 'Q';
}
return 'C';
}
#include
#include"game.h"
void menu()
{
printf("********************\n");
printf("****** 1.play ******\n");
printf("****** 0.exit ******\n");
printf("********************\n");
}
void game()
{
char board[ROW][COL];
Initboard(board, ROW, COL);
Displayboard(board, ROW, COL);
char ret = 0; //接收游戏返回的值
while (1)
{
Playerboard(board, ROW, COL);//玩家下棋
Displayboard(board, ROW, COL); //打印棋盘
ret = Iswin(board, ROW, COL); //判断是否赢
if (ret != 'C')
{
break; //不是c则退出循环
}
Computerboard(board, ROW, COL); //电脑下棋
Displayboard(board, ROW, COL); //打印棋盘
ret = Iswin(board, ROW, COL); //判断电脑赢
if (ret != 'C')
{
break; //跳出循环,不下了
}
}
if (ret == '*')
{
printf("玩家赢了!\n");
}
else if (ret == '#')
{
printf("电脑赢了!\n");
}
else
{
printf("平局!\n");
}
Displayboard(board, ROW, COL);
}
int main()
{
int input = 0;
printf("请选择:\n");
srand((unsigned int)time(NULL)); //使用rand函数要调用srand
do
{
menu();
scanf_s("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default :
printf("输入错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
让代码跑起来,我来跟电脑交交手:
轻而易举战胜电脑,哈哈。还挺好玩的,大家有兴趣的话可以玩玩,或者你来告诉我怎么让电脑永远赢不了,嘻嘻。
三子棋游戏考验了我们对数组的理解和运用,对循环分支语句的运用,关键是要理清思路,一步一步实现,像这种这么长的代码,我们最好是写完一部分就去运行一下,以防到最后出现错误很难去找。
本期文章就到这,希望能给大家带来帮助,那怕是一点点也好,我都非常高兴能帮助到大家,你们的认可是对我最大的帮助,你们的一键三连是我的动力。最后感谢大家的阅读,如有不足敬请谅解,欢迎大家批评指正!