From 76c33a8178db40d6ca5f0a72f3cb2d943296f8fc Mon Sep 17 00:00:00 2001 From: Bryson Zimmerman Date: Tue, 12 Nov 2024 19:10:51 -0500 Subject: [PATCH] Initial setup for converting from HashMap to TileNavigatedArray --- ...PathFinder.kt => ArrayBackedPathfinder.kt} | 6 +- src/main/kotlin/Main.kt | 2 +- src/main/kotlin/MapBackedPathfinder.kt | 76 +++++++++++++++++++ src/test/kotlin/TileNavigatedArrayTest.kt | 16 ++++ 4 files changed, 95 insertions(+), 5 deletions(-) rename src/main/kotlin/{PathFinder.kt => ArrayBackedPathfinder.kt} (96%) create mode 100644 src/main/kotlin/MapBackedPathfinder.kt create mode 100644 src/test/kotlin/TileNavigatedArrayTest.kt diff --git a/src/main/kotlin/PathFinder.kt b/src/main/kotlin/ArrayBackedPathfinder.kt similarity index 96% rename from src/main/kotlin/PathFinder.kt rename to src/main/kotlin/ArrayBackedPathfinder.kt index 4653e24..3263fc0 100644 --- a/src/main/kotlin/PathFinder.kt +++ b/src/main/kotlin/ArrayBackedPathfinder.kt @@ -4,12 +4,12 @@ import technology.zim.data.Directions import technology.zim.data.Tile import technology.zim.data.TileHeap -//A* to be upgraded with hierarchies +//A* pathfinder backed by an array to improve efficiency //Needs https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/PriorityQueue.html //and https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Comparator.html -object PathFinder { +object ArrayBackedPathfinder { //TODO: Replace with array for coordinate lookups for speed, do it in a separate pathfinder class to demonstrate the difference val gVals = HashMap() //work along the path, marking tiles with VISITED along the way @@ -53,8 +53,6 @@ object PathFinder { //At this point, a path is found println("Path found!") - - } fun markPath(start: Tile, end:Tile) { diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 7a73a36..2df9037 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -13,7 +13,7 @@ class HierarchicalPathfinding { println("Pathfinding") var startTime = System.currentTimeMillis() - PathFinder.generatePath(Tile(0, 0), Tile(n-1, (n-1))) + MapBackedPathfinder.generatePath(Tile(0, 0), Tile(n-1, (n-1))) var endTime = System.currentTimeMillis() val aStarMs = endTime - startTime diff --git a/src/main/kotlin/MapBackedPathfinder.kt b/src/main/kotlin/MapBackedPathfinder.kt new file mode 100644 index 0000000..a953b37 --- /dev/null +++ b/src/main/kotlin/MapBackedPathfinder.kt @@ -0,0 +1,76 @@ +package technology.zim + +import technology.zim.data.Directions +import technology.zim.data.Tile +import technology.zim.data.TileHeap + +//A* to be upgraded with hierarchies + +//Needs https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/PriorityQueue.html +//and https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Comparator.html + +object MapBackedPathfinder { + val gVals = HashMap() + //work along the path, marking tiles with VISITED along the way + //if marking with visited is too expensive, just make the path and finalize it + fun generatePath(start: Tile, end: Tile) { + if(!start.isInBounds() || !end.isInBounds()) { + throw IndexOutOfBoundsException("Cannot generate a path to or from an out of bounds tile") + } + + if(start == end) { + println("Ouroboros detected") + return + } + + val frontier = TileHeap(end, gVals) + + //Prime the things + gVals.put(start, 0) + frontier.insert(start) + + var current: Tile + var currentG: Int + + do { + current = frontier.popMin() + currentG = gVals.get(current) ?: 0.also { println("Failed to get gVal that must exist") } + + + current.getConnections().forEach { candidateTile -> + val candidateG = gVals.get(candidateTile)?:-1 + //Ensure that the tile is within bounds + if(candidateTile.isInBounds() && candidateG == -1) + { + //Otherwise, the tile has been reached and this path is not better, so carry on + gVals.put(candidateTile, currentG + 1) + frontier.insert(candidateTile) + World.update(candidateTile, candidateTile.getProperties() +Directions.FRONTIER) + } + } + } while( current != end) + + //At this point, a path is found + println("Path found!") + } + + fun markPath(start: Tile, end:Tile) { + //Step through the path from end until start + var current = end + var lowestG = Int.MAX_VALUE + var lowestCost = end + while(current != start) { + World.update(current, current.getProperties() + Directions.INPATH) + current.getConnections().forEach { candidateTile -> + val candidateG = gVals.get(candidateTile) ?: -1 + if(candidateTile.isInBounds() && candidateG != -1 && candidateG < lowestG ) { + lowestG = candidateG + lowestCost = candidateTile + } + } + current = lowestCost + } + World.update(start, start.getProperties() + Directions.INPATH) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/TileNavigatedArrayTest.kt b/src/test/kotlin/TileNavigatedArrayTest.kt new file mode 100644 index 0000000..28e2e32 --- /dev/null +++ b/src/test/kotlin/TileNavigatedArrayTest.kt @@ -0,0 +1,16 @@ +import kotlin.test.BeforeTest +import kotlin.test.Test + +class TileNavigatedArrayTest { + + @BeforeTest + fun setUp() { + + } + + + @Test + fun addAndAccessTest() { + + } +} \ No newline at end of file