(C语言) 三子棋速通N子棋

文章详细介绍了如何编写三子棋游戏的代码,包括开始菜单、棋盘设置、玩家和电脑下棋的逻辑,以及胜利条件的判断。使用二维数组表示棋盘,通过玩家和电脑的交互进行游戏,并提供了简单的胜利判断算法。文章强调了编写清晰逻辑和逐步测试的重要性,并提出了增加电脑智能性的思考。
摘要由CSDN通过智能技术生成

首先分析三子棋代码的写作步骤,

大体如下:

一、开始菜单

二、棋盘

三、玩家下棋

四、电脑下棋

五、胜利判断

以及实现这几个部分需要用到的函数

1、初始化棋盘 InitBoard

2、打印棋盘 DisplayBoard

3、玩家下棋 PlayerMove

4、电脑下棋 ComputerMove

5、玩家胜利 PlayerWins

6、电脑胜利 ComputerWins

接下来我们一一实现。


一、开始菜单

首先打印菜单,由游玩者输入1或者0选择是否玩游戏。

利用switch语句选择。

这里直接通过input的值判断是否结束循环,较为简便:

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));//要在主函数中
	do
	{
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("请输入1或者0\n");
			break;
		}

	} while (input);
	return 0;
}

但如果这样使用,注意input需定义在循环之外。

Tips:写逻辑较长的代码时,养成逐步测试的习惯,便于出现问题时快速定位。

如:

这里用“三子棋”代替实际的游戏函数,测试当前代码是否有问题


 二、棋盘

通过三子棋棋盘的形状,容易联想到二维数组,便于理解,于是使用二维数组来作为棋盘格子。

 通过#define定义行列,这样便于后续修改,

同时所有有关行列的代码都要注意,应面向ROW与COL写代码,而不是“3”.

函数1、初始化棋盘 InitBoard

使用for循环对二维数组进行赋值,十分简易,不多赘述。

也可以在创建数组时直接初始化为空格。

void InitBoard(char Board[ROW][COL])
{
	int i = 0;
	for (i = 0; i < ROW; i++)
	{
		int j = 0;
		for (j = 0; j < COL; j++)
		{
			Board[i][j] = ' ';
		}
	}
}

初始化为空格是为了打印棋盘后的美观

函数 2、打印棋盘 DisplayBoard

void DisplayBoard(char Board[ROW][COL])
{
	int i = 0;
	for (i = 0; i < ROW; i++)
	{
		int j = 0;
		for (j = 0; j < COL; j++)
		{
			printf(" %c ", Board[i][j]);
			if (j < COL - 1)
			{
				printf("|");
			}
		}
		printf("\n");
		if (i < ROW - 1)
		{
			int j = 0;
			for (j = 0; j < COL; j++)
			{
				printf("---");
				if (j < COL - 1)
				{
					printf("|");
				}
			}
			printf("\n");
		}
	}
}

 逻辑较为简单,但初见可能不好理清棋盘分割线的打印,理解如下:

 

 此处棋盘分割线的打印,依照右图分为蓝色部分与红色部分,分别打印


三、玩家下棋

玩家输入坐标,将数组坐标处的空格赋值为*,视为玩家下的棋子:

void PlayerMove(char Board[ROW][COL])
{
	printf("请玩家输入坐标,用空格分隔:>");
	int row = 0;
	int col = 0;
	while (1)
	{
		scanf("%d%d", &row, &col);
		if (row >= 1 && row <= ROW && col >= 1 && col <= COL)
		{
			if (Board[row - 1][col - 1] == ' ')
			{
				Board[row - 1][col - 1] = '*';
				break;
			}
			else
			{
				printf("请重新输入:>");
			}
		}
		else
		{
			printf("请重新输入:>");
		}
	}
}

下过棋之后再次调用DisplayBoard打印棋盘,直观的展现给玩家棋盘情况。

同样,使用while(1)测试代码可行性 

此部分有几个地方需要注意:

第一,玩家若输入坐标 1   1,实际在数组中的位置应该为Board[0][0],故而赋值时应注意减一

第二,除了正确输入坐标外,还存在输入的坐标超过棋盘格子范围,或是该格子已经被下过了的情况,需要考虑到。

第三,写成死循环,保证玩家最终能够正确的下完这一步棋。


四、电脑下棋

从简单的做起,不考虑电脑下棋的智能,先让电脑随机下棋;

代码实现如下:

void ComputerMove(char Board[ROW][COL])
{
	int row = 0;
	int col = 0;
	while (1)
	{
		row = rand() % ROW;
		col = rand() % COL;
		if (Board[row][col] == ' ')
		{
			Board[row][col] = '#';
			break;
		}
	}
	printf("电脑下棋:>%d %d\n", row + 1, col + 1);
}

同样需要注意电脑不能选择已经下过的格子下棋


五、胜利判断

此部分较其他内容较为复杂:

其一,

玩家的胜利与电脑的胜利理论上说应该是同样的逻辑,

但共用一个函数,就意味着会在一方下棋后判断另一方胜利与否,实属不必,故分为两个函数。

其二,

需要注意,在3X3的棋盘上,率先3个棋子连成一线的一方胜利,仅仅是井字棋的玩法;

井字棋的胜利方式除了横线与竖线,就只有对角线了;

而但凡修改棋盘大小,就会多出许多斜着的胜利方法,更别提修改胜利的棋子数了。

所以不能简单的按三子棋来写胜利判断。

胜利条件应该是:

对于行列为ROW与COL的棋盘,一方的棋子在行,列,斜线上连续排列WIN个棋子。

首先是行与列的判断:

int count = 0;
	int row = 0;
	int col = 0;
	for (row = 0; row < ROW; row++)//判断行
	{
		for (col = 0; col < COL; col++)
		{
			if (Board[row][col] == '*')
			{
				count++;
			}
			else
			{
				count = 0;
			}
			if (count == WIN)
			{
				return 1;
			}
		}
	}
	count = 0;
	for (col = 0; col < COL; col++)//同样逻辑,判断列
	{
		for (row = 0; row < ROW; row++)
		{
			if (Board[row][col] == '*')
			{
				count++;
			}
			else
			{
				count = 0;
			}
			if (count == WIN)
			{
				return 1;
			}
		}
	}
	count = 0;

逻辑基本一致 ,需要注意的是如果按本文逻辑写胜利判断,那么在行或列的判断结束后,必须将count重新赋值为0,避免影响后续判断。

接着是最为复杂的斜线部分,

这里分为与棋盘上边缘相交的斜线,以及与棋盘下边缘相交的斜线。

上边缘部分:

for (col = 0; col < COL; col++)
	{
		int tmp = col;
		row = 0;
		while ((row >= 0) && (row < ROW) && (col >= 0) && (col < COL))
		{
			if (Board[row][col] == '*')
			{
				count++;
			}
			else
			{
				count = 0;
			}
			row++;
			col--;
			if (count == WIN)
			{
				return 1;
			}
		}
		row = 0;
		col = tmp;
		count = 0;
		while ((row >= 0) && (row < ROW) && (col >= 0) && (col < COL))
		{
			if (Board[row][col] == '*')
			{
				count++;
			}
			else
			{
				count = 0;
			}
			row++;
			col++;
			if (count == WIN)
			{
				return 1;
			}
		}
		col = tmp;
		count = 0;
	}

下边缘部分:

for (col = 0; col < COL; col++)
	{
		int tmp = col;
		row = ROW - 1;
		while ((row >= 0) && (row < ROW) && (col >= 0) && (col < COL))
		{
			if (Board[row][col] == '*')
			{
				count++;
			}
			else
			{
				count = 0;
			}
			row--;
			col--;
			if (count == WIN)
			{
				return 1;
			}
		}
		row = ROW - 1;
		col = tmp;
		count = 0;
		while ((row >= 0) && (row < ROW) && (col >= 0) && (col < COL))
		{
			if (Board[row][col] == '*')
			{
				count++;
			}
			else
			{
				count = 0;
			}
			row--;
			col++;
			if (count == WIN)
			{
				return 1;
			}
		}
		col = tmp;
		count = 0;
	}

此处需要注意的地方容易导致函数崩坏:

第一,每条“线”(包括横线,竖线,斜线)判断结束后,要将count重新赋值为0;

第二,因判断过程中会改变row与col的值,故需要在下次使用前将row与col还原成修改前的值。

此节上述代码仅为PlayerWins的逻辑;

ComputerWins与其几乎完全相同,只需将六处‘*’改为‘#’

以及,这种胜利判断的方式简单粗暴,也许还有更好更简便的方式,不过我没有想到:(


零、智能提升

如果只需让电脑随机选择空格下棋,实现起来非常简单,可以考虑尝试增加电脑下棋的智能性,为电脑下棋设置一些优先级,例如:

1、如果电脑快要取胜,则优先胜利

2、如果玩家快要取胜,则优先阻止

3、1优先于2

等等等等......

鉴于此部分代码的编写较前文难度高得多,笔者能力有限,故不予实现。


至此,通关N子棋。

作为我第一个实现的超微型小游戏,还是有点点成就感的。

希望能再接再厉。

以上。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
GeoPandas是一个开源的Python库,旨在简化地理空间数据的处理和分析。它结合了Pandas和Shapely的能力,为Python用户提供了一个强大而灵活的工具来处理地理空间数据。以下是关于GeoPandas的详细介绍: 一、GeoPandas的基本概念 1. 定义 GeoPandas是建立在Pandas和Shapely之上的一个Python库,用于处理和分析地理空间数据。 它扩展了Pandas的DataFrame和Series数据结构,允许在其中存储和操作地理空间几何图形。 2. 核心数据结构 GeoDataFrame:GeoPandas的核心数据结构,是Pandas DataFrame的扩展。它包含一个或多个列,其中至少一列是几何列(geometry column),用于存储地理空间几何图形(如点、线、多边形等)。 GeoSeries:GeoPandas中的另一个重要数据结构,类似于Pandas的Series,但用于存储几何图形序列。 二、GeoPandas的功能特性 1. 读取和写入多种地理空间数据格式 GeoPandas支持读取和写入多种常见的地理空间数据格式,包括Shapefile、GeoJSON、PostGIS、KML等。这使得用户可以轻松地从各种数据源中加载地理空间数据,并将处理后的数据保存为所需的格式。 2. 地理空间几何图形的创建、编辑和分析 GeoPandas允许用户创建、编辑和分析地理空间几何图形,包括点、线、多边形等。它提供了丰富的空间操作函数,如缓冲区分析、交集、并集、差集等,使得用户可以方便地进行地理空间数据分析。 3. 数据可视化 GeoPandas内置了数据可视化功能,可以绘制地理空间数据的地图。用户可以使用matplotlib等库来进一步定制地图的样式和布局。 4. 空间连接和空间索引 GeoPandas支持空间连接操作,可以将两个GeoDataFrame按照空间关系(如相交、包含等)进行连接。此外,它还支持空间索引,可以提高地理空间数据查询的效率。
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值