- 相信大家小时候都有听说过或者玩过三子棋,你是否还记得那时玩三子棋的快乐呢
- 今天我们就来用C语言写一个简易的三子棋,让你回忆起儿时的快乐吧
- 玩家和电脑轮流下棋子,若有一方下的棋子先连成一个棋子总数为3的直线,则获胜
- 若所有的位置都下完了,还没有分出胜负的话,即为平局
- 首先我们可以先写一个游戏的界面
- 那像这样的界面怎么编写呢,
- 首先可以先定义一个打印菜单的函数
- 注:为了让代码的结构更加清晰,我们一般会在VS上分别创建 test.c 、game,c和game.h 头文件来编写代码
- test.c 为主函数的编写和测试
- game.c 为自定义函数的编写
- game.h 头文件为函数的申明
- 以下的代码我会在第一行注释应该写在哪个文件里
void menu()//game.c文件
{
printf("********************\n");
printf("*****1.开始游戏*****\n");
printf("*****0.结束游戏*****\n");
printf("********************\n");
}
- 直接利用 printf 函数打印即可
- 接着就是接受输入的 0或1 ,然后编写对应的执行逻辑
- 这里我们用一个 game 函数来实现游戏的功能
int main()//test.c文件
{
int input = 0;
srand((unsigned int)time(NULL));//生成随机数种子
do
{
printf("\n");
menu();
printf("请输入1或0\n-->");
scanf("%d", &input);
switch (input)
{
case 1:
{
game();//开始游戏
break;
}
case 0:
{
printf("游戏结束\n");
break;
}
default:
{
printf("输入错误,请重新输入\n");
}
}
} while (input != 0);
return 0;
}
- 因为这是一个3 * 3的游戏,所以我们可以考虑用一个3 * 3的数组来实现
- 首先就是要定义一个数组并对其初始化
- 这里我们可以先把数组的元素都初始化为空格
#pragma once//game.h文件
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 3
#define COL 3
void chushihua(char board[ROW][COL], int row, int col)//game.c文件
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
board[i][j] = ' ';//初始化所有元素为空格
}
}
}
void game(void)
{
char board[ROW][COL] = { 0 };
chushihua(board, ROW, COL);//初始化棋盘
}
- 这里我们用宏定义棋盘的大小,这样在后期如果我们想要改棋盘的大小只要改宏定义即可
- 接下来就是打印棋盘,这里的打印棋盘不仅仅只是把所有的数组元素都一个个打印出来,还需要加一点修饰
- 可以参考这样的棋盘格式
- 总共有九个大空格,仔细看会发现每一个大空格由三个小空格组成,
- 其中中间的那个空格就可以用来放数组的元素,
- 然后每个大空格在每一行上用 “ | ” 隔开,在每一竖行上用三个 “ - ” 隔开
- 那应该怎么实现这样的棋盘呢?
- 我们可以分分组,然后一组一组打印,把大化小,
- 我们再想一下,数组的行数和列数正好是 3 ,那我们可以分成三组吗?这样我们就可以和行数和列数关联起来了
- 我们可以这样分成三块
- 有的同学可能要反驳了,那你这分的三组也不一样啊,
- 对喽,最后一行确实不一样,那我们是不是可以控制一定的条件是的最后一行少输出一点东西呢
- 同样的,我们也可以对每一行也分成三块
- 分成三块后我们发现,最后一列依然和前两列不一样,但是也差那么一点。
- 那我们是不是也可以照着上面的行也对分出来的最后一列做一点操作使得最后一列少一点东西呢?
- 我们具体看代码的实现
void dayin(char board[ROW][COL], int row, int col) //game.c文件
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if (j < col -1)
{
printf("|");
}//最右边不打印"|"
}
printf("\n");
if (i < row - 1)
{
for (int j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
}//最下面不打印 "---"
}
}
void game(void)
{
char board[ROW][COL] = { 0 };
chushihua(board, ROW, COL);//初始化棋盘
dayin(board, ROW, COL);//打印棋盘
}
- 这里我可以看到,我们利用行数作为循环的条件,
- 当打印到最后一行的时候,我们可以利用 if 语句不打印最后一行和最后一列
- 接下来我们来实现玩家下棋的功能,我们可以让玩家输入坐标的方式来布置棋子
- 首先玩家下棋的时候,对应的位置需要时空的,并且输入的坐标不能超出棋盘的范围
- 接下来编写对应的代码
int wjxiaqi(char board[ROW][COL], int row, int col)//game.c文件
{
printf("玩家下棋(请输入坐标)-->");
static int n = 0;//记录下棋次数
while (1)
{
int x, y;
scanf("%d %d", &x, &y);
if (1 <= x && x <= row && 1 <= y && y <= col)
{
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';//玩家下的棋用 " * "来表示
n++;
break;
}
else
{
printf("该位置已经下过棋了,请重新输入-->\n");
}
}
else
{
printf("输入坐标超出范围,请重新输入-->\n");
}
}
return n;
}
void game(void)
{
char board[ROW][COL] = { 0 };
chushihua(board, ROW, COL);//初始化棋盘
dayin(board, ROW, COL);//打印棋盘
wjxiaqi(board, ROW, COL);//玩家下棋
}
- 这里定义一个常变量 n 来记录下棋的次数,并且函数的返回值为整型就是为后面判断谁输谁赢
- 因为我们玩家输入的时候是从 1 开始输入的,而数组的下标是从 0 开始,所以为 board[x-1][y-1]
- 当玩家输入错误的时候应该重新输入,所以这里用到了 while 循环来实现
- 这里就利用随机数随机生成一个坐标来实现,当然这样的电脑是不聪明的
- 想要让三子棋更加好玩的可以参考这个文章——电脑下棋优化
void dnxiaqi(char board[ROW][COL], int row, int col)//game.c文件
{
printf("电脑下棋-->\n");
while (1)
{
int x = rand() % 3;
int y = rand() % 3;//生成0-2的随机数
if (board[x][y] == ' ')
{
board[x][y] = '#';//电脑下的棋用 “ # ”来表示
break;
}
}
}
void game(void)
{
char board[ROW][COL] = { 0 };
chushihua(board, ROW, COL);//初始化棋盘
dayin(board, ROW, COL);//打印棋盘
while (1)
{
wjxiaqi(board, ROW, COL);//玩家下棋
dnxiaqi(board,ROW, COL);//电脑下棋
}
}
- 这里用到了随机数的知识, 如果不是很理解可以看一下我之前的文章——猜数字游戏
- 这里玩家和电脑下棋应该你一个我一个,所以用到了 while 循环来实现
- 首先看每一行或者每一列是否都一样
- 接着看对角线是否是一样的
- 如果当棋子都下完了,但是还是没有满足以上两个,那就是平局了
- 代码如下
char panduan(char board[ROW][COL], int row, int col, int n)//game.c文件
{
for (int i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] == '*'
|| board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] == '*')
{
return '*';
}//判断行和列
else if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] == '*'
|| board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] == '*')
{
return '*';
}//判断对角线
else if (board[i][0] == board[i][1]&& board[i][1] == board[i][2]&& board[i][1] == '#'
|| board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] == '#')
{
return '#';
}
else if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] == '#'
|| board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] == '#')
{
return '#';
}
}
if (n == (row * col) / 2 + 1)
{
return 'Q';
}//棋盘都下完了
return 'C';//还未分出胜负且棋盘还未下完
}
void game(void)
{
int num = 0;
char flag = '0';
char board[ROW][COL] = { 0 };
chushihua(board, ROW, COL);//初始化棋盘
dayin(board, ROW, COL);//打印棋盘
while (1)
{
num = wjxiaqi(board, ROW, COL);//玩家下棋
flag = panduan(board, ROW, COL, num);
dayin(board, ROW, COL);
if (flag != 'C')
{
if (flag == '*')
{
printf("玩家赢了!\n");
break;
}
else if (flag == 'Q')
{
printf("平局!\n");
break;
}
}//判断输赢
dnxiaqi(board, ROW, COL);//电脑下棋
flag = panduan(board, ROW, COL, num);
dayin(board, ROW, COL);
if (flag == '#')
{
printf("电脑赢了!\n");
}
}
}
- 这里通过判断输赢的函数的返回值来确定谁输谁赢还是平局
- 当然这个代码还是有局限性的,因为当棋盘更大的时候,就不再适用了
- 最后把所有函数的申明写到game.h头文件里即可
void menu();//game.h文件
void chushihua(char board[ROW][COL], int row, int col);
void dayin(char board[ROW][COL], int row, int col);
int wjxiaqi(char board[ROW][COL], int row, int col);
void dnxiaqi(char board[ROW][COL], int row, int col);
char panduan(char board[ROW][COL], int row, int col, int n);
void game(void);
- 完整的代码可以点这里直达我的gitee仓库复制源码——game_sanziqi
最后,
如果上述代码或表述有问题,
欢迎一起交流