我受够了等待,你所谓的安排................................................................................................
文章目录
目录
前言
讲述了三子棋小游戏的实现思路和实现方法,希望能帮到大家,完整的代码在第四部分。也欢迎大家指正错误,谢谢啦!
一、模块化编程的介绍
三子棋游戏需要采用模块化编程方式来实现 :分为三子棋头文件模块(game.h)、功能模块(game.c)、测试模块(test.c)这三个模块分工合作。
所以这里需要介绍一下模块化编程:把各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数的声明,其它.c文件想使用其中的代码时,只需要#include "XXX.h"文件即可。使用模块化编程可极大的提高代码的可阅读性、可维护性、可移植性等。
而在三子棋游戏的实现中,我们只需要将需要用到的库函数头文件包含在三子棋头文件(game.h),其他文件文件只需要包含到编写的三子棋头文件即可。三子棋头文件(game.h)包含自定义函数的声明即可,不需要实现函数的功能和调用。而在三子棋功能文件中(game.c),将函数体实现即可。测试文件(test.c)则主要负责游戏界面和函数调用的代码实现。当我们需要写一个大的工程或者项目(包含几万行代码那种),检查代码和维护代码是相当困难的,而采用模块化编程的方式式,,大大提高检查的效率和可阅读性。而且,有许多开发框架和工具也提供了模块化的支持,以简化模块的创建、管理和使用。由此可见,模块化编程是一种相当便利的编程方式。
二、游戏实现思路及功能模块的编写
首先,我么们都知道三子棋游戏,也被称为井字游戏(Tic-Tac-Toe),是一种简单的策略棋类游戏。它通常在一个3x3的方格棋盘上进行,两个玩家交替在空格内放置自己的标记,一般是一个玩家使用"X"标记,另一个玩家使用"O"标记。
游戏的目标是使自己的标记在水平、垂直或对角线上连成一条直线,先达到这个目标的玩家获胜。如果棋盘填满而没有任何一方连成一条线,则为平局。
而要编写三子棋游戏,就需要把其中一名玩家替换为电脑,从而进行编写。
1.编写函数实现供玩家选择的游戏菜单。
2.利用二维数组打印下棋的棋盘。
2.1游戏菜单的实现
在玩游戏的过程中,需要给玩家选择的权利,即玩家能够选择是进行游戏还是退出游戏,所以我们需要编写一个函数来实现这个功能,函数内部只采用了简单的打印功能,当我们需要是直接调用该函数即可。
代码展示:
void menu()
{
printf("*********************\n");
printf("**** 请选择操作 ****\n");
printf("**** .1--play. ****\n");
printf("**** .0--exit. ****\n");
//printf("*****************\n");
printf("*********************\n");
}
效果图:
2.2打印棋盘
1.二维数组的创建和初始化
玩家在选择进入游戏后,应首先将游戏过程中需要用到的棋盘打印出来,想要实现这一目的,就要搞清楚三子棋游戏的本质——其实就是“二维数组”,可由3x3的方格棋盘轻松地联想到二维数组里的“行”和“列”那么关键在于这个二维数组里的元素是什么,在游戏开始时棋盘上是没有棋子的所以二维数组里的元素应该是“ ”(空格),所以我们需要创建一个二维数组,并将其内置元素初始化为空格。
1.注:这里的初始化部分我们应该用一个函数来写,因为每进行一次新的游戏都要进行一次初始化,也就是清空棋盘。
创建部分(ROW,COL为二维数组的行和列):
2.注:这里并没有直接把二维数组的行和列规定写死成3行3列,而是用#define定义的常量ROW和COL来代替,这样更方便后续的维护和修改。#define定义的这部分应该在三子棋的头文件部分完成。同样的功能模块里的函数在编写前也应在头文件模块中提前声明。
char board[ROW][COL] = { 0 };
初始化部分可以采用两层for循环的嵌套也可以用memset函数:
//for循环的嵌套:
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] = ' ';
}
}
}
//memset函数:memset(&board[0][0], ' ', row*col*sizeof(board[0][0]));
2.棋盘分割线的打印
这样棋盘的基础部分就设置好了,只需要将其打印出来就好了,同初始化部分相同,该部分也应写成函数形式(因为这部分也是重复性动作,写成函数形式方便调用)但是如果你再用两个for循环嵌套打印这个二维数组,会发现什么也打印不出来,这是因为数组的元素是空格,显示在电脑屏幕上什么也看不到,所以接下来要做的是,将棋盘的分割线打印出来。
先上代码1.(图中有解释):
void DisplayBoard(char board[ROW][COL], int row, int col)//传参,二维数组及其行和列
{
int i = 0;
int j = 0;
//两层for循环打印“ ”(空格)和“|”将(“ ”和“|”)看成一组内容进行打印
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)//打印完两组(“---”和“|”)时应在打印一个“|”而不打印“---”才能达到#棋盘的目的。
{
//打印另外一组内容(“---”和“|”)以达到效果。
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)//打印完两组(“ ”和“|”)时应在打印一个空格而不打印“|”才能达到#棋盘的目的。
{
printf("|");
}
}
}
printf("\n");
}
}
效果图:
到这里棋盘的打印就算成功了。
看到这里可能会对上面的代码1.产生疑问因为完全可以简单点完成
代码2.:
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
if(i < row-1)
printf("---|---|---\n");
}
}
上述代码2.也可以完成,可是上述代码会把棋盘的宽度规定死,比较局限,如果把行数(ROW)改成5则会出现下面的情况:
而写成上述代码1.的形式就会显得更加实用和方便。
2.3玩家下棋和电脑下棋
通过上述的操作,解决了棋盘的问题,下面就要进行玩家与电脑之间的下棋和对弈了。那么怎么实现玩家和电脑在棋盘上落子呢?首先我们知道棋盘上的空位上分布的有元素——“ ”(空格)所以我们可以通过将“ ”(空格)替换为玩家所执的子来完成“落子”这一功能,电脑落子也是如此。需要注意的是玩家下棋和电脑下棋应用两个函数来分装,并且这两个函数也应在头文件模块中声明假定玩家用“*”充当棋子,电脑用“#”来充当棋子。废话不多说了,看代码:
//玩家下棋
void SPlayGame(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)//保证棋子落在棋盘上
{
//【x-1】【y-1】指的是数组下标所以要减一
if (board[x-1][y-1] = ' ')//判断落子的位置是否已经被下上了棋子
{
board[x - 1][y - 1] = '*';
break;
}
else
{
printf("错误\n");
}
}
else
{
printf("输入错误\n");
}
}
}
//电脑下棋
void CPlayGame(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋\n");
while (1)//使用循环的原因:循环到电脑下棋正确为止
{
x = rand() % row;//采用srand函数来生成随机数,并用生成的随机数取模,来确定棋子的坐标
y = rand() % col;//同时也能确保棋子能够落在棋盘上。
if (board[x][y] == ' ')//判断落子的位置是否已经被下上了棋子
{
board[x][y] = '#';
break;
}
}
}
效果图:
2.4判断输赢
当玩家和电脑每次下完棋如何判断它们之间的胜负关系呢?首先玩家和电脑下棋会出现以下情况。玩家赢,电脑赢,棋盘铺满没分出胜负即平局,棋盘没铺满棋局继续。不妨编写一个函数来判断棋局进行情况:
1.函数返回值为“*”——玩家赢
2.函数返回值为“#”——电脑赢
3.函数返回值为“s”——平局
4.函数返回值为“k”——棋局继续
代码:
//判断棋盘是否被铺满,铺满返回1,没满返回0,该函数用来判断是否平局
int Full(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 Win(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
//1.当横着三颗棋子连成一条线获胜的情况
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++)
{
//2.当竖着三颗棋子连成一条线获胜的情况
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
{
return board[0][i];//返回返回获胜的一方所执棋子
}
}
//3.当左上角,中间,右下角三颗棋子连成一条线获胜的情况
if (board[0][0] == board[2][2] && board[2][2] == board[1][1] && board[0][0] != ' ')
{
return board[0][0];//返回返回获胜的一方所执棋子
}
//4.当右上角,中间,左下角三颗棋子连成一条线获胜的情况
if (board[0][2] == board[2][0] && board[2][0] == board[1][1] && board[1][1] != ' ')
{
return board[0][2];//返回返回获胜的一方所执棋子
}
//棋盘被铺满未决出胜负————平局的情况
if (Full(board,row, col))
{
return 's';//返回平局所对应的值
}
//上述的if语句均不成立,说明是棋局未结束的情况
return 'k';返回“继续”对应的值
}
注3.:图中的Full函数并不用在头文件模块中进行声明,因为它包含在函数Win中,而Win需要在头文件模块中进行定义。
效果图:
好了,写到这里三子棋这个小游戏的所有功能就全部写完了,接下来把他们组装到测试模块中就大功告成了。
三、功能函数的组装及测试模块的编写
这部分主要是把前面编写的各种功能进行组装:
//自己编写的头文件
#include "game.h"
//菜单函数
void menu()
{
printf("*********************\n");
printf("**** 请选择操作 ****\n");
printf("**** .1--play. ****\n");
printf("**** .0--exit. ****\n");
//printf("*****************\n");
printf("*********************\n");
}
//存放功能的game函数
void game()
{
//创建二维数组
char board[ROW][COL] = { 0 };
//二维数据的初始化
InitBoard(board, ROW, COL);
//打印棋盘
DisplayBoard(board, ROW, COL);
//定义ret来接收Win函数的返回值
char ret = 0;
while (1)
{
//玩家下棋并打印下完后的棋盘
SPlayGame(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//ret来接收Win函数的返回值
ret = Win(board, ROW, COL);
//用ret来判断棋局进行情况
if (ret != 'k')//只要不是“继续”就终止循环,进而判断胜负情况
{
break;
}
//电脑下棋并打印下完后的棋盘
CPlayGame(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//ret来接收Win函数的返回值
ret = Win(board, ROW, COL);
if (ret != 'k')//只要不是“继续”就终止循环,进而判断胜负情况
{
break;
}
}
//根据对应的符号,判断胜负平局
if (ret == '*')
{
printf("你赢了\n");
}
else if (ret == '#')
{
printf("你输了\n");
}
else if (ret == 's')
{
printf("平局\n");
}
}
//主函数
int main()
{
定义input来存放玩家的选择
int input = 0;
srand((unsigned int)time(NULL));//用于生成随机数
do//因为玩家如果选择1,也就是开始游戏,而每下完一局都要回到菜单部分让玩家进行选择,所以应写在循环里。
{
menu();//菜单函数
printf("请选择:>");
scanf("%d", &input);//玩家输入选择
//选1和选0对应的结果不同所以用switch多分支语句
switch(input)
{
case 1:
game();//功能函数都组装在了game函数里面
break;
case 0:
printf("退出游戏\n");
break;
default://输入(1和0之外数字则重新输入)
printf("重新选择\n");
break;
}
} while (input);
return 0;
}
注4:图中代码中玩家和电脑下完棋分别要判断一下胜负的原因是:我们不知道玩家和电脑谁赢,也不清楚是在一局游戏当中下第几次棋赢得,所以不妨每下一次棋就判断一下胜负情况。
四、三子棋小游戏
到这里整个三子棋就写完了,下面看一下成果吧!
完整代码:
1.测试部分代码(源文件):
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void menu()
{
printf("*********************\n");
printf("**** 请选择操作 ****\n");
printf("**** .1--play. ****\n");
printf("**** .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)
{
SPlayGame(board, ROW, COL);
DisplayBoard(board, ROW, COL);
ret = Win(board, ROW, COL);
if (ret != 'k')
{
break;
}
CPlayGame(board, ROW, COL);
DisplayBoard(board, ROW, COL);
ret = Win(board, ROW, COL);
if (ret != 'k')
{
break;
}
}
if (ret == '*')
{
printf("你赢了\n");
}
else if (ret == '#')
{
printf("你输了\n");
}
else if (ret == 's')
{
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;
}
2.功能模块代码(源文件):
#define _CRT_SECURE_NO_WARNINGS
#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;
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)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
}
printf("\n");
}
}
void SPlayGame(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 CPlayGame(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 Full(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 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][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[2][2] && board[2][2] == board[1][1] && board[0][0] != ' ')
{
return board[0][0];
}
if (board[0][2] == board[2][0] && board[2][0] == board[1][1] && board[1][1] != ' ')
{
return board[0][2];
}
if (Full(board,row, col))
{
return 's';
}
return 'k';
}
3.头文件部分代码(头文件):
#define ROW 3
#define COL 3
#include <stdio.h>
#include <string.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 SPlayGame(char board[ROW][COL],int row,int col);
void CPlayGame(char board[ROW][COL], int row, int col);
char Win(char board[ROW][COL], int row, int col);
运行结果图:
总结
这样就完成了三子棋这样一个小游戏。
.................................................................................................................宁愿没出息,求我别离开
————《倒带》