1 /++
2 Assembly module for some game algorithms.
3 
4 Macros:
5     LREF = <a href="#$1">$1</a>
6     HREF = <a href="$1">$2</a>
7     PHOBREF = <a href="https://dlang.org/phobos/$1.html#$2">$2</a>
8 
9 Authors: $(HREF https://github.com/TodNaz,TodNaz)
10 Copyright: Copyright (c) 2020 - 2021, TodNaz.
11 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT)
12 +/
13 module tida.algorithm;
14 
15 import tida.vector;
16 import std.algorithm : find, canFind, remove, reverse;
17 import std.math : pow;
18 
19 /++
20 Finds a path at given coordinates in a grid using the A* algorithm.
21 
22 Params:
23     grid = Grid.
24     begin = The point is where you need to start looking for a way.
25     from = The point where you need to find the path.
26 
27 Returns: An array of points, i.e. consistent path to the end of the path.
28 If the last point is not equal to the last point, then the path was not
29 found and only the closest path to the point was found.
30 +/
31 Vector!int[] findPath(inout bool[][] grid, Vector!int begin, Vector!int from)
32 @safe nothrow pure
33 {
34     immutable gridWidth = grid[0].length;
35     immutable gridHeight = grid.length;
36 
37     struct Node
38     {
39         Node* parent;
40         Vector!int position;
41         float g = 0.0f;
42         float h = 0.0f;
43         float f = 0.0f;
44     }
45 
46     Vector!int[] getPath(Node* node) @safe nothrow pure
47     {
48         Vector!int[] result;
49         Node* current = node;
50         while(current !is null)
51         {
52             result ~= current.position;
53             current = current.parent;
54         }
55 
56         result.reverse;
57 
58         return result;
59     }
60 
61     scope Node* startNode = new Node(null, begin);
62     scope Node* endNode = new Node(null, from);
63 
64     Node*[] yetVisitList = [startNode];
65     Node*[] visitList = [];
66     scope Node* currentNode = null;
67     size_t currentIndex = 0;
68 
69     immutable move = 	[
70                             Vector!int(-1,0),
71                             Vector!int(0,-1),
72                             Vector!int(1, 0),
73                             Vector!int(0, 1)
74                         ];
75 
76     size_t currIter = 0;
77     const maxIter = (grid.length * grid[0].length) ^^ 2;
78     const cost = 1;
79     while (yetVisitList.length > 0)
80     {
81         currIter++;
82 
83         currentNode = yetVisitList[0];
84         currentIndex = 0;
85         foreach (size_t index, Node* item; yetVisitList)
86         {
87             if (item.f < currentNode.f)
88             {
89                 currentNode = item;
90                 currentIndex = index;
91             }
92         }
93 
94         if (currIter > maxIter) return getPath(currentNode);
95 
96         yetVisitList.remove(currentIndex);
97         visitList ~= currentNode;
98 
99         if (currentNode.position == endNode.position) return getPath(currentNode);
100 
101         Node*[] children = [];
102         foreach (newPos; move)
103         {
104             Vector!int nodePos = currentNode.position + newPos;
105 
106             if (nodePos.x > gridWidth - 1 ||
107                 nodePos.x < 0 ||
108                 nodePos.y > gridHeight - 1 ||
109                 nodePos.y < 0)
110                 continue;
111 
112             if (grid[nodePos.y][nodePos.x] != 0)
113                 continue;
114 
115             children ~= new Node(currentNode, nodePos);
116         }
117 
118         foreach (child; children)
119         {
120             if (visitList.canFind!(a => child.position == a.position)) continue;
121 
122             child.g = currentNode.g + cost;
123             child.h = ((sqr(child.position.x - endNode.position.x)) +
124                        (sqr(child.position.y - endNode.position.y)));
125             child.f = child.g + child.h;
126 
127             if (yetVisitList.canFind!(a => child.position == a.position && child.g > a.g))
128                 continue;
129 
130             yetVisitList ~= child;
131         }
132     }
133 
134     return getPath(currentNode);
135 }
136 
137 unittest
138 {
139     immutable bool[][]
140             grid =
141                         [
142                             [1, 1, 1, 1, 1, 1, 1],
143                             [1, 0, 0, 0, 0, 0, 1],
144                             [1, 1, 1, 1, 1, 0, 1],
145                             [1, 0, 0, 0, 0, 0, 0],
146                             [1, 0, 1, 1, 1, 1, 0],
147                             [1, 0, 0, 0, 1, 0, 0],
148                             [1, 1, 1, 0, 0, 0, 1],
149                             [1, 1, 1, 1, 0, 1, 1]
150                         ];
151     auto result = findPath(grid, Vector!int(1, 1), Vector!int(5, 6));
152     //result[$ - 1].should.equal(Vector!int(5, 6));
153     assert(result[$ - 1] == Vector!int(5, 6));
154 }