一. 简介
; o% U5 Q4 r) U$ j% m* o借助在前面stm32完成的贪吃蛇小游戏,现在借助A算法,来完成贪吃蛇的一个自动寻找食物的过程,从而解放我们的双手。终于从完成功能代码,到了算法的部分啦。经过这里例子,可以很好的感受将我们学习到的算法应用到实际的项目当中去。例如这里的寻路算法可以利用深度优先和广度优先搜索算法来完成,都是寻路入门级的算法。在A算法中,可以感受到排序算法的用处,以及数据结构的魅力。需要的可以关注哦; R$ [. q) h# q, r0 }# D" G
' ^6 t: {+ X6 V F8 y3 f# |2 O
7 }7 f( _+ P, n' u, C: O7 B/ E4 C, H+ u2 @+ F0 R) G
二. 算法简介# n6 ?) L, ~! }
算法的详细过程就不细细介绍了,主要来说一下实现的具体步骤,这样根据步骤,也可以自己实现出来,而不用看很多很多文章。
: `1 }2 y1 X' R6 t* m6 M4 [3 S" u9 t5 `5 R) G' k# v3 o
1.创建两个列表,一个为待访问列表,另外一个为已经访问列表
7 A+ b4 k* G" t9 G4 x2.将起始点加入到待访问列表中,并计算其预估值F。
% ?/ i7 c5 n* I" C; m' ?& E3.从待访问列表中选择预估值最小的点,作为当前节点,并在待访问列表中删除。; b+ a$ L; Y5 Y; a# Y2 Z. ^, A
4.寻找当前节点附近的点,并计算其预估值,已经将当前节点,设置为它们的父节点,然后将这些点加入到待访问列表中
# t5 P) M8 {8 m( V' h/ O0 ?5.将当前节点加入到已经访问列表中
7 y4 }" k: J8 H; [9 k. l6.重复执行3-5。直到寻找到目标点。然后通过该点的父节点依次递推,几个获取到起始点到目标点的路径。6 x5 s1 i: `% n; {" e
F = G + H.
$ Z$ P' a! y7 C6 Y0 r/ @. J3 yH表示当前节点到目标点之间的距离值
' x5 ?( A% M6 Y9 R! J+ }2 `( yG表示当前节点到起始点之间的距离值5 J% d1 y8 S. d' L$ X. ]( }& o
计算得过程中,可以在前面加上权重,表示当前节点到那个点更为重要。
! d9 R* M$ Z! ^8 Z
9 V( r# P* N- ?& ?三. 代码实现" d0 i% x3 S$ t) `
通过上面的步骤,一步一步就可以实现A*算法。
% W' z9 p! u- V" u2 J
D2 g0 ^8 Z3 B9 k/ k% ]! q1.节点的定义,主要是定义了节点的预估值和父节点
3 k3 c8 g& o* K0 D' k- //A*算法的节点6 L* P5 h$ G' y" e! ^& |
- typedef struct Node0 d4 f. g$ k4 \+ z7 T1 j2 f4 ^
- {' Q- m& @, J4 ^+ k; \' ]- Z, m/ G
- int F;
+ Q, k* v2 `* Q, E! x- w# Q( p - Point2 p;
) O! K5 g# Y$ o! Z, Y - struct Node* father;
7 ]# a5 f' T( {- Q. T! w - }Node;
复制代码
2 x- k' [+ J' @7 `, v5 _& r6 x- t. ?2.待访问列表和已经访问列表,这样比较消耗内存,特别是对于单片机带说,后面再改善吧$ G7 M. R& G. E
- Node nodeLists[140]; //待访问列表
; t( S* |( Z) B5 Z - Node visitedNodeLists[140]; //已经访问的列表* M! B) n+ N* _" `: e2 j, c; ^
- uint16_t nodeListsIndex = 0; * g9 c' b* _* R7 R: C/ C
- uint16_t visitedNodeListsIndex = 0;
+ V+ H% u: F" D+ ? - uint16_t CurrListIndex = 0;
复制代码
' d+ }/ V2 R' l8 S* {% a5 W3.A*算法
! H6 [7 r, r" q( T- G主要流程入下,和上面的实现步骤是一致的,很好理解。3 F1 g# i& A! g2 a) O# R3 R
$ c. z% S- W v
对待访问列表的排序是从大到小进行排序。这样我每次只需要取出最后一个就是最小的那个了,对于的Index值减一,就表示将其从待访问列表中移除,非常方便。 这个思想非常重要。4 k) ?4 z! v" L! u' i6 f( `
同时这里需要注意的是malloc可能申请不到内存,这也是头疼的一个问题。ε=(´ο`*)))唉
, y+ I( u9 F1 [5 U% G0 a/ C; W' x2 Z
/ K5 Q* L, {7 f) ^% c" _- uint8 AutoEateByAStar()' r# l+ J/ T7 b+ b
- {+ @0 b0 G, W; \; A! y3 D( B
- uint16 i;6 j7 z3 q6 D/ W* w6 T0 X
- nodeListsIndex = 0;$ i6 `8 n* o* `' V
- visitedNodeListsIndex = 0;
& ^5 ^8 {- s" ^5 c - CurrListIndex = 0;9 O) f8 p6 h5 R2 I: M% A2 }
- PathLen = 0;, _1 U# p7 _9 E: v) r. r
- //目标位置
3 s. n% q# I( z3 N - end.x = FoodPosition.x;# s% Z7 o- u( o, v9 ]
- end.y = FoodPosition.y;! i9 |, q- N s. U' c
- 8 H2 L3 \! Q: I4 X5 s
- //起始位置
; A7 {1 @; A9 A5 W U - start.p.x = head->postion.x;
Z& [% z G [! C - start.p.y = head->postion.y;: D, i) Z2 J. o- z) T* x
- start.father = NULL; J4 j- O! J7 }2 o: ~5 R6 {
- start.F = Assess(start.p, start.p, end);, U2 |3 e/ q( h3 T7 g
- ?) Y' V& _: A: ~# s
- //将起始点,添加到待访问点5 m9 s! Q$ N5 K' B1 l; u
- nodeLists[nodeListsIndex] = start;, Z6 C |+ e& U) @5 v0 k
- nodeListsIndex += 1;
9 h2 V0 l0 |; K' k9 { G* [ - n3 Q6 y- |% e9 |1 D
- while (nodeListsIndex > 0)5 S4 t3 V2 t2 p/ t
- {
( }, h/ e# F. d0 }. T, t! f y -
3 Q( G. p7 S* `& G& k5 T - Curr = (Node*)malloc(sizeof(Node));4 M" ?8 G$ Q% }/ D
- Curr->F = nodeLists[nodeListsIndex - 1].F;
1 Y4 l( B, g7 d- D: [ - Curr->p = nodeLists[nodeListsIndex - 1].p;
4 S; X" H1 D0 Z% R - Curr->father = nodeLists[nodeListsIndex - 1].father;
3 C- K* S' w' N - nodeListsIndex -= 1;" j/ B3 `2 _; G, _9 h7 [/ Q/ A7 S+ n
Q1 N+ Y7 q! l, r |# ~' v- searchNeighboor();
% s8 q6 b F0 A; f5 S1 J2 p: s; B8 l - visitedNodeLists[visitedNodeListsIndex] = *Curr;4 A2 X% c) u$ |9 X: [4 d
- visitedNodeListsIndex += 1;
5 L3 A6 P5 ?6 Z& ? U- O
6 u' L- [! Q/ ?4 `9 S" S- # A* p0 o9 P/ J, o7 N- M9 u
- //保存申请到的内存+ L; w% w. r* ?! @! o6 }2 O* S7 Y* [
- CurrList[CurrListIndex] = Curr;
4 ^; n% D* l% t2 ~ - CurrListIndex += 1;, G+ s* E7 Q% e) ?* P7 H
- if (Curr->p.x == end.x && Curr->p.y == end.y) //查找到路径$ i5 I) s* b! ]5 @: M3 v
- {
& H' J* Y2 N; d4 {, y
2 C6 a. d1 s. b- //保存路径9 f6 _; a* Z8 V5 W8 H. A. `# |: A
- while (Curr->father != NULL)
% b J: X7 `6 v: J } - {
" a) f) Y5 p+ [ c) j- u - PathByAStar[PathLen].x = Curr->p.x;2 P; r* v( f+ d6 z3 u" k! N
- PathByAStar[PathLen].y = Curr->p.y;
( r$ S' v- e( P/ T+ F; M - PathLen += 1;
/ f5 H I# x% q' G - printf("%d %d \r\n",Curr->p.x,Curr->p.y);. N, `/ f6 ?' T1 @
- Curr = Curr->father;
' p9 M0 P! l. K w - 7 ~8 H% i- E2 L4 [# r
- }
( Z0 m0 ~/ A% Q( A - PathByAStar[PathLen].x = Curr->p.x;
6 {! _2 ?& A! {3 P7 Z - PathByAStar[PathLen].y = Curr->p.y;
. Q7 |/ ^! W7 x1 o1 K* B, m - PathLen += 1;
{& Q$ Y v. d4 ?9 f; [% P. X" H - //释放申请到的内存
! I" t7 V8 t" _! h, z3 N# W: r - for (i = 0; i < CurrListIndex; i++)/ ~$ n+ q X( r! O0 g4 R( x
- {% G2 y9 }, Z2 G& D. w) C; E# z
- free(CurrList<i>);
& K) m& ]4 J5 N+ t - CurrList<i> = NULL;" K u9 A1 W* ]& o: g* b+ o0 W
- }; V2 h, w* f8 _7 ?, t- v7 G
- return 0;5 h2 n! [ k& r* p: E+ d% i5 Z# T# u: N
- }
1 v/ K4 s" S1 M! N; V1 i, U% l9 e - }: J: m4 E- z- r M/ N* H7 w
- return 1; _7 z* A3 j$ s* i
- }</i></i>
复制代码 2 t a: \8 @: U' h
到这里A*寻路算法就差不多完成了,主要是在贪吃蛇中进行路径解析。2 ]2 I; s, R/ e" l4 I, i' J
% x; ]( ~" m5 v: B1 O' W1 Y四. 贪吃蛇路径解析
: ]" I; c2 j* X- O2 A8 I这里解析也是非常容易的,因为在PathByAStar中已经保存到了路径值。所以只需要在每一次移动的时候,蛇头和下一个位置进行对比,就可以确定移动的方向了。代码如下," O$ c" @6 b- b; f- H D4 v
6 x" R2 z% @% U" S
- if( PathByAStar[PathLen-1].y == head->postion.y)
3 ^" e9 ~0 d1 T* Z+ x - {, r% N3 f' F/ @: |
- if(PathByAStar[PathLen-1].x == head->postion.x -1)
9 t3 Y' p' r" `* Y+ { - MoveDirection = MoveLeft;
# a# O# e+ N) S, Y4 c2 o& a( x% r - else
q& G2 I) l' G0 v/ E" L - MoveDirection = MoveRight;- A1 Y3 |2 l' G. Q U/ V
- }4 u. f3 V) i/ ]8 X3 K
- else7 x: x) C5 Y% I: B* m V
- {) c e% O' l9 y, ]" r% c
- if(PathByAStar[PathLen-1].y == head->postion.y -1)
+ M2 S: `: z. y; S5 V* l3 b - MoveDirection = MoveUp;! W% `) q0 u. b0 m9 n% h1 E. G
- else8 {& |/ F6 N4 h) i% Z0 x* d
- MoveDirection = MoveDown;
) Z* t3 J3 r; }8 V' T2 }- T - }
5 I5 x2 G$ c% D* L: L, R5 S; P - PathLen -= 1;
' o! {3 X$ O$ t; g* Y/ R
复制代码 ) V4 D2 q4 k9 t: H8 k; D- x6 G& E
2 i. _5 ^) H# k0 G' Q
$ h: f2 o, i4 P" ~. z4 N3 l |