1.三子棋游戏规则
在设计游戏前,我们先得知道最基础的游戏规则,以实现我们最基本的设计需求:
1、人机双方轮流在格子里落子棋子,先连成三棋一线者视为胜利
2、棋盘被摆满棋子仍未分出胜利,视为平局
2.分析需求
其实设计程序的难点就在于,把抽象的逻辑概念转化为计算机语言可识别的逻辑语句,再简单的设计需求,都要用科学的分析方法,先分析需求,再针对性的设计,不可想当然的拿起手就开始写代码,也就是我们始终强调的编程思维。要用工程化的方法解决实际问题。
首先,一个游戏是必须有菜单界面的,供以玩家选择是否开始游戏或者退出游戏,同时我们也要考虑游戏多次游玩的情况,需要用一个循环语句将菜单界面封装起来,可以让玩家在游玩后再次进入菜单选择是否开始游戏;其次是游戏的初始化问题,我们需要一个数组来保存玩家和电脑下棋时落子的位置,并且要在屏幕上打印出来,在这里我们就得考虑落子的问题,玩家落子很简单,用一个数组保存就行了,但是电脑的落子应该是随机的,我们可以考虑使用rand函数和time函数来做到随机生成的功能,,并且在落子的时候我们得考虑输入是否合理,不能越界输入,不能重复在同一个位置落子;解决了以上的问题后,我们要思考,怎么设计一个判断输赢的逻辑,怎么样是玩家赢,怎么样是电脑赢,怎么样算是平局。
在有了以上大致的规划和思考后,我们就可以正式的开始设计了。
在开始设计之前,我们首先得明确,这个程序是由多个功能的函数语句共同实现的,因此在创建工程时,我们就应该提前做好创建多个文件的工作,不同的文件实现不同的功能和逻辑,以保证程序的健康性和完整性,也方便我们日后更改。我们可以分为一下三个文件实现,game.h用于包含程序所需要的头文件和函数的申明,chessboard.c用于实现棋盘上具体的初始化和判断逻辑,game.c文件为主要的主程序所在的文件,是程序的主体,也是各个函数的集合实现。
3.菜单界面设计
首先需要在屏幕上打印菜单,然后让玩家选择,若玩家输入不正确或者想在游玩后再次游玩的话,则需要个循环来实现,因此,这里需要的是先执行循环体再判断循环条件的语句,也就是do...whlie...语句,以下是代码实现
void screen() { printf("--------------------------\n"); printf("---------1.Play ----------\n"); printf("--------------------------\n"); printf("---------2.Esc ----------\n"); printf("--------------------------\n"); } int input = 0;//记录玩家的输入选择 printf("请进行输入确认是否开始游戏\n"); do { screen();//调用屏幕打印菜单的函数 scanf("%d", &input); switch (input) { case 1: printf("开始游戏\n"); printf("三子棋\n"); game(); break; case 2: printf("退出游戏\n"); break; default: printf("非法输入,请重新输入\n"); break; } } while (input != 2);
4.游戏主体设计(棋盘的初始化部分)
在这里,为了方便以后对棋盘大小进行更改,我们可以在头文件中进行宏定义,我们用ROW和COL分别来代表行和列
#define ROW 3 //定义 行为3 #define COL 3 //定义 列为3
然后我们需要用一个3*3的数组去对我们的棋盘的数据进行保存,并对其进行初始化,将其封装成一个函数,并且放在chessboard.c文件中实现,需要传入的参数是一个数组和行以及列。我们对数组中每一个赋值为空格。
char board[ROW][COL] = { 0 }; initialize(board, ROW, COL);//初始化棋盘
void initialize(char board[ROW][COL], int row, int col) { for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { board[i][j] = ' '; } } }
在完成了初始化之后,我们需要打印棋盘到屏幕上供玩家看见,但是此时打印出来的全是空格,玩家是看不见什么有意义的信息的,所以我们要制定一个棋盘的框架,既要实现需求,也要保证美观,所以需要打印部分分割线
isplayboard(board, ROW, COL);//打印棋盘
void displayboard(char board[ROW][COL], int row, int col) { for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { printf(" %c ", board[i][j]); //打印分割列 if (j < col - 1) printf("|"); } //打印分割行 if (i < row - 1) printf("\n---|---|---\n"); } printf("\n"); }
我们可以运行着测试一下
5.游戏主体设计(玩家下棋部分)
在拥有完整的棋盘后,我们就可以开始着手设计玩家下棋和电脑下棋的部分了,首先是玩家下棋的部分,我们可以用之前定义的board数组来防止玩家落子的结果,可以设计如下的封装函数。使用while循环来保证玩家落子是真确合法的,否则就一直进行循环,直到输入正确的落子坐标。
palyer_move(board, ROW, COL);//玩家下棋
void palyer_move(char board[ROW][COL], int row, int col) { int x = 0; int y = 0; printf("\n----玩家下棋----\n");//提示语句 while (1) { printf("请输入落子的坐标:"); scanf("%d %d", &x, &y); if (x <= 0 || x > 3 || y <= 0 || y > 3)//判断输入是否越界 { printf("非法输入,请重新输入\n"); } else if (board[x-1][y-1] == ' ') { board[x-1][y-1] = '*';//用*表示玩家落子 break; } else printf("输入位置被占用,请重新输入\n"); } }
6.游戏主体(电脑下棋部分)
电脑下棋,最需要的就是随机,不然每一次电脑都是一个下法,游戏就不能正常游玩了,在这里,我们可以使用rand函数来生成随机数,使用srand函数来作为生成随机数的种子,为了保证随机数的动态变化,我们使用time函数来获取时间戳供以srand函数作为参数,对于以上知识点有不了解的小伙伴可以参考笔者的这篇文章,里面对rand函数,srand函数,time函数有非常详细的教学和参数返回值的讲解:c语言实现猜数字小游戏以及如何做到生成真正的随机数
computer_move(board, ROW, COL);//电脑下棋
srand((unsigned int)time(NULL));//初始化随机数的种子
void computer_move(char board[ROW][COL], int row, int col) { int x = 0; int y = 0; printf("\n----电脑下棋----\n");//提示语句 while (1) { int x = rand() % ROW;//使得随机数大小为1--3 int y = rand() % COL;//使得随机数大小为1--3 if (board[x][y] == ' ') { board[x][y] = '#';//用#表示电脑落子 break; } } }
在这里的while循环是为了避免不合理的落子,比如在以及有玩家落子的地方是不允许再一次落子的,在不满足这样的条件下会再一次生成新的随机数,直到可以有正确的落子坐标为止
7.判断输赢逻辑逻辑设计
在玩家和电脑落子都设计完成后,我们需要思考,怎么样的情况下算是玩家赢,怎么样的情况下算是电脑赢,怎么样算是平局,在玩家或者电脑赢了后怎么样实现立即停止游戏。
我们可以设计一个函数,用其返回值来表示谁赢的了游戏的胜利,谁先完成三子连成一线就返回谁的棋子符号,在这里可以用俩个for循环判断3行和3列是否已经达成三子一线的情况,并且不能是三个空格,如果满足条件,则返回满足条件的元素符号,也就是*或者#,在用俩个if语句去判断俩条对角线是否满足条件,都没完成就继续游戏,并且设计一个函数判断棋盘是否满了,当棋盘下满后返回Q表示棋盘已经下满了,游戏平局,否则返回C表示游戏继续正常进行
//玩家赢 - '*' //电脑赢 - '#' //平局 --- 'Q' //游戏继续-'C' char IsWin(char board[ROW][COL], int row, int col) { int i = 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]; } } for (i = 0; i < col; i++) { //实现判断每一列是否相同 if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ') { return board[0][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 (IsFull(board, row, col)) { return 'Q'; } return 'C'; }
其中的判断棋盘是否下满的函数具体实现如下
static int IsFull(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;//判断有空位,返回0 } } return 1;//判断棋盘已满,返回1 }
并且在游戏的主程序中,玩家每下一次棋以及电脑每下一次棋,都进行一次判断,用一个while循环,当某一方胜利或者平局时,也就是ret拿到的返回值只要不是C(游戏继续),就执行if语句,跳出循环,具体实现如下,在跳出循环后我们要对结果进行打印,也就是使用if...else if...else的多分支语句进行判断
while (1) { palyer_move(board, ROW, COL);//玩家下棋 displayboard(board, ROW, COL);//打印棋盘 //判断输赢 ret = IsWin(board, ROW, COL); if (ret != 'C') { break; } computer_move(board, ROW, COL);//电脑下棋 displayboard(board, ROW, COL);//打印棋盘 //判断输赢 ret = IsWin(board, ROW, COL); if (ret != 'C') { break; } } if (ret == '*') { printf("玩家赢\n"); } else if (ret == '#') { printf("电脑赢\n"); } else { printf("平局\n"); }
综上完成了各个模块的设计后,我们就可以进行组装,最后完成程序的设计
8.游戏完整代码
1.头文件代码
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #include<time.h> #define ROW 3 //定义 行为3 #define COL 3 //定义 列为3 //打印屏幕菜单 void screen(); //初始化 void initialize(char board[ROW][COL], int row, int col); //打印棋盘 void displayboard(char board[ROW][COL],int row,int col); //玩家下棋 void palyer_move(char board[ROW][COL], int row, int col); //电脑下棋 void computer_move(char board[ROW][COL], int row, int col); //判断输赢 char IsWin(char board[ROW][COL], int row, int col);
2.棋盘模块代码
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void screen() { printf("--------------------------\n"); printf("---------1.Play ----------\n"); printf("--------------------------\n"); printf("---------2.Esc ----------\n"); printf("--------------------------\n"); } void initialize(char board[ROW][COL], int row, int col) { for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { board[i][j] = ' '; } } } void displayboard(char board[ROW][COL], int row, int col) { for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { printf(" %c ", board[i][j]); //打印分割列 if (j < col - 1) printf("|"); } //打印分割行 if (i < row - 1) printf("\n---|---|---\n"); } printf("\n"); } void palyer_move(char board[ROW][COL], int row, int col) { int x = 0; int y = 0; printf("\n----玩家下棋----\n"); while (1) { printf("请输入落子的坐标:"); scanf("%d %d", &x, &y); if (x <= 0 || x > 3 || y <= 0 || y > 3) { printf("非法输入,请重新输入\n"); } else if (board[x-1][y-1] == ' ') { board[x-1][y-1] = '*'; break; } else printf("输入位置被占用,请重新输入\n"); } } void computer_move(char board[ROW][COL], int row, int col) { int x = 0; int y = 0; printf("\n----电脑下棋----\n"); while (1) { int x = rand() % ROW;//使得随机数大小为1--3 int y = rand() % COL;//使得随机数大小为1--3 if (board[x][y] == ' ') { board[x][y] = '#'; break; } } } static int IsFull(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; } //玩家赢 - '*' //电脑赢 - '#' //平局 --- 'Q' //游戏继续-'C' char IsWin(char board[ROW][COL], int row, int col) { int i = 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]; } } for (i = 0; i < col; i++) { if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ') { return board[0][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 (IsFull(board, row, col)) { return 'Q'; } return 'C'; }
3.游戏主函数文件代码
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void game() { //打印棋盘 void screen(); char ret = 0; char board[ROW][COL] = { 0 }; initialize(board, ROW, COL);//初始化棋盘 displayboard(board, ROW, COL);//打印棋盘 while (1) { palyer_move(board, ROW, COL);//玩家下棋 displayboard(board, ROW, COL);//打印棋盘 //判断输赢 ret = IsWin(board, ROW, COL); if (ret != 'C') { break; } computer_move(board, ROW, COL);//电脑下棋 displayboard(board, ROW, COL);//打印棋盘 //判断输赢 ret = IsWin(board, ROW, COL); if (ret != 'C') { break; } } if (ret == '*') { printf("玩家赢\n"); } else if (ret == '#') { printf("电脑赢\n"); } else { printf("平局\n"); } } int main() { srand((unsigned int)time(NULL)); int input = 0; printf("请进行输入确认是否开始游戏\n"); do { screen(); scanf("%d", &input); switch (input) { case 1: printf("开始游戏\n"); printf("三子棋\n"); game(); break; case 2: printf("退出游戏\n"); break; default: printf("非法输入,请重新输入\n"); break; } } while (input != 2); return 0; }
9.程序运行部分截图
本次的分享就到此为止啦,希望对各位小伙伴有所帮助