新手发文,请您斧正。@A@
目录
一 ) 前言
二) 准备工作
三) 游戏运行的基本函数(简单提一嘴)
四) 字符型二维数组储存数据
五)定义常量
六) 初始化棋盘
七)打印棋盘
八)玩家下棋和电脑下棋
九)判断输赢
十)效果展示
十一)完整代码
大家好!我是管不住嘴的小吃货,今天小吃货将用C语言来实现“三子棋”。“三子棋”就是类似于下图,只要你所执的棋子连成一线即为胜利的小游戏。话不多说,我们立即开始。
首先,要实现“三子棋”我们要先理清在代码层面的逻辑。
我们可以创建三个文件:
game.h //头文件 用来储存函数声明和定义常量,使代码更简洁
game.c //源文件 用来存放函数主体,实现游戏的逻辑和内容
text.c //源文件 存放主函数,测试游戏逻辑
当我们测试这个游戏时,就要想想我们平时玩游戏时,玩完了一局游戏肯定会有2个选择“继续玩”或“退出”,这时我们就可以选择‘do—while’循环,而在“选择”上我们可以运用‘switch’语句。简单编写一下大致框架就可以得到。
#define _CRT_SECURE_NO_WARNINGS
#include
#include "game.h"
void menu()
{
printf("<<<<<<<********>>>>>>>\n");
printf("<<<<<<<*1.play*>>>>>>>\n");
printf("<<<<<<<*0.exit*>>>>>>>\n");
printf("<<<<<<<********>>>>>>>\n");
}
int main()
{
int n = 0;
do
{
printf("请选择:\n");
menu();//菜单打印
scanf_s("%d", &n);
switch (n)
{
case 1 :
printf("游戏开始》》》》\n");//当选择为1时,游戏就会开始
game();
break;
case 0 :
printf("退出游戏《《《《\n");//当选择为0时,游戏就会结束
break;
default:
printf("错误输入,请重新输入\n");//当选择不为0或1时,游戏就会报错,提示重新输入
break;
}
}while(n);
return 0;
}
当我们已经拥有了大致框架,我们就可以插入游戏运行所必需的函数:game()。我们可以把诸多放在game.h的函数按照逻辑安放,可以使text.c文件看起来更加简洁美观。
以下只展示了一小部分
void game()
{
//初始化棋盘
InitBoard(board,ROW ,COL );
//打印棋盘
DisplayBoard(board, ROW, COL);
//玩家走
PlayerMove(board, ROW, COL);
//电脑走
ComputerMove(board, ROW, COL);
}
//game.h
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#define ROW 3
#define COL 3
void menu();
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 Is_win(char board[ROW][COL], int row, int col);
想要实现下面的“三子棋”显然我们应该创建一个二维数组来存储数据。如果要存储‘*’ ‘#’ ‘ ’(空格)等字符,我们理应采取----字符型二维数组。
采用: char board[3][3]={0};
不知道你们有没有想到,要实现“三行三列”很简单只需要采用char board[3][3]={0};。但如果我们以后想变“三子棋”为“五子棋”或“百子棋”在改写代码过程中却会很麻烦,所以我们可以在game.h中定义两个常量:#define ROW 3 #define COL 3
在日后若想改变只需将'ROW''COL'后的3改为你想改成的数即可
(后面的 ‘3’ 将会用ROW,COL代替)
注意:如果想使用game.h里的符号,我们需要在game.c和text.c里引用这个头文件。引用自己写的头文件要用双引号。#include "game.h"而对于其他头文件如stdio.h,我们不需要在game.c和text.c里包含两次,只需在game.h里包含就行了。(这就叫头文件的包含)
我们要想到,一开始或者新的一局游戏中棋盘应该是“空白的”,是“没有棋子”的,所以要创建一个初始化棋盘的函数来将棋盘重置。我们可以将函数主体放在game.c中实现。这是该函数的函数声明:void InitBoard(char board[ROW][COL], int row, int col);存放在game.h中。在text.c中运用此函数InitBoard(board, row, col);(之后的函数也会进行相似步骤,列如函数声明等会进行省略)。
而函数的实现只需遍历整个数组,全部赋值为空格即可。
以下是参考
void InitBoard(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++)
{
board[i][j] =' ';//遍历数组,全部赋值为‘ ’
}
}
}
当我们已经完成了棋盘的初始化,想要下棋则当然需要有一块棋盘。因此我们应该打印一块棋盘,这样也可以查看棋盘是否初始化。我们来创建DisPlayBoard(board ,ROW,COL);--一个可以打印棋盘的函数。显然想要完成下图棋盘的样式仅仅直接打印棋盘是不够的。事实上,此时如果你直接打印棋盘,打印的将会是“一堆空格”,非常难看。
而如果我们想达到上图的效果,可以加上一些横向和竖向的分割。我们可以按下图将其分开来打印三次。先打印“ | | ”,再打印“---|---|---”。
| | 1
---|---|--- 2
简单来写就可以按照下面的代码
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (; i < row; ++i)
{
// 打印“ | | |”
printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
// 打印“---|---|---”
printf("---|---|---\n");
}
}
但是会出现一些问题。
我们会发现多打印了一行“---|---|---”。所以我们要加一行if判断语句来进行控制。
if (i < row - 1)
printf("---|---|---\n");
但~是!如果用这种方法我们就只能打印3行3列的棋盘,当我们定义的常量ROW,COL的值改变时如:ROW,COL = 5时。就会出现下面的情况。
所以我们要进行一下代码的优化。(使用多个if语句进行判断,分开打印“ %c ”,“|”和“---”,“|”)
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 < row - 1)
{
printf("|");
}
}
printf("\n");
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
实现效果:(正常的效果)
假如把ROW,COL改为7,则:(也可以成功实现 !W !,表明了这种代码更加通用)
接下来我们继续完成玩家下棋的函数:PlayerMove(board, ROW, COL);
在我们的设想中玩家下棋以输入坐标来代替,实际坐标则需‘-1’.
在完成代码的过程中,我们要思考玩家输入坐标与程序识别,坐标是否被占用,坐标是否合法(是否在输入范围之内)等问题,我们可以使用一个循环以及"if语句"来进行实现。
然后我们可以添加一个DisplayBoard(board, ROW, COL);函数,以便看到棋下在了哪。
(参考函数代码如下)
void PlayerMove(char board[ROW][COL], int row, int col)
{
printf("玩家走>>>\n");
int x = 0;
int y = 0;
while (1)
{
printf("请输入坐标:>>>\n");
scanf_s("%d %d", &x, &y);//玩家输入的坐标
if (x >= 1 && x <= row && y >= 1 && y <= col)//判断坐标是否合法
{
if (board[x - 1][y - 1] == ' ')//判断坐标是否被占用
{
board[x - 1][y - 1] = '*';//board[x - 1][y - 1]:实际坐标“x-1,y-1”
break;
}
else
{
printf("坐标被占用,请重新输入\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
实现完玩家下棋后,我们紧接着完成电脑下棋函数:ComputerMove(board, ROW, COL);
电脑与玩家不同,不需要我们判断坐标是否‘非法’,只需让其在我们允许的范围内随机生成坐标便可,所以我们只需要使电脑在未被占用的‘格子’里‘下棋’即可。
首先,我们先实现电脑随机下棋的程序。
在game.h中放入#include
然后在text.c中实现随机数:rand((unsigned int)time(NULL));
随后在game.c中实现坐标随机:x = rand() % row; y = rand() % col;
其余步骤和玩家下棋相似,在这里不多做叙述。
(参考函数代码如下)
void ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑走<<<\n");
while (1)
{
x = rand() % row; //x的范围在 0~2 之间
y = rand() % col; //y的范围在 0~2 之间
if (board[x][y] == ' ')//判断坐标是否被占用
{
board[x][y] = '#';
break;
}
}
}
text.c:
//玩家走
PlayerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//电脑走
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
一局游戏自然有始有终,所以我们需要一个判断输赢的函数:Is_win(board, ROW, COL);
如果玩家赢了,或电脑赢了,或平局,游戏就结束了,否则游戏继续。那么我们规定一下这四种状态的的值,如果Is_win函数返回了特定的值(所以函数类型为char型),那么就代表某种状态。
“玩家赢”-------‘ * ’
“电脑赢”-------‘ # ’
“平局”-----------‘ Q ’
“游戏继续”-----‘ C ’
如果Is_win的返回值不是‘C’就意味着游戏结束跳出循环。
void game()
{
char ret = 0;
char board[ROW][COL];//储存了空格的数组
//初始化棋盘
InitBoard(board,ROW ,COL );
//打印棋盘
DisplayBoard(board, ROW, COL);
while (1)
{
//玩家走
PlayerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断输赢
ret = Is_win(board, ROW, COL);
if (ret != 'C')
break;
//电脑走
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断输赢
ret = Is_win(board, ROW, COL);
if (ret != 'C')
break;
}
if (ret == '*')
printf("恭喜,您取得了胜利!\n");
else if (ret == '#')
printf("电脑获胜\n");
else
printf("不分胜负\n");
DisplayBoard(board, ROW, COL);
}
那怎样实现Is_win函数呢?
我们只需要判断玩家或电脑的棋子是否把一行或一列或一条对角线占满就行了。
char Is_win(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][1];
}
//判断叁列
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
return board[1][i];
}
//判断/对角线
if (board[0][col-1] == board[row/2][col/2] && board[row/2][col/2] == board[row-1][0] && board[row/2][col/2] != ' ')
return board[row / 2][col / 2];
//判断\对角线
if (board[0][0] == board[row / 2][col / 2] && board[row / 2][col / 2] == board[row-1][col-1] && board[row-1][col-1] != ' ')
return board[row - 1][col - 1];
}
(像使用“return board[i][1];”等的这些操作就体现了前面我们令“玩家赢”--为‘*’等这一操作的方便)
但我们还需判断棋局是否平局或继续,所以我们创建一个is_full(board, row, col);函数进行判断。
int is_full(char board[ROW][COL], int row, int col)
{
int i = 0;
for (; i < row; ++i)
{
int j = 0;
for (; j < col; ++j)
{
if (board[i][j] == ' ')
{
return 0; // 棋盘没有被占满
}
}
}
return 1; // 棋盘被占满
}
然后将Is_win函数补充完整:
char Is_win(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][1];
}
//判断叁列
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
return board[1][i];
}
//判断/对角线
if (board[0][col-1] == board[row/2][col/2] && board[row/2][col/2] == board[row-1][0] && board[row/2][col/2] != ' ')
return board[row / 2][col / 2];
//判断\对角线
if (board[0][0] == board[row / 2][col / 2] && board[row / 2][col / 2] == board[row-1][col-1] && board[row-1][col-1] != ' ')
return board[row - 1][col - 1];
if (is_full(board, row, col) == 1)
{
return 'Q';//棋局结束————平局
}
return 'C';//棋局继续
}
至此我们的程序就完成了,下面是效果展示。
玩家赢:
电脑赢:
平局:
下面是完整代码展示:
game.h:
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#define ROW 3
#define COL 3
void menu();
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 Is_win(char board[ROW][COL], int row, int col);
game.c:
#define _CRT_SECURE_NO_WARNINGS
#include
#include "game.h"
void menu()
{
printf("<<<<<<<********>>>>>>>\n");
printf("<<<<<<<*1.play*>>>>>>>\n");
printf("<<<<<<<*0.exit*>>>>>>>\n");
printf("<<<<<<<********>>>>>>>\n");
}
void InitBoard(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++)
{
board[i][j] =' ';
}
}
}
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
printf(" ");
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if (j < row - 1)
{
printf("|");
}
}
printf("\n");
printf(" ");
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
void PlayerMove(char board[ROW][COL], int row, int col)
{
printf("玩家走>>>\n");
int x = 0;
int y = 0;
while (1)
{
printf("请输入坐标:>>>\n");
scanf_s("%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 ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑走<<<\n");
while (1)
{
x = rand() % row; // 0~2
y = rand() % col; // 0~2
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
int is_full(char board[ROW][COL], int row, int col)
{
int i = 0;
for (; i < row; ++i)
{
int j = 0;
for (; j < col; ++j)
{
if (board[i][j] == ' ')
{
return 0; // 没有被占满
}
}
}
return 1; // 棋盘被占满
}
char Is_win(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][1];
}
//判断叁列
for (i = 0; i < col; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
return board[1][i];
}
//判断/对角线
if (board[0][col-1] == board[row/2][col/2] && board[row/2][col/2] == board[row-1][0] && board[row/2][col/2] != ' ')
return board[row / 2][col / 2];
//判断\对角线
if (board[0][0] == board[row / 2][col / 2] && board[row / 2][col / 2] == board[row-1][col-1] && board[row-1][col-1] != ' ')
return board[row - 1][col - 1];
if (is_full(board, row, col) == 1)
{
return 'Q';
}
return 'C';
}
text.c:
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void game()
{
char ret = 0;
char board[ROW][COL];//储存了空格的数组
//初始化棋盘
InitBoard(board,ROW ,COL );
//打印棋盘
DisplayBoard(board, ROW, COL);
while (1)
{
//玩家走
PlayerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断输赢
ret = Is_win(board, ROW, COL);
if (ret != 'C')
break;
//电脑走
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断输赢
ret = Is_win(board, ROW, COL);
if (ret != 'C')
break;
}
if (ret == '*')
printf("恭喜,您取得了胜利!\n");
else if (ret == '#')
printf("电脑获胜\n");
else
printf("不分胜负\n");
DisplayBoard(board, ROW, COL);
}
int main()
{
int n = 0;
srand((unsigned int)time(NULL));
printf("请选择:\n");
do
{
menu();//菜单打印
scanf_s("%d", &n);
switch (n)
{
case 1 :
printf("游戏开始》》》》\n");
game();
break;
case 0 :
printf("退出游戏《《《《\n");
break;
default:
printf("erorr\n");
break;
}
printf("请选择是否继续游玩:\n");
}while(n);
return 0;
}
至此我的第一篇博客完成了,谢谢大家观看,若有不对之处还请斧正。
最后求一键三连 + 关注 QAQ