目录
一.游戏实现的整体思路
二.实现游戏大体框架
二.游戏函数的实现
1.初始化键盘
2.打印棋盘
3.下棋及胜负判断
(1)玩家下棋
(2)电脑下棋
(3)判断输赢
三.结语及源码
三子棋想必大家都不陌生,本篇文章带大家一起用C语言来实现三子棋小游戏,来体会体会编程的乐趣,本篇文章可以给你带来一种新的编程体验,带你实际了解逼真项目开发过程。(文章末尾贴有源码)
首先,通过我们的观察以及结合我们之前学过的知识,我们发现三子棋的棋盘与二维数组及其类似,他们都是三行三列,我们可以将这个3✖3的游戏棋盘中的数据储存进二维数组,玩家或电脑每次下一步棋,我们就将数组对应位置的数据改为*或#号作为下的棋子(本文中以*作为玩家下的棋子,#作为电脑下的棋子),每下一步棋或者说改一下数组数据,我们都要打印一遍棋盘。每次打印完棋盘,我们需要判断游戏是否继续进行,也就是看是否有哪一方获得游戏胜利或者平局。
在项目开始之前,我们将创建三个文件,主文件test.c放游戏主要大致框架,game.c实现游戏逻辑,game.h存放函数声明,以及头文件引用等内容。
我们首先在game.h文件中引入头文件stdio.h,再分别再game.c和test.c中引入头文件game.h,这样仅仅只需要在game.h文件中引用头文件。
注:引用自己编写的头文件得用双引号,而不是尖括号
我们先做一个简易的游戏菜单,回想我们平时在玩游戏时,一般是都先弹出游戏菜单让我们进行选择,这里我们也一样,设置一个input变量来保存用户的选择即输入值,我们一般玩游戏也不只是只玩一局,我们每次玩一局以后,我们希望能供用户选择是退出游戏还是继续再玩一局,这时候,我们可以通过do while循环实现,当然别的循环语句也可以,这里我采用的是do while循环,当用户输入完值以后,我们通过使用分支语句对用户的选择进行分流,来实现用户选择后对应的功能,以下代码仅仅是测试主体逻辑,游戏的实现用prinft函数替代,下面会详细讲解游戏主要实现逻辑。
//test.c文件
void menu()
{
printf("******************************\n");
printf("******** 1.play ***********\n");
printf("******** 0.exit ***********\n");
printf("******************************\n");
}
int main()
{
int input = 0;
do
{
//打印游戏菜单
menu();
printf("请选择>:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("玩游戏");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
这里我们将编写以上switch语句case 1 中的游戏逻辑函数。
我们首先在test.c文件中创建一个游戏逻辑的函数,我们会在这个函数里实现游戏的逻辑。我们先创建一个二维数组,这个数组将用来储存棋盘中的数据,为了增加代码的通用性,我们将定义一个宏作为二维数组的行和列,使我们未来想修改成别的数据时更加便利,我们将这个宏定义到game.h文件中,接着我们在game.h声明初始化键盘的函数,并在game.c来定义这个函数,最后在test.c调用这个函数。
//game.h文件
//游戏实现中各种函数的声明
#include
#define ROW 3
#define COL 3
//声明初始化棋盘函数
void init_keyboard(char keyboard[ROW][COL], int row, int col);
观察下图和代码,我们想先用空格将棋盘填充起来,这时,我们可以先将二维数组中的每个数据初始化为空格。
//game.c文件
//初始化棋盘
void init_keyboard(char keyboard[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
keyboard[i][j] = ' ';
}
}
}
//test.c文件
void game()
{
//创建一个存放棋子的二维数组
char keyboard[ROW][COL] = { 0 };
printf("三子棋小游戏\n");
//调用初始化游戏棋盘函数
init_keyboard(keyboard, ROW, COL);
}
如果说初始化键盘是磨刀,那么打印键盘就是切菜了,这是准备阶段的最后一步了,和上面声明和定义一样,都在特定的文件中编写,下面就仅仅展示定义的代码,仔细观察下图,我们发现可以通过双层循环的方式打印这个棋盘。
我们把1、2行合并看作一行,不难看出,我们可以通过循环三次,最后一次不打印最后横线那一行。竖排也是类似,不打印最后一排竖线。以下代码for循环里嵌套的第一个for循环也就是打印上图数字编号为1的那一行,for循环里嵌套的第二个for循环也就是打印上图编号为2的那一行。
//game.h文件
//打印棋盘
void display_keyboard(char keyboard[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
// %c | %c | %c
for (j = 0; j < col; j++)
{
printf(" %c ", keyboard[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");
}
}
}
下棋分为玩家下棋,电脑下棋,其中还有胜负的判断,大概思路是写一个循环,循环里首先是玩家下棋,接着打印棋盘和判断输赢,然后是电脑下棋,打印棋盘和判断输赢,无论有哪一方获得了胜利,就退出循环。
//test.c文件
char ret = 0;
do
{
//玩家下棋
printf("玩家下棋:\n");
player_move(keyboard, ROW, COL);
//打印棋盘
display_keyboard(keyboard, ROW, COL);
//判断输赢
ret = is_win(keyboard, ROW, COL);
if (ret != 'C')
{
break;
}
//电脑下棋
printf("电脑下棋:\n");
computer_move(keyboard, ROW, COL);
//打印棋盘
display_keyboard(keyboard, ROW, COL);
//判断输赢
ret = is_win(keyboard, ROW, COL);
if (ret != 'C')
{
break;
}
} while (1);
以下来编写这个循环里的几个函数
我们通过让玩家输入他们想要下棋的坐标来实现下棋步骤,在玩家输入坐标又可能出现以下情况。
a.玩家输入正确坐标,更改棋盘数组的数据
b.玩家输入坐标不合法,即输入了错误的数字(默认输入是1到3)
c.玩家输入的坐标被占用
出现b、c情况时,我们需要让玩家重新输入,这时我们可以设计一个循环,如果玩家输入正确的坐标就退出循环,如果玩家输入错误的坐标就让玩家重新输入,知道玩家输入正确坐标为止。
//game.c文件
//玩家下棋(*)
void player_move(char keyboard[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("请输入您要下的坐标:");
while (1)
{
scanf("%d %d", &x, &y);
if ((x >= 1 && x <= row) || (y >= 1 && y <= col))
{
if (keyboard[x - 1][y - 1] == ' ')
{
keyboard[x - 1][y - 1] = '*';
break;
}
else
{
printf("输入的左边已占用!请重新输入");
}
}
else
{
printf("请输入一个合法的坐标!\n");
}
}
}
电脑下棋也是同样的,我们用坐标来决定下哪,这个坐标如何形成呢,我们这里使用的是一个随机数字,我们用rand函数随机生成一个随机数,但使用rand函数之前必须要用srand函数设置一个随机数起点,srand函数得使用一个实时变化的量才可以确保我们生成的数字是随机的想想我们的生活中,时间是每时每刻都在发生变化的,于是我们将时间戳作为srand函数的参数生成随机数起点,time函数是生成时间戳的函数。
注:rand和srand函数得使用头文件stdlib.h,而time函数得使用头文件time.h,前面说过所有得头文件引用和声明都在game.h文件中编写。
因为生成得随机数范围是0~RAND_MAX,C语言中规定RAND_MAX的大小是0x7fff,因此我们必须将得到的随机数进行一个求余的操作才能得到我们想要得到的数据,同样,得到这个数据后我们必须判断该数据的坐标是否可以使用,这时我们需通过循环来寻找一个符合的随机数。得到这个数后,再修改棋盘数据来实现下棋的功能。
//game.c文件
//电脑下棋(#)
void computer_move(char keyboard[ROW][COL], int row, int col)
{
//用时间戳随机生成随机数起始点(用时间绑定)
srand((unsigned int)time(NULL));
while (1)
{
//生成随机数
int x = rand() % row;
int y = rand() % col;
if (keyboard[x][y] == ' ')
{
keyboard[x][y] = '#';
break;
}
}
}
每当有一方下了棋,就必须有判断输赢的机制,我们设计一个is_win函数来判断输赢,如果玩家赢则返回字符*,如果电脑赢则返回#,如果平局则返回Q,如果游戏未分胜负,继续进行,则返回C。
a.判断哪方输赢,我们通过列举法,赢了无非是三个棋子在同一个横排,同一个竖排或两条对角线,无非就是这三种可能。
b.平局即以上都不满足和棋盘满了,我们这里仅仅需要判断棋盘是否满了就可以了,因为如果有哪一方赢了,上面的return函数早就将结果带回了原函数,这里我们编写一个is_full函数来判断棋盘是否满了,如果满了则返回1,没满返回0,再将结果返回is_win函数。
c.以上几种情况都不满足就只有游戏未分胜负一种可能了,我们仅仅用一个return函数返回字符C即可。
//game.c文件
//判断棋盘是否满了
int is_full(char keyboard[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
int flat = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (keyboard[i][j] == ' ')
{
flat = 0;
}
}
}
return flat;
}
//判断游戏是否结束
char is_win(char keyboard[ROW][COL], int row, int col)
{
int i = 0;
//同一个横排获得胜利
for (i = 0; i < row; i++)
{
if (keyboard[i][0] == keyboard[i][1] && keyboard[i][1] == keyboard[i][2] && keyboard[i][0] != ' ')
{
return keyboard[i][0];
}
}
//同一个竖列获得胜利
for (i = 0; i < col; i++)
{
if (keyboard[0][i] == keyboard[1][i] && keyboard[1][i] == keyboard[2][i] && keyboard[0][i] != ' ')
{
return keyboard[0][i];
}
}
//对角线获得胜利
if (keyboard[0][0] == keyboard[1][1] && keyboard[1][1] == keyboard[2][2] && keyboard[1][1] != ' ')
{
return keyboard[1][1];
}
if (keyboard[0][2] == keyboard[1][1] && keyboard[1][1] == keyboard[2][2] && keyboard[1][1] != ' ')
{
return keyboard[1][1];
}
//平局
if (is_full(keyboard, row, col))
{
return 'Q';
}
//继续
return 'C';
}
以下是该文章所有代码总结
test.c文件
//test.c文件
#define _CRT_SECURE_NO_WARNINGS 1
//实现游戏主要框架
#include "game.h"
void game()
{
char keyboard[ROW][COL] = { 0 };
printf("三子棋小游戏\n");
//初始化游戏棋盘
init_keyboard(keyboard, ROW, COL);
//打印棋盘
display_keyboard(keyboard, ROW, COL);
//下棋
char ret = 0;
do
{
//玩家下棋
printf("玩家下棋:\n");
player_move(keyboard, ROW, COL);
ret = is_win(keyboard, ROW, COL);
if (ret != 'C')
{
break;
}
display_keyboard(keyboard, ROW, COL);
printf("电脑下棋:\n");
computer_move(keyboard, ROW, COL);
display_keyboard(keyboard, ROW, COL);
ret = is_win(keyboard, ROW, COL);
if (ret != 'C')
{
break;
}
} while (1);
if (ret == '*')
{
printf("玩家胜利!\n");
}
else if(ret == '#')
{
printf("电脑胜利!\n");
}else
{
printf("平局!\n");
}
}
void menu()
{
printf("******************************\n");
printf("******** 1.play ***********\n");
printf("******** 0.exit ***********\n");
printf("******************************\n");
}
int main()
{
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;
}
game.h文件
//game.h文件
#define _CRT_SECURE_NO_WARNINGS 1
//游戏实现中各种函数的声明
#include
#include
#include
#define ROW 3
#define COL 3
//初始化键盘
void init_keyboard(char keyboard[ROW][COL], int row, int col);
//打印键盘
void display_keyboard(char keyboard[ROW][COL], int row, int col);
//玩家下棋
void player_move(char keyboard[ROW][COL], int row, int col);
//电脑下棋
void computer_move(char keyboard[ROW][COL], int row, int col);
//判断游戏是否结束
char is_win(char keyboard[ROW][COL], int row, int col);
game.c文件
//game.c文件
#define _CRT_SECURE_NO_WARNINGS 1
//实现游戏的逻辑
#include "game.h"
//初始化键盘
void init_keyboard(char keyboard[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
keyboard[i][j] = ' ';
}
}
}
//打印键盘
void display_keyboard(char keyboard[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
//printf(" %c | %c | %c \n", keyboard[i][0], keyboard[i][1], keyboard[i][2]);
// | |
for (j = 0; j < col; j++)
{
printf(" %c ", keyboard[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 keyboard[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("请输入您要下的坐标:");
while (1)
{
scanf("%d %d", &x, &y);
if ((x >= 1 && x <= row) || (y >= 1 && y <= col))
{
if (keyboard[x - 1][y - 1] == ' ')
{
keyboard[x - 1][y - 1] = '*';
break;
}
else
{
printf("输入的左边已占用!请重新输入");
}
}
else
{
printf("请输入一个合法的坐标!\n");
}
}
}
//电脑下棋(#)
void computer_move(char keyboard[ROW][COL], int row, int col)
{
srand((unsigned int)time(NULL));
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (keyboard[x][y] == ' ')
{
keyboard[x][y] = '#';
break;
}
}
}
//判断棋盘是否满了
int is_full(char keyboard[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
int flat = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (keyboard[i][j] == ' ')
{
flat = 0;
}
}
}
return flat;
}
//判断游戏是否结束
char is_win(char keyboard[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
if (keyboard[i][0] == keyboard[i][1] && keyboard[i][1] == keyboard[i][2] && keyboard[i][0] != ' ')
{
return keyboard[i][0];
}
}
for (i = 0; i < col; i++)
{
if (keyboard[0][i] == keyboard[1][i] && keyboard[1][i] == keyboard[2][i] && keyboard[0][i] != ' ')
{
return keyboard[0][i];
}
}
if (keyboard[0][0] == keyboard[1][1] && keyboard[1][1] == keyboard[2][2] && keyboard[1][1] != ' ')
{
return keyboard[1][1];
}
if (keyboard[0][2] == keyboard[1][1] && keyboard[1][1] == keyboard[2][2] && keyboard[1][1] != ' ')
{
return keyboard[1][1];
}
//平局
if (is_full(keyboard, row, col))
{
return 'Q';
}
//继续
return 'C';
}
以上是本文所有内容了,本文仅仅想传达开发中的一种设计模式和游戏设计的思路,更重要是提高编程过程中的趣味性,码文不易,大家的三连是对小编最大的鼓励,谢谢。