Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
64 / 64 |
|
100.00% |
5 / 5 |
CRAP | |
100.00% |
1 / 1 |
Grid | |
100.00% |
64 / 64 |
|
100.00% |
5 / 5 |
39 | |
100.00% |
1 / 1 |
createGridFromArray | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
7 | |||
setNode | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getNode | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
isWalkable | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
3 | |||
getNeighbors | |
100.00% |
49 / 49 |
|
100.00% |
1 / 1 |
25 |
1 | <?php |
2 | /** |
3 | * Jingga |
4 | * |
5 | * PHP Version 8.1 |
6 | * |
7 | * @package phpOMS\Algorithm\PathFinding |
8 | * @copyright Dennis Eichhorn |
9 | * @license OMS License 2.0 |
10 | * @version 1.0.0 |
11 | * @link https://jingga.app |
12 | * |
13 | * Extended based on: |
14 | * MIT License |
15 | * (c) 2011-2012 Xueqiao Xu <xueqiaoxu@gmail.com> |
16 | * (c) PathFinding.js |
17 | */ |
18 | declare(strict_types=1); |
19 | |
20 | namespace phpOMS\Algorithm\PathFinding; |
21 | |
22 | /** |
23 | * Grid of nodes. |
24 | * |
25 | * @package phpOMS\Algorithm\PathFinding |
26 | * @license OMS License 2.0 |
27 | * @link https://jingga.app |
28 | * @since 1.0.0 |
29 | */ |
30 | class Grid |
31 | { |
32 | /** |
33 | * Grid system containing all nodes |
34 | * |
35 | * @var array<int, array<int, Node>> |
36 | * @since 1.0.0 |
37 | */ |
38 | private array $nodes = [[]]; |
39 | |
40 | /** |
41 | * Create a grid from an array |
42 | * |
43 | * [ |
44 | * [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,], |
45 | * [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0,], |
46 | * [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,], |
47 | * [0, 0, 9, 9, 9, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0,], |
48 | * [0, 0, 0, 0, 9, 9, 9, 9, 9, 0, 9, 0, 0, 0, 0,], |
49 | * [0, 0, 1, 0, 9, 0, 0, 0, 0, 0, 9, 0, 9, 9, 9,], |
50 | * [0, 0, 0, 0, 9, 0, 0, 9, 9, 9, 9, 0, 0, 0, 0,], |
51 | * [0, 0, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,], |
52 | * [0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 9, 9, 9, 9, 0,], |
53 | * [0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0,], |
54 | * [0, 0, 0, 9, 0, 0, 0, 9, 0, 0, 9, 9, 0, 0, 0,], |
55 | * [0, 0, 0, 0, 0, 9, 9, 9, 0, 0, 9, 2, 0, 0, 0,], |
56 | * [0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 9, 9, 0, 0, 0,], |
57 | * [0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0,], |
58 | * [0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0,], |
59 | * ] |
60 | * |
61 | * @param array<int, int[]> $gridArray Grid defined in an array (0 = empty, 1 = start, 2 = end, 9 = not walkable) |
62 | * @param string $node Node type name |
63 | * |
64 | * @return Grid |
65 | * |
66 | * @since 1.0.0 |
67 | */ |
68 | public static function createGridFromArray(array $gridArray, string $node) : self |
69 | { |
70 | $grid = new self(); |
71 | foreach ($gridArray as $y => $yRow) { |
72 | foreach ($yRow as $x => $xElement) { |
73 | if ($xElement === 0 || $xElement === 1 || $xElement === 2) { |
74 | /** @var \phpOMS\Algorithm\PathFinding\Node $empty */ |
75 | $empty = new $node($x, $y, 1.0, true); |
76 | $grid->setNode($x, $y, $empty); |
77 | } elseif ($xElement === 9) { |
78 | /** @var \phpOMS\Algorithm\PathFinding\Node $wall */ |
79 | $wall = new $node($x, $y, 1.0, false); |
80 | $grid->setNode($x, $y, $wall); |
81 | } |
82 | } |
83 | } |
84 | |
85 | return $grid; |
86 | } |
87 | |
88 | /** |
89 | * Set node at position |
90 | * |
91 | * @param int $x X-Coordinate |
92 | * @param int $y Y-Coordinate |
93 | * @param Node $node Node to set |
94 | * |
95 | * @return void |
96 | * |
97 | * @since 1.0.0 |
98 | */ |
99 | public function setNode(int $x, int $y, Node $node) : void |
100 | { |
101 | $this->nodes[$y][$x] = $node; |
102 | } |
103 | |
104 | /** |
105 | * Get node at position |
106 | * |
107 | * @param int $x X-Coordinate |
108 | * @param int $y Y-Coordinate |
109 | * |
110 | * @return null|Node |
111 | * |
112 | * @since 1.0.0 |
113 | */ |
114 | public function getNode(int $x, int $y) : ?Node |
115 | { |
116 | if (!isset($this->nodes[$y]) || !isset($this->nodes[$y][$x])) { |
117 | return null; |
118 | } |
119 | |
120 | return $this->nodes[$y][$x]; |
121 | } |
122 | |
123 | /** |
124 | * Is node walkable" |
125 | * |
126 | * @param int $x X-Coordinate |
127 | * @param int $y Y-Coordinate |
128 | * |
129 | * @return bool |
130 | * |
131 | * @since 1.0.0 |
132 | */ |
133 | public function isWalkable(int $x, int $y) : bool |
134 | { |
135 | return isset($this->nodes[$y]) && isset($this->nodes[$y][$x]) && $this->nodes[$y][$x]->isWalkable; |
136 | } |
137 | |
138 | /** |
139 | * Get neighbors of node |
140 | * |
141 | * @param Node $node Node to get neighbors from |
142 | * @param int $movement Allowed movements |
143 | * |
144 | * @return Node[] |
145 | * |
146 | * @since 1.0.0 |
147 | */ |
148 | public function getNeighbors(Node $node, int $movement) : array |
149 | { |
150 | $x = $node->getX(); |
151 | $y = $node->getY(); |
152 | |
153 | $neighbors = []; |
154 | |
155 | $s0 = false; |
156 | $s1 = false; |
157 | $s2 = false; |
158 | $s3 = false; |
159 | $d0 = false; |
160 | $d1 = false; |
161 | $d2 = false; |
162 | $d3 = false; |
163 | |
164 | if ($this->isWalkable($x, $y - 1)) { |
165 | $neighbors[] = $this->getNode($x, $y - 1); |
166 | $s0 = true; |
167 | } |
168 | |
169 | if ($this->isWalkable($x + 1, $y)) { |
170 | $neighbors[] = $this->getNode($x + 1, $y); |
171 | $s1 = true; |
172 | } |
173 | |
174 | if ($this->isWalkable($x, $y + 1)) { |
175 | $neighbors[] = $this->getNode($x, $y + 1); |
176 | $s2 = true; |
177 | } |
178 | |
179 | if ($this->isWalkable($x - 1, $y)) { |
180 | $neighbors[] = $this->getNode($x - 1, $y); |
181 | $s3 = true; |
182 | } |
183 | |
184 | if ($movement === MovementType::STRAIGHT) { |
185 | /** @var Node[] $neighbors */ |
186 | return $neighbors; |
187 | } |
188 | |
189 | if ($movement === MovementType::DIAGONAL_NO_OBSTACLE) { |
190 | $d0 = $s3 && $s0; |
191 | $d1 = $s0 && $s1; |
192 | $d2 = $s1 && $s2; |
193 | $d3 = $s2 && $s3; |
194 | } elseif ($movement === MovementType::DIAGONAL_ONE_OBSTACLE) { |
195 | $d0 = $s3 || $s0; |
196 | $d1 = $s0 || $s1; |
197 | $d2 = $s1 || $s2; |
198 | $d3 = $s2 || $s3; |
199 | } elseif ($movement === MovementType::DIAGONAL) { |
200 | $d0 = true; |
201 | $d1 = true; |
202 | $d2 = true; |
203 | $d3 = true; |
204 | } |
205 | |
206 | if ($d0 && $this->isWalkable($x - 1, $y - 1)) { |
207 | $neighbors[] = $this->getNode($x - 1, $y - 1); |
208 | } |
209 | |
210 | if ($d1 && $this->isWalkable($x + 1, $y - 1)) { |
211 | $neighbors[] = $this->getNode($x + 1, $y - 1); |
212 | } |
213 | |
214 | if ($d2 && $this->isWalkable($x + 1, $y + 1)) { |
215 | $neighbors[] = $this->getNode($x + 1, $y + 1); |
216 | } |
217 | |
218 | if ($d3 && $this->isWalkable($x - 1, $y + 1)) { |
219 | $neighbors[] = $this->getNode($x - 1, $y + 1); |
220 | } |
221 | |
222 | /** @var Node[] $neighbors */ |
223 | return $neighbors; |
224 | } |
225 | } |