Compare commits

..

No commits in common. '9c087ea6425277e18640910a283d1d27c93f1664' and 'ed103c30fea44348e568a4a7f8832c2d16772314' have entirely different histories.

@ -4,8 +4,8 @@ import technology.zim.data.Directions
import technology.zim.data.Tile import technology.zim.data.Tile
class HierarchicalPathfinding { class HierarchicalPathfinding {
companion object { companion object {
//TODO: Run tests of World with nested ArrayList vs TileNavigatedArray
@JvmStatic @JvmStatic
fun main(args: Array<String>) { fun main(args: Array<String>) {
val n = 1000 val n = 1000
@ -18,16 +18,18 @@ class HierarchicalPathfinding {
println("Pathfinding") println("Pathfinding")
startTime = System.currentTimeMillis() startTime = System.currentTimeMillis()
ArrayBackedPathfinder.generatePath(Tile(0, 0), Tile(n-1, (n-1))) MapBackedPathfinder.generatePath(Tile(0, 0), Tile(n-1, (n-1)))
endTime = System.currentTimeMillis() endTime = System.currentTimeMillis()
val ArrayBackedPathfinderTime = endTime - startTime val MapBackedPathfinderTime = endTime - startTime
//World.scrubDirections(listOf(Directions.FRONTIER, Directions.INPATH, Directions.NOPATH)) World.tiles.scrubDirections(listOf(Directions.FRONTIER, Directions.INPATH, Directions.NOPATH))
startTime = System.currentTimeMillis() startTime = System.currentTimeMillis()
//MapBackedPathfinder.generatePath(Tile(0, 0), Tile(n-1, (n-1))) ArrayBackedPathfinder.generatePath(Tile(0, 0), Tile(n-1, (n-1)))
endTime = System.currentTimeMillis() endTime = System.currentTimeMillis()
val MapBackedPathfinderTime = endTime - startTime val ArrayBackedPathfinderTime = endTime - startTime
println(World.toString()) println(World.toString())
println(n*n) println(n*n)
@ -38,7 +40,7 @@ class HierarchicalPathfinding {
//Clear the maze of pathfinding markers before running another pathfinding algorithm //Clear the maze of pathfinding markers before running another pathfinding algorithm
fun clearMaze() { fun clearMaze() {
World.scrubDirections(listOf(Directions.FRONTIER, Directions.INPATH)) World.tiles.scrubDirections(listOf(Directions.FRONTIER, Directions.INPATH))
} }
fun buildMaze(n: Int) { fun buildMaze(n: Int) {
@ -53,6 +55,9 @@ class HierarchicalPathfinding {
println(World.toString()) println(World.toString())
println(e.message) println(e.message)
} }
} }
} }
} }

@ -46,7 +46,7 @@ object MapBackedPathfinder {
//Otherwise, the tile has been reached and this path is not better, so carry on //Otherwise, the tile has been reached and this path is not better, so carry on
gVals.put(candidateTile, currentG + 1) gVals.put(candidateTile, currentG + 1)
frontier.insert(candidateTile) frontier.insert(candidateTile)
World.update(candidateTile, candidateTile.getProperties() +Directions.HMFRONTIER) World.update(candidateTile, candidateTile.getProperties() +Directions.FRONTIER)
} }
} }
} while( current != end) } while( current != end)
@ -70,7 +70,7 @@ object MapBackedPathfinder {
var lowestG = Int.MAX_VALUE var lowestG = Int.MAX_VALUE
var lowestCost = end var lowestCost = end
while(current != start) { while(current != start) {
World.update(current, current.getProperties() + Directions.HMINPATH) World.update(current, current.getProperties() + Directions.INPATH)
current.getConnections().forEach { candidateTile -> current.getConnections().forEach { candidateTile ->
val candidateG = gVals.get(candidateTile) ?: -1 val candidateG = gVals.get(candidateTile) ?: -1
if(candidateTile.isInBounds() && candidateG != -1 && candidateG < lowestG ) { if(candidateTile.isInBounds() && candidateG != -1 && candidateG < lowestG ) {

@ -1,12 +1,10 @@
package technology.zim package technology.zim
import technology.zim.data.Directions
import technology.zim.data.Tile import technology.zim.data.Tile
import technology.zim.data.TileProperties import technology.zim.data.TileProperties
import technology.zim.data.WorldData import technology.zim.data.WorldData
import java.util.* import java.util.*
import technology.zim.data.Directions.* import technology.zim.data.Directions.*
import technology.zim.data.TileNavigatedArray
//Singleton object containing a set of tiles //Singleton object containing a set of tiles
//Has helper functions included //Has helper functions included
@ -21,7 +19,7 @@ import technology.zim.data.TileNavigatedArray
object World { object World {
//Default size should be 10 //Default size should be 10
val tiles = TileNavigatedArray<TileProperties>() val tiles = WorldData(ArrayList<ArrayList<TileProperties>>())
var sizeX = 10 //Default size var sizeX = 10 //Default size
var sizeY = 10 var sizeY = 10
const val ANSI_RESET = "\u001B[0m" const val ANSI_RESET = "\u001B[0m"
@ -36,11 +34,11 @@ object World {
fun update(tile: Tile, to: TileProperties) { fun update(tile: Tile, to: TileProperties) {
tiles.set(tile, to) tiles.value[tile.x()][tile.y()] = to
} }
fun get(tile: Tile): TileProperties { fun get(tile: Tile): TileProperties {
return tiles.get(tile)?: TileProperties(0) return tiles.value[tile.x()][tile.y()]
} }
//Returns a coordinate pair //Returns a coordinate pair
@ -51,29 +49,20 @@ object World {
fun setSize(x: Int, y: Int) { fun setSize(x: Int, y: Int) {
sizeX = x sizeX = x
sizeY = y sizeY = y
tiles.resize(x, y) tiles.setSize(x, y)
} }
//Accepts a list of directions, removes those directions from every TileProperties in WorldData //TODO: https://en.wikipedia.org/wiki/Box-drawing_characters
fun scrubDirections(rem: List<Directions>) { //^ make the maze look like a maze
var mask = rem.fold(0) { sum, element -> sum + element.dir}
mask = mask.inv()
tiles.forEachIndexed {
index, tile ->
tiles[index] = TileProperties((tile?:TileProperties(0)).connections and(mask))
}
}
override fun toString(): String { override fun toString(): String {
val str = StringBuilder() val str = StringBuilder()
var inPath = false var inPath = false
var checked = false var checked = false
//Reading left to right, top to bottom - can do a simple for each on the rows //Reading left to right, top to bottom - can do a simple for each on the rows
for (y in 0..sizeY - 1) { for (y in 0..tiles.value[0].size - 1) {
//Upper line: Print each tile, print right-hand connections //Upper line: Print each tile, print right-hand connections
for (x in 0..sizeX - 1) { for (x in 0..tiles.value[0].size - 1) {
if(get(Tile(x, y)).connections and(INPATH.dir) != 0) if(get(Tile(x, y)).connections and(INPATH.dir) != 0)
inPath = true inPath = true
else if (get(Tile(x, y)).connections and(FRONTIER.dir) != 0) else if (get(Tile(x, y)).connections and(FRONTIER.dir) != 0)

@ -7,13 +7,9 @@ enum class Directions(val dir: Int) {
LEFT(4), LEFT(4),
RIGHT(8), RIGHT(8),
MANIFEST(16),//Was added to MazeFinder's frontier, but the pathfinder has a frontier so this is named something other than frontier MANIFEST(16),//Was added to MazeFinder's frontier, but the pathfinder has a frontier so this is named something other than frontier
FRONTIER(32), //Added to the ArrayBackedPathfinder's frontier FRONTIER(32), //Added to the pathfinder's frontier
INPATH(64), //Chosen by the ArrayBackedPathfinder INPATH(64), //Chosen by the pathfinder
HMFRONTIER(128), //Tile is not a viable path NOPATH(128) //Tile is not a viable path
HMINPATH(256),
BFSFRONTIER(512),
BFSINPATH(1024),
; ;
companion object { companion object {

@ -18,11 +18,11 @@ class TileNavigatedArray<T>(val data: ArrayList<T?> = ArrayList<T?>(), var colSi
} }
} }
//TODO: Test resize properly
fun resize(colSize:Int, rowSize:Int) { fun resize(colSize:Int, rowSize:Int) {
this.colSize = colSize this.colSize = colSize
this.rowSize = rowSize this.rowSize = rowSize
data.clear() for(i in data.size..colSize*rowSize-data.size) {
for(i in 0..rowSize*colSize){
data.add(null) data.add(null)
} }
assert(data.size == this.colSize*this.rowSize) assert(data.size == this.colSize*this.rowSize)

@ -1,9 +1,23 @@
package technology.zim.data package technology.zim.data
//Data structure wrapper for a set of tiles //Data structure wrapper for a set of tiles
//Todo: Convert to TileNavigatedArray //Todo: Test converting to a single row-major array, multiplying the y value by row size to find the element position
class WorldData(val value: ArrayList<ArrayList<TileProperties>>) { @JvmInline
value class WorldData(val value: ArrayList<ArrayList<TileProperties>>) {
//Accepts a list of directions, removes those directions from every TileProperties in WorldData
fun scrubDirections(rem: List<Directions>) {
var mask = rem.fold(0) { sum, element -> sum + element.dir}
mask = mask.inv()
value.forEachIndexed {
x, row ->
row.forEachIndexed {
y, tile ->
value[x][y] = TileProperties(tile.connections and(mask))
}
}
}
fun setSize(xmin : Int, ymin : Int) { fun setSize(xmin : Int, ymin : Int) {
val emptyTile = TileProperties(0) val emptyTile = TileProperties(0)
@ -21,5 +35,7 @@ class WorldData(val value: ArrayList<ArrayList<TileProperties>>) {
x.add(emptyTile) x.add(emptyTile)
} }
} }
//println("WorldData now sized at: " + value.size + ", " + value[0].size)
} }
} }

@ -13,41 +13,46 @@ class MazeFinderTest {
MazeFinder.primsAlgorithm() MazeFinder.primsAlgorithm()
} }
//Top row must have at least one south connection and no north connections
@Test @Test
fun topRowOutOfBoundsCheck() { fun topRowOutOfBoundsCheck() {
var southExists = false var southExists = false
for(x in 0..<World.sizeX) { World.tiles.value.forEach {
assert(!World.get(Tile(x, 0)).isNorth()) col ->
if(World.get(Tile(x, 0)).isSouth()) if(col[0].isSouth())
southExists = true southExists = true
} }
assert(southExists) assert(southExists)
} }
//Bottom row cannot have any south connections
@Test @Test
fun bottomRowOutOfBoundsCheck() { fun bottomRowOutOfBoundsCheck() {
for(x in 0..<World.sizeX) { var southNotExists = true
assert(!World.get(Tile(x, World.sizeY-1)).isSouth()) World.tiles.value.forEach {
col ->
if(col[9].isSouth())
southNotExists = false
} }
assert(southNotExists)
} }
@Test @Test
fun allTilesVisited() { fun allTilesVisited() {
for(x in 0..<World.sizeX) { World.tiles.value.forEach {
for(y in 0..<World.sizeY) { col ->
assert(World.get(Tile(x, y)).visited()) col.forEach {
tile ->
assert(tile.visited())
} }
} }
} }
@Test @Test
fun allTilesHaveConnections() { fun allTilesHaveConnections() {
for(x in 0..<World.sizeX) { World.tiles.value.forEachIndexed {
for(y in 0..<World.sizeY) { x,
val tileprop = World.get(Tile(x, y)) col ->
col.forEachIndexed { y,
tileprop ->
if(!tileprop.visited()) if(!tileprop.visited())
println("Empty Connections at: " + Tile(x, y).toString()) println("Empty Connections at: " + Tile(x, y).toString())
assert(tileprop.visited()) assert(tileprop.visited())

@ -1,24 +1,18 @@
import technology.zim.ArrayBackedPathfinder
import technology.zim.data.Tile import technology.zim.data.Tile
import technology.zim.data.TileHeap import technology.zim.data.TileHeap
import kotlin.math.abs
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.BeforeTest import kotlin.test.BeforeTest
//TODO: update to use Tile adjustment to heap
class TileHeapTest { class TileHeapTest {
companion object { companion object {
var gVals = HashMap<Tile, Int>() var gVals = HashMap<Tile, Int>()
var heap = TileHeap(Tile(20, 20), this::fValue) var heap = TileHeap(Tile(20, 20), gVals)
private fun fValue(prospect: Tile, end: Tile): Int {
return hValue(prospect, end).plus(gVals.get(prospect) ?: 0)
}
private fun hValue(prospect: Tile, end:Tile): Int {
return abs(prospect.x() - end.x()) + abs(prospect.y() - end.y())
}
} }
@BeforeTest @BeforeTest
fun setUp() { fun setUp() {
heap = TileHeap(Tile(20, 20), Companion::fValue) heap = TileHeap(Tile(20, 20), gVals)
arrayOf(Tile(0, 0), Tile(1, 1), Tile(5, 5), Tile(4, 4), Tile(19, 19), Tile(2, 2)).forEach { item -> arrayOf(Tile(0, 0), Tile(1, 1), Tile(5, 5), Tile(4, 4), Tile(19, 19), Tile(2, 2)).forEach { item ->
heap.insert(item) heap.insert(item)
} }

Loading…
Cancel
Save