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 }