前言
🎈大家好,我是何小侠🎈
🌀大家可以叫我**小何或者小侠🌀**
🔴我是一名普通的博客写作者🔴
💐希望能通过写博客加深自己对于学习内容的理解💐
🌸也能帮助更多人理解和学习🌸
🍃我的主页:何小侠的主页🍃
这篇博客我们一起来学习如何用C语言所学知识来实现三子棋,另外我们还将推广到更大的棋盘。希望能帮助大家理解和学习
🍊
引子🍊
三子棋这个游戏大家都应该不陌生,大家应该在学校和小伙伴们玩过,这里就不介绍规则了,给大家介绍一下三子棋的历史(也可能是野史)。
关于《三子棋》的起源,年代之远古已经不可考究。但是严肃地讲,三子棋很可能是历史上最早使用类似“三消”原则作为棋理的棋种。古有记载,所谓三消,即以三为理,遇三则消失。
据说,明朝史上最会玩的正德皇帝–朱厚照经常会有一些出人意料的想法和举动,比如在宫中模拟开酒店、店铺和怡红院荒诞行为,甚至还经常偷跑出宫去玩。为了安抚这位不省心的皇帝,老臣李东阳曾将三子棋推荐给皇帝,奈何这个棋皇上玩了几次就没兴趣了,因为它有一个极大的BUG:熟悉规则的双方,理论上可以达到100%的平局。
于是古人在三连为赢的基础上逐渐升级难度并改进玩法,创造出更多种类的游戏。比如说五子棋,其实就是根据三子棋演变而来,甚至是如今流行的各种版本的消消乐其实也是在三连消的基础规则上演变而来的。
游戏模块介绍🍊
这是我们第一次接触模块化编程,下面我们解释一下模块化编程的好处 |
- 首先我们先介绍一下什么是模块化编程:
我们在写一个项目的时候,需要将这个项目分成用多个.c的源文件或者.h的头文件来实现,每个.c文件就相当于一个模块,比如计算器的实现,我们将加减乘除分成4个.c文件来实现,而.h文件则是用来放声明。
-模块化的实际应用:
比较传统的编程模式,即将所有文件全部集中于main函数,或者全部集中在一个.c文件。我们可以试想如果你写了一个很长的代码有1万行,当你想修改5500行的代码。在这个过程中,你会觉得很繁琐,冗杂,因为你的代码没有一个清晰的分类,把所有功能集中在一起不仅不利于你自己的观看,别人看起来也没有兴趣。而且在我们以后的工作中或者多人一起开发项目的时候,每个人只要分工明确,各种完成自己的模块就行,将你的.c和.h文件与其他人的一结合,这个项目就初步完成了,后续再经过调试等过程,就完成了整个工程。
- 模块化编程的优点
极大的提高代码的可阅读性、可维护性、可移植性并且它使得整个项目分工明确,条理清晰
游戏思路🍊
这张图片就是我们的整体思路,接下来我们会逐步实现。
main
void test() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); printf("请输入\n"); scanf("%d", &input); switch (input) { case 1: printf("三子棋\n"); game(); break; case 0: printf("退出游戏\n"); break; default: printf("输入错误,请重新输入\n"); break; } } while (input); } int main() { test(); return 0; }
还是是我们经典的do-while+Switch的结构srand和time函数如果看到这里不懂可以去看看我之前的关于猜数字游戏的博客 link这个链接里面是有srand rand 以及time函数的讲解的。
menu
void menu() { printf("*********************\n"); printf("*** 1.play *****\n"); printf("*** 0.eixt *****\n"); printf("*********************\n"); }
二维数组定义和初始化
char board[ROW][COL];
void intiboard(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] = ' '; } } }
这里的ROW COL 是我们在头文件中用#define定义的,因为我们要玩三子棋所以我们设置为3,3。之后我们会拓展一下,例如,在5x5的棋盘里面玩三子棋。
打印棋盘
实际上我们打印棋盘有两种写法,一种是只适合3x3棋盘的,一种是可以随我们设定的ROW,COL随之改变的。我们先介绍第一种只适合3x3的
这是我们要打印的棋盘,我们仔细观察一下行,然后想一下该如何写代码。
通过观察我们知道第一行就是 这个两个竖线 | | ,第二行就是—|—|—
,然后第三行第五行都与第一行一样,第4行与第2行一样。这是我们初步的分析。但是我们知道我们要在这个棋盘上面下棋 × ○这样的符号 ,所以第一行其实实际上是space%cspace,为什么不只是一个%c呢?大家可以尝试在自己的电脑上对比一下 - 和 space,这两个实际上都占有一个字符。
然后我们尝试一下用代码写出一个棋盘。如果有问题可以与下面的代码作比较
void printboard(char board[ROW][COL], int row, int col) { int i = 0; for (i = 0; i < row; i++) { printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]); if (i < row -1) printf("---|---|---\n"); } }
上面就是我们没有优化的打印棋盘的代码,我们简单讲解一下,因为
%c | %c | %c 要打印三行就可以直接用for循环打印,但是—|—|—只需要打印两行就要控制一下,,i < row是三次,用 i < row -1就是两次。
我们在最开始就说过这个打印代码是不符合3x3以上棋盘的,所以我们要在词基础上加以改进。下面展示一下10x10情况下这个代码的打印情况让大家知道确实不能进行推广。
我们看到行确实没有出错但是我们更需要在列上下功夫。
下面我们再加深讨论,请看图。
我们将列进行拆分。
我们看蓝花这一行,发现space%cspace打印三次而 | 只打印两次。
我们看红花这一行,发现—打印三次 | 也只打印两次。
通过分析我们就能够就用代码实现,大家可以先动手实践一下。
下面的代码。
void printboard(char board[ROW][COL], int row, int col) { int j = 0; int i = 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"); for (j = 0; j < col; j++) { if (i < row - 1) { printf("---"); if (j < col - 1) printf("|"); } } printf("\n"); } }
我们还是和最初版本一样用 j < col -1来控制只出现两次的线条。用三个for循环,最外层控制有多少行,里面的两个for循环用来控制每行的列的打印。