diff --git a/.idea/libraries/it_unimi_dsi_dsiutils.xml b/.idea/libraries/it_unimi_dsi_dsiutils.xml
new file mode 100644
index 0000000..d99c635
--- /dev/null
+++ b/.idea/libraries/it_unimi_dsi_dsiutils.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 98a458a..8d6b0e1 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -5,13 +5,15 @@
-
-
+
+
+
+
diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt
index 1aa8f33..852f3f5 100644
--- a/src/main/kotlin/Main.kt
+++ b/src/main/kotlin/Main.kt
@@ -14,7 +14,7 @@ class HierarchicalPathfinding {
companion object {
@JvmStatic
fun main(args: Array) {
- val n = 5000
+ val n = 10
println("Building world")
World.setSize(n, n)
println("Start")
diff --git a/src/main/kotlin/MazeFinder.kt b/src/main/kotlin/MazeFinder.kt
index 2459f69..9aefb90 100644
--- a/src/main/kotlin/MazeFinder.kt
+++ b/src/main/kotlin/MazeFinder.kt
@@ -31,31 +31,37 @@ object MazeFinder {
+
+ //Prime the graph with the first connection, which marks the first visited Tiles
startingTile = World.getRandomLocation()
- startingTile.getProperties().visited = true
+ val tmpIndex = randGen.nextInt(Directions.ALL.size)
+ startingTile.connect(Directions.ALL.get(tmpIndex))
//Choose an arbitrary vertex from G (the graph), and add it to some (initially empty) set V.
frontier.addAll(startingTile.getAdjacentTiles(false))
-
+ frontier.addAll((startingTile + Directions.ALL.get(tmpIndex)).getAdjacentTiles(false))
//Choose a random edge from G, that connects a vertex in V with another vertex not in V.
var current: Tile
- var inGraph: Pair
- var adjacentExplored: Set>
+ var inGraph: Tile
+ var adjacentExplored: Set
while(frontier.isNotEmpty()) {
//Grab a random tile from the frontier, mark it as visited
val random = randGen.nextInt(frontier.size())
- current = frontier.data[random]
- current.getProperties().visited = true
+ current = frontier.data.get(random)
+ current.getProperties().visited()
frontier.removeAt(random)
//Find adjacent tiles that are in the graph
- adjacentExplored = current.getAdjacent(true)
+ adjacentExplored = current.getAdjacentTiles(true)
+
+ if(adjacentExplored.isEmpty())
+ println("No explored adjacent tiles found")
//Select a random tile from possibleTiles
inGraph = adjacentExplored.elementAt(randGen.nextInt(adjacentExplored.size))
//Connect the frontier tile to the graph
- current.connect(inGraph.second)
+ current.connect(Directions.convertModifier(current.value - inGraph.value))
//Add current's unexplored tiles to the frontier, if not already in frontier
frontier.addAll(current.getAdjacentTiles(false))
diff --git a/src/main/kotlin/World.kt b/src/main/kotlin/World.kt
index 1dbfd0a..b5cb5a4 100644
--- a/src/main/kotlin/World.kt
+++ b/src/main/kotlin/World.kt
@@ -20,9 +20,13 @@ object World {
//Default size should be 20
val tiles = WorldData(ArrayList>())
+ fun update(tile: Tile, to: TileProperties) {
+ tiles.value[tile.x()][tile.y()] = to
+ }
+
//Returns a coordinate pair
fun getRandomLocation(): Tile {
- return Tile(Pair((0..tiles.value.size-1).random(), (0..tiles.value[0].size-1).random()))
+ return Tile((0..tiles.value.size-1).random(), (0..tiles.value.get(0).size-1).random())
}
fun setSize(x: Int, y: Int) {
@@ -45,7 +49,7 @@ object World {
//Upper line: Print each tile, print right-hand connections
for (x in 0..tiles.value[0].size - 1) {
str.append(fi)
- if(tiles.value[x][y].isEast())
+ if(tiles.value.get(x).get(y).isEast())
str.append(fi)
else
str.append(em)
@@ -55,7 +59,7 @@ object World {
//Lower line: Print downward connections
for (x in 0..tiles.value.size - 1) {
- if(tiles.value[x][y].isSouth()) {
+ if(tiles.value.get(x).get(y).isSouth()) {
str.append(fi)
}
else
diff --git a/src/main/kotlin/data/Directions.kt b/src/main/kotlin/data/Directions.kt
index fc02f80..7230210 100644
--- a/src/main/kotlin/data/Directions.kt
+++ b/src/main/kotlin/data/Directions.kt
@@ -1,40 +1,51 @@
package technology.zim.data
-enum class Directions(val value: Pair) {
- NORTH(Pair(0, -1)) {
- override fun opposite(): Directions {
- return SOUTH
- }
- },
- SOUTH(Pair(0, 1)) {
- override fun opposite(): Directions {
- return NORTH
- }
- },
- EAST(Pair(1, 0)) {
- override fun opposite(): Directions {
- return WEST
- }
- },
- WEST(Pair(-1, 0)) {
- override fun opposite(): Directions {
- return EAST
+enum class Directions(val dir: Int) {
+ NONE(0),
+ UP(1),
+ DOWN(2),
+ LEFT(4),
+ RIGHT(8),
+ VISITED(16)
+
+ ;
+
+ companion object {
+ fun getModifier(dir: Directions): Long {
+ return when(dir) {
+ UP -> NORTH
+ DOWN -> SOUTH
+ LEFT -> WEST
+ RIGHT -> EAST
+ else -> 0L
+ }
}
- };
- //Return the opposite direction
- abstract fun opposite(): Directions
+ fun convertModifier(mod: Long): Directions {
+ return when(mod) {
+ NORTH -> UP
+ SOUTH -> DOWN
+ WEST -> LEFT
+ EAST -> RIGHT
+ else -> NONE
+ }
+ }
- fun x():Int {
- return value.first
+ const val SOUTH: Long = 0x1L
+ const val NORTH: Long = -0x1L
+ const val WEST: Long = -0x100000000L
+ const val EAST: Long = 0x100000000L
+ val ALL = arrayOf(UP, DOWN, LEFT, RIGHT)
+ const val NONEMOD = 0L
}
- fun y():Int {
- return value.second
+ fun inv(): Int {
+ return dir.inv()
}
- companion object {
- val ALL = setOf(NORTH, EAST, WEST, SOUTH)
+ fun opposite(dir: Long): Long {
+ val x = (dir shr 32).toInt() * -1
+ val y = dir.toInt() * -1
+ return x.toLong() shl 32 + y
}
-
}
\ No newline at end of file
diff --git a/src/main/kotlin/data/Tile.kt b/src/main/kotlin/data/Tile.kt
index ad6e5a7..49d76d5 100644
--- a/src/main/kotlin/data/Tile.kt
+++ b/src/main/kotlin/data/Tile.kt
@@ -2,10 +2,21 @@ package technology.zim.data
import technology.zim.World
-class Tile(val value: Pair): Comparable {
+@JvmInline
+value class Tile(val value: Long) {
- constructor(x: Int, y: Int): this(Pair(x, y))
+ constructor(x: Int, y: Int): this(pack(x, y)) {
+ }
+ fun connect(candidateTile: Tile) {
+ val dir = Directions.convertModifier(this.value - candidateTile.value)
+ if(candidateTile.isInBounds()) {
+ World.update(this, getProperties().add(dir))
+ }
+ else {
+ throw(ArrayIndexOutOfBoundsException("Cannot connect to an out of bounds tile"))
+ }
+ }
//Connect two tiles together.
//Calls utility function on the connected cell
fun connect(dir: Directions) {
@@ -14,10 +25,8 @@ class Tile(val value: Pair): Comparable {
//Ensure that the tile is within bounds
if(candidateTile.isInBounds())
{
- this.getProperties().add(dir)
- candidateTile.getProperties().add(dir.opposite())
- candidateTile.getProperties().visited = true
- candidateTile.getProperties().visited = true
+ World.tiles.value[x()][y()] = getProperties().add(dir)
+ World.tiles.value[candidateTile.x()][candidateTile.y()] = candidateTile.getProperties().add(dir)
}
else {
//Shouldn't matter whether we skip connecting an out-of-bounds item
@@ -39,7 +48,7 @@ class Tile(val value: Pair): Comparable {
dirs.forEach { dir ->
val candidateTile = this + dir
//Ensure that the tile is within bounds
- if(candidateTile.isInBounds() && candidateTile.getProperties().visited == explored)
+ if(candidateTile.isInBounds() && candidateTile.getProperties().visited() == explored)
{
adj.add(candidateTile)
}
@@ -47,95 +56,72 @@ class Tile(val value: Pair): Comparable {
return adj
}
- fun getAdjacent(explored:Boolean): Set> {
- val adj = mutableSetOf>()
- val dirs = Directions.ALL
-
- dirs.forEach { dir ->
- val candidateTile = this + dir
- //Ensure that the tile is within bounds
- if(candidateTile.isInBounds() && candidateTile.getProperties().visited == explored)
- {
- adj.add(Pair(candidateTile, dir))
- }
- }
- return adj
- }
fun hasConnections(): Boolean {
- return getProperties().connections.isNotEmpty()
+ return getProperties().connections != 0
}
- //Gets Tile objects for all connected directions
- //Used for finding a path through the maze
- fun getConnections(): ArrayList {
- val listTiles = ArrayList()
- for (dir: Directions in getProperties().connections) {
- //Use the ghost of linear algebra to identify potential neighbor tiles
- listTiles.add(this+dir)
- }
- return listTiles
- }
-
//Arguments could be made for either World or Tile knowing whether a Tile is in bounds
fun isInBounds(): Boolean {
return x() >= 0 &&
y() >= 0 &&
x() < World.tiles.value.size &&
- y() < World.tiles.value[0].size
+ y() < World.tiles.value.get(0).size
}
//Get the properties of the tile at the given coordinates
fun getProperties(): TileProperties {
- return World.tiles.value[x()][y()]
+ return World.tiles.value.get(x()).get(y())
}
//Get tile at given direction
operator fun plus(dir: Directions): Tile {
- return Tile(x() + dir.x(), y() + dir.y())
+ return Tile(x() + x(dir), y() + y(dir))
}
//Get tile at direction opposite of given direction
- operator fun minus(dir: Directions): Tile {
- return Tile(x() - dir.x(), y() - dir.y())
+ operator fun minus(dir: Long): Tile {
+ return Tile(x() - x(dir), y() - y(dir))
}
fun x(): Int {
- return value.first
+ return (value shr 32).toInt()
}
- fun y():Int {
- return value.second
+ //Gets the x value of the given coordinate form
+ fun x(coord: Long):Int {
+ return (coord shr 32).toInt()
}
- override fun toString():String {
- return "<" + value.first + ", " + value.second + ">"
+ //Gets the x value of the coordinate form of the given direction
+ fun x(dir: Directions):Int {
+ return (Directions.getModifier(dir) shr 32).toInt()
}
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (other !is Tile) return false
+ fun y():Int {
+ return value.toInt()
+ }
- if (x() != other.x() || y() != other.y()) return false
+ //Gets the y value of the given coordinate form
+ fun y(coord: Long):Int {
+ return coord.toInt()
+ }
- return true
+ //Gets the y value of the coordinate form of the given direction
+ fun y(dir: Directions):Int {
+ return y() + Directions.getModifier(dir).toInt()
}
- override fun compareTo(other: Tile): Int {
- if(x() == other.x() && y() == other.y())
- return 0
- else if(y() > other.y())
- return 1
- else if(x() > other.x())
- return 1
- else
- return -1
+ override fun toString():String {
+ return "<" + x() + ", " + y() + ">"
}
- override fun hashCode(): Int {
- return value.hashCode()
+ companion object {
+ fun pack(x: Int, y: Int):Long {
+ return (x.toLong() shl 32) + y.toLong()
+ }
}
}
\ No newline at end of file
diff --git a/src/main/kotlin/data/TileProperties.kt b/src/main/kotlin/data/TileProperties.kt
index 6b3c56a..cfe2c38 100644
--- a/src/main/kotlin/data/TileProperties.kt
+++ b/src/main/kotlin/data/TileProperties.kt
@@ -1,5 +1,8 @@
package technology.zim.data
+import technology.zim.data.Directions.*
+
+
//Data holder for a Tile
//Should contain a mutable set of connected directions
//Later, can hold jumps to other locations other such fancy features
@@ -8,35 +11,53 @@ package technology.zim.data
-/*@JvmInline
-value */
-class TileProperties(val connections:MutableSet = mutableSetOf()) {
- var visited = false
+@JvmInline
+value class TileProperties(val connections: Int) {
+
+ fun visited(): Boolean {
+ return connections != 0
+ }
+
//Remove a direction from the list of connections
- fun remove(dir: Directions) {
- connections.remove(dir)
+ fun remove(dir: Directions): TileProperties {
+ return TileProperties(connections and(dir.inv()))
}
//Add a direction to the list of connections
//Should only be accessed by the Tile class
- fun add(dir: Directions) {
- connections.add(dir)
+ fun add(dir: Directions): TileProperties {
+ return TileProperties(connections or(dir.dir))
}
fun isWest(): Boolean {
- return connections.contains(Directions.WEST)
+ return connections and(LEFT.dir) != 0
}
fun isEast():Boolean {
- return connections.contains(Directions.EAST)
+ return connections and(RIGHT.dir) != 0
}
fun isNorth(): Boolean {
- return connections.contains(Directions.NORTH)
+ return connections and(UP.dir) != 0
}
fun isSouth():Boolean {
- return connections.contains(Directions.SOUTH)
+ return connections and(DOWN.dir) != 0
+ }
+
+ infix fun and(dir: Directions): TileProperties {
+ return TileProperties(connections and(dir.dir))
+ }
+
+ infix fun and(dir: Int): TileProperties {
+ return TileProperties(connections and(dir))
}
-}
+ infix fun or(dir: Directions): TileProperties {
+ return TileProperties(connections or(dir.dir))
+ }
+
+ infix fun or(dir: Int): TileProperties {
+ return TileProperties(connections or(dir))
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/data/WorldData.kt b/src/main/kotlin/data/WorldData.kt
index 4129378..8287e5c 100644
--- a/src/main/kotlin/data/WorldData.kt
+++ b/src/main/kotlin/data/WorldData.kt
@@ -2,7 +2,6 @@ package technology.zim.data
//Data structure wrapper for a set of tiles
-
@JvmInline
value class WorldData(val value: ArrayList>) {
fun setSize(xmin : Int, ymin : Int) {
@@ -17,10 +16,14 @@ value class WorldData(val value: ArrayList>) {
//Fill every row with TileProperties
for(x in value) {
for(i in 0..ymin-1) {
- x.add(TileProperties())
+ x.add(emptyTile)
}
}
//println("WorldData now sized at: " + value.size + ", " + value[0].size)
}
+
+ companion object {
+ val emptyTile = TileProperties(0)
+ }
}
diff --git a/src/test/kotlin/TileTest.kt b/src/test/kotlin/TileTest.kt
index d248977..0d47766 100644
--- a/src/test/kotlin/TileTest.kt
+++ b/src/test/kotlin/TileTest.kt
@@ -15,10 +15,10 @@ class TileTest {
}
@Test
- fun connectBottomToSouthInvalid() {
+ fun connectOutOfBoundsInvalid() {
val bottom = Tile(1, 9)
- bottom.connect(SOUTH)
- assertFalse(bottom.getConnections().contains(bottom+SOUTH))
+ bottom.connect(DOWN)
+ assertFalse(bottom.getProperties().isSouth())
}
@Test
@@ -40,18 +40,13 @@ class TileTest {
assertFalse(tooSouth.isInBounds())
}
- @Test
- fun allDirectionsTest() {
- val dirs = NORTH.all()
- assert(dirs.containsAll(listOf(NORTH, EAST, SOUTH, WEST)))
- }
@Test
fun connectNorthSouthTest() {
//Assumes the world is at least 10, 10 in size as set up by this test class
val someTile = Tile(5, 5)
- someTile.connect(NORTH)
- val northTile = someTile + NORTH
+ someTile.connect(UP)
+ val northTile = someTile + UP
assert(someTile.getProperties().isNorth())
assertFalse(someTile.getProperties().isSouth())
@@ -67,16 +62,16 @@ class TileTest {
@Test
fun adjacentTest() {
val someTile = Tile(1, 1)
- val northTile = someTile + NORTH
- val southTile = someTile + SOUTH
- val westTile = someTile + WEST
- val eastTile = someTile + EAST
+ val northTile = someTile + UP
+ val southTile = someTile + DOWN
+ val westTile = someTile + LEFT
+ val eastTile = someTile + RIGHT
//Generating raw tiles without built-in bounds checking, do it here
if(northTile.isInBounds())
- northTile.getProperties().visited = true
+ northTile.getProperties().visited()
if(southTile.isInBounds())
- southTile.getProperties().visited = true
+ southTile.getProperties().visited()
val adjacent = someTile.getAdjacentTiles(false)