C语言程序设计笔记---019
- 初阶三子棋小游戏(开源)
- 1、arr_main.c程序大纲
- 2、arr_game1.h
- 3、arr_game1.c
- 3.1、 自定义初识化函数 InitBoard( ) 和 自定义显示函数 DisPlayBoard( )
- 3.2、 自定义玩家下棋函数 PlayerMove( )
- 3.4、 自定义电脑下棋函数 ComputerMove( )
- 3.5、 输赢判断函数 IsWin( )
- 4、结束语
初阶三子棋小游戏(开源)
前言:
游戏规则:
(1)、在打印的数组棋盘里,输入坐标,下棋。
(2)、坐标格式:x(空格) y
(3)、x横坐标,y竖坐标
(4)、连成三个棋子为一线则判定胜利
如图所示:
采用模块化编写:
arr_game1.c执行主要逻辑程序
arr_game1.h存放头文件或函数声明等程序
arr_main.c放主函数逻辑程序
1、arr_main.c程序大纲
首先,从以往玩游戏的经验来谈,我们需要为游戏写一个游戏开始菜单,由玩家选择是否开始游戏。
这里可以借助所学的menu( )自定义函数,设计一个简易的菜单。
void menu()
{printf("****************************************\n");printf("************** 1.play game *************\n");printf("************** 0.game over *************\n");printf("****************************************\n");
}
当我们选择1,则开始游戏;当选择0,则退出游戏。
那么就得思考,如何对玩家得选择进行判定?
1.利用 scanf( ) 函数获取输入值,将获取的值,借用 do while 循环语句和switch( )选择语句,进行下一步。
2.当选择1,开始游戏则执行game()自定义,游戏主逻辑执行程序
3.当选择0,switch中 进入case 0 : 的入口,执行退出游戏,并且 do while( ),判定为0,则退出程序
4.当玩家误选择非法数值,则default : 提示玩家输入错误
int input = 0;do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:printf("\n*********** 三子棋游戏开始 *************\n\n");game();//游戏执行逻辑函数break;case 0:printf("\n退出游戏\n");break;default:printf("\n选择错误请重新选择\n\n");break;}} while (input);
接下来,主要阐述game( )函数内容,游戏的执行逻辑:
1.当玩家选择1,开始游戏后,会显示整个空棋盘
如何实现空棋盘的显示呢?
(1)、利用所学的数组知识可以联想到,棋盘不过是由一个个字符拼接而成,在需要下棋的地方,放置空格字符进行占位,达到肉眼所见的空棋盘。
(2)、又因为棋盘是一个平面,平面由一条条线组成,线又由一个个点组成,首先得定义和初识化一个二维数组 board[ROW][COL] ,由几行几列的方式实现一个面的显示/打印。
(3)、自定义初识化函数 InitBoard( ) ,自定义显示棋盘函数 DisPlayBoard( ) ;
(4)、函数的参数,可想而知,需要数组名(board) — 首元素的地址指定需操作的数组,需要行(ROW)和列(COL)指定操作的元素或坐标或地址;
void game()
{//定义棋盘大小char board[ROW][COL] = { 0 };//初识化棋盘InitBoard(board,ROW,COL);//显示棋盘DisPlayBoard(board, ROW, COL);}
2.当我们完成棋盘的初识化和显示后,那么默认玩家先下棋,电脑再下棋;
所以就得写一个玩家下棋的自定义逻辑函数和电脑下棋的自定义函数,交替下棋直到游戏判定输赢或平局。
(1)、玩家下棋自定义函数 PlayerMove( ) ;
(2)、电脑下棋自定义函数 ComputerMove( ) ;
(3)、判断输赢函数 IsWin( );
那么很容易思考到,交替进行下棋,就是反复的调用玩家下棋函数和电脑下棋函数,同时不停的判断每一步棋是否达到输赢的结果。所以需要一个循环来实现,这里就用while循环,实现不停的下棋。
但是,不停的下棋,始终在死循环,那么就思考利用,break跳出循环,所以 IsWin( )函数是具备返回值的,可以由IsWin( )函数的返回值进行判定跳出。 且由于棋局均有字符进行的操作,所以确定了 IsWin( )数据类型也为 char 类型。
(4)、IsWin( )跳出循环的返回值判定:
a、玩家获胜 - - - - ‘ * ’
a、电脑获胜 - - - - ‘ # ’
a、平局 - - - - ‘ Q ’
a、游戏继续 - - - - ‘ C ’
//游戏执行逻辑函数
void game()
{//定义棋盘大小char board[ROW][COL] = { 0 };//初识化棋盘InitBoard(board,ROW,COL);//显示棋盘DisPlayBoard(board, ROW, COL);//根据IsWin()返回值,判断输赢char ret = 0;//下棋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玩家赢\n\n");else if (ret == '#')printf("\n电脑赢\n\n");elseprintf("\n平局\n\n");//显示棋局DisPlayBoard(board, ROW, COL);
}
arr_main.c程序大纲展示
#include "arr_game1.h"//游戏菜单
void menu()
{printf("****************************************\n");printf("************** 1.play game *************\n");printf("************** 0.game over *************\n");printf("****************************************\n");
}
//游戏执行逻辑函数
void game()
{//定义棋盘大小char board[ROW][COL] = { 0 };//初识化棋盘InitBoard(board,ROW,COL);//显示棋盘DisPlayBoard(board, ROW, COL);//根据IsWin()返回值,判断输赢char ret = 0;//下棋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玩家赢\n\n");else if (ret == '#')printf("\n电脑赢\n\n");elseprintf("\n平局\n\n");//显示棋局DisPlayBoard(board, ROW, COL);
}int main()
{srand((unsigned int)time(NULL));//初识化随机值生成器int input = 0;do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:printf("\n*********** 三子棋游戏开始 *************\n\n");game();//游戏执行逻辑函数break;case 0:printf("\n退出游戏\n");break;default:printf("\n选择错误请重新选择\n\n");break;}} while (input);return 0;
}
2、arr_game1.h
用于存放所自定义的函数和头文件等声明的程序
通俗易懂,就不多赘述,详见代码注释的说明。
#include <stdio.h>
#include <time.h>//调用time函数,所需声明的头文件
#include <stdlib.h>//调用srand和rand函数,所需声明的头文件//宏定义棋盘大小
#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);
3、arr_game1.c
用于存放对 arr_main.c 程序大纲做提到的函数进行封装,实现具体的功能的程序
说明:基于arr_main.c 程序大纲逻辑对代码进行讲解
注意:这里均以3*3的棋盘为例哦
3.1、 自定义初识化函数 InitBoard( ) 和 自定义显示函数 DisPlayBoard( )
首先,根据需求我们需要一个棋盘才可以正常的下棋.
如何让棋盘初识化呢?
1.根据所学的二维数组知识,便可以知道,当我们遍历二维数组的每一个元素,使得填充为空格字符,便可以由InitBoard( ) 达到效果。
//初识化棋盘
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] = ' ';}}
}
如图所示:
如图所示:
2.但是,这样的棋盘全是空格字符,只有程序员可见字符空格,玩家并不知道,所以将对棋盘进行修饰美化,使得大众化。所以借助 DisPlayBoard( ) 达到修饰棋盘的效果。
这里使用一些分隔符,将棋盘划分出,三组,并且数组元素加分隔符为一组。
//打印棋盘
void DisPlayBoard(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;for (i = 0; i < row; i++){for (j = 0; j < col; j++){printf(" %c ",board[i][j]);printf("|");}printf("\n");//每行打印完换行}printf("\n");
}
如图所示:
但是目前的效果达不到理想的棋盘效果,导致最后一组的分隔符,也被打印了出来。
所以思考可知:由 if (j < col - 1) 进行判断让最后一行的分割符被约束不打印出来。
然后打印完一行,换行一次。
同理,每一列打印完,使用 if (i < row - 1) 使得最后一列的分隔符不被打印出来
所以优化后得到以下代码:
//打印棋盘
void DisPlayBoard(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;for (i = 0; i < row; i++){for (j = 0; j < col; j++){printf(" %c ",board[i][j]);if(j < col-1)printf("|");}printf("\n");//每行打印完换行if (i < row - 1){int j = 0;for (j = 0; j < col; j++){printf("---");if (j < col - 1)printf("|");}printf("\n");//每行打印完换行}}printf("\n");
}
如图所示:
棋盘显示最终效果如图所示:
3.2、 自定义玩家下棋函数 PlayerMove( )
1.首先,我们下棋,需要给棋子一个坐标,并且需要以作为玩家的角度,思考坐标的取值范围。因为玩家不一定知道数组的下标从0开始,所以我们定义 x,y 坐标变量,使得 if (x >= 1 && x <= row && y >= 1 && y <= col )//符合玩家下棋坐标的逻辑
2.当我们坐标符合了范围,还得判定玩家输入错误坐标超出范围时,给予提示信息,超出范围,重新输入。
3.当玩家输入坐标范围正确,但是此时的坐标处已经被上一步棋子占用时,也得提示重新输入。
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{//定义下棋的坐标int x = 0;int y = 0;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坐标被占用,请重新输入坐标\n");}}else {printf("\n坐标超出棋盘范围,请重新输入棋子,坐标\n");}}
}
如图所示:
3.4、 自定义电脑下棋函数 ComputerMove( )
1.下棋的逻辑与玩家如出一辙,不多赘述。
2.这里强调一下,电脑如何实现产生随机的坐标下棋?
首先,在之前的篇章链接: link 里提到的srand函数与rand函数的应用原理相同。通过初识化srand随机值生成器函数,提供变化的随机值范围,给rand随机值函数,将rand()%row 和 rand()%col,就可以得到符合范围的数组元素坐标。
然后根据srand的参数要求,放一个时刻变化的数据,这里就用到了time时间转换函数,相互作用即可。
查阅资料文档的具体参数:
void srand( unsigned int seed ); 头文件 <stdlib.h>
int rand(void ); 头文件 <stdlib.h>
time_t time( time_t *timer ); 头文件 <time.h>
所以得到:
srand((unsigned int)time(NULL));
x = rand() % row;
y = rand() % col;
注意:srand只需要整个工程初识化一次即可
//电脑下棋
void ComputerMove(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;}}
}
如图所示:
3.5、 输赢判断函数 IsWin( )
由arr_main.c程序大纲提到的思路可知:
如何判定输赢:
玩家赢 — ‘*’
电脑赢 — ‘#’
平局 ---- ‘Q’
继续游戏 — ‘C’
所以可以明确 IsWin( ) 的返回值类型为char字符型
根据三子棋的游戏规则,三个棋子连城一线即可判定获胜,所以有:
(1)、一行三个棋子
(2)、一列三个棋子
(3)、对角线三个棋子
那么如何判定平局呢?
顾名思义,当我们的棋盘被占用完,还是没有连成一线,则判定为平局。
这里利用 int IsFull( )函数,遍历判断数组每个元素是否已用完即可,当返回0,则仍然有空位,继续游戏;当返回1,则所有元素均被占用且未连成一线,判定为平局。
//判断输赢
//玩家赢 --- '*'
//电脑赢 --- '#'
//平局 ---- 'Q'
//继续游戏 --- 'C'
int Isfull(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;for (i = 0; i < row; i++){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][1] != ' '){return board[i][1];//*/#}}//判断三列,均相同for (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[1][1] != ' '){return board[1][1];//*/#}//判断平局int ret = Isfull(board, ROW, COL);if (ret == 1)//棋盘无空位,平局{return 'Q';}//以上所有情况不满足时,继续游戏return 'C';
}
输赢结果如图所示:
4、结束语
相信通过这样一个三子棋的小游戏,更具掌握了对数组的操作以及对自定义函数的深刻认识;
如果觉着文章对您有所帮助,请不要吝啬的一赞三连哦,谢谢阅读,不足之处还请多多指教。
三子棋源码获取链接: 三子棋