当前位置:网站首页>[详解C语言]一文带你玩转C语言小游戏---三子棋
[详解C语言]一文带你玩转C语言小游戏---三子棋
2022-08-02 03:16:00 【Zoroxs】
作者简介:大家好我是zoroxs
个人主页:c/c++学习之路
如果觉得博主的文章还不错的话,请三连支持一下博主哦
今天来讲解一下三子棋,大家小时候应该都玩过,记得之前上课和同桌画来画去玩这个被老师骂,哈哈哈,
给大家说一下三子棋的规则----三子成杀
将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了
1.游戏整体思路
这就是游戏大体思路,图应该有些问题,我来给大家简述一下游戏思路
- 打印 3*3 棋盘
- 玩家下棋
- 判断是否三子成杀
- 电脑下棋
- 判断是否三子成杀
我们在这里规定:
玩家下棋的子用 ’ * ‘表示,电脑下棋的 棋子’ # ’
三个* 先成玩家胜,###先成电脑胜,如果棋盘下满了,还没分出胜负,则平局
2.棋盘设计
下棋必不可少的两个东西,就是棋盘和棋子,我们先来设计一下棋盘 3 * 3的棋盘,我们很容易就想到了前边学过的二维数组,没学过的朋友可以看我之前博客传送门
就像这样子的,没下棋之前棋盘是空的,空并不是无,我们这里用空格填充的二维数组,我们把最边界的几个横线省略掉,来看一下VS下的调试
就像这样棋盘就设计好了, 这其实就是一个二维数组,只不过匹配的打印了很多线
那我们来总结一下,我们需要
- 一个字符的二维数组 3 * 3
- 将二维数组初始化为全空格
- 打印相匹配的横竖线
我们需要两个函数来帮我们完成, 一个初始化,一个打印,我们起为
void init_board(char board[ROW][COL], int row, int col);
void display_board(char board[ROW][COL], int row, int col);
我们来实现一个这两个函数
void init_board(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 display_board(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)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
}
printf("\n");
}
}
这样我们只需要一个二维数组,调用这两个函数就有了一个棋盘了。
3.玩家下棋实现
棋盘上边已经准备好了,那有了棋盘就得有人下棋吧,我们这里考虑让玩家先手棋。
上边我们规定了,玩家下棋就是 * ,其实就是向二维数组中填充 *
我们接受一下玩家输入的坐标,然后判断此坐标是否非法,是否已经被下过了,如果都没有的话,就让玩家落子
我们定义一个函数,让玩家下棋;
void player_move(char board[ROW][COL], int row, int col);
我们来实现一下
void player_move(char board[ROW][COL], int row, int col)
{
printf("玩家下棋\n");
//检查坐标是否被占用or非法
while (1)
{
int x = 0;
int y = 0;
printf("请输入下棋坐标:>\n");
scanf("%d %d", &x, &y);
if (x <= row && y <= col && x >= 1 && y >= 1 && board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = '*';
//减1的原因: 因为大多数人会认为第一个格子坐标是1 1 ,但是对应到数组是 0 0
break;
}
else if (board[x-1][y-1] != ' ')
{
printf("坐标已经被占用,请重新输入\n");
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
我们来测试一下
在每次下棋之后,打印一下棋盘。
这里就有一部分帅气的观众就要问了,void player_move(char board[ROW][COL], int row, int col);
这里接受到的数组中的ROW COL是什么?
这个是定义的宏,常量,还是3,为了降低耦合
#define ROW 3
#define COL 3
这个我们定义在头文件中,然后如果需要改3*3的棋盘,只需要把这个常量改了就行,极大降低耦合。
4.电脑下棋实现
我们来看一下电脑下棋,电脑下棋可以堵截玩家下棋,也谋划着如何赢。说实话,我连象棋的人机都赢不过去,
但是我们这里实现这个笨笨的机器人,就是斗地主中的那个
我们这里的机器人随机落子,我们来实现一下
照样编写一个函数
void computer_move(char board[ROW][COL], int row, int col);
这里用到如何创建随机数
void computer_move(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 < row && y < col)
{
board[x][y] = '#';
break;
}
}
}
来给大家讲解一下随机数的创建
这里用到之前查函数的一个网站,www.cplusplus.com ,不了解的朋友可以看我博客一文带你详解函数
但是每次更新rand函数返回的值都是一样的,需要创建一个随机数触发器
srand(time(NULL));
srand的参数是一个需要变化的数,时间一直在变化,所以我们把时间戳传进去。
这里给大家简单介绍一下,具体请去www.cplusplus.com自行探索
rand()%n得到的一定是 0 —(n-1) 之间的数字
我们这里实现的笨笨的机器人来陪你下棋
5.判断输赢
玩家可以下棋,电脑也可以下棋,那我们就需要判断输赢了,胜负何时出现?
当任何一方有三子成杀的时候,便分出胜负
当棋盘已满,平局
我们在这里实现一个is_win() 函数判断输赢:
函数返回值:
- Q —代表平局
- 一颗* —代表玩家赢
- #—代表电脑赢
- C----表示游戏继续
三子成杀----横竖成杀都算,对角线成杀也算,我们只需要判断是否有这样的情况出现即可
还需要判断棋盘是否满,我们实现一个工具函数—is_full 用来判断棋盘是否满
我们来看一下代码
char is_win(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
//判断横线
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
{
return board[i][0];
}
//判断竖线
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[2][i] != ' ')
{
return board[i][0];
}
}
//判断对角线
if (board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[1][1] != ' ' ||
board[0][2] == board[1][1] && board[0][0] == board[2][0] && board[1][1] != ' ')
{
return board[0][0];
}
if (is_full(board, row, col) != 0)
{
return 'Q';
}
return 'C';
}
//判断棋盘是否满
//满了返回1,不满返回0
int is_full(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++)
{
if (board[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
这样我们的游戏核心代码就已经实现了,我们来复盘一下:
打印棋盘–玩家下棋–电脑下棋—判断输赢,我们都已经实现
6.测试函数编写
我们来测试一下我们的代码,编写一个测试函数。
我们使用do-while循环,因为有人玩了游戏可能意犹未尽,可以再来一次
我们首先打印一个菜单,供用户选择.
//打印菜单
void menu()
{
printf("***************************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("***************************\n");
}
然后编写主函数用来测试。
int main(void)
{
int input = 0;
srand(time(NULL));
do
{
//打印菜单
menu();
printf("请选择:>\n");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入有误,请重新输入\n");
break;
}
} while (input);
return 0;
}
我们还需要一个game函数用来玩游戏
game函数需要实现的东西:
- 定义二维数组当棋盘
- 初始化棋盘
- 展示棋盘
- 玩家下棋
- 电脑下棋
注: 每次落子之后都要判断输赢,如果出现游戏结果则跳出循环
每次落子之后应该打印一下棋盘给用户展示
我们来看一下代码
void game()
{
//定义棋盘
char board[ROW][COL] = {
0 };
//初始化棋盘
init_board(board, ROW, COL);
//打印棋盘
display_board(board, ROW, COL);
//玩家下棋 棋盘中放的是*
int ret = 0;
while (1)
{
player_move(board, ROW, COL);
//判断是否赢
ret = is_win(board, ROW, COL);
display_board(board, ROW, COL);
if (ret != 'C')
{
break;
}
//电脑下棋 棋盘中放的是#
computer_move(board, ROW, COL);
display_board(board, ROW, COL);
//判断是否赢
ret = is_win(board, ROW, COL);
if (ret != 'C')
{
break;
}
}
if (ret == 'Q')
{
printf("平局\n");
}
else if (ret == '*')
{
printf("玩家胜\n");
}
else
{
printf("电脑胜\n");
}
}
7.游戏代码与测试代码拼接
上边我们把游戏代码以及如何测试进行了讲解,但是需要把游戏代码和测试代码综合起来
我们把测试代码写成一个test.c 把游戏代码封装成一个game.c
然后在一个game.h的头文件中进行定义宏,函数声明…
大家可以自己编写一下,不懂得地方可以私信我
8.总结
在学习了数组之后,简单的三子棋就带大家写到这里了,这就是数组的使用,把我们前边的知识做了充分的使用,我们下一期带大家写一个扫雷,大家一定要多练习。一步一步通过思考,逻辑分明,去实现代码
如果觉得博主的文章还不错的话,请三连支持一下博主哦
边栏推荐
猜你喜欢
CV-Model [4]: MobileNet v3
bgp机房的动态路由和静态路由的区别
HCIP第十一天_MPLS实验
ROS2自学笔记:launch文件完整编写流程
# ODS及DWD层自动化构建##, 220731,
关于#sql#的问题:该怎么写sql语句,
Istio微服务治理网格的全方面可视化监控(微服务架构展示、资源监控、流量监控、链路监控)
JSP WebSehll backdoor script
iVX低代码平台系列详解 -- 概述篇(二)
MySQL8 -- use msi (graphical user interface) under Windows installation method
随机推荐
MySql中的like和in走不走索引
线性代数学习笔记3-3:逆矩阵的理解
LeetCode:1374. 生成每种字符都是奇数个的字符串【签到题】
MySQL8--Windows下使用msi(图形界面)安装的方法
MySQL8.0.26安装配置教程(windows 64位)
(Repost) HashCode Summary (1)
知识体系树
Foundry教程:使用多种方式编写可升级的智能代理合约(下)
R16 Type II量化反馈码本的产生
7-41 PAT排名汇总 (25 分)多样排序
day11--shell脚本
Daily practice------There are n integers, so that the previous numbers are moved back m positions in order, and the last m numbers become the first m numbers
【遥控器开发基础教程3】疯壳·开源编队无人机-ADC(摇杆控制)
IPIDEA的使用方式
Double Strings (别总忘记substr)
基于分布式随机森林的火电厂燃烧系统设备建模方法
暴力破解全攻略
深度学习:目标检测入门知识
I will give you a chance to interview in a big factory. Can you interview?Come in and see!
Difference between #{} and ${}