前言
三子棋又名井字棋,是个老少皆宜的小游戏,相信大家都玩过吧,游戏规则就不多说了。今天我们就来用 C 语言来简单的实现它。
一、游戏预期画面
二、游戏的实现
1、菜单
void menu()
{
printf("*****************************\n");
printf("******* 1.play ********\n");
printf("******* 0.exit ********\n");
printf("*****************************\n");
}
2、主函数
首先,我们思考一下,本次游戏应该最少执行一次,在执行过程中进行选择,那么我们应该使用 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);
return 0;
}
3、game函数的实现
3.1、棋盘的打印
我们根据预期画面可以判断出,我们应该创建一个 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)
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");
}
}
3.2、玩家下棋
接下来我们应该开始正式玩游戏了,也就是输入落子位置。 我们思考一下,我们坐标应该合理,不能超过数组规定,还有我们下的子应该保存下来,下次该点不能被选择。
我们可以写出如下代码:
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("坐标非法请重新输入");
}
}
}
3.3、电脑下棋
现在我们下了一步,接下来轮到电脑了,同样的逻辑,但电脑下棋是随机的,因此我们要生成随机数,随机数的生成代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
srand((unsigned int)time(NULL));
int m = rand()%3;
printf("%d",m);
}
因此电脑下棋代码我们就可以写出了:
srand((unsigned int)time(NULL));
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;
}
}
}
这样我们就能完成玩家与电脑之间的对弈,但是美中不足的是,我们无法判断输赢。因此,下一步我们判断输赢。
3.4、判断输赢
我们知道游戏过程中会有几种状态:玩家赢、电脑赢、平局、游戏继续,这四种状态我们就分别用 ’ * ’ 、’ # ‘、’ Q ‘、’ C '来作为返回值。 我们知道三子棋的无非就是横或竖或斜三子成线,这样我们便能写出如下代码:
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];
}
}
我们已经可以判断输赢了,但是我们似乎还漏了平局。 我们便再创建个函数来遍历 arr 数组,来判断是否还有空格,如果有也就是棋盘没满,返回0,反之返回1。 由此,我们可以写出如下代码:
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';
}
3.5、game函数的真正实现
我们将 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;
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);
}
注意,我们判断输赢都是在打印棋盘之前的,如果出了结果,我们是不知道棋盘状况的,所以游戏最后应该打印棋盘。
4、游戏具体代码
像这样一个游戏,我们应该分三个部分:game.h、test.c、game.c。 game.h : 包含头文件和对 test.c 中函数的声明。 test.c :将函数封装起来,使得 game.c 尽量简洁 game.c : 游戏主体,包含 main 函数。
4.1、game.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#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);
4.2、test.c
#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;
}
}
}
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';
}
4.3、game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"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;
}
这样我们的三子棋游戏就圆满完成了,咋一看似乎头晕晕的,但是如果我们把它拆分为一个个小步骤,我们便能轻易理解并写出来。 学习就是这样,碰到难题往往拆分步骤就变简单了。
下期预告
下期肯定要给大家带来惊险又刺激的扫雷了。 那我们下期见了~
|