三子棋详解(C语言)
各位老铁们好!三子棋或许是你们童年的美好回忆,因其简易的制作方法,使得我们能够随时随地的与小伙伴们共同享受三子棋给我们带来的乐趣。而这期博客,庄哥给你们带来如何用C语言实现三子棋。
话不多说,我们上流程图:
温馨提示:
1.博客末尾附有庄哥的源码链接
2.请老铁们自行引头文件和函数的声明,文中庄哥不再赘述哦
正文
一.制作游戏开始的选项
- 首先,我们需要制作进入游戏的菜单栏,打印出如图的效果
这一部分只需要用到printf函数,比较简单,我们直接上代码
void menu()
{
printf("****************************\n");
printf("******** 1.开始游戏 ********\n");
printf("******** 0.退出游戏 ********\n");
printf("****************************\n");
printf("请输入你的选择:>");
}
- 其次,我们需要制作选择不同选项后的效果,这里就要用到分支语句 switch 和循环语句 do while
我们设定:输入1为开始游戏,输入0为退出游戏,若输入其它数字则需要重新选择
#include<stdio.h>
int main()
{
int input;//创建input变量来接收我们的选择
do
{
menu();//打印菜单栏
scanf("%d",&input);//选择选项
switch(input)
{
case 1:
game();//game函数从这里进入,等会儿我们就来编写game函数
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
}
}while(input);
}
二.初始化棋盘以及打印棋盘
- 首先,在初始化棋盘时,我们需要设定棋盘的大小,这里可以用到庄哥的小妙招
我们可以直接在文件开头定义我们的行和列,分别为ROW和COL
本文中的棋盘采用3*3的大小
#define ROW 3
#define COL 3
当我们想改变棋盘大小的时候,直接将后面的3改成其它数字即可
- 其次,我们可以创建一个字符类型的二维数组来充当棋盘,注意是在game函数中创建,这里我们设数组名为board,同时我们创建Initboard函数来对数组进行初始化
void game()
{
char board[ROW][COL];
Initboard(board, ROW, COL);
}
- 然后,我们在Initboard函数中,将二维数组全部放入空格字符,即可完成初始化
void Initboard(char board[ROW][COL], int row, int col)
{
int i, j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
注意:我们此时只完成二维数组的创建和初始化,运行后还不能看见我们的棋盘
- 接下来,我们开始打印棋盘轮廓(稍微有点绕)
首先,我们要在game函数中创建新的函数printfboard来打印棋盘(类比上面的Initboard函数)
打印棋盘我们采用两种字符’ | ‘和’ — ',通过循环语句for和条件语句if来打印,这里的编程知识并不难,难的是能否理清其中的逻辑
void printboard(char board[ROW][COL], int row, int col)
{
int i, j,x;
for (i = 0; i < row; i++)//每一行
{
for (j = 0; j < col; j++)//用来打印' | | '
{
printf(" %c ", board[i][j]);
if (j < col - 1)//注意:这里'|'只需要打印两次
{
printf("|");
}
else
{
printf("\n");
}
}
for (x = 0; x < col; x++)//用来打印'---|---|---'
{
if (i < row - 1)//这里'---|---|---'只需要打印两次
{
printf("---");
if (x < col - 1)//注意:这里'|'同样只需要打印两次
{
printf("|");
}
else
{
printf("\n");
}
}
}
}
}
/*逻辑难点: 1.'|'的打印次数,及其限制条件如何编写
2.什么时候换行
3..如何在' | | '下面,穿插打印'---|---|---'
4.'---|---|---'的打印次数,及其限制条件如何编写*/
/*难点解析:1.加限制条件:当j<col-1时才打印
2.当j==col-1时才换行,可以理解为这时把'|'换成'\n'
3.写两个for循环
4.加限制条件:当j<row-1时才打印
运行之后,效果如下图所示
三.实现玩家和电脑在棋盘下棋
-
玩家下棋
思路:此部分我们需要注意两个限制条件:1.坐标是否在棋盘范围内 2.若在棋盘内,坐标是否被占用 不满足任一条件,则需要重新选择
根据思路,我们大概知道需要用到条件语句和循环语句,下面看庄哥的代码
void playermove(char board[ROW][COL], int row, int col)
{
int x, y;
while (1)
{
printf("请输入你的坐标:>");
scanf("%d,%d", &x, &y);
if (x > 0 && x <= row && y > 0 && y <= col)//注意:由于游戏面向的时大众,他们并不知道数组的下标从0开始,所以我们的限制条件是从1到row
{
if (board[x - 1][y - 1] == ' ')
{
board[x-1][y-1] = '*';//这里的[x-1][y-1]是由于上面的坐标范围调大了1,故放入坐标时,坐标得减去1
break;//若玩家放入棋子,则跳出循环
}
else
{
printf("坐标已被占用,请重新选择\n");
}
}
else
{
printf("坐标超出范围,请重新选择\n");
}
}
}
void game()
{
char board[ROW][COL];
Initboard(board, ROW, COL);
printboard(board, ROW, COL);
playermove(board, ROW, COL);//记得在game函数中引入playermove函数
}
- 电脑下棋
思路:重复产生两个0~2的随机数,作为横坐标与纵坐标。如果,随机产生的坐标上是空的,则填入字符’#',并跳出循环
编写电脑下棋则简单一些,只有一个难点:如何产生0~2的随机数?
解决方法:
(1).需要用到产生伪随机数的rand函数,并且我们需要在主函数中,引入srand函数,使得rand函数能够产生随机数,而不是伪随机数
(2).将产生的随机数除与3取余数
void computermove(char board[ROW][COL], int row, int col)
{
int x, y;
while (1)
{
x = rand() % 3;//取随机数除与3后的余数,即为0~2的数
y = rand() % 3;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
void game()
{
char board[ROW][COL];
Initboard(board, ROW, COL);
printboard(board, ROW, COL);
playermove(board, ROW, COL);
computermove(board, ROW, COL);
printboard(board, ROW, COL);//玩家和电脑各走一次后,应打印一次棋盘,各位老铁不要忘了
}
完成这玩家下棋与电脑下棋的函数后,效果应该如图所示:
四.判断输赢
好了,不知前三部分的难度老铁们是否能接受呢?
接下来我们进行最后一部分,有点小复杂,但解决它就能够实现三子棋啦!!!
- 编写Iswin函数(这里用i表示行,j表示列)
思路:首先,我们需要创建两个for循环,使得每一个坐标都能够覆盖到。其次,需要对每一个坐标的周围进行判断是否有三个连续的字符。这里我们需要知道无论是从上到下,还是从左至右扫描,还是沿着正反对角线扫描,都有它们的限制条件
- 当从上往下扫描时,i<row-2
- 当从左往右扫描时,i<col-2
- 当沿着正对角线扫描时,i<row-2&&i<col-2
- 当沿着反对角线扫描时,i>=2&&i<col-2
当所有的坐标都根据它所满足的限制条件进行扫描后,我们的判断就可以结束了!
char Iswin(char board[ROW][COL],int row,int col)
{
int i, j,x,y;
for (i = 0; i < row; i++)
{
for (j = 0; j < col ; j++)
{
if (j < col - 2)//从左至右扫描
{
if (board[i][j] == board[i][j + 1] && board[i][j + 1] == board[i][j + 2] && board[i][j] != ' ')
{
return board[i][j];
}
}
if (i < row - 2)//从上到下扫描
{
if (board[i][j] == board[i + 1][j] && board[i + 1][j] == board[i + 2][j] && board[i][j] != ' ')
{
return board[i][j];
}
}
if (i<row-2&&j<col-2)//沿正对角线扫描
{
if (board[i][j] == board[i + 1][j + 1] && board[i + 1][j + 1] == board[i + 2][j + 2] && board[i][j] != ' ')
{
return board[i][j];
}
}
if (i >= 2&&j<col-2)//沿反对角线扫描
{
if (board[i][j] == board[i - 1][j + 1] && board[i - 1][j + 1] == board[i - 2][j + 2] && board[i][j] != ' ')
{
return board[i][j];
}
}
}
}
//最后还要判断未决出胜负的情况
for (x = 0; x < row; x++)
{
for (y = 0; y < col; y++)
{
if (board[x][y] == ' ')
{
return 'C';
}
}
}
return 'Q';
}
- 在主函数中接收Iswin函数的返回值
思路:这里我们可以在game()函数中使用if语句来编写不同返回值所产生的结果,这里比较简单,我们直接上代码
void game()
{
char board[ROW][COL];
char ret;
Initboard(board, ROW, COL);
printboard(board, ROW, COL);
while (1)
{
playermove(board, ROW, COL);
computermove(board, ROW, COL);
printboard(board, ROW, COL);
ret = Iswin(board, ROW, COL);
//不同返回值处理不同的情况,这里也可以用到switch语句(但返回值得要整形),感兴趣的老铁可以自行编写,同样非常方便
if (ret == '#')
{
printf("电脑赢\n");
break;
}
else if (ret == '*')
{
printf("玩家赢\n");
break;
}
else if (ret == 'Q')
{
printf("平局\n");
break;
}
}
}
总结
好啦!不知各位老铁完成的怎么样呢?
三子棋游戏可以很好地锻炼到初学者的逻辑思维能力,但完成的过程中必定会出现许多的困难(庄哥当初写了三个晚上才弄出来,期间心态可谓大起大落),但只要我们沉下心,怀着“千淘万漉虽辛苦,吹尽狂沙始到金”的毅力和志气,庄哥相信各位老铁一定能够成为一名优秀的程序员!!!
最后附上三子棋函数的思维导图
下面附上三子棋源码地址:
https://gitee.com/zhuang-ge-c-language/test_c
文章有需要改进的地方,欢迎各位老铁们提出宝贵的建议!!!