“人工智障”——n字棋小游戏
Posted 小奔同学
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了“人工智障”——n字棋小游戏相关的知识,希望对你有一定的参考价值。
🎸
作者:爱弹吉他的小奔同学
🎸
专栏:《C语言》
🎸
仓库:gitee(所有的代码都在这个仓库里面,名字就是三子棋5.9)
🎸
如果感觉学累了,那么就看一个视频放松一下吧,猜猜点进去你会看到什么😜(视频)
🎸
我向往以后悠闲的生活,但现在的我们正处于需要努力的年华
这里就简单发一个n字棋游戏,和井字棋一样,不过这个游戏你可以自定义棋盘的大小。
井字棋是3×3大小,满足三个平齐就获得胜利。
小奔写的这个游戏,你可以自定义为10×10大小,满足6个平齐就获得胜利,都是可以随便定义的。
如果感兴趣的话就可以来尝试一下,或许你可以找到一些bug😝
(至于为什么说它是“人工智障”呢?因为它是随机下的,并不会去针对你,它很有自己的想法,不过有一次小奔测试的时候,没有注意到,就被它反杀了……)
目录
思路
- 用#define定义的标识符常量来确定n字棋的大小和n字棋获胜的条件
- 打印一个开始面板
- 选择是否开始游戏
- 开始游戏
- 根据#define定义的标识符常量来创建二维数组
- 把二维数组初始化为空格
- 打印一个n字棋的面板
- 游戏者输入坐标
- 判断游戏者输入的坐标是否已输入,已输入就重新输入
- 未输入的话,把O记录到数组里选择的坐标上
- 判断游戏者是否获得胜利,胜利结束游戏
- 判断是否填满了表格,填满就平局
- 电脑根据随机值输入坐标
- 判断电脑输入的坐标是否已输入,已输入就重新输入
- 未输入的话,把X记录到数组里选择的坐标上
- 判断电脑是否获得胜利,胜利结束游戏
- 判断是否填满了表格,填满就平局
- 回到步骤7,不断循环,直到某方获胜或者平局
- 结束后输入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后应该到达的位置”。具体原理如下图。
图片生成自:https://visualgo.net/zh/dfsbfs
为了更好地了解 DFS 的特性,你可以用 BFS(广度优先搜索) 进行对比:
如何用 DFS 匹配我们的小游戏
•给自己一个左上的力;•给自己一个右上的力;•什么也不做,这一时刻任由自己受重力牵制而掉落。
因此,我们每层也就有三个结点,如下图:
但是因为算法本身的时间复杂度过大,我们可以不考虑“什么也不做”这一动作。否则,将如下图,需要搜索的结点过多,导致程序运行过慢或内存溢出。
这样,每层的父结点就只有两个子结点,大大减少需要遍历的空间。
使用递归的实现
我使用递归来实现 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字棋小游戏的主要内容,如果未能解决你的问题,请参考以下文章