书摘文案:尽最大的努力,留最小的遗憾。
在前面的几篇文章中详细的介绍了,分支与循环语句,函数,数组的基础知识,现在我们利用他们来做一个三子棋的小游戏吧!
目录标题
1.游戏描述
三子棋游戏规则: 电脑(随机)和玩家一次下一子,双方轮流交替下子,直到电脑和玩家任意一方获胜,或者棋盘满了,结束游戏。
获胜判定: 只要电脑或者玩家任意一方的棋子有三子连在一起, 可以为横连、纵连、斜连,则该方获胜,游戏结束。若棋盘满了,则判定为平局。
程序需求:游戏的框架,游戏菜单,棋盘打印,玩家下棋,电脑下棋,输赢判断。
2.实现思路
前提条件:先创建3个文件实现不同的功能。
test.c文件:实现游戏的整体框架,执行流程。
game.c文件:实现游戏中出现的游戏函数:棋盘打印,玩家下棋等等。
game.h文件:声明函数。
3.讲解思路
3.1菜单的打印以及整体框架
当我们想要玩这个游戏的时候,运行时一般开始时都会有游戏菜单的显现(让玩家可以做出选择),所以我们需要一个菜单。
void menu()
{
printf("***********************\n");
printf("***********************\n");
printf("****1.play 0.exit*****\n");
printf("***********************\n");
printf("***********************\n");
}
在这里我们打印菜单,让玩家输入1玩游戏,输入0退出游戏。
1.在玩家一运行程序这个菜单就会出现,我们需要在主函数中使用 do while 语句。
2.玩家在菜单出现后需要做出选择,我们不妨使用switch case 语句。
代码展现:
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;
}
注意:我们这里定义了一个变量 input 我们将它同时放置在了 switch(),while()语句项中,首先进入 do while语句时,做出选择。
输入1:
进入switch语句,并进入case1,游戏结束后,break语句跳出 switch 语句到 while 语句中进行判断,我们一开始是输入1会再次进入do语句中。
输入0:
进入switch语句,并进入case0中,break语句跳出switch语句到while语句中进行判断,我们输入的是0为假不会再次进入do语句中,游戏结束,
输入其他数字:
进入switch语句,并进入default语句中,break语句跳出switch语句到while语句中进行判断,我们输入的是非零数字,会再次进入do语句中。
3.2game函数中的游戏思路
在玩家做出选择想要玩游戏,进入case1进入game函数(游戏开始)。
这里先展示一下游戏。
game函数的代码流程:
void game()
{
char board[ROW][COL] = { 0 };
//初始化棋盘
Initboard(board, ROW, COL);
//打印棋盘
Displayboard(board, ROW, COL);
char ret = 0;
while (1)
{
//玩家下棋
Playerboard(board, ROW, COL);
//打印棋盘
Displayboard(board, ROW, COL);
//判断输赢
ret = Iswin(board, ROW, COL);
if (ret != 'C')
break;
//电脑下棋
Computerboard(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");
}
关于game函数中所出现的各种函数我们都写入game,c函数中,然后在game.h文件中声明。
但是想要使用这些函数,我们就需要在 test.c和game.c文件中引用 game.h 文件即可。
因为是我们创建的一个头文件所以我们以“ ”来引用,#Include "game.h"
3.4 game.h文件中游戏函数实现
初始化棋盘
//初始化棋盘
void Initboard(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] = ' ';
}
}
}
这里的ROW,COL是我们在game.h文件中#define定义的宏,只要修改宏我们就可以实现多行多列的棋盘。
#define ROW 3
#define COL 3
打印棋盘
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 < row; j++)
{
printf(" %c ", board[i][j]);
if (j < col-1)
{
printf("|");
}
}
printf("\n");
if (i < row - 1)
{
for (j = 0; j < row; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
玩家下棋
void Playerboard(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("请玩家下棋。\n");
while (1)
{
//输入坐标
printf("请输入下棋的坐标,中间使用空格>:");
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");
}
}
}
注意:
1.在玩家下棋的输入的坐标时我们首先是要判断它是否合法的。因为如果坐标被占用的话,玩家就不可以下棋。
2.下棋我们要考虑所输入的坐标是否会超过所定义的范围。
3.我们使用while语句是防止玩家下棋超过定义范围,以及输入的棋盘坐标非法能够重新下棋。
4.因为玩家输入的坐标(x,y)肯定是除0以外的数字,但是访问数组是从下标0开始的,所以需要(x-1,y-1)来符合 条件。
电脑下棋
//电脑下棋
void Computerboard(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋:\n");
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
这里我们先介绍一下rand函数。
rand()函数: 随机数生成器,执行时生成一个随机数,但是多次调用时随机数都是相同的,所以也称它为伪随机数,若想要不同我们就要调用 srand() 函数。
srand()函数: 初始化随机数生成器,设置供rand()函数使用的随机数生成种子,使每次调用rand函数时生成的随机数不同。如果你在第一次调用rand()之前没有调用srand(),那么系统会为你自动调用srand()。而使用同种子相同的数调用 rand()会导致相同的随机数序列被生成。使用方法:
srand((unsigned int)time(NULL));
注意需要引用头文件> #include <stdlib.h> 和 #include<time.h>(在头文件game.h中引用即可)
玩家下棋
玩家赢返回 ’*‘ 字符
电脑赢返回 '#‘ 字符
平局返回 ’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) == 1)
{
return 'Q';
}
//继续
return 'C';
}
注意:
因为判断输赢时候时连成3子就获得胜利,但以空格初始化的棋盘也会造成连成3子,所以我们就需要排除3子都是空格的情况。
平局
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] == ' ')
{
return 0;
}
}
}
return 1;
}
上面这些函数实现都是定义在game.c文件中,然后我们在game.中声明函数。
3.5game.c中函数的声明
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//初始化棋盘
void Initboard(char board[ROW][COL], int row, int col);
//打印棋盘
void Displayboard(char board[ROW][COL], int row, int col);
//玩家下棋
void Playerboard(char board[ROW][COL], int row, int col);
//电脑下棋
void Computerboard(char board[ROW][COL], int row, int col);
//判断输赢
char Iswin(char board[ROW][COL], int row, int col);
小细节:因为不仅仅是test.c文件中需要 #include<stdio.h>,game.c文件中各种函数的实现(有使用printf函数等),所以我们将#include<stdio.h>放在game.h文件中即可。
4.最终效果
玩家赢:
一个技巧
当我们运行程序时我们玩完一个游戏时,上一局的游戏痕迹依旧存在,我们想要玩完一局后屏幕可以清空,可以调用系统命令system(”cls")来清屏, 但相应的需要使用引用头文件#include < Windows > .
5.整体代码
test.c文件
#include"game.h"
void menu()
{
Sleep(1000);
system("cls");
//这两句代码时等待1秒钟,清屏。
printf("***********************\n");
printf("***********************\n");
printf("****1.play 0.exit*****\n");
printf("***********************\n");
printf("***********************\n");
}
void game()
{
char board[ROW][COL] = { 0 };
//初始化棋盘
Initboard(board, ROW, COL);
//打印棋盘
Displayboard(board, ROW, COL);
char ret = 0;
while (1)
{
//玩家下棋
Playerboard(board, ROW, COL);
//打印棋盘
Displayboard(board, ROW, COL);
//判断输赢
ret = Iswin(board, ROW, COL);
if (ret != 'C')
break;
//电脑下棋
Computerboard(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()
{
int input = 0;
srand((unsigned int)time(NULL));
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.c文件
#include"game.h"
//初始化棋盘
void Initboard(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 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 < row; j++)
{
printf(" %c ", board[i][j]);
if (j < col-1)
{
printf("|");
}
}
printf("\n");
if (i < row - 1)
{
for (j = 0; j < row; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
//玩家下棋
void Playerboard(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("请玩家下棋。\n");
while (1)
{
//输入坐标
printf("请输入下棋的坐标,中间使用空格>:");
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 Computerboard(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋:\n");
while (1)
{
x = rand() % row;
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
//判断是否平局
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] == ' ')
{
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][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[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) == 1)
// {
// return 'Q';
// }
//
// //继续
// return '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) == 1)
{
return 'Q';
}
//继续
return 'C';
}
game.h文件:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<Windows.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 Playerboard(char board[ROW][COL], int row, int col);
//电脑下棋
void Computerboard(char board[ROW][COL], int row, int col);
//判断输赢
char Iswin(char board[ROW][COL], int row, int col);
结语:这次讲的是很适合新手练习的一个小游戏三子棋,可以综合练习前文所学习的知识,当然这里还有很多可以改进的地方,比如多子棋的实现,或者改进电脑下棋的代码(因为这里赢电脑太简单了😂😂😂),大家都可以去尝试去努力实现一下,后续中我也会改进的。如果文章中有什么错误或者疑惑都欢迎大家在评论区提出,谢谢。