三子棋、扫雷......这些游戏大家并不陌生吧?但是如果能够自己通过编程实现这些游戏,那该是多酷的一件事啊✨
目录
前言
一、三子棋游戏展示及介绍
二、三子棋游戏编程实现
1.游戏开始界面及代码雏形模板
2.构建棋盘
3.过渡
4. 玩家与电脑落棋设计
5.游戏输赢判定设计
三、最终代码呈现
1.test.c文件
2.game.h文件
3.game.c文件
四、心得体会
C语言在学习了函数还有数组后,我们可以自己写一些经典的小游戏了。这能让我们更加熟练地掌握知识。今天我们将通过C语言,实现简单版三子棋小游戏!下面就一起来一步一步地实现吧!!
在开始今天的分享前,我们先总体的了解一下游戏的最终呈现并且可以先准备好编程思路
✔️当我们对我们将要实现的游戏有了大概的设计及思路后便可开始我们的编程之旅啦~~
在开始编程之前我们应该已经掌握函数知识,因为游戏的代码并没有很简单并且会分成好多个部分。这时就需要利用封装函数使得我们的程序更加简洁易读。
✏️在此我想先分享一个我自己的小心得:
当我们已经熟练掌握函数时,在要开始编程的时候可以先顺着思路给出一个程序雏形,当要实现什么功能时可以先定义一个函数,等到思路捋清后再进行函数的实现
代码展示:
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;
}
当程序雏形和思路已经初步形成后,便开始对游戏的具体实现了。
首先是Menu()函数的实现,便是打印出游戏开始菜单。此函数比较简单,在此不过多赘述。
void menu() {
printf("*******************************\n");
printf("***** 1.play 0.exit *****\n");
printf("*******************************\n");
}
接着便是对Game( )函数的实现。这才是今天的重头戏✈️......
代码展示:
void InitBoard(char board[3][3], int 3, int 3)
{
for(int i = 0;i<3;i++)
for (int j = 0; j < 3; j++)
{
board[i][j] = ' ';
}
}
void DispalyBoard(char board[3][3], int 3, int 3)
{
for (int i = 0; i < 3; i++)
{
printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
//打印分割行
if (i < row - 1)//构建这个判断能够使得棋盘的最后一行没有分割行
printf("---|---|---\n");
}
}
❓看完以上的代码后你是否有一些发现或者想法?
如果有一天,想要追求更高难度,不局限与三子棋。想要变成五子棋?十字棋?那岂不是要将代码里所有的“3”都改掉?并且打印棋盘的棋格分割也会出现错误需要重写。
所以上面的程序被我们写“死”了,也就是代码的耦合度太高,拓展力太差。这时我们就需要做到“解耦合”从而提高我们程序的拓展力
我们可以在程序的开头进行宏定义#define,定义棋盘的行数ROW和列数COL,并且对打印棋盘的函数进行优化。
优化版本代码展示:
#define ROW 3
#define COL 3
void InitBoard(char board[ROW][COL], int row, int col)
{
for(int i = 0;i|
注:以上是对初始化棋盘函数IntialBoard() 和 呈现棋盘函数DisplayBoard()的定义。而在打印棋盘的分割符时由于想要提高程序的拓展力,因而有些难度。有关的思考我通过在代码中的注释呈现。
根据第一部分的游戏展示及其思路,我们以及完成了游戏开始菜单以及游戏函数Game()的部分功能(初始化棋盘、呈现棋盘)而接下来将要实现的便是游戏的重点 ------人机博弈功能。同样的我们可以先通过定义函数将我们的大概思路框架呈现出来。
我们准备设计玩家先于电脑并依次下棋,并且每方下完一次棋后呈现新的棋盘。同时也应该具备判断输赢的功能。于是我大概构建了以下框架
void game() { char board[ROW][COL] = { ' ' }; InitialBoard(board, ROW, COL); DisplayBoard(board, ROW, COL); while (1) { PlayerMove();//玩家落子 DisplayBoard(board, ROW, COL); IsWin() //判断输赢机制 ComputerMove();//电脑落子 DisplayBoard(board, ROW, COL); IsWin() //判断输赢机制 } }
在此框架中我定义了玩家下棋函数PlayerMove( ),电脑下棋函数ComputerMove( ),还有输赢判断函数IsWin( )。接下来我们将一一实现。
- 从游戏的展示我们可以知道,我们想要玩家下棋时只需输入棋格坐标即可下棋。而数组的行列坐标都是从0开始的,但是可能玩家并不知道这一点对吧?所以这是我们接下来应该解决的问题。
- 其次,我们还能再考虑到什么问题呢?下棋下棋,是不是有可能会下错位置,没在棋盘范围里?也可能是不是下的位置已经有棋子了?所以我们应该对下棋坐标的合法性进行判断
代码展示:
void PlayerMove(char board[ROW][COL], int row, int col)
{
int x, y;
printf("玩家请下棋:\n");
while (1)
{
printf("请输入坐标:\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");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
当玩家下棋后便是电脑下棋。而我们这款简易版三子棋游戏是设计电脑下棋时,没有策略,随机找空格下棋。所以这个随机,就需要调用我们生成随机数的函数了。
要引用头文件#include
、 #include 然后在主函数设置随机数srand((unsigned)time(NULL));
具体原理这里就不过多赘述了
代码展示:
void ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋:\n");
int x, y;
//判断坐标是否被占用
while (1)
{
x = rand() % row;//表示范围是0~row
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
- 三子棋想必大家肯定知道规则吧,所以我们设计输赢判断的功能其实也就是一个遍历数组的过程,看是否有三个相同元素相连
- 而输赢判断应该在每下一次棋就执行一次,若有结果了就结束游戏,返回结果
- 然后此时我们应该思考一个问题:判定后有多少种结果?-------1.玩家赢 2.电脑赢 3.平局(棋盘已经下满) 4.还没分出胜负,游戏继续
- 说到底,输赢判断应该返回不同结果,并根据不同结果做出不同的反应。那么我们可以设计当玩家赢时,返回‘ * ’,当电脑赢时返回‘ # ’,当平局时返回‘ Q ’,当还未分出胜负时返回‘ C ’✈️
代码展示:
char IsWin(char board[ROW][COL], int row, int col)
{
//先判断行
for (int 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];
}
}
//列
for (int i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
{
return board[1][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[0][2] != ' ')
{
return board[1][1];
}
//能走到这里说明没人赢,那就可能是平局
if (IsFull(board,row,col))
{
return 'Q';
}
//如果没人赢又不是平局,那游戏继续
return 'c';
}
在设计输赢判断函数的过程中,由于棋盘下满才会返回平局结果,所以我们再封装一个IsFull()函数对棋盘是否已满做出判断 ✔️
int IsFull(char board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0;
}
}
return 1;
}
完成了下棋函数和输赢判断函数的实现后,我们的代码也将接近尾声了。剩下的就是设计Game( )函数根据输赢判断函数的不同结果做出不同反应了。
代码展示:
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");
}
else if (ret == '#')
{
printf("电脑赢了!\n");
}
else
{
printf("平局!\n");
}
DisplayBoard(board, ROW, COL);
}
由于这次代码的函数较多,我们可以另起一个以.h为后缀的头文件专门放置函数声明,另起一个以.c为后缀的源文件去定义函数,这样子我们游戏测试的源文件中就较为简洁易读
#include"game.h"
void menu() {
printf("*******************************\n");
printf("***** 1.play 0.exit *****\n");
printf("*******************************\n");
}
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");
}
else if (ret == '#')
{
printf("电脑赢了!\n");
}
else
{
printf("平局!\n");
}
DisplayBoard(board, ROW, COL);
}
int main() {
srand((unsigned)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;
}
#include
#include
#include
#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);
//输赢判断
char IsWin(char board[ROW][COL], int row, int col);
#include"game.h"
void InitBoard(char board[ROW][COL], int row, int col)
{
for(int i = 0;i= 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 ComputerMove(char board[ROW][COL], int row, int col)
{
printf("电脑下棋:\n");
int x, y;
//判断坐标是否被占用
while (1)
{
x = rand() % row;//表示范围是0~row
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
int IsFull(char board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return 0;
}
}
return 1;
}
//判断输赢
char IsWin(char board[ROW][COL], int row, int col)
{
//先判断行
for (int 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];
}
}
//列
for (int i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
{
return board[1][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[0][2] != ' ')
{
return board[1][1];
}
if (IsFull(board,row,col))
{
return 'Q';
}
//如果没人赢又不是平局,那游戏继续
return 'c';
}
这个代码可以算是我学到目前为止写过最难的一个,学习后也是受益匪浅。其实它运用的知识没有很多,主要运用了数组、函数的知识。而更多收获的是一个顺着思路一步一步前进一步一步优化拓展的编程感受,虽然在前进的时候有时候会感觉到举步维艰,但是当解决了一关又一关后那个豁然开朗和成就感又是令人沉醉且享受的。
今天的分享就到此为止啦,欢迎大家探讨及建议!✔️❤️点赞关注走一波~~~