有了模拟算法的基础,在特别篇中,我们可以看下AI了。在GOOGLE公司,每年都有GOOGLE AI CHALLENGE挑战赛,其中就包括国际贪吃蛇AI大赛。但是,由于源码的封闭性,我无法获得了。在网上只有某些70后网友写的“半成品”AI。幸运 地,我联系上了一个北京理工大学的大学生,他做的AI貌似很不错(有单机版和双人模式两种模式可供选择),不过木有源代码,他给出了他自己的QQ以便于 DEBUG。这样子,我正在全力联系那位23岁的大神,目前,只能给出基于“权重”的一种设计思想的AI算法了,虽说是半成品,不过,也是一种思路吧!
文章以及其AI的思想整理如下:
本来去年在写了贪吃蛇之后,就有想要写看AI来耍看的,后来又觉得写起麻烦没想好,没写.
今天又想起了,写来耍看.以前想的要麻烦些,当然可以直接搜索,那个是"可以",但是不太"可行".
//吴昊评注:搜索行不通,便想到了贪心的思想,不考虑全局最优解,根据最短路径(上下左右)+每次(上下左右)被自己“咬死”的可能性局部判断一个操作,贪心出局部最优解
今天上午想的就是求最优解的过程,不过要求最优解要麻烦些.
先以当前状态求到最短路径(需要做一些判断避免咬到自己,和被自己困死),并在每次移动后,地图改变状态后,再以新状态去计算下一步.
那个写起代码就多了,想简单点的几条规则就能达到那种效果.所以就只做当前最优选择,不考虑全局最优解,这样的智能效果应该还可以吧.
那个局部最优解是否可以达到全局最优解,不晓得囊个证明,如果能够达到的话,那工作量就少黑多了.
当前最优选择,按3个步骤:
1.先计算到达目标的,可行的前进方向,上,下,左,右,评估每个方向的权重.
2.计算当前这次移动所产生的影响,然后修正各方向的权重.
3.仲裁选出权重最大的那个方向,作为移动方向.
第2步中,要分解为两个阶段:(考虑两种情况)
1).先判断每个方向移动后,是否会咬到自己,如果是那个方向上的权重设为极小.(解决咬到自己)
2).如果仍一个方向移动后,会和自己接触(形成闭环),需要修正权重避开进入死胡同.(解决困死)
下面是代码:(现在解决了咬到自己,解决困死还没完成,食物在背后还没有完成)
1
//
***************************************************************
2
//
LittleAI version: 1.0 ? date: 08/28/2010
3
//
-------------------------------------------------------------
4
//
File: LittleAI.h
5
//
System: Dual-Core T4300 2.10GHz ,DDR2 2GB ,Geforce GT 240M ,
6
//
Windows XP SP3 ,MSVC++ 8.0
7
//
Author: 风轻炫舞 HoneyCat
8
//
Purpose: 贪吃蛇AI
9
//
-------------------------------------------------------------
10
//
Copyright (C) 2010 - All Rights Reserved
11
//
***************************************************************
12
//
13
//
***************************************************************
14
#ifndef __SNAKEAI_H__
15
#define __SNAKEAI_H__
16 #include
"
Map.h
"
17 #include
"
Snake.h
"
18 #include
"
Food.h
"
19
20
#define DEFAULTWEIGHT 1
21
#define MINWEIGHT -1
22
23
class SnakeAI
24 {
25
struct Action
26 {
27
int _Weight;
28 MoveDir _Dir;
29 };
30
31
public:
32
void Ready(Map& map,Snake& snake,Food& food)
33 {
34
35 }
36
37
void Go(Map& map,Snake& snake,Food& food)
38 {
39
if (snake.IsState(SS_Death))
40
return;
41
42 snake.ChangDir(Decision(map,snake,food));
43 }
44
45 MoveDir Decision(Map& map,Snake& snake,Food& food)
46 {
47 Action nextact[
4];
48 memset(nextact,
0x0,
sizeof(nextact));
49
int actnum=
4;
50
51 CalcFeasibleAct(nextact,actnum,map,snake,food);
52 NextActEffect(nextact,actnum,map,snake,food);
53
54
return Arbitration(nextact,actnum,snake);
55 }
56
57
protected:
58
59
void NextActEffect(Action* nextact,
int& actnum,Map& map,Snake& snake,Food& food)
60 {
61
if (NoBiteSelf(nextact,actnum,map,snake,food))
62 NoBesiegeSelf(nextact,actnum,map,snake,food);
63 }
64
65
void CalcFeasibleAct(Action* nextact,
int& actnum,Map& map,Snake& snake,Food& food)
66 {
67 InitAction(nextact,actnum,snake);
68 CalcWeight(nextact,actnum,snake,food);
69 }
70
71
void InitAction(Action* nextact,
int& actnum,Snake& snake)
72 {
73 actnum=
4;
74 nextact[snake.GetDir()]._Weight=DEFAULTWEIGHT; nextact[snake.GetDir()]._Dir=snake.GetDir();
75 nextact[snake.GetLeftDir()]._Weight=
0; nextact[snake.GetLeftDir()]._Dir=snake.GetLeftDir();
76 nextact[snake.GetRightDir()]._Weight=
0; nextact[snake.GetRightDir()]._Dir=snake.GetRightDir();
77 nextact[snake.GetBackDir()]._Weight=MINWEIGHT; nextact[snake.GetBackDir()]._Dir=snake.GetBackDir();
78 }
79
80
void CalcWeight(Action* nextact,
int& actnum,Snake& snake,Food& food)
81 {
82
if (food.IsState(FS_Eaten))
83 {
84
return;
85 }
86 MapPoint curpos=snake.GetBody(snake.GetHead());
87 MapPoint dest=food.GetPos();
88
89
int len=abs(dest._X-curpos._X)+abs(dest._Y-curpos._Y);
90
91
if ((dest._X-curpos._X)>
0)
92 {
93 nextact[MD_RIGHT]._Weight=len;
94 }
95
else
if ((dest._X-curpos._X)<
0)
96 {
97 nextact[MD_LEFT]._Weight=len;
98 }
99
else
100 {
101 nextact[MD_RIGHT]._Weight=
0;
102 nextact[MD_LEFT]._Weight=
0;
103 }
104
105
if ((dest._Y-curpos._Y)>
0)
106 {
107 nextact[MD_UP]._Weight=len;
108 }
109
else
if ((dest._Y-curpos._Y)<
0)
110 {
111 nextact[MD_DOWN]._Weight=len;
112 }
113
else
if ((dest._Y-curpos._Y)==
0)
114 {
115 nextact[MD_UP]._Weight=
0;
116 nextact[MD_DOWN]._Weight=
0;
117 }
118 }
119
120
bool NoBiteSelf(Action* nextact,
int& actnum,Map& map,Snake& snake,Food& food)
121 {
122 MapPoint headpos=snake.GetBody(snake.GetHead());
123 MapPoint tailpos=snake.GetBody(snake.GetTail());
124 MapPoint dirpos;
125
bool touchself=
false;
126
for (
int i=
0;i<actnum;++i)
127 {
128 NextHeadPos(dirpos,headpos,nextact[i]);
129
if (map.WhatInMap(dirpos._X,dirpos._Y)==OT_Snake)
130 {
131
if (dirpos._X==tailpos._X&&dirpos._Y==tailpos._Y)
132
continue;
133
134 nextact[i]._Weight=MINWEIGHT;
135 touchself=
true;
136 }
137 }
138
return touchself;
139 }
140
141
void NextHeadPos(MapPoint& pos,MapPoint& curpos,Action& nextact)
142 {
143
switch(nextact._Dir)
144 {
145
case MD_DOWN:
146
case MD_UP:
147 pos._X=curpos._X;
148 pos._Y=curpos._Y+(nextact._Dir<<
1)-
1;
149
break;
150
case MD_LEFT:
151
case MD_RIGHT:
152 pos._X=curpos._X+((nextact._Dir-MD_LEFT)<<
1)-
1;
153 pos._Y=curpos._Y;
154
break;
155 }
156 };
157
158
void NoBesiegeSelf(Action* nextact,
int& actnum,Map& map,Snake& snake,Food& food)
159 {
160
161 }
162
163 MoveDir Arbitration(Action* nextact,
int actnum,Snake& snake)
164 {
165 Action act;
166 act._Weight=MINWEIGHT;
167
for (
int i=
0;i<actnum;++i)
168 {
169
if (act._Weight<nextact[i]._Weight)
170 {
171 act=nextact[i];
172 }
173 }
174 LogAct(act);
175
return act._Dir;
176 }
177
178
void LogAct(Action& act)
179 {
180
switch (act._Dir)
181 {
182
case MD_UP:
183 printf(
"
Move Up/n
");
184
break;
185
case MD_DOWN:
186 printf(
"
Move Down/n
");
187
break;
188
case MD_LEFT:
189 printf(
"
Move Left/n
");
190
break;
191
case MD_RIGHT:
192 printf(
"
Move Right/n
");
193
break;
194 }
195 }
196
197 };
198
199
#endif __SNAKEAI_H__
200
201