[C语言]连子棋游戏

文章目录

一、前言

对于指针和数组理解尚不深入的读者,我们强烈建议您先通过以下链接进行学习,以便更好地理解和参与后续的连子棋游戏讨论:传送门。这些基础知识将为您搭建连子棋游戏框架提供坚实的支撑。


二、游戏思路

我们的连子棋游戏将基于经典的五子棋规则进行开发:传送门

  • 游戏中,玩家和电脑将分别使用黑色和白色的棋子进行轮流下棋。
  • 当某一方的棋子能够在横向、纵向或斜向上连成连续的指定个数棋子时,该方即获得胜利。
  • 用户可以在前面选择游戏连子数量(四子以上)。

三、游戏方法

1、初始化

在游戏开始前,我们需要进行一系列初始化操作,包括创建游戏棋盘(通常使用二维数组表示),并初始化棋盘上的每个位置为空(表示尚未放置棋子)。此外,还需要设置当前玩家为黑方,并准备接收玩家的输入。

2、判断胜利

每当玩家在棋盘上放置一枚棋子后,我们需要遍历棋盘以检查是否存在已经连成了胜利所需的棋子串。这通常涉及到对棋盘进行多次遍历,分别检查横向、纵向和斜向上的棋子连续性。

3、交互

游戏过程中,玩家将通过标准输入(如键盘)来输入棋子的放置位置(通常是棋盘的行列坐标)。程序需要接收这些输入,并验证其有效性(确保坐标在棋盘范围内且该位置尚未放置棋子)。然后,程序将根据输入更新棋盘状态,并再次检查是否获得胜利。

4、电脑下棋

为了实现单人游戏模式,我们还需要为电脑玩家实现一个下棋算法。这通常涉及到一些人工智能算法的应用,如随机下棋、简单策略判断或更复杂的搜索算法(如蒙特卡洛树搜索)。在实现时,我们可以根据游戏复杂度和性能要求来选择合适的算法。


四、核心方法说明

1、初始化游戏

c 复制代码
/*
 * 初始化连子棋游戏
 * @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、销毁棋盘

c 复制代码
/*
 * 销毁连子棋棋盘
 * @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、显示游戏

c 复制代码
/*
 * 显示连子棋游戏
 * @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、电脑下棋

c 复制代码
/*
 * 电脑下棋
 * @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、用户下棋

c 复制代码
/*
 * 用户下棋
 * @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、判断游戏状态

c 复制代码
/*
 * 判断是否连续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、游戏交互

c 复制代码
/*
 * 清除标准输入缓冲区中直到换行符的所有字符,并返回一个标志位。
 * @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上开源:传送门


相关推荐
我是哈哈hh25 分钟前
HTML5和CSS3的进阶_HTML5和CSS3的新增特性
开发语言·前端·css·html·css3·html5·web
Dontla1 小时前
Rust泛型系统类型推导原理(Rust类型推导、泛型类型推导、泛型推导)为什么在某些情况必须手动添加泛型特征约束?(泛型trait约束)
开发语言·算法·rust
Neophyte06082 小时前
C++算法练习-day40——617.合并二叉树
开发语言·c++·算法
慕容复之巅2 小时前
基于MATLAB的条形码的识别图像处理报告
开发语言·图像处理·matlab
zqzgng2 小时前
Python 数据可视化pilot
开发语言·python·信息可视化
写bug的小屁孩2 小时前
websocket初始化
服务器·开发语言·网络·c++·websocket·网络协议·qt creator
Dr_eamboat2 小时前
【Java】枚举类映射
java·开发语言·python
代码小鑫2 小时前
A031-基于SpringBoot的健身房管理系统设计与实现
java·开发语言·数据库·spring boot·后端
五味香3 小时前
Linux学习,ip 命令
linux·服务器·c语言·开发语言·git·学习·tcp/ip