[C语言]连子棋游戏

news2024/11/12 18:11:02

文章目录

  • 一、前言
  • 二、游戏思路
  • 三、游戏方法
    • 1、初始化
    • 2、判断胜利
    • 3、交互
    • 4、电脑下棋
  • 四、核心方法说明
    • 1、初始化游戏
    • 2、销毁棋盘
    • 3、显示游戏
    • 4、电脑下棋
    • 5、用户下棋
    • 6、判断游戏状态
    • 7、游戏交互
  • 五、游戏效果展示与源码分享
    • 1、游戏效果
    • 2、源代码

一、前言

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


二、游戏思路

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

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

三、游戏方法

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上开源:传送门


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2143759.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt:智能指针QScopedPointer 的使用(以及如何写一个QScopedPointer )

前言 本文讲述QScopedPointer 的使用&#xff0c;以及自己如何写一个QScopedPointer . 正文 QScopedPointer 的常用方法 以下是 QScopedPointer 的一些常用方法及其详细说明&#xff1a; 构造函数&#xff1a; QScopedPointer<T> ptr(new T);用于创建一个 QScopedPoi…

【C/C++】涉及string类的经典OJ编程题

【C/C】涉及string类的经典OJ编程题 一. 把字符串转化成整数&#xff08;atoi&#xff09;解法一&#xff1a;&#xff08;不用long&#xff09;完整代码&#xff1a;解法二&#xff1a;&#xff08;用long&#xff09; 二.字符串相加代码实现&#xff08;含注释&#xff09;&a…

【RabbitMQ】RabbitMQ 概述

RabbitMQ 概述 前⾔什么是MQMQ的作⽤为什么选择RabbitMQ1. Kafka2. RocketMQ3. RabbitMQ 前⾔ Rabbit 也是⼀个公司名. MQ ( message queue ) 消息队列 的意思 , RabbitMQ 是 Rabbit 企业下的⼀个消息队列产品. RabbitMQ 是⼀个实现了 AMQP 的 消息队列 服务,是当前主流的消息…

IBM中国研发部裁员:全球化背景下的中国IT产业挑战与机遇

文章目录 每日一句正能量前言整体分析人才发展裁员对中国IT人才市场的影响&#xff1a;IT从业者提升竞争力的策略&#xff1a; 产业未来后记 每日一句正能量 一切美好的事物都是曲折地接近自己的目标&#xff0c;一切笔直都是骗人的&#xff0c;所有真理都是弯曲的&#xff0c;…

如何写出军工级的代码?

编写军工级代码意味着要达到极高的可靠性、安全性、可维护性和效率&#xff0c;这通常涉及到严格的标准和流程。以下是一些关键点&#xff0c;帮助你编写军工级的代码&#xff1a; 1. 遵循严格的开发流程 军工项目通常需要遵循特定的软件开发标准&#xff0c;例如&#xff1a;…

iptables限制网速

1、使用hashlimit来限速 #从eth0网卡进入INPUT链数据&#xff0c;使用模块hashlimit 限制网速为100kb/s或2mb/s,超过限制的数据包会被DROP。OUTPUT链同理&#xff0c;mode为srcip&#xff0c;有4个mode选项: srcip&#xff08;默认匹配每个源地址IP&#xff0c;配置指定源地址…

VisionPro - 基础 - 00 模板匹配技术和在VP中的使用 - PMAlign - PatMax - (1)

前言 模板匹配是机器视觉领域&#xff0c;尤其是工业视觉领域内&#xff0c;自动化经常要使用的一个视觉算法应用模式。在VP里面&#xff0c;有几种简单的模版匹配的算子&#xff0c;这里大致介绍一下VP的PatMax。 在视觉应用领域&#xff0c;搜索匹配的特征是经常要用到的方…

面试突击-多线程和IO专题(至尊典藏版)

多线程和IO专题 一、多线程专题 1.介绍下进程和线程的关系 进程:一个独立的正在执行的程序 线程:一个进程的最基本的执行单位,执行路径 多进程:在操作系统中,同时运行多个程序 多进程的好处:可以充分利用CPU,提高CPU的使用率 多线程:在同一个进程(应用程序)中同时…

【微服务】Eureka的自我保护机制

Eureka的自我保护模式正是一种针对网络异常波动的安全保护措施&#xff0c;使用自我保护模式能使Eureka集群更加的健壮&#xff0c;稳定的运行。 在正常情况下&#xff0c;Eureka客户端会定期向Eureka服务器发送心跳&#xff0c;以表明它仍然存活和可用。如果Eureka服务器在配…

你认为嵌入式软件开发的尽头是什么?

嵌入式软件开发的“尽头”是一个富有哲理且多维度的概念&#xff0c;因为它不仅关乎技术发展的极限&#xff0c;还涉及到行业应用、市场需求、技术融合与创新等多个方面。从几个不同的视角来看&#xff1a; 技术极限&#xff1a;从纯技术的角度来看&#xff0c;嵌入式软件开发…

C++ List (带你一篇文章搞定C++中的List类)

感谢大佬的光临各位&#xff0c;希望和大家一起进步&#xff0c;望得到你的三连&#xff0c;互三支持&#xff0c;一起进步 数据结构习题_LaNzikinh篮子的博客-CSDN博客 初阶数据结构_LaNzikinh篮子的博客-CSDN博客 收入专栏&#xff1a;C_LaNzikinh篮子的博客-CSDN博客 其他专…

0基础带你入门Linux之使用

1.Ubuntu软件管理 回顾一下&#xff0c;我们之前使用su root切换到root模式&#xff0c;使用who 发现为什么显示的还是bd用户呢&#xff1f;为什么呢&#xff1f; 这个who是主要来查看的是我们登录的时候是以什么用户登录的 所以即使我们使用who进行查看的时候显示的还是bd用…

如何扫描试卷去除笔迹?4种方法还原整洁试卷

如何扫描试卷去除笔迹&#xff1f;扫描试卷去除笔迹&#xff0c;作为现代学习管理与评估的革新手段&#xff0c;不仅显著提升了试卷的整洁美观度&#xff0c;更在环保和资源再利用层面发挥了积极作用。它使得试卷的保存、分享与复习变得更加便捷高效&#xff0c;减少了纸质资源…

2024年9月12日美国Embarcadero公司正式发布RAD Studio Delphi/C++ Builder 12.2 雅典

Embarcadero 非常高兴地宣布&#xff0c;从今天开始&#xff0c;RAD Studio 12.2 Athens 以及 Delphi 12.2 和 CBuilder 12.2 可供客户使用。RAD Studio 12.2 Athens 版本提供了我们在 IDE 中的第一次生成式 AI 集成、用于 Web 开发的新模板库、基于 C Win64 Clang 的新编译器和…

【MATLAB源码-第266期】基于Matlab的k-means算法遥感图像分割系统仿真。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 基于K-means算法的图像分割在遥感图像处理中的应用十分广泛&#xff0c;尤其是对于需要自动提取特定区域或目标的场景。遥感图像通常包含了大量的地物信息&#xff0c;不同的地物如水体、建筑物、植被等在遥感图像中会表现出…

电脑的主板,内存条插多少合适?

首先&#xff0c;不是插满4条内存就是最好的。 内存条插得多&#xff0c;确实可以扩充容量&#xff0c;提升性能。但是有些低端的主板配低端CPU&#xff0c;插满4条内存&#xff0c;稳定性下降。这里的稳定性包括供电&#xff0c;单独的内存供电容量等。此时CPU会通过降低内存…

为什么使用 Rust over C++ 进行 IoT 解决方案开发

物联网已成为我们日常生活中不可或缺的一部分&#xff0c;设备也越来越智能。随着该领域的扩展&#xff0c;迫切需要保证这些支持软件的设备的安全性、生产力和效率。因此&#xff0c;Rust 编程语言正在成为 IoT 设备开发人员仅次于 C 的第二大热门选择。本文将探讨为什么 Rust…

《机器学习》周志华-CH7(贝叶斯分类)

7.1贝叶斯决策论 对分类任务而言&#xff0c;在所有相关概率已知的理想情形下&#xff0c;贝叶斯决策论考虑如何基于这些概率核误判损失来选择最优的类别标记。 R ( x i ∣ x ) ∑ j 1 N λ i j P ( c j ∣ x ) \begin{equation} R(x_{i}|x)\sum_{j1}^{N}\lambda_{ij}P(c_{j}…

【C++】vector详解,模拟实现

目录 1. vector的介绍 2. vector的使用 2.1 构造函数 2.2 遍历方式 2.3 reserve与resize 2.4 shrink_to_fit 2.5 insert&#xff0c;erase&#xff0c;find 3. vector模拟实现 3.1 初始结构 3.2 析构函数 3.3 获取容量和元素个数 3.4 扩容reserve 3.5 resize改变…

最新简洁大方的自动发卡网站源码/鲸发卡v11.61系统源码/修复版

源码简介&#xff1a; 最新简洁大方的自动发卡网站源码&#xff0c;它就是鲸发卡v11.61系统源码&#xff0c;它是修复版。 说到鲸发卡系统&#xff0c;鲸发卡系统在发卡圈很多人都知道的&#xff0c;它是市面最好发卡系统之一&#xff0c;操作起来简单得很&#xff0c;界面也…