📘游戏基本介绍:
嘻嘻,想必大家都玩过吧,有时上课玩,下课玩儿,玩的不亦乐乎。哈哈!
规则:双方依次在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';
}
篇幅太长咯,抱歉了哈!
最后美图奉上:
遥望彩霞,奔赴梦想,我们都在,一起加油(兄弟们)!!!