📘游戏基本介绍:
嘻嘻,想必大家都玩过吧,有时上课玩,下课玩儿,玩的不亦乐乎。哈哈!
规则:双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子连成一条线的一方则视为胜利者。
让我们重温经典,从新拾起我们儿时的回忆。把它带入C世界。
📙基本功能分配:
📕A.游戏相关定义–game.h
简单介绍:
- game.h这个头文件就像海上巨轮的总舵,总舵发出一个个的指令 ,指挥着船员合理进行工作。game.h也是如此“指挥”着game.c从而实现游戏的运行。
这里包含几个关键的点,让我们一起来看看吧😁
- 头文件集合地:
#pragma once #include<stdio.h> #include<time.h> #include<stdlib.h>
这里(游戏)包含了我们会使用的几个头文件,我们可以把这些头文件放在game.h里,这样我们在其他板块引用game.h即可使用其他文件。
- 引用方式:
#include"game.h" //这里用的是" "而不是<>哦!
- define的整数定义:
#define ROW 3 #define COL 3 #define WZQ 3
这也是三子棋盘可以变化为N子棋盘的原因,所以这个相当于一个很不错的优化。
- 关键函数的定义:
//初始化棋盘 void init_board(char board[ROW][COL], int row, int col); //打印棋盘 void print_board(char board[ROW][COL], int row, int col); //玩家下棋 void player_move(char board[ROW][COL], int row, int col); //电脑下棋 void computer_move(char board[ROW][COL], int row, int col); //判断输赢 char who_is_win(char board[ROW][COL], int row, int col);
在这里定义关键函数,在我看来其实是梳理程序的一部,建立游戏实现框架,我尝试把这个部分屏蔽,并不影响游戏的运行。
好吧,总部大佬我们已经知道了,接下来我们来看看游戏躯干吧!
game.h
#pragma once #include<stdio.h> #include<time.h> #include<stdlib.h> #define ROW 3 #define COL 3 #define WZQ 3 //初始化棋盘 void init_board(char board[ROW][COL], int row, int col); //打印棋盘 void print_board(char board[ROW][COL], int row, int col); //玩家下棋 void player_move(char board[ROW][COL], int row, int col); //电脑下棋 void computer_move(char board[ROW][COL], int row, int col); //判断输赢 char who_is_win(char board[ROW][COL], int row, int col);
📕B. 测试的逻辑–test.c
- test.c我认为它是游戏的躯干,主题,毕竟主函数在这不是嘛(虽然很细)。好吧,我们来慢慢认识它吧。
int main() { //游戏测试 test(); return 0; }
a.游戏菜单–menu函数
- 这个菜单与游戏设计的menu一样,直接上链接
void menu() { printf("******************************\n"); printf("******** 1、play **********\n"); printf("******** 0、eixt **********\n"); printf("******************************\n"); }
b.游戏总框架–test函数:
1.游戏可持续性的实现
- 这里我们为了是程序实现可持续性,用到了do-while循环来实现。在通过选项判断是否循环,或者进入下个部分。
- 通过上面菜单,我们有三种选择:
- 1.进行游戏,进入game()函数。但未脱离循环,函数执行完,仍在循环内。
- 2.退出游戏,脱离循环,结束程序。
- 3.非法输入,强行输入其他字符,无法识别,重新输入,继续循环。
- 综上,我们会选择采取Switch语句并且我们可以确定while的判断条件为input输入是否为0。
—————————————————————————代码如下——————————————————————
void test() { 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); }
- 这里也是 游戏实现框架–game函数与我们简要讲的game.c有着逻辑关系
2.game函数框架:
1.game()函数来理清框架,把游戏逻辑全部理清,是游戏实现的索引。我们用具代码来看看吧。
2.在我看来其实和test.c很相像。
3.具体内容我们就不在这里赘述,我们在game.c中来看吧!
void game() { char board[ROW][COL]; //初始化棋盘 init_board(board,ROW,COL); //打印棋盘 print_board(board,ROW,COL); //玩家下棋 char ret = 0; while (1) { //玩家下棋 player_move(board, ROW, COL); print_board(board, ROW, COL); //判断输赢 ret = who_is_win(board, ROW, COL); if (ret != 'C') { break; } //电脑下棋 computer_move(board, ROW, COL); print_board(board, ROW, COL); //判断输赢 ret = who_is_win(board, ROW, COL); if (ret != 'C') { break; } } switch (ret) { case '#': printf("电脑胜利\n"); break; case '*': printf("玩家胜利\n"); break; default: printf("平局\n"); break; } //游戏继续:'C' //电脑胜利:'#' //玩家胜利:'*' //平局:'P' }
test.c
#include"game.h" void menu() { printf("******************************\n"); printf("******** 1、play **********\n"); printf("******** 0、eixt **********\n"); printf("******************************\n"); } void game() { char board[ROW][COL]; //初始化棋盘 init_board(board,ROW,COL); //打印棋盘 print_board(board,ROW,COL); //玩家下棋 char ret = 0; while (1) { //玩家下棋 player_move(board, ROW, COL); print_board(board, ROW, COL); //判断输赢 ret = who_is_win(board, ROW, COL); if (ret != 'C') { break; } //电脑下棋 computer_move(board, ROW, COL); print_board(board, ROW, COL); //判断输赢 ret = who_is_win(board, ROW, COL); if (ret != 'C') { break; } } switch (ret) { case '#': printf("电脑胜利\n"); break; case '*': printf("玩家胜利\n"); break; default: printf("平局\n"); break; } //游戏继续:'C' //电脑胜利:'#' //玩家胜利:'*' //平局:'P' } void test() { //随机数生成器 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); } int main() { //游戏测试 test(); return 0; }
📕C.游戏实现–game.c
- game.c这里就是游戏核心的运行,也是游戏实现运行的关键。来吧,来吧,看看真正的实体吧😁
📗a.棋盘的建立:
这里棋盘建立主要是二维数组,实现的细节很多,好好听哦。
1.棋盘初始化–init函数
这里我们的是思路是建立所需棋盘大小并且还可以更改,这里大家联想到了什么。
对咯,就是game.h里的#define定义整数喽,棋盘需要变化只需要改变定义整数大小即可。
创建一个二维数组:
输入‘空格’完成初始化。
void init_board(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] = ' ';//初始化 } } }
这些地方一定要调试检查,不然等代码后面多了,可能就不那么容易了。
有的朋友肯定会提出疑问,我打出来我看不到啊,怎么检查呢。
这里大家可以将数组初始化为‘*’;这样就便于检查喽。
2.棋盘打印–print函数
棋盘打印有很多细节,让我给大家慢慢道来!
首先我们要知道棋盘如何构成:
我们可以把棋盘拆分成两种类型来解释:
1.第一层是由“ | | ”构成的,我们还要拆分变为是由三个“ |”组成,但少了最后一个“|”
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("|"); } }
2.同样的道理,第二层我们可以细分为由三个“—|"组成,同样少了最后一个”|“这里就不插入代码筹字数咯。
3.细心的朋友一定发现了,我们最后一行也少了一排“—”。代码同理。
void print_board(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)//少一行 for (j = 0; j < col; j++) { printf("---"); if (j < col - 1) printf("|"); } printf("\n"); } }
OK,棋盘已经搭建好了,那便来战胜负吧。
📗b.下棋:
1.玩家下棋–player函数
玩家下棋我们就自定义个函数:void player_move(char board[ROW][COL], int row, int col);
函数需要注意哪些问题呢,下面两点值得注意:
1)程序员与玩家区别
棋盘的自述:
大家好,我是棋盘YYDS,由我来为大家介绍下,程序员和玩家眼中的我。
玩家眼中,我是1,2,3并排走的,坐标范围都1-3.
可是程序员眼中,一眼看穿我的本质,明白我是一个二维数组,所以程序员能够找出我的真实坐标。0,1,2.
但我想让两边都认识我,都不矛盾,那就写个函数吧。
从棋盘的自述中,我们明白了两者的区别,很明显发现,两者认识只是差一,故此,我们便用函数更改
printf("请输入下棋坐标:>"); int x = 0; int y = 0; 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; }
2)判断输入是否合规
好了,意见统一,但要合法吧!有两法要记住。
~判断是否在范围限制内;
~判断是否输入重复;
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"); }
2.电脑下棋–computer函数
引入void computer_move(char board[ROW][COL], int row, int col);
1)生成范围内的随机数;
//随机数生成器 srand((unsigned int) time(NULL)); //**生成器放在主函数哦**
随机数生成了,范围咋办,别急,来答案了。
//这里row==col,都等于3; int x = rand() % row; int y = rand() % col; //取余一个数会得到比这个数小的结果,从而限定范围。
2)判断落子位置是否重复;
有空白就输入,没空白就算喽。
if (board[x][y] == ' ') { board[x][y] = '#'; break; }
📗c.胜负判断–is_win函数
引入char who_is_win(char board[ROW][COL], int row, int col);
1.玩家/电脑胜利:
这里我是采用了计数的方法,大家可以借鉴借鉴哈。
并且玩家和电脑判断高度相似,我就拿玩家举例子喽。
电脑判断只需把‘*’改为‘#’。
1)行列判断
int i = 0; for (i = 0; i < row; i++) { int j = 0; int count1 = 0, count2 = 0; //玩家处判断 for (j = 0; j < col; j++) { if (board[i][j] == '*') //行判断 count1++; if (board[j][i] == '*') //列判断 count2++; } switch (count1) { case WZQ: return '*'; } switch (count2) { case WZQ: return '*'; }
2)对角线判断
int i = 0; for (i = 0; i < row; i++) { int j = 0; int count3 = 0, count4 = 0; for (j = 0; j < col; j++) { if (board[j][j] == '*') //这里判断的是主对角线 count3++; if (board[j][ROW - 1 - j] == '*') //判断的是副对角线 count4++; } switch (count3) { case WZQ: return '*'; } switch(count4) { case WZQ: return '*'; } }
综上,其实我们可以把两种判断放在一起,通用一个循环,并且要注意一个点,就是count的归零。尤其小心哈
2.平局:
引入static int is_full(char board[ROW][COL], int row, int col);
~full_board函数
核心: 度过上面两个判断,判断是否还有空格,若无,则平局。
int i = 0; int count = 0; for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { if (board[i][j] != ' ') //这里是所有格子,判断是否有空格子 count++; } } if (count == ROW*COL)//这里是所有格子数量 return 1; else return 0;
3.游戏继续:
上面所有都结束了,那只能是继续喽。
判断结束,返回字符,在宣布结果。
好了,游戏就讲完喽,三个代码分享如下:
game.c
#include"game.h" void init_board(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 print_board(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)//少一行 for (j = 0; j < col; j++) { printf("---"); if (j < col - 1) printf("|"); } printf("\n"); } } void player_move(char board[ROW][COL], int row, int col) { printf("玩家下棋:\n"); while (1) { printf("请输入下棋坐标:>"); int x = 0; int y = 0; 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 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] == ' ') { board[x][y] = '#'; break; } } } static int is_full(char board[ROW][COL], int row, int col) { int i = 0; int count = 0; for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { if (board[i][j] != ' ') count++; } } if (count == ROW*COL) return 1; else return 0; } char who_is_win(char board[ROW][COL], int row, int col) { //玩家胜利或者电脑胜利:判断行n列n斜杠n int i = 0; for (i = 0; i < row; i++) { int j = 0; int count1 = 0, count2 = 0; int count3 = 0, count4 = 0; //玩家处判断 for (j = 0; j < col; j++) { if (board[i][j] == '*') count1++; if (board[j][i] == '*') count2++; if (board[j][j] == '*') count3++; if (board[j][ROW - 1 - j] == '*') count4++; } switch (count1) { case WZQ: return '*'; } switch (count2) { case WZQ: return '*'; } switch (count3) { case WZQ: return '*'; } switch(count4) { case WZQ: return '*'; } //电脑处判断 count1 = 0, count2 = 0; count3 = 0, count4 = 0; for (j = 0; j < col; j++) { if (board[i][j] == '#') count1++; if (board[j][i] == '#') count2++; if (board[j][j] == '#') count3++; if (board[j][ROW - 1 - j] == '#') count4++; } switch (count1) { case WZQ: return '#'; } switch (count2) { case WZQ: return '#'; } switch (count3) { case WZQ: return '#'; } switch (count4) { case WZQ: return '#'; } } if (is_full(board, ROW, COL) == 1) return 'P'; return 'C'; }
篇幅太长咯,抱歉了哈!
最后美图奉上:
遥望彩霞,奔赴梦想,我们都在,一起加油(兄弟们)!!!