简单不先于复杂,而是在复杂之后。
目录
1. 分文件实现
2.分步骤实现
2.1 游戏菜单
2.2 创建棋盘
2.3 初始化棋盘
2.4 打印棋盘
2.5 玩家下棋
2.6 电脑下棋
2.7 判断输赢
3. 附完整代码
3.1 test.c
3.2 game.h
3.2 game.c
1. 分文件实现
当我们实现一个有较多功能的三子棋小游戏时,我们要用到许多库函数,需要包含头文件,还有符号的定义,函数的声明等等,还要写很多“高内聚、低耦合”的函数来实现具体的一些功能,所以代码量会非常庞大。
我们就需要在一个工程里创建多个文件,赋予其不同的分工,来使我们的程序可读性提高,清晰易懂。
我在vs2019中创建了3个文件来实现三子棋小游戏:
test.c //测试游戏的逻辑 game.c //游戏代码的实现 game.h //游戏代码的声明(函数声明,符号定义)
2.分步骤实现
在写一个完整的大型的程序之前,我们需要先梳理好实现程序的逻辑:
1. 我们需要实现一个游戏菜单,来让用户选择游戏或者退出游戏,打印在屏幕上。
2. 既然要下棋,我们需要提供一个棋盘来承载棋子,所以下一步我们要创建一个棋盘。
3. 创建了棋盘之后,我们需要将棋盘初始化,将所有的格子都初始化为空格。
4. 上一步仅仅初始化了棋盘,并没有打印到屏幕上,我们需要让用户看到棋盘才可以正常下棋,所以要打印棋盘。
5. 棋盘已就位,我们要开始下棋,由我们来和电脑对弈,所以这部分要实现的功能是玩家在棋盘上下棋。我们还需要在其中实现一个检验合法性的功能,来检验棋盘是否已经下满了,如果已经下满了,当然不能继续在棋盘上下棋。
6. 玩家下棋之后轮到电脑下棋。
7.当双方达到一定条件之后,一方或输或赢或双方平局,我们需要实现判断输赢的功能。
1. 游戏菜单
2. 创建棋盘
3. 初始化棋盘
4. 显示棋盘
5. 玩家下棋
6. 电脑下棋
7. 判断输赢
2.1 游戏菜单
我们在 game.c 和 test.c 里面均要用到库函数以及函数,为了更加便捷,我们把所有库函数头文件的包含、宏定义以及函数声明都放到 game.h 中,然后在 game.c 和 test.c 中包含上game.h。
首先在 main 函数中实现一个菜单,我们选择用do while循环来实现,因为无论游戏如何进行,都必须先让用户看到菜单界面选择是否进入游戏。do while 循环的特点就是先去做,再去判断条件,至少执行一次。
我们用 printf 函数和 scanf 函数来提醒用户进行选择并且输入选择。
用户选择之后我们需要对用户的选择做出相应的反馈,选择进入游戏,就进入游戏逻辑的函数,或者选择退出,选错了就要重新选择。
接下来测试一下功能:
2.2 创建棋盘
接下来创建一个行列都是3的棋盘
此时我们用到符号定义,用 ROW 和 COL 分别代表行和列的值,这样做的好处是我们在之后维护或者迭代代码的时候,不必修改所有行列的值,直接在头文件里更改就可以·,同时也增加了代码的可读性。
2.3 初始化棋盘
接下来我们需要写一个函数来初始化棋盘,并且将每一个格子都设置为空格。
在 test.c 文件中调用函数,game.h 中声明函数,game.c中定义函数。
函数调用:
函数声明:
函数定义:
2.4 打印棋盘
接下来我们需要写一个打印棋盘函数,用来打印当前棋盘的状态。
函数调用:
函数声明:
函数定义:
测试一下:
但是如果这么写的话不便于维护,如果我们后期要迭代成五子棋,需要打印5*5的棋盘,当ROW 和 COL 的值更改了之后,打印的行没有问题,但是列还是不变,会变成这个样子:
为了适应更多情况,更改代码如下:
行列改为5的情况也适用:
2.5 玩家下棋
接下来我们要实现让玩家能够输入坐标并在棋盘上下棋。
函数调用:
玩家下棋实际上还是对表示棋盘的数组进行操作,而且要检验下棋位置是否合法,所以函数传参的时候需要把数组和行列都传过去。
而且玩家下棋和电脑下棋是一个不断交互的过程,双方轮次下棋,直到一方获胜或者平局为止。
我们暂时不考虑游戏终止的条件,用一个 while 循环里面置玩家和电脑下棋的函数。
注:验证两点是否合法
1. 落子范围是否合法
2. 落子位置是否已被占用
函数声明:
函数定义:
2.6 电脑下棋
要实现电脑下棋,我们可以用生成随机数的方法,然后通过对生成随机数的限制控制随机数生成的范围,随机数代表 board 这个二维数组的坐标。
然后通过合法性验证:
如果随机数生成的坐标处没有下棋,则电脑落子。
如果已经下棋,则继续循环生成随机数,直至电脑落子后跳出循环。
函数调用:
函数声明:
函数定义:
包含头文件:
srand 函数需要包含<stdlib.h>
time 函数需要包含<time.h>
设置随机数生成起点:
2.7 判断输赢
我们要实现的这个函数需要判断四种状态:
1. 玩家赢 - ’*‘
2. 电脑赢 - ’#‘
3. 平局 - 'Q'
4. 继续 - 'C'
我们可以约定不同状态返回不同的符号如上
在每一次下棋后都要判断一次输赢,且判断输赢之后显示最终的棋盘。
函数声明:
函数定义:
3. 附完整代码
3.1 test.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void game() { char ret = 0; char board[ROW][COL] = { 0 };//创建棋盘 InitBoard(board, ROW, COL);//初始化棋盘 DisplayBoard(board, ROW, COL);//打印棋盘 while (1) { PlayerMove(board, ROW, COL);//玩家下棋 ret = IsWin(board, ROW, COL);//判断输赢 if (ret != 'C') { break; } DisplayBoard(board, ROW, COL); ComputerMove(board, ROW, COL);//电脑下棋 ret = IsWin(board, ROW, COL);//判断输赢 if (ret != 'C') { break; } DisplayBoard(board, ROW, COL); } if (ret == '*') { printf("\n玩家获胜!!!!!!!!!!!!!!!!\n"); } else if (ret == '#') { printf("\n电脑获胜!!!!!!!!!!!!!!!!\n"); } else { printf("\n平局\n"); } DisplayBoard(board, ROW, COL);//打印棋盘 } void menu() { printf("********************\n"); printf("***1.play 0.exit***\n"); printf("********************\n"); } int main() { srand((unsigned int) time(NULL));//设置随机数生成的起点 int input = 0; 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; }#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void game() { char ret = 0; char board[ROW][COL] = { 0 };//创建棋盘 InitBoard(board, ROW, COL);//初始化棋盘 DisplayBoard(board, ROW, COL);//打印棋盘 while (1) { PlayerMove(board, ROW, COL);//玩家下棋 ret = IsWin(board, ROW, COL);//判断输赢 if (ret != 'C') { break; } DisplayBoard(board, ROW, COL); ComputerMove(board, ROW, COL);//电脑下棋 ret = IsWin(board, ROW, COL);//判断输赢 if (ret != 'C') { break; } DisplayBoard(board, ROW, COL); } if (ret == '*') { printf("\n玩家获胜!!!!!!!!!!!!!!!!\n"); } else if (ret == '#') { printf("\n电脑获胜!!!!!!!!!!!!!!!!\n"); } else { printf("\n平局\n"); } DisplayBoard(board, ROW, COL);//打印棋盘 } void menu() { printf("********************\n"); printf("***1.play 0.exit***\n"); printf("********************\n"); } int main() { srand((unsigned int) time(NULL));//设置随机数生成的起点 int input = 0; 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; }
3.2 game.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); //判断输赢 //玩家赢 - '*' //电脑赢 - '#' //平局 - 'Q' //继续 - 'C' char IsWin(char board[ROW][COL], int row, int col);
3.2 game.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void InitBoard(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 DisplayBoard(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"); // // } // } //} void DisplayBoard(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++) { printf(" %c ", board[i][j]); if (j < col - 1) { printf("|"); } } printf("\n"); //打印分割行 if (i < row - 1) { int j = 0; 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; printf("\n玩家下棋:\n"); while (1) { printf("\n请输入坐标:\n"); 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坐标被占用,请选择其他位置\n"); } } } void ComputerMove(char board[ROW][COL], int row, int col) { printf("\n电脑下棋:\n"); int x = 0; int y = 0; while (1) { x = rand() % row;//0~2 y = rand() % col;//0~2 if (board[x][y] == ' ') { board[x][y] = '#'; break; } } } //棋盘被下满了返回1 //棋盘没下满返回0 int IsFull(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] != '0') { return 0;//不满 } } } return 1; } 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][1] != ' ') { return board[i][1]; } } //列 int j = 0; for (j = 0; j < col; j++) { if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ') { return board[1][j]; } } //对角线 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'; }