目录
设计思路
功能实现
展示棋盘 Show_board
棋盘的初始化 Initial_Board
玩家下棋 Player_Move
电脑下棋 Computer_Move
判断棋局?
优化与改进
判定胜出的优化
提升效率的小设计
设计思路
在设计整个程序前,我们要先思考出整个程序设计的框架,三子棋游戏的流程是什么,具体需要什么函数来实现每一步的功能。
根据经验来看,首先需要一个菜单功能来确定玩家想要进入游戏,而非误触等操作。
在进入游戏后,我们可以选择单人模式或双人模式,单机和电脑玩,设计出来和朋友一起玩。
(带一点凡尔赛性质,是不是瞬间在朋友面前就高大起来了!那就接着看下去吧!
在单机模式下,在我们出子之后,需要电脑出子,
出子完毕后需要通过当前的棋盘判断下一次落子的位置
当有三个棋子连成一条线时,判定胜利。
至此我们可以分析出来,这个游戏需要实现的功能有:玩家下棋、电脑下棋、展示棋盘、判断棋局。
功能实现
首先建立三个文件,包含相应的函数
test.c——测试
game.h——函数的声明
game.c——函数的实现
之后我们实现相应的功能。
展示棋盘 Show_board
首先我们需要对棋盘进行设计,通过打印,分割出九个格子,实现简单的可视化。
void Show_board(char board[ROW][COL], int r, int c)
{
int i = 0;
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
printf(" %c ", board[i][j]);
if(j<COL-1) printf("|");
else printf("\n");
}
for (int k = 0; k < ROW; k++)
{
if (i < ROW - 1)
{
printf("---");
if (k < COL - 1)
printf("|");
else printf("\n");
}
}
}
}
打印后就是这个样子啦。
棋盘的初始化 Initial_Board
?我们通过一个3*3的二维数组存储用户输入来实现三子棋的落子,在此之前,先对没有下过子的棋盘进行初始化。为简明起见,我们这里将数组中的每一个元素设置为空。
void Initinal_Board(char board[ROW][COL], int r, int c)
{
for (int i = 0; i < r; i++)
{
for (int j = 0; j < c; j++)
{
board[i][j] = ' ';
}
}
}
玩家下棋 Player_Move
用一个字符表示玩家下棋。对于玩家来说,玩家并没有“这是用数组模拟实现的一个小游戏,而数组的下标是从零开始”的意识,为了服务用户,我们这里将用户输入的数-1后的值记为真实的坐标值。当用户输入有误时,提醒用户。
void Player_Move(char board[ROW][COL], int r, int c)
{
int x = 0;
int y = 0;
printf("\n--------玩家的回合-------\n");
printf("请输入落子的位置->x:__ y:__\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] = 'p';
}
else
{
printf("该位置已被占用");
}
}
else
{
printf("不能下在这里!输入的坐标有误\n");
}
Show_board(board, ROW, COL);
}
电脑下棋 Computer_Move
这里我们仅仅实现一个傻瓜操作,电脑:哪里有空下哪里!
void Computer_Move(char board[ROW][COL], int r, int c)
{
printf("\n-------电脑的回合:-------\n");
Sleep(500);
while (1)
{
int x = rand() % 3;
int y = rand() % 3;
if (board[x][y] == ' ')
{
board[x][y] = 'c';
break;
}
}
Show_board(board, ROW, COL);
}
判断棋局?
这里比较复杂,我们需要判断棋盘是否有三子连成一条线,而三子连线可能有三种情况:行相连、列相连,对角线相连。
如果没有,查看棋盘是否为“满”的状态。
如果有三子连成一条线,判断是电脑胜出还是玩家胜出。
char is_win(char board[ROW][COL], int r, int c)
{
int i = 0;
//行相同
for (i = 0; i < ROW; i++)
{
if (board[i][0] == board[i][1] == board[i][2] &&board[i][0] !=' ')
return board[i][0];
}
//列相同
for (i = 0; i < ROW; i++)
{
if (board[0][i] == board[1][i] == board[2][i]&&board[0][i]!=' ')
return board[i][0];
}
//对角线1
for (i = 0; i < ROW; i++)
{
if (board[0][0] == board[1][1] == board[2][2]&&board[0][0]!=' ')
return board[i][0];
}
//对角线2
for (i = 0; i < ROW; i++)
{
if (board[0][2] == board[1][1] == board[2][0] && board[0][0] != ' ')
return board[i][0];
}
//判断平局
if (is_full(board, ROW, COL) == 1)
{
return 'Q';
}
return 'c';
}
优化与改进
判定胜出的优化
之前的代码仅适用于三子棋,复用性差,因此我们做一个很挫但有用的提升版本,欢迎各位佬多多指教。(这个版本的优化真的很挫,但是我尽力了)
void Judge(char board[ROW][COL], int r, int c)
{
//判断行或列 是否连成x子
for (int i = 0; i < r; i++)
{
int countp1 = 0;
int countc1 = 0;
int countp2 = 0;
int countc2 = 0;
for (int j = 0; j < c; j++)
{
if (board[i][j] == board[i][j + 1] && j + 1 < c )
{
if (board[i][j] == 'c') countc1++;
if (board[i][j] == 'p') countp1++;
}
if (board[j][i] == board[j+1][i] && j+1 < r && board[i][j] != ' ')
{
if (board[i][j] == 'c') countc2++;
if (board[i][j] == 'p') countp2++;
}
}
if (countc1 == c - 1 || countc2 == c - 1) return Player;
if (countp1 == r - 1 || countp2 == r - 1) return Computer;
}
//判断两条对角线
for (int i = 0; i < c; i++)
{
int countc1 = 0;
int countc2 = 0;
int countp1 = 0;
int countp2 = 0;
for (int j = 0; j < r; j++)
{
if (i == j && board[i][j] == 'c') countc1++;
if (i == j && board[i][j] == 'p') countp1++;
if (i + j == r && board[i][j] == 'c') countc2++;
if (i + j == r && board[i][j] == 'p') countp2++;
}
if (countc1 == r || countc2 == c) return Player;
if (countp1 == r || countp2 == c) return Computer;
}
//判断是否为“满”
int count = 0;
for (int i = 0; i < ROW; i++)
{
int j = 0;
for (j = 0; j < COL; j++)
{
if (board[i][j] == ' ')
{
return Continue;
}
else count++;
}
}
if (count++ != 0) return Equal;
//接着玩
return Continue;
}
提升效率的小设计
使用enum定义棋局判断常量
当你不知道枚举类型到底有什么用的时候,它来了,它来了,它带着代码的易读性走来了。?
?使用define定义宏
#define CASE break;case
//避免因忘记break而出错
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择->");
scanf("%d", &input);
switch (input)
{
CASE 0:
printf("退出游戏\n");
CASE 1 :
game();
default:
printf("输入错误");
break;
}
} while (input);
}
?双人模式
增设一个Player1_Move()和Player2_Move(),并修改游戏细节。
void menu()
{
printf("\n");
printf("*********************\n");
printf("********0.EXIT*******\n");
printf("****1.ONE PLAYER*****\n");
printf("****2.TWO PLAYER*****\n");
printf("*********************\n");
printf("\n");
}
debug
在测试时发现了如下问题:在单击模式下,如果玩家输入错误,将自动延顺至电脑的回合。
解决这个问题我们只需要在玩家下棋的函数中嵌套Player_Move()
怎么样是不是很简单呢,快码起代码叫你的朋友和你一起来玩吧!
?
|