“人工智障”——n字棋小游戏

Posted 小奔同学

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了“人工智障”——n字棋小游戏相关的知识,希望对你有一定的参考价值。

🎸
作者:爱弹吉他的小奔同学
🎸
专栏:《C语言》
🎸
仓库:gitee(所有的代码都在这个仓库里面,名字就是三子棋5.9)
🎸
如果感觉学累了,那么就看一个视频放松一下吧,猜猜点进去你会看到什么😜(视频)
🎸
我向往以后悠闲的生活,但现在的我们正处于需要努力的年华


这里就简单发一个n字棋游戏,和井字棋一样,不过这个游戏你可以自定义棋盘的大小。
井字棋是3×3大小,满足三个平齐就获得胜利。
小奔写的这个游戏,你可以自定义为10×10大小,满足6个平齐就获得胜利,都是可以随便定义的。
如果感兴趣的话就可以来尝试一下,或许你可以找到一些bug😝

(至于为什么说它是“人工智障”呢?因为它是随机下的,并不会去针对你,它很有自己的想法,不过有一次小奔测试的时候,没有注意到,就被它反杀了……)


目录


思路

  1. 用#define定义的标识符常量来确定n字棋的大小和n字棋获胜的条件
  2. 打印一个开始面板
  3. 选择是否开始游戏
  4. 开始游戏
  5. 根据#define定义的标识符常量来创建二维数组
  6. 把二维数组初始化为空格
  7. 打印一个n字棋的面板
  8. 游戏者输入坐标
  9. 判断游戏者输入的坐标是否已输入,已输入就重新输入
  10. 未输入的话,把O记录到数组里选择的坐标上
  11. 判断游戏者是否获得胜利,胜利结束游戏
  12. 判断是否填满了表格,填满就平局
  13. 电脑根据随机值输入坐标
  14. 判断电脑输入的坐标是否已输入,已输入就重新输入
  15. 未输入的话,把X记录到数组里选择的坐标上
  16. 判断电脑是否获得胜利,胜利结束游戏
  17. 判断是否填满了表格,填满就平局
  18. 回到步骤7,不断循环,直到某方获胜或者平局
  19. 结束后输入1重新开始游戏,输入0结束游戏

你看懂了吗?


游戏中的一些图片

这里的自定义的是10×10大小的,胜利条件是大于等于5,游戏方使用的是大写O


开始的界面:


棋盘的样子:


随机打的坐标:


获得胜利


结束程序

怎么样,感觉还不错吧,还不快去支持一下小奔


代码

创建了两个.c文件test.c和game.c,一个头文件game.h

test.c

#define _CRT_SECURE_NO_WARNINGS

#include"game.h"

int main()

	int num = 1;
	
	srand((unsigned int)time(NULL));
	
  do
  
		if (num == 1)
		
			//打印一个开始面板
			playboard();
		

		printf("输入1则进行游戏,输入0则结束程序\\n");

		//输入选择
		num = choose();

		//通过输入的选择来判断是否进行游戏
	switch (num)
	
		case 1://开始游戏
		

			do
			
				playgame();
				num = 0;

				printf("是否重新开始游戏,重新开始输入1,结束游戏输入0:>");
				scanf("%d", &num);
				if (num == 1)
					;
				else if (num == 0)
					break;
				else
					printf("输入错误,");
			 while (1);
			break;
		
		case 0:
		
			printf("结束程序\\n");
			break;
		
		default:
		
			printf("\\n输入错误,未能识别你的选择,请重新输入\\n\\n");
			break;
		
	
		//判断是否跳出循环
		if (num == 0)
			break;

   while (1);

	return 0;

game.c

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>

#define WID 10
#define NID 10
#define SIC 5

void playboard()

	printf("**********************************\\n");
	printf("**********************************\\n");
	printf("************ 1.play **************\\n");
	printf("************ 0.exit **************\\n");
	printf("**********************************\\n");
	printf("**********************************\\n");



int choose()

	int num = 0;

	printf("请输入你的选择:>");
	scanf("%d", &num);

	return num;



void space(char arr[WID][NID], int x, int y)

	int i = 0;
	for (i = 0; i < x; i++)
	
		int j = 0;
		for (j = 0; j < y; j++)
		
			arr[i][j] = ' ';

		
	


board(char arr[WID][NID],int x,int y)

	int i = 0;
	int j = 0;
	printf("\\n-");

	for (i = 0; i < x; i++)
	
		printf("----");
	
	printf("-\\n");

	printf("0");
	for (i = 1; i <= x; i++)
	
		printf("  %d ", i);

	
	printf("\\n");

	printf("-");

	for (i = 0; i < x; i++)
	
		printf("----");
	
	printf("-\\n");

	for (i = 0; i < x; i++)
	
		int k = 0;
		printf("%d", i+1);
		for (j = 0; j < y; j++)
		
			printf("|");
			printf(" %c ", arr[i][j]);
		
		printf("|\\n");

		printf("-");

		for (k = 0; k < x; k++)
		
			printf("----");
		
		printf("-\\n");


	



void axis(int* x,int* y)

	scanf("%d %d", &*x, &*y);
	*x=(*x) - 1;
	*y=(*y) - 1;


void sure(char arr[WID][NID],int x,int y,char c)

	arr[x][y] = c;



void human_machine(int* x, int* y)

	*x = rand() % WID;
	*y = rand() % NID;


int judge(char arr[WID][NID],int x,int y)

	if (arr[x][y] == ' ')
		return 0;
	else
		return 1;



int judgesure(char arr[WID][NID], int x, int y,char siz)

	int count = 0;
	
		int i = 0;
		for (i = 1; i <= y; i++)
		
				if (arr[x][y - i] == siz)
					count++;
				else
					break;
		
		for (i = 1; i <= NID-y-1; i++)
		
			if (arr[x][y + i] == siz)
				count++;
			else
				break;
		
		if (count >= SIC - 1)
			return 1;

		count = 0;

		for (i = 1; i <= x; i++)
		
			if (arr[x-i][y] == siz)
				count++;
			else
				break;
		
		for (i = 1; i <= NID - x-1; i++)
		
			if (arr[x+i][y ] == siz)
				count++;
			else
				break;
		

		if (count >= SIC - 1)
			return 1;

		count = 0;

		if (WID >= NID)
		

		
			for (i = 1; i <= y; i++)
			
				if (arr[x - i][y - i] == siz)
					count++;
				else
					break;
			
		
			for (i = 1; i <= NID - y - 1; i++)
			
				if (arr[x + i][y + i] == siz)
					count++;
				else
					break;
			


			if (count >= SIC - 1)
				return 1;

			count = 0;

			for (i = 1; i <= y; i++)
			
				if (arr[x + i][y - i] == siz)
					count++;
				else
					break;
			

			for (i = 1; i <= NID - y - 1; i++)
			
				if (arr[x - i][y + i] == siz)
					count++;
				else
					break;
			

			if (count >= SIC - 1)
				return 1;

			count = 0;
		

		if (WID <= NID)
		
			for (i = 1; i <= x; i++)
			
				if (arr[x - i][y - i] == siz)
					count++;
				else
					break;
			
	
			for (i = 1; i <= NID - x; i++)
			
				if (arr[x + i][y + i] == siz)
					count++;
				else
					break;
			
			//

			if (count >= SIC - 1)
				return 1;

			count = 0;

			for (i = 1; i <= x; i++)
			
				if (arr[x + i][y - i] == siz)
					count++;
				else
					break;
			

			for (i = 1; i <= NID - x; i++)
			
				if (arr[x - i][y + i] == siz)
					count++;
				else
					break;
			

			if (count >= SIC - 1)
				return 1;

		
			return 0;


int judgefill(char arr[WID][NID])

	int count = 0;
	int i = 0;
	int j = 0;
	for (i = 0; i < WID; i++)
	
		int j = 0;
		for (j = 0; j < NID; j++)
		
			if (arr[i][j] != ' ')
				count++;
		

	
	return WID*NID-count;



void playgame()

	//建立一个二维数组
	char arr[WID][NID];

	//把数组初始化为空格 
	space(arr,WID,NID);

	int over = 0;
	int i = 0;
	int count = 0;

	int X = 0;
	int Y = 0;
	int* P1 = &X;
	int* P2 = &Y;

	do
	
		//打印一个n字棋的面板
		board(arr, WID, NID);

		do
		
			printf("输入你选择的坐标:>");

			//游戏者输入坐标
			axis(P1, P2);

			//判断游戏者输入的坐标是否已输入,已输入返回1,未输入返回0
			int z = judge(arr, X, Y);

			if (z == 1)
				printf("此位置已输入,请重新");
			else
				break;
			//printf("%d %d", X, Y);
		 while (1);

		//把O记录坐标到数组上
		sure(arr, X, Y, 'O');

		//判断是否获得胜利,胜利就返回1,没有胜利就返回0
		over = judgesure(arr, X, Y, 'O');

		if (over == 1)
		
			printf("你获得胜利\\n");
			board(arr, WID, NID);

			break;
		

		//判断是否填满了表格
		if (judgefill(arr) == 0)
		
			printf("平局");
			break;
		
		
		do
		
			//电脑输入坐标
			human_machine(P1, P2);

			//判断电脑输入的坐标是否已输入,已输入返回1,未输入返回0
			int z = judge(arr, X, Y);

			if (z == 0)
			
				//把X记录坐标到数组上
				sure(arr, X, Y, 'X');
				//结束循环
				break;
			

		 while (1);

		//判断电脑是否获得胜利,胜利就返回1,没有胜利就返回0
		over = judgesure(arr, X, Y, 'X');

		if (over == 1)
		
			printf("电脑获得胜利\\n");
			board(arr, WID, NID);

			break;
		

		//判断是否填满了表格
		if (judgefill(arr) == 0)
		
			printf("平局\\n");
			break;
		

	 while (1);


game.h

#pragma once

#include<stdio.h>
使用深度优先搜索方法实现游戏的自动控制 

本文涉及一个 .py 文件:

dfs_play.py

如上图,我们将使用“深度优先搜索”的方法,来控制黑色方块自动闯关。

所谓“深度优先搜索”,即:

搜索:精准预测下一步操作后,黑色方块将到达什么位置;并再次精准预测在这个位置进行操作后,黑色方块将到达什么位置...直到触发终止条件,即找到最终得分的路径;深度优先:假设黑色方块有两个动作可以选择:A与B,那么黑色方块做出“选择A后应该到达的位置”的预测后,继续接着这条路径预测,而非去预测在初始状态下“选择B后应该到达的位置”。具体原理如下图。

【人工智障入门实战1】使用深度优先搜索实现 Amazing-Brick 小游戏的自动控制

图片生成自:https://visualgo.net/zh/dfsbfs

为了更好地了解 DFS 的特性,你可以用 BFS(广度优先搜索) 进行对比:

【人工智障入门实战1】使用深度优先搜索实现 Amazing-Brick 小游戏的自动控制

如何用 DFS 匹配我们的小游戏

给自己一个左上的力;给自己一个右上的力;什么也不做,这一时刻任由自己受重力牵制而掉落。

因此,我们每层也就有三个结点,如下图:

【人工智障入门实战1】使用深度优先搜索实现 Amazing-Brick 小游戏的自动控制

但是因为算法本身的时间复杂度过大,我们可以不考虑“什么也不做”这一动作。否则,将如下图,需要搜索的结点过多,导致程序运行过慢或内存溢出。

【人工智障入门实战1】使用深度优先搜索实现 Amazing-Brick 小游戏的自动控制

这样,每层的父结点就只有两个子结点,大大减少需要遍历的空间。

【人工智障入门实战1】使用深度优先搜索实现 Amazing-Brick 小游戏的自动控制

使用递归的实现

我使用递归来实现 DFS 算法,我大概描述一下这个过程。数据结构不够硬的同学,应该静下心来读读我的源码、或者其他经典的 DFS 教程、或者刷刷 LeetCode 。

我的源码见:https://github.com/PiperLiu/Amazing-Brick-DFS-and-DRL/blob/master/dfs_play.py

final_s_a_list = []def dfs_forward(root_state, show=False): # 最后需要返回的就是这个(状态、动作)列表 global final_s_a_list final_s_a_list = []
# 在内部定义 dfs ,用于递归 # 在递归过程中,修改 final_s_a_list 的值 # 总是保留目前最优解 def dfs(state, s_a_list): global final_s_a_list # a trick # 每次结点的排列都不一样 # 这样搜索速度更快 # 能更快地找到可行解 if len(s_a_list) % 2 == 1: ACTIONS_tmp = (2, 1) else: ACTIONS_tmp = (1, 2)
for action in ACTIONS_tmp: if len(final_s_a_list) > 0: break new_state = move_forward(state, action) new_s_a_list = s_a_list.copy() new_s_a_list.append((new_state, action)) if check_crash(new_state): if show: # 绘图部分 pygame.draw.rect(SCREEN, (255, 0, 0), (new_state['x'] - game_state.s_c.x, new_state['y'] - game_state.s_c.y, game_state.player.width, game_state.player.height)) pygame.display.update() del new_state del new_s_a_list else: if show: # 绘图部分 pygame.draw.rect(SCREEN, (100, 100, 100), (new_state['x'] - game_state.s_c.x, new_state['y'] - game_state.s_c.y, game_state.player.width, game_state.player.height)) pygame.display.update() if check_for_score(new_state): if show: # 绘图部分 pygame.draw.rect(SCREEN, (0, 0, 255), (new_state['x'] - game_state.s_c.x, new_state['y'] - game_state.s_c.y, game_state.player.width, game_state.player.height)) pygame.display.update() final_s_a_list = new_s_a_list break dfs(new_state, new_s_a_list)
# 开始递归 dfs(root_state, [])
return final_s_a_list

我这里 DFS 算法效果较好:

python dfs_play.py

输入参数 --display 可以查看寻路过程:

python dfs_play.py --display

源码:https://github.com/PiperLiu/Amazing-Brick-DFS-and-DRL

欢迎 star 。

引用链接

以上是关于“人工智障”——n字棋小游戏的主要内容,如果未能解决你的问题,请参考以下文章

井字棋版本1.0(对抗人工智障)

人工智障入门实战1使用深度优先搜索实现 Amazing-Brick 小游戏的自动控制

三子棋的实现,人工智能与人工智障

Python怎么学?资深大牛分享经验希望可以帮助你

机器学习与人工智障:决策树与随机森林

经验分享大学C语言入门到底怎么学才可以走捷径?