目录
前言:
1.菜单的打印
2.三子棋构思
3.实现三子棋
3.1使用宏的方式定义数组
3.2打印棋盘
3.3玩家下棋
3.4电脑随机下棋
3.5判断结局
4.源码
4.1test.c
4.2game.c
4.3game.h
❤博主CSDN:啊苏要学习
▶专栏分类:C语言◀
C语言的学习,是为我们今后学习其它语言打好基础,C生万物!
开始我们的C语言之旅吧!✈
前言:
在学完二维数组的知识后,我们来应用一下二维数组中的行列思维,敲上三子棋这个小游戏。
链接:二维数组---(行列抽象思维)
1.菜单的打印
和前面讲到的猜数字一样。玩游戏前需要打印一个菜单,让玩游戏的人知道这是个什么游戏,怎么玩,需要玩家做什么等,我们通过封装一个菜单函数来实现。
void menu()
{
printf("********************\n");
printf("***** 1.play ******\n");
printf("***** 0.exit ******\n");
printf("********************\n");
}
int main()
{
//接收玩家输入的值
int input = 0;
menu();
printf("这是一个三字棋游戏,请输入>:");
scanf("%d", &input);
return 0;
}
这个时候,在控制台打印出来的效果如下:
光标闪烁在等待我们输入值。接下来的一步就是:对玩家输入的值进行相应不同的处理,玩家输入1,进入玩游戏的分支,玩家输入0,退出程序,当然不排除一些调皮捣蛋的玩家,喜欢鼓弄,我们使用default(默认)来应付。
补充:return 0;语句在制定规则的时候,一部分人提出希望程序最后可以省略这句话,最终通过了。当主函数(仅对主函数)结束时,没遇到return 0;语句,则自动使用return 0;返回。相当于有写return 0;但我们最好写上去!
当然,我们不只是每次都选择一次就结束掉程序。比如我玩一把游戏后,还觉得挺好玩,想再来一把,相应的需要把这段代码写成一个循环才能实现。
分析:一运行程序就让我们选择玩还是不玩,如果不玩,选择0就退出游戏,结束循环;如果玩,选择了1,在游戏结束后,再次打印菜单让我们重新选择玩不玩。还有就是输入非法值,我们得重新进行选择,最后直到我们输入0退出循环,结束程序。
在这个过程中,一运行程序,不管什么都让我们选择玩不玩游戏,这是循环里的循环部分,符合这种特性的循环是do while循环,一上来就执行一次循环体;while()判断部分我们仔细一想,在选择的时候,选择0退出游戏,选择1或选择输入非法值的时候重新执行一次循环体。对应到循环,如果判断表达式是0则退出循环,非0(1或非法值)则继续循环,我们可以得出使用input做循环的判断条件最合适了。
这上面相当于是一个模块,我们理解里面的思想(主要是循环用input当判断条件)就没问题了。博主把这部分当成菜单模块。
2.三子棋构思
在写代码之前,需要初步思考实现三子棋需要用什么方式实现。
这个棋子与棋子之间分割线,我们使用循环来控制打印就可以了。然后是玩家下棋,每下一步棋,都会改变数组里的元素,接着是电脑下棋,电脑下棋也是一样会改变数组元素的,到最后直到有一方胜出或平局结束游戏。
通过简单分析,我们需要创建一个三行三列的二维数组,这个数组起初存放的值我们都初始化为空格,因为空格打印出来看不见。接着就是实现玩家下棋的函数、电脑下棋的函数、判断输赢的函数。
3.实现三子棋
3.1使用宏的方式定义数组
函数的声明、头文件的包含,宏的定义都放在.h文件里。.c文件里包含.h文件就可以正常使用.h里定义的标识符以及引用的头文件里的库函数。
3.2打印棋盘
也许对于初学者,打印棋盘是最难消化接收的一个,没事,咱们娓娓道来。
- 打印数据行
“井”字有三行,二维数组也是有三行。其实棋盘是根据二维数组的大小来变换的。我们来看到数据行,数据行有两部分组成,一部分是:空格+%c+空格,另一部分是|(竖杠)。
相信大家都已经懂数据行的打印了(doge),我们看分割行的部分(---|---|---):
- 打印分割行
不难发现,打印分割行第的时候,最右边的|还是不需要打印,简直就像是把数据行中,用---替代空格+%c+空格,|还是|一样。
ok,到这里最难的棋盘打印就讲完了,这时候,如果我们改变标识符ROW、COL的值,棋盘的大小也会随之改变。因为我们都是使用row,col做控制的,当然这个循环就有变得性啦~
3.3玩家下棋
下棋就是说,把二维数组里的元素改成玩家用的棋子(字符)就行,这样打印的时候,相当于数组相应位置放了棋子一样。电脑下棋也是同理,既然这样,那我们快点来实现吧。
这里需要提的点是:坐标合法部分为什么是x(1-3),y(1-3)。而且在下棋的时候反而减掉1,这不是没事找事做吗,直接输入(0-2)之间的数不就好了吗?是的,数组的访问下标是从0开始,我们知道是因为我们是程序猿,但玩家不知道呀,把这个游戏放出去,随便拉一个玩家让他输入一个坐标下棋子,玩家想下第一行第一列必定会输入1 1。所以我们程序猿要考虑下用户的习惯,这叫做用户友好,用户并不需要知道程序内部是进行了什么样的操作,根据常识玩游戏就可以了。
3.4电脑随机下棋
我们这里实现的是电脑随机下棋,如果想提升难度,可以对电脑下棋的位置进行更多的操作判断。也就是说,当玩家下了某一个棋子后,电脑不是随机下棋,而是根据棋盘上的情况选择下棋,这个棋子对围堵玩家的棋子有效或是对电脑进一步取胜有效。
我们先看一下test.c文件里的调用接口函数情况吧:
我们说应该是游戏有结果了,也就是人机下棋应该是一个循环,下棋下到能判断输赢还是平局才能退出来。所以应该是这样的~
那么有读者会问了,如何退出这个一直下棋的循环呢?我们使用判断输赢的函数Is_win,玩家下棋后判断游戏是不是结束了(有一方赢了或棋盘满了),电脑下棋后判断游戏是不是结束了,来退出循环。
3.5判断结局
我们应该怎么设计这个判断结局的函数呢?我们知道三子棋要赢的话,横三列、竖三行,两条对角线都是其中一方的棋子的时候,这时就有一方获胜。如果棋盘满了,我们也没有继续的必要了。那么我们如何将某一方获胜或棋盘满了或还没到结局需要继续下棋的状态告诉主调函数(game)呢?我们使用如下的返回值:
- 某一方赢就返回赢的条件里任意一个位置的棋子(看下面就理解了~)
- 棋盘没有满,返回字符'C'告诉主调函数说:"继续下棋"
- 棋盘满了,返回字符'F'说明棋盘满了
我们来看一下主调函数的部分:
再来看实现判断结局部分:
横三列和竖三行:
对角线:
判断满没满的函数:
好啦,这就是三字棋的全部实现啦。都到这份上了,不玩一把很难收场,博主替大家玩一把并把截图放出来证明可行性~
抱歉,图片有点长,哈哈~
其实对于三子棋还有一些改进的地方,比如我们可以设置成五子棋呀;把电脑下棋设智能一点;这些也是可以实现的。还有设置一个图形化游戏界面等等。博主暂且先不研究,后期学有余力再研究写篇博客出来~
4.源码
4.1test.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 board[ROW][COL];//ROW是3行,COL是3列
int ret = 0;
//初始化棋盘,将数组元素都初始化成空格字符' ',
Initboard(board, ROW, COL);
DisPlayboard(board, ROW, COL);
while (1)
{
PlayerMove(board, ROW, COL);
DisPlayboard(board, ROW, COL);
ret = Is_win(board, ROW, COL);
if (ret != 'C')//如果ret不是C,那就是游戏有结果了退出下棋
break;
ComputerMove(board, ROW, COL);
DisPlayboard(board, ROW, COL);
ret = Is_win(board, ROW, COL);
if (ret != 'C')
break;
}
if (ret == '*')//这里就体现出来返回赢方胜利条件的某一位置元素的好处了
printf("恭喜你,你赢了\n");//比如玩家第一行连成三子棋,Is_win返回其中一个棋子,代表使用这个棋子的一方获胜
else if (ret == '#')
printf("很遗憾,你输了\n");
else
printf("该局平局\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;
}
4.2game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void Initboard(char board[ROW][COL], int row, int col)
{
//遍历整个二维数组
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
void DisPlayboard(char board[ROW][COL], int row, int col)
{
int i = 0;
//控制行
for (i = 0; i < row; i++)
{
int j = 0;
//数据行,有col列
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
//数据行的分割符数是列数少1,也就是col-1
if (j < col - 1)
{
printf("|");
}
}
//打印完数据行换行
printf("\n");
//分割行是行数减1,也就是row-1
if (i < row - 1)
{
//分割行里有相应的列数
for (j = 0; j < col; j++)
{
printf("---");
//最后的一个|不用打印
if(j < col - 1)
printf("|");
}
}
//打印分割行完后换行
printf("\n");
}
}
void PlayerMove(char board[ROW][COL], int row, int col)
{
//一对坐标
int x = 0;
int y = 0;
while (1)
{
printf("请玩家下棋(输入相应位置的坐标)>:");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//坐标合法
{
//判断这个位置是不是一个可以下的空位
if (board[x - 1][y - 1] == ' ')
{
//玩家的棋子假设是*
board[x - 1][y - 1] = '*';
break;//当我们成功下了一颗棋后,就不能还是玩家下棋了,退出循环
}
else//这个位置有棋子了,重新输入,这里也提醒我们需要使用循环
{
printf("这个位置已有棋,不能重复下,请重新输入\n");
}
}
else//坐标不合法来到这里,发现需要重新输入的步骤,我们就加上循环。
{
printf("输入坐标非法,请重新输入\n");
}
}
}
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋>:\n");
while (1)
{
//x,y取模row、co;后,坐标一定是合法的,只需要判断这个位置是不是空位就可以了
//使用rand生成随机数记得在程序执行起来的时候使用srand设置一次随机数生成起点
int x = rand() % row;
int y = rand() % col;
//找到合适的位置下棋
if (board[x][y] == ' ')
{
board[x][y] = '#';
//成功下完一颗棋子就退出
break;
}
}
}
int Is_Full(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0;
}
}
//遍历完,前面所有元素都不是空格,那就是满了,返回1
return 'F';
}
int Is_win(char board[ROW][COL], int row, int col)
{
//先假设棋盘满了
int flag = 'F';
int i = 0;
//横三列
for (i = 0; i < row; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
{
return board[i][1];
}
}
//竖三行
for (int i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
{
return board[1][i];
}
}
//对角线
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
{
return board[1][1];
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
{
return board[1][1];
}
if (flag == Is_Full(board, row, col))//如果判断棋盘的函数返回'F',条件表达式成立,返回flag('F')
{ //如果没满,函数返回0,条件表达式成立,程序走到最后返回'C',继续下棋
return flag;
}
//前面所有情况都不成立,那就是游戏没结束继续下棋
return 'C';
}
4.3game.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 3
#define COL 3
void Initboard(char board[ROW][COL], int row, int col);
void DisPlayboard(char board[ROW][COL], int row, int col);
void PlayerMove(char board[ROW][COL], int row, int col);
void ComputerMove(char board[ROW][COL], int row, int col);
int Is_win(char board[ROW][COL], int row, int col);
OK,到此,三子棋简易实现结束~
结语:希望读者读完有所收获!在学C的路上,祝福我们能越来越C!✔
读者对本文不理解的地方,或是发现文章在内容上有误等,请在下方评论区留言告诉博主哟~,也可以对博主提出一些文章改进的建议,感激不尽!最后的最后!文章来源:https://www.toymoban.com/news/detail-439201.html
❤求点赞,求关注,你的点赞是我更新的动力,一起努力进步吧。文章来源地址https://www.toymoban.com/news/detail-439201.html
到了这里,关于三子棋小游戏---(C语言)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!