1
+ import Grid , { INode } from "./grid" ;
2
+ import Point = Phaser . Point ;
3
+ import Sprite = Phaser . Sprite ;
4
+
5
+
6
+ const DIAGONAL_COST = 1.4 ;
7
+
8
+
9
+ export default class Pathfinding < T extends INode > {
10
+ private start : T ;
11
+ private end : T ;
12
+ open : T [ ] ;
13
+ closed : T [ ] ;
14
+ private current : T ;
15
+ private neighbours : T [ ] ;
16
+ private neighbourIndex : number ;
17
+
18
+ constructor (
19
+ private grid : Grid < T > ,
20
+ startPoint : Point ,
21
+ endPoint : Point
22
+ ) {
23
+ this . start = grid . getNodeFromPoint ( startPoint ) ;
24
+ this . end = grid . getNodeFromPoint ( endPoint ) ;
25
+ this . open = < T [ ] > [ ] ;
26
+ this . closed = < T [ ] > [ ] ;
27
+ this . setOpen ( this . start ) ;
28
+ }
29
+
30
+ next ( ) {
31
+ return this . neighbours ?
32
+ this . processNeighbour ( ) :
33
+ this . processOpen ( ) ;
34
+ }
35
+
36
+ private processOpen ( ) {
37
+ let best = 0 ;
38
+
39
+ for ( let i = 1 ; i < this . open . length ; i ++ ) {
40
+ const bestNode = this . open [ best ] ;
41
+ const entry = this . open [ i ] ;
42
+
43
+ if ( entry . fCost < bestNode . fCost || ( entry . fCost === bestNode . fCost && entry . hCost < bestNode . hCost ) )
44
+ best = i ;
45
+ }
46
+
47
+ if ( this . current )
48
+ ( < Sprite > < any > this . current ) . tint = 0x888888 ;
49
+
50
+ this . current = this . open [ best ] ;
51
+ this . setClosed ( this . current , best ) ;
52
+ ( < Sprite > < any > this . current ) . tint = 0x00FFFF ;
53
+
54
+ if ( this . current === this . end )
55
+ return Pathfinding . retrace ( this . start , this . end ) ;
56
+
57
+ this . neighbours = this . grid . getNeighbours ( this . current ) ;
58
+ // console.log(`current x:${this.current.gridPosition.x} y:${this.current.gridPosition.y} neighbors:${this.neighbours.length}`);
59
+ this . neighbourIndex = 0 ;
60
+
61
+ while ( this . neighbours )
62
+ this . processNeighbour ( ) ;
63
+ }
64
+
65
+ private processNeighbour ( ) {
66
+ const neighbour = this . neighbours [ this . neighbourIndex ++ ] ;
67
+ // console.log(`processsing x:${neighbour.gridPosition.x} y:${neighbour.gridPosition.y}`);
68
+
69
+ if ( this . neighbourIndex >= this . neighbours . length )
70
+ this . neighbours = null ;
71
+
72
+ if ( neighbour . isObstacle || this . closed . indexOf ( neighbour ) !== - 1 )
73
+ return ;
74
+
75
+ const movement = this . current . gCost + Pathfinding . getDistance ( this . current , neighbour ) ;
76
+ if ( movement < neighbour . gCost || this . open . indexOf ( neighbour ) === - 1 ) {
77
+ neighbour . gCost = movement ;
78
+ neighbour . hCost = Pathfinding . getDistance ( neighbour , this . end ) ;
79
+ neighbour . parentNode = this . current ;
80
+
81
+ if ( this . open . indexOf ( neighbour ) === - 1 )
82
+ this . setOpen ( neighbour ) ;
83
+ }
84
+ }
85
+
86
+ private setClosed ( node : T , openIndex : number ) {
87
+ this . open . splice ( openIndex , 1 ) ;
88
+ this . closed . push ( node ) ;
89
+ }
90
+
91
+ private setOpen ( node : T ) {
92
+ this . open . push ( node ) ;
93
+ ( < Sprite > < any > node ) . tint = 0x00FF00 ;
94
+ }
95
+
96
+
97
+ static findPath < U extends INode > ( grid : Grid < U > , startPoint : Point , endPoint : Point ) : U [ ] {
98
+ const start = grid . getNodeFromPoint ( startPoint ) ;
99
+ const end = grid . getNodeFromPoint ( endPoint ) ;
100
+ const open = [ start ] ;
101
+ const closed : INode [ ] = [ ] ;
102
+
103
+ while ( open . length ) {
104
+ let best = 0 ;
105
+
106
+ for ( let i = 1 ; i < open . length ; i ++ ) {
107
+ if ( open [ i ] . fCost < open [ best ] . fCost || ( open [ i ] . fCost === open [ best ] . fCost && open [ i ] . hCost < open [ best ] . hCost ) )
108
+ best = i ;
109
+ }
110
+
111
+ const current = open . splice ( best , 1 ) [ 0 ] ;
112
+ closed . push ( current ) ;
113
+
114
+ if ( current === end )
115
+ return Pathfinding . retrace ( start , end ) ;
116
+
117
+ const neighbours = grid . getNeighbours ( current ) ;
118
+
119
+ for ( let i = 0 ; i < neighbours . length ; i ++ ) {
120
+ const neighbour = neighbours [ i ] ;
121
+ if ( neighbour . isObstacle || closed . indexOf ( neighbour ) !== - 1 )
122
+ continue ;
123
+
124
+ const movement = current . gCost + this . getDistance ( current , neighbour ) ;
125
+ if ( movement < neighbour . gCost || open . indexOf ( neighbour ) === - 1 ) {
126
+ neighbour . gCost = movement ;
127
+ neighbour . hCost = Pathfinding . getDistance ( neighbour , end ) ;
128
+ neighbour . parentNode = current ;
129
+
130
+ if ( open . indexOf ( neighbour ) === - 1 )
131
+ open . push ( neighbour ) ;
132
+ }
133
+ }
134
+ }
135
+ }
136
+
137
+ private static getDistance < U extends INode > ( nodeA : U , nodeB : U ) {
138
+ const x = Math . abs ( nodeA . gridPosition . x - nodeB . gridPosition . x ) ;
139
+ const y = Math . abs ( nodeA . gridPosition . y - nodeB . gridPosition . y ) ;
140
+
141
+ return x > y ?
142
+ DIAGONAL_COST * 10 * y + 10 * ( x - y ) :
143
+ DIAGONAL_COST * 10 * x + 10 * ( y - x ) ;
144
+ }
145
+
146
+ private static retrace < U extends INode > ( start : U , end : U ) {
147
+ const path : U [ ] = [ ] ;
148
+ let current = end ;
149
+
150
+ while ( current !== start ) {
151
+ path . push ( current ) ;
152
+ current = < U > current . parentNode ;
153
+ }
154
+
155
+ return path . reverse ( ) ;
156
+ }
157
+ }
0 commit comments