C语言手把手教你实现贪吃蛇AI(中)

2025-05-27 0 99

手把手教你实现贪吃蛇AI,具体内容如下

1. 目标

这一部分主要是讲解编写贪吃蛇AI所需要用到的算法基础。

2. 问题分析

贪吃蛇AI说白了就是寻找一条从蛇头到食物的一条最短路径,同时这条路径需要避开障碍物,这里仅有的障碍就是蛇身。而A star 算法就是专门针对这一个问题的。在A star 算法中需要用到排序算法,这里采用堆排序(当然其他排序也可以),如果对堆排序不熟悉的朋友,请移步到这里——堆排序,先看看堆排序的内容。

3. A*算法

A star(也称A*)搜寻算法俗称A星算法。这是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法。常用于游戏中对象的移动计算上。A* 算法是一种启发式搜寻算法,有别于DFS, BFS搜索。可以这样理解“启发式”的涵义,比如从起点A到达目的地B的路线,并不是直接告诉你,从A出发,向东行驶200米,右转进入XX路,直行500米到达B;而是从A出发,直行,直到遇到第一家肯德基,右转直到看到B大厦。而A*算法中用来启发的线索就是移动成本,也就是权重。

3.1 移动成本

如下图所示,从A点出发,可以有四个方向可走(由于贪吃蛇仅仅可以走上下左右四个方向,所以这里不考虑走斜线的情况),假设每个方向移动一格的成本为10,A*算法中采用的F值来评价移动成本,F=G+H。假设节点C是待考察的一个点,G代表的是从起点A到C的移动成本,如下图的情况G=10。那么H代表的就是从C点到目标B点的移动代价的预估值,如下图的情况H=50,那么F=60。为什么说是预估,因为现在对于从C点到B点的情况还不清楚,因为中间可能存在障碍物,那么实际的移动代价就会大于预估的情况。而对于待考察点D,其F=80,显然在C 和D点中(当然这里待考察的点不止C和D点),A*算法会选择C点。

C语言手把手教你实现贪吃蛇AI(中)

3.2 算法流程图

C语言手把手教你实现贪吃蛇AI(中)

4. 源代码

代码中假定起始点A(5,10),食物B(5,15),如下图。其中‘X'代表障碍物,‘O'代表的就是寻找到的从A到B的路径。

C语言手把手教你实现贪吃蛇AI(中)

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217
#include<stdio.h>

#include<stdlib.h>

#define N 32

#define W 10

typedef struct STARNODE{

int x;//节点的x,y坐标

int y;

int G;//该节点的G, H值

int H;

int is_snakebody;//是否为蛇身,是为1,否则为0;

int in_open_table;//是否在open_table中,是为1,否则为0;

int in_close_table;//是否在close_table中,是为1,否则为0;

struct STARNODE* ParentNode;//该节点的父节点

} starnode, *pstarnode;

starnode mapnode[N/2+2][N+4];

pstarnode opentable[N*N/2];

pstarnode closetable[N*N/2];

int opennode_count=0;

int closenode_count=0;

starnode food;

//根据指针所指向的节点的F值,按大顶堆进行调整

void heapadjust(pstarnode a[], int m, int n)

{

int i;

pstarnode temp=a[m];

for(i=2*m;i<=n;i*=2)

{

if(i+1<=n && (a[i+1]->G+a[i+1]->H)>(a[i]->G+a[i]->H) )

{

i++;

}

if((temp->G+temp->H)>(a[i]->G+a[i]->H))

{

break;

}

a[m]=a[i];

m=i;

}

a[m]=temp;

}

void swap(pstarnode a[],int m, int n)

{

pstarnode temp;

temp=a[m];

a[m]=a[n];

a[n]=temp;

}

void crtheap(pstarnode a[], int n)

{

int i;

for(i=n/2;i>0;i--)

{

heapadjust(a, i, n);

}

}

void heapsort(pstarnode a[], int n)

{

int i;

crtheap(a,n);

for(i=n;i>1;i--)

{

swap(a,1,i);

heapadjust(a, 1,i-1);

}

}

//x1, y1是邻域点坐标

//curtnode是当前点坐标

void insert_opentable(int x1, int y1, pstarnode pcurtnode)

{

int i;

if(!mapnode[x1][y1].is_snakebody && !mapnode[x1][y1].in_close_table)//如果不是蛇身也不在closetable中

{

if(mapnode[x1][y1].in_open_table && mapnode[x1][y1].G>pcurtnode->G+W)//如果已经在opentable中,但是不是最优路径

{

mapnode[x1][y1].G=pcurtnode->G+W;//把G值更新

mapnode[x1][y1].ParentNode=pcurtnode;//把该邻点的双亲节点更新

//由于改变了opentable中一个点的F值,需要对opentable中的点的顺序进行调整,以满足有序

for(i=1;i<=opennode_count;i++)

{

if(opentable[i]->x==x1 && opentable[i]->y==y1)

{

break;

}

heapsort(opentable, i);

}

}

else//把该点加入opentable中

{

opentable[++opennode_count]=&mapnode[x1][y1];

mapnode[x1][y1].G=pcurtnode->G+W;

mapnode[x1][y1].H=(abs(food.x-x1)+abs(food.y-y1))*W;

mapnode[x1][y1].in_open_table=1;

mapnode[x1][y1].ParentNode=pcurtnode;

heapsort(opentable, opennode_count);

}

}

}

//寻找当前点的四邻域点,把符合条件的点加入opentable中

void find_neighbor(pstarnode pcurtnode)

{

int x=pcurtnode->x;

int y=pcurtnode->y;

if(x+1<=N/2)

{

insert_opentable(x+1, y, pcurtnode);

}

if(x-1>=1)

{

insert_opentable(x-1, y, pcurtnode);

}

if(y+1<=N+1)

{

insert_opentable(x,y+1, pcurtnode);

}

if(y-1>=2)

{

insert_opentable(x,y-1, pcurtnode);

}

}

int search_road(pstarnode startnode, pstarnode endnode)

{

int is_search_road=0;

opennode_count=0;

closenode_count=0;

pstarnode pcurtnode;

opentable[++opennode_count]=startnode;//起始点加入opentable中

startnode->in_open_table=1;

startnode->ParentNode=NULL;

startnode->G=0;

startnode->H=(abs(endnode->x-startnode->x)+abs(endnode->y-startnode->y))*W;

if(startnode->x==endnode->x && startnode->y==endnode->y)//如果起点和终点重合

{

is_search_road=1;

return is_search_road;

}

while(1)

{

//取出opentable中第1个节点加入closetable中

pcurtnode=opentable[1];

opentable[1]=opentable[opennode_count--];

closetable[++closenode_count]=pcurtnode;

pcurtnode->in_open_table=0;

pcurtnode->in_close_table=1;

if(pcurtnode->x==endnode->x && pcurtnode->y==endnode->y)

{

is_search_road=1;

break;

}

find_neighbor(pcurtnode);

if(!opennode_count)//如果opentable已经为空,即没有找到路径

{

break;

}

}

return is_search_road;

}

int main(void)

{

int i, j;

pstarnode startnode;

for(i=0;i<N/2+2;i++)

for(j=0;j<N+4;j++)

{

mapnode[i][j].G=0;

mapnode[i][j].H=0;

mapnode[i][j].in_close_table=0;

mapnode[i][j].in_open_table=0;

mapnode[i][j].is_snakebody=0;

mapnode[i][j].ParentNode=NULL;

mapnode[i][j].x=i;

mapnode[i][j].y=j;

}

startnode=&mapnode[5][10];

food.x=5;

food.y=15;

mapnode[5][13].is_snakebody=1;

mapnode[6][13].is_snakebody=1;

mapnode[4][13].is_snakebody=1;

mapnode[4][12].is_snakebody=1;

mapnode[6][12].is_snakebody=1;

int flag;

flag=search_road(startnode, &food);

pstarnode temp=&mapnode[5][15];

do{

printf("%d %d ",temp->x, temp->y);

temp=temp->ParentNode;

}while(temp);

return 0;

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持快网idc。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 C语言手把手教你实现贪吃蛇AI(中) https://www.kuaiidc.com/72503.html

相关文章

发表评论
暂无评论