三子棋又名井字棋,是个老少皆宜的小游戏,相信大家都玩过吧,游戏规则就不多说了。今天我们就来用 C 语言来简单的实现它。
void menu()
{
printf("*****************************\n");
printf("******* 1.play ********\n");
printf("******* 0.exit ********\n");
printf("*****************************\n");
}
首先,我们思考一下,本次游戏应该最少执行一次,在执行过程中进行选择,那么我们应该使用 do while 循环来实现。根据选择不同,来执行相应的程序,那么应该使用 switch 语句。
下面我们来看代码:
int main()
{
int input = 0;
do
{
menu(); //每次在选择前打印菜单
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game(); //开始游戏
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择!\n");
break;
}
} while (input); //根据输入的值来确定是否进行循环,0则退出循环
return 0; //这也是为什么在菜单中把退出游戏设置成0的原因
}
我们根据预期画面可以判断出,我们应该创建一个 3*3 的数组来存放我们下的棋,并且得把数组初始化为空格。我们可以根据坐标来选择落子位置。那么问题来了,我们应该如何打印棋盘呢?
首先,我们来仔细观察一下画面中的棋盘,我们可以发现每次下棋都是下在格子的中间也就是下图:
也就是说每个数的旁边有空格,同行的数之间用 | 隔开,第二行是由 - 和 | 组成。
我们把第一行和第二行看成一组,我们就可以得到三组(假设第三组第二行存在)
也就是像下图:
#define M 3
#define N 3
void game()
{
char arr[M][N] = { 0 };
chu_shi(arr, M, N); //初始化数组
da_yin(arr, M, N); //打印棋盘
}
void chu_shi(char arr[M][N], int m, int n)
{
int i = 0;
int j = 0;
for(i=0;i<m;i++)
for (j = 0; j < n; j++)
{
arr[i][j] = ' '; //将数组初始化为空格
}
}
void da_yin(char arr[M][N], int m, int n)
{
int i = 0;
for (i; i < m; i++)
{
printf(" %c | %c | %c \n", arr[i][0], arr[i][1], arr[i][2]);
}
if (i < m - 1) //若没有 if 则打印了第三组的第二行,if 起限制作用
printf("---|---|---\n");
}
这样,我们就打印出来了整个棋盘。但有个小问题,就是如果我改动棋盘大小呢,现在是 3 * 3 ,我改动 M N 的值为 5 5 呢,我们可以看出,行是没错的,但是列只打印了三列,这明显不行,于是我们按照打印行的思路来打印列,同样的将列分成三组,如图:
void da_yin(char arr[M][N], int m, int n)
{
int i = 0;
for (i; i < m; i++)
{
int j = 0;
for (j = 0; j < n; j++)
{
printf(" %c ", arr[i][j]);
if (j < n - 1) //限制 | 的打印
printf("|");
}
printf("\n");
if (i < m - 1)
{
for (j = 0; j < n; j++)
{
printf("---");
if (j < n - 1) //限制 | 的打印
printf("|");
}
}
printf("\n");
}
}
接下来我们应该开始正式玩游戏了,也就是输入落子位置。
我们思考一下,我们坐标应该合理,不能超过数组规定,还有我们下的子应该保存下来,下次该点不能被选择。
我们可以写出如下代码:
void wan_jia(char arr[M][N], int m, int n)
{
int x = 0;
int y = 0; //因为玩家可能不知道是从0行0列开始的
printf("玩家下棋:>\n"); //所以我们就设置成从第1行1列到第3行3列
while (1)
{
printf("请输入要下棋的坐标:>\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= m && y >= 1 && y <= n)
{
if (arr[x - 1][y - 1] == ' ')
{
arr[x - 1][y - 1] = '*';
break;
}
else
{
printf("该坐标被占用,请重新输入\n");
}
}
else
{
printf("坐标非法请重新输入");
}
}
}
现在我们下了一步,接下来轮到电脑了,同样的逻辑,但电脑下棋是随机的,因此我们要生成随机数,随机数的生成代码如下:
#include
#include
#include
int main()
{
srand((unsigned int)time(NULL)); //设置一个随机数的生成器
int m = rand()%3; //因为 rand 生成随机数范围0~32767
printf("%d",m); //所以 %3 使随机数生成范围为0~2
}
因此电脑下棋代码我们就可以写出了:
srand((unsigned int)time(NULL)); //我们需要放在 main 函数中
void dian_nao(char arr[M][N], int m, int n)
{
printf("电脑下棋:>\n");
while (1)
{
int x = rand() % m;
int y = rand() % n;
if (arr[x][y] == ' ')
{
arr[x][y] = '#';
break;
}
}
}
这样我们就能完成玩家与电脑之间的对弈,但是美中不足的是,我们无法判断输赢。因此,下一步我们判断输赢。
我们知道游戏过程中会有几种状态:玩家赢、电脑赢、平局、游戏继续,这四种状态我们就分别用 ’ * ’ 、’ # ‘、’ Q ‘、’ C '来作为返回值。
我们知道三子棋的无非就是横或竖或斜三子成线,这样我们便能写出如下代码:
char is_win(char arr[M][N], int m, int n)
{ //我们要返回字符所以用 char
int i = 0;
for (i = 0; i < m; i++) //判断行
{
if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2] && arr[i][0] != ' ')
{
return arr[i][0];
}
}
for (i = 0; i < n; i++) //判断列
{
if (arr[0][i] == arr[1][i] && arr[1][i] == arr[2][i] && arr[0][i] != ' ')
{
return arr[0][i];
}
}
//以下判断两条斜线
if (arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[0][0] != ' ')
{
return arr[0][0];
}
if (arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0] && arr[1][1] != ' ')
{
return arr[1][1];
}
}
我们已经可以判断输赢了,但是我们似乎还漏了平局。
我们便再创建个函数来遍历 arr 数组,来判断是否还有空格,如果有也就是棋盘没满,返回0,反之返回1。
由此,我们可以写出如下代码:
//如果棋盘满了,返回1
//不满返回0
int is_full(char arr[M][N], int m, int n)
{
int i = 0;
for (i = 0; i < m; i++)
{
int j = 0;
for (j = 0; j < n; j++)
{
if (' ' == arr[i][j])
return 0;
}
}
return 1;
}
我们再将其插入判断属于代码中,可以得到:
char is_win(char arr[M][N], int m, int n)
{
int i = 0;
for (i = 0; i < m; i++)
{
if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2] && arr[i][0] != ' ')
{
return arr[i][0];
}
}
for (i = 0; i < n; i++)
{
if (arr[0][i] == arr[1][i] && arr[1][i] == arr[2][i] && arr[0][i] != ' ')
{
return arr[0][i];
}
}
if (arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[0][0] != ' ')
{
return arr[0][0];
}
if (arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0] && arr[1][1] != ' ')
{
return arr[1][1];
}
//判断平局
if (is_full(arr, m, n) == 1)
{
return 'Q';
}
//继续
return 'C';
}
我们将 game 函数的实现过程,调用的函数了解了一遍,下面我们来看具体 game 函数如何实现:
void game()
{
char ret = 0;
char arr[M][N] = { 0 };
chu_shi(arr, M, N); //首先,我们初始化数组
da_yin(arr, M, N); //打印一下棋盘
while (1) //进行游戏
{
wan_jia(arr, M, N); //玩家先下
ret = is_win(arr, M, N); //每下一步进行判断
if (ret != 'C') //判断是否出现输赢或平局·
break; //'C'代表继续,不等于 'C' 即出现了结果
//break 跳出循环
da_yin(arr, M, N); //每下完一步再打印一下棋盘
dian_nao(arr, M, N); //电脑下
ret = is_win(arr, M, N);
if (ret != 'C')
break;
da_yin(arr, M, N);
}
if (ret == '*')
printf("玩家赢\n");
else if (ret == '#')
printf("电脑赢\n");
else
printf("平局\n");
da_yin(arr, M, N); //最后游戏结束打印棋盘
}
注意,我们判断输赢都是在打印棋盘之前的,如果出了结果,我们是不知道棋盘状况的,所以游戏最后应该打印棋盘。
像这样一个游戏,我们应该分三个部分:game.h、test.c、game.c。
game.h : 包含头文件和对 test.c 中函数的声明。
test.c :将函数封装起来,使得 game.c 尽量简洁
game.c : 游戏主体,包含 main 函数。
#pragma once
#include
#include
#include
#define M 3
#define N 3
//初始化数组
void chu_shi(char arr[M][N], int m, int n);
//打印棋盘
void da_yin(char arr[M][N], int m, int n);
//玩家下棋
void wan_jia(char arr[M][N], int m, int n);
//电脑下棋
void dian_nao(char arr[M][N], int m, int n);
//判断游戏状态
char is_win(char arr[M][N], int m, int n);
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//初始化数组
void chu_shi(char arr[M][N], int m, int n)
{
int i = 0;
int j = 0;
for(i=0;i<m;i++)
for (j = 0; j < n; j++)
{
arr[i][j] = ' ';
}
}
//打印棋盘
void da_yin(char arr[M][N], int m, int n)
{
int i = 0;
for (i; i < m; i++)
{
int j = 0;
for (j = 0; j < n; j++)
{
printf(" %c ", arr[i][j]);
if (j < n - 1)
printf("|");
}
printf("\n");
if (i < m - 1)
{
for (j = 0; j < n; j++)
{
printf("---");
if (j < n - 1)
printf("|");
}
}
printf("\n");
}
}
//玩家下棋
void wan_jia(char arr[M][N], int m, int n)
{
int x = 0;
int y = 0;
printf("玩家下棋:>\n");
while (1)
{
printf("请输入要下棋的坐标:>\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= m && y >= 1 && y <= n)
{
if (arr[x - 1][y - 1] == ' ')
{
arr[x - 1][y - 1] = '*';
break;
}
else
{
printf("该坐标被占用,请重新输入\n");
}
}
else
{
printf("坐标非法请重新输入");
}
}
}
//电脑下棋
void dian_nao(char arr[M][N], int m, int n)
{
printf("电脑下棋:>\n");
while (1)
{
int x = rand() % m;
int y = rand() % n;
if (arr[x][y] == ' ')
{
arr[x][y] = '#';
break;
}
}
}
//如果棋盘满了,返回1
//不满返回0
int is_full(char arr[M][N], int m, int n)
{
int i = 0;
for (i = 0; i < m; i++)
{
int j = 0;
for (j = 0; j < n; j++)
{
if (' ' == arr[i][j])
return 0;
}
}
return 1;
}
//判断输赢
char is_win(char arr[M][N], int m, int n)
{
int i = 0;
for (i = 0; i < m; i++)
{
if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2] && arr[i][0] != ' ')
{
return arr[i][0];
}
}
for (i = 0; i < n; i++)
{
if (arr[0][i] == arr[1][i] && arr[1][i] == arr[2][i] && arr[0][i] != ' ')
{
return arr[0][i];
}
}
if (arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[0][0] != ' ')
{
return arr[0][0];
}
if (arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0] && arr[1][1] != ' ')
{
return arr[1][1];
}
//判断平局
if (is_full(arr, m, n) == 1)
{
return 'Q';
}
//继续
return 'C';
}
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h" //包含 game.h
void menu()
{
printf("*****************************\n");
printf("******* 1.play ********\n");
printf("******* 0.exit ********\n");
printf("*****************************\n ");
}
void game()
{
char ret = 0;
char arr[M][N] = { 0 };
chu_shi(arr, M, N);
da_yin(arr, M, N);
while (1)
{
wan_jia(arr, M, N);
ret = is_win(arr, M, N);
if (ret != 'C')
break;
da_yin(arr, M, N);
dian_nao(arr, M, N);
ret = is_win(arr, M, N);
if (ret != 'C')
break;
da_yin(arr, M, N);
}
if (ret == '*')
printf("玩家赢\n");
else if (ret == '#')
printf("电脑赢\n");
else
printf("平局\n");
da_yin(arr, M, N);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL)); //设置一个随机数的生成器
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择!\n");
break;
}
} while (input);
return 0;
}
这样我们的三子棋游戏就圆满完成了,咋一看似乎头晕晕的,但是如果我们把它拆分为一个个小步骤,我们便能轻易理解并写出来。
学习就是这样,碰到难题往往拆分步骤就变简单了。
以后会给大家带来惊险又刺激的扫雷游戏。
那我们下期见了~