文章目录
一、前言
对于指针和数组理解尚不深入的读者,我们强烈建议您先通过以下链接进行学习,以便更好地理解和参与后续的连子棋游戏讨论:传送门。这些基础知识将为您搭建连子棋游戏框架提供坚实的支撑。
二、游戏思路
我们的连子棋游戏将基于经典的五子棋规则进行开发:传送门。
- 游戏中,玩家和电脑将分别使用黑色和白色的棋子进行轮流下棋。
- 当某一方的棋子能够在横向、纵向或斜向上连成连续的指定个数棋子时,该方即获得胜利。
- 用户可以在前面选择游戏连子数量(四子以上)。
三、游戏方法
1、初始化
在游戏开始前,我们需要进行一系列初始化操作,包括创建游戏棋盘(通常使用二维数组表示),并初始化棋盘上的每个位置为空(表示尚未放置棋子)。此外,还需要设置当前玩家为黑方,并准备接收玩家的输入。
2、判断胜利
每当玩家在棋盘上放置一枚棋子后,我们需要遍历棋盘以检查是否存在已经连成了胜利所需的棋子串。这通常涉及到对棋盘进行多次遍历,分别检查横向、纵向和斜向上的棋子连续性。
3、交互
游戏过程中,玩家将通过标准输入(如键盘)来输入棋子的放置位置(通常是棋盘的行列坐标)。程序需要接收这些输入,并验证其有效性(确保坐标在棋盘范围内且该位置尚未放置棋子)。然后,程序将根据输入更新棋盘状态,并再次检查是否获得胜利。
4、电脑下棋
为了实现单人游戏模式,我们还需要为电脑玩家实现一个下棋算法。这通常涉及到一些人工智能算法的应用,如随机下棋、简单策略判断或更复杂的搜索算法(如蒙特卡洛树搜索)。在实现时,我们可以根据游戏复杂度和性能要求来选择合适的算法。
四、核心方法说明
1、初始化游戏
/*
* 初始化连子棋游戏
* @param board 指向存储游戏棋盘的指针的指针
* @param row 棋盘的行数
* @param col 棋盘的列数
*/
void init_board(char*** board, int row, int col)
{
//开辟棋盘
*board = (char**)malloc(sizeof(char*) * row);
if (!(*board))
{
fprintf(stderr, "内存分配失败\n");
exit(EXIT_FAILURE);
}
for (int i = 0; i < row; i++)
{
(*board)[i] = (char*)malloc((col + 2) * sizeof(char));
if ((*board)[i] == NULL)
{
fprintf(stderr, "内存分配失败\n");
//释放之前已分配的内存
for (int j = 0; j < i; j++)
{
free((*board)[j]);
}
free(*board);
exit(EXIT_FAILURE);
}
}
//初始化棋盘
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
(*board)[i][j] = ' ';
}
}
}
2、销毁棋盘
/*
* 销毁连子棋棋盘
* @param board 指向存储游戏棋盘的指针的指针
* @param row 棋盘的行数
* @param col 棋盘的列数
*/
void free_board(char*** board, int row, int col)
{
assert(board != NULL);
//销毁每一行
for (int i = 0; i < row; i++)
{
free((*board)[i]);
}
//销毁指针数组
free(*board);
*board = NULL;
}
3、显示游戏
/*
* 显示连子棋游戏
* @param board 游戏棋盘
* @param row 棋盘的行数
* @param col 棋盘的列数
*/
void show_board(char** board, int row, int col)
{
assert(board != NULL);
//打印列标
for (int i = 0; i <= col; i++)
printf("%d ", i);
printf("\n");
for (int i = 0; i < row; i++)
{
printf("%d ", i + 1);//打印行标
for (int j = 0; j < col; j++)
{
//不同颜色显示棋子
if (board[i][j] == '*')
printf("\033[1;31m*\033[0m ");
else if (board[i][j] == '#')
printf("\033[1;33m*\033[0m ");
else
printf("%c ", board[i][j]);
}
printf("\n");
}
}
4、电脑下棋
/*
* 电脑下棋
* @param board 指向存储游戏棋盘的指针的指针
* @param row 棋盘的行数
* @param col 棋盘的列数
* @param sum 连子个数
* @return 游戏状态
*/
int computer_play(char*** board, int row, int col, int sum)
{
start:
assert(board != NULL);
//电脑随机下棋
int x = rand() % row + 1;
int y = rand() % col + 1;
if ((*board)[x - 1][y - 1] == ' ')
{
(*board)[x - 1][y - 1] = '*';
}
else
{
goto start;
}
return is_win(*board, row, col, sum, x, y);
}
5、用户下棋
/*
* 用户下棋
* @param board 指向存储游戏棋盘的指针的指针
* @param row 棋盘的行数
* @param col 棋盘的列数
* @param sum 连子个数
* @return 游戏状态
*/
int user_play(char*** board, int row, int col, int sum)
{
start:
assert(board != NULL);
int x = -1, y = -1;
printf("请选择下棋点(x,y):");
scanf("%d,%d", &x, &y);
if (clear_input())
{
x = -1, y = -1;
}
if ((x < 1 || x > row || y < 1 || y > col) || (*board)[x - 1][y - 1] != ' ')
{
system("cls");
printf("输入错误!\n");
getchar();
system("cls");
show_board(*board, row, col);
goto start;
}
(*board)[x - 1][y - 1] = '#';
return is_win(*board, row, col, sum, x, y);
}
6、判断游戏状态
/*
* 判断是否连续sum个
* @param board 游戏棋盘
* @param row 棋盘的行数
* @param col 棋盘的列数
* @param sum 连子个数
* @param x 当前下棋的行
* @param y 当前下棋的列
* @param dx 判断偏移x方向
* @param dy 判断偏移y方向
* @return 是否连续sum个
*/
int is_sum(char** board, int row, int col, int sum, int x, int y, int dx, int dy)
{
assert(board != NULL);
assert(x >= 1 && x <= row && y >= 1 && y <= col);
char flag = board[x - 1][y - 1];
int count = 0;
for (int i = -(sum - 1); i < sum; i++)
{
int nx = x + dx * i;
int ny = y + dy * i;
if (nx >= 1 && nx <= row && ny >= 1 && ny <= col)
{
if (board[nx - 1][ny - 1] == flag)
{
count++;
if (count == sum)
return 1;
}
else
{
count = 0;
}
}
}
return 0;
}
/*
* 判断是否结束
* @param board 游戏棋盘
* @param row 棋盘的行数
* @param col 棋盘的列数
* @param sum 连子个数
* @param x 当前下棋的行
* @param y 当前下棋的列
* @return 游戏状态
*/
int is_win(char** board, int row, int col, int sum, int x, int y)
{
assert(board != NULL);
assert(x >= 1 && x <= row && y >= 1 && y <= col);
int direxction[4][2] = { {-1, 0}, {-1, 1}, {0, 1}, {1, 1} };
for (int i = 0; i < 4; i++)
{
if (is_sum(board, row, col, sum, x, y, direxction[i][0], direxction[i][1]))
{
return board[x - 1][y - 1] == '*' ? -1 : 1;
}
}
//是否平局
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (board[i][j] == ' ')
return sum;
}
}
return 0;
}
7、游戏交互
/*
* 清除标准输入缓冲区中直到换行符的所有字符,并返回一个标志位。
* @return 如果缓冲区中有字符被清除,则返回1;否则返回0。
*/
int clear_stdin(void)
{
int flag = 0;
while (getchar() != '\n')
flag = 1;
return flag;
}
/*
* 连子棋游戏交互
* @param board 指向存储游戏棋盘的指针的指针
* @param row 棋盘的行数
* @param col 棋盘的列数
* @param sum 连子个数
*/
void game(char*** board, int row, int col, int sum)
{
init_board(board, row, col);
srand((unsigned)time(NULL));
int win = 0;
while (1)
{
system("cls");
show_board(*board, row, col);
win = user_play(board, row, col, sum);
if (win == 1)
{
system("cls");
show_board(*board, row, col);
free_board(board, row, col);
printf("游戏胜利!\n");
getchar();
system("cls");
break;
}
if (win == 0)
{
system("cls");
show_board(*board, row, col);
free_board(board, row, col);
printf("游戏平局!\n");
getchar();
system("cls");
break;
}
win = computer_play(board, row, col, sum);
if (win == -1)
{
system("cls");
show_board(*board, row, col);
free_board(board, row, col);
printf("游戏失败!\n");
getchar();
system("cls");
break;
}
if (win == 0)
{
system("cls");
show_board(*board, row, col);
free_board(board, row, col);
printf("游戏平局!\n");
getchar();
system("cls");
break;
}
}
}
五、游戏效果展示与源码分享
1、游戏效果
上图所展示的是我们精心打造的游戏效果,动态的画面、流畅的操作,是否已经让你跃跃欲试了呢?
2、源代码
为了让更多爱好者能够深入学习和交流,我们已将本游戏的源代码开源至Gitee平台。你可以通过以下传送门轻松获取,一起探索游戏的奥秘,共同进步。
已经在Gitee上开源:传送门