From 479dc5c3fbb5f48e4d4a32106dceeef1dbe7d7fc Mon Sep 17 00:00:00 2001 From: minjaesong Date: Fri, 13 Aug 2021 14:39:20 +0900 Subject: [PATCH] new and sane graph traversal; only works for first of the multiple wires tho --- .../torvald/terrarum/gameworld/GameWorld.kt | 24 ++-- .../terrarum/gameworld/WorldSimulator.kt | 124 ++++++------------ .../gameactors/FixtureLogicSignalEmitter.kt | 8 +- 3 files changed, 56 insertions(+), 100 deletions(-) diff --git a/src/net/torvald/terrarum/gameworld/GameWorld.kt b/src/net/torvald/terrarum/gameworld/GameWorld.kt index 73f6eb964..597d4c087 100644 --- a/src/net/torvald/terrarum/gameworld/GameWorld.kt +++ b/src/net/torvald/terrarum/gameworld/GameWorld.kt @@ -78,8 +78,8 @@ open class GameWorld : Disposable { private val wirings: HashMap private val wiringGraph = HashMap>() - private val WIRE_POS_MAP = byteArrayOf(1,2,4,8) - private val WIRE_ANTIPOS_MAP = byteArrayOf(4,8,1,2) + private val WIRE_POS_MAP = intArrayOf(1,2,4,8) + private val WIRE_ANTIPOS_MAP = intArrayOf(4,8,1,2) /** * Used by the renderer. When wirings are updated, `wirings` and this properties must be synchronised. @@ -334,11 +334,11 @@ open class GameWorld : Disposable { // figure out wiring graphs val matchingNeighbours = WireActor.WIRE_NEARBY.mapIndexed { index, (tx, ty) -> (getAllWiresFrom(x + tx, y + ty)?.contains(tile) == true).toInt() shl index - }.sum().toByte() + }.sum() // setup graph of mine setWireGraphOfUnsafe(blockAddr, tile, matchingNeighbours) // setup graph for neighbours - for (i in 0.toByte() .. 3.toByte()) { + for (i in 0..3) { if (matchingNeighbours and WIRE_POS_MAP[i] > 0) { val (tx, ty) = WireActor.WIRE_NEARBY[i] val old = getWireGraphOf(x + tx, y + ty, tile) ?: 0 @@ -347,13 +347,13 @@ open class GameWorld : Disposable { } } - fun getWireGraphOf(x: Int, y: Int, itemID: ItemID): Byte? { + fun getWireGraphOf(x: Int, y: Int, itemID: ItemID): Int? { val (x, y) = coerceXY(x, y) val blockAddr = LandUtil.getBlockAddr(this, x, y) return getWireGraphUnsafe(blockAddr, itemID) } - fun getWireGraphUnsafe(blockAddr: BlockAddress, itemID: ItemID): Byte? { + fun getWireGraphUnsafe(blockAddr: BlockAddress, itemID: ItemID): Int? { return wiringGraph[blockAddr]?.get(itemID)?.connections } @@ -377,19 +377,19 @@ open class GameWorld : Disposable { return wiringGraph[blockAddr]?.get(itemID)?.recvStates } - fun setWireGraphOf(x: Int, y: Int, itemID: ItemID, byte: Byte) { + fun setWireGraphOf(x: Int, y: Int, itemID: ItemID, cnx: Int) { val (x, y) = coerceXY(x, y) val blockAddr = LandUtil.getBlockAddr(this, x, y) - return setWireGraphOfUnsafe(blockAddr, itemID, byte) + return setWireGraphOfUnsafe(blockAddr, itemID, cnx) } - fun setWireGraphOfUnsafe(blockAddr: BlockAddress, itemID: ItemID, byte: Byte) { + fun setWireGraphOfUnsafe(blockAddr: BlockAddress, itemID: ItemID, cnx: Int) { if (wiringGraph[blockAddr] == null) wiringGraph[blockAddr] = HashMap() if (wiringGraph[blockAddr]!![itemID] == null) - wiringGraph[blockAddr]!![itemID] = WiringSimCell(byte) + wiringGraph[blockAddr]!![itemID] = WiringSimCell(cnx) - wiringGraph[blockAddr]!![itemID]!!.connections = byte + wiringGraph[blockAddr]!![itemID]!!.connections = cnx } fun setWireEmitStateOf(x: Int, y: Int, itemID: ItemID, vector: Vector2) { @@ -647,7 +647,7 @@ open class GameWorld : Disposable { * These values must be updated by none other than [WorldSimulator]() */ data class WiringSimCell( - var connections: Byte = 0, // connections + var connections: Int = 0, // connections var emitState: Vector2 = Vector2(0.0, 0.0), // i'm emitting this much power var recvStates: ArrayList = ArrayList() // how far away are the power sources ) diff --git a/src/net/torvald/terrarum/gameworld/WorldSimulator.kt b/src/net/torvald/terrarum/gameworld/WorldSimulator.kt index 54627d33d..506f26598 100644 --- a/src/net/torvald/terrarum/gameworld/WorldSimulator.kt +++ b/src/net/torvald/terrarum/gameworld/WorldSimulator.kt @@ -1,6 +1,7 @@ package net.torvald.terrarum.gameworld import com.badlogic.gdx.Input +import com.badlogic.gdx.utils.Queue import net.torvald.terrarum.* import net.torvald.terrarum.AppLoader.printdbg import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE @@ -21,6 +22,7 @@ import org.dyn4j.geometry.Vector2 import org.khelekore.prtree.* import java.util.* import kotlin.collections.ArrayList +import kotlin.experimental.and import kotlin.math.roundToInt /** @@ -480,105 +482,55 @@ object WorldSimulator { } private fun traverseWireGraph(world: GameWorld, fixture: FixtureBase, wireType: String, bbi: BlockBoxIndex) { - fixture.worldBlockPos?.let { sourceBlockPos -> - val branchesVisited = ArrayList() - val points = ArrayList() // a queue, enqueued at the end - var point = WireGraphCursor(sourceBlockPos + fixture.blockBoxIndexToPoint2i(bbi)) - var terminate = false - fun dequeue() { - //printdbg(this, "points before deq: $points") - points.removeAt(0) - //printdbg(this, "points after deq: $points") - if (points.size == 0) terminate = true - else { - point = points[0] - //printdbg(this, "new point: $point") - } + fun getAdjacent(cnx: Int, point: WireGraphCursor): List { + val r = ArrayList() + for (dir in intArrayOf(RIGHT,DOWN,LEFT,UP)) { + if (cnx and dir != 0) r.add(point.copy().moveOneCell(dir)) } + return r + } - points.add(point.copy()) + fixture.worldBlockPos?.let { sourceBlockPos -> - while (points.isNotEmpty() && !terminate) { - // break if there are no available wires underneath - if (world.getAllWiresFrom(point.x, point.y)?.filter { WireCodex[it].accepts == wireType }?.isEmpty() != false) - break + val signal = (fixture as Electric).wireEmission[bbi] ?: Vector2(0.0, 0.0) + var point = WireGraphCursor(sourceBlockPos + fixture.blockBoxIndexToPoint2i(bbi)) - //printdbg(this, branchesVisited) + world.getAllWiresFrom(point.x, point.y)?.filter { WireCodex[it].accepts == wireType }?.forEach { wire -> + // this makes sure that only the emitters with wires installed will get traversed + world.getWireGraphOf(point.x, point.y, wire)?.let { _ -> + printdbg(this, wire) + + val points = Queue() // a queue, enqueued at the end + var marked = HashSet() - // get all wires that matches 'accepts' (such as Red/Green/Blue wire) and propagate signal for each of them - world.getAllWiresFrom(point.x, point.y)?.filter { WireCodex[it].accepts == wireType }?.forEach { wire -> + fun mark(point: WireGraphCursor) { + marked.add(point.longHash()) + // do some signal action + world.setWireEmitStateOf(point.x, point.y, wire, signal) + } - world.getAllWiringGraph(point.x, point.y)?.get(wire)?.let { node -> + fun isMarked(point: WireGraphCursor) = marked.contains(point.longHash()) + fun enq(point: WireGraphCursor) = points.addFirst(point.copy()) + fun deq() = points.removeLast() - val signal = Vector2(1.0, 0.0)//node.emitState // FIXME branching wires somehow fetches wrong signal value - val cnx = node.connections.toInt() // 1-15 - val nextDirBit = cnx and (15 - point.fromWhere) // cnx minus where the old cursur was; also 1-15 - //printdbg(this, "(${point.x}, ${point.y}) from ${point.fromWhere} to $nextDirBit") + enq(point) + mark(point) - // mark current position as visited - branchesVisited.add(point.copy()) - - // termination condition 1 - if (branchesVisited.linearSearch { it.x == point.x && it.y == point.y }!! < branchesVisited.lastIndex) { - //printdbg(this, "(${point.x}, ${point.y}) was already visited") - dequeue() - } - else { - - when (nextDirBit.bitCount()) { - in 5..64 -> { throw IllegalArgumentException("Bad nextDirBit: $nextDirBit") } - // nowhere to go - 0 -> { - dequeue() - } - // only one direction to go - 1 -> { - // move the "cursor" - point.moveOneCell(nextDirBit) - - // propagate the signal to next position - world.setWireEmitStateOf(point.x, point.y, wire, signal) - } - // two, three or four (starting point only) directions to go - else -> { - // mark this branch - //branchesVisited.add(point.copy()) - - // spawn and move the "cursor" by try right, down, left then up - val movableDirs = IntArrayStack(4) - for (s in 3 downTo 0) { // so that top of the stack holds lowest of the direction-number - (nextDirBit and (1 shl s)).let { - if (it != 0) movableDirs.push(it) - } - } - //printdbg(this, movableDirs.depth) // stack size is correct..? - // take top of the stack as mine - val mine = movableDirs.pop() - - while (movableDirs.isNotEmpty()) { - // obviously 'point' must not me altered beforehand - movableDirs.pop().let { dir -> - points.add(point.copy().moveOneCell(dir)) - } - } - //printdbg(this, points) // and points are also correct... - - // finally move the cursor of mine - point.moveOneCell(mine) - - // propagate the signal to next position - world.setWireEmitStateOf(point.x, point.y, wire, signal) - } + while (points.notEmpty()) { + point = deq() + // TODO if we found a power receiver, do something to it + for (x in getAdjacent(world.getWireGraphOf(point.x, point.y, wire)!!, point)) { + if (!isMarked(x)) { + mark(x) + enq(x) } } } } + } - //printdbg(this, "Point = $point") - } // end While - - //printdbg(this, "------------------------------------------") + printdbg(this, "------------------------------------------") } } @@ -616,5 +568,7 @@ object WorldSimulator { return this } + + fun longHash() = x.toLong().shl(32) or y.toLong() } } \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureLogicSignalEmitter.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureLogicSignalEmitter.kt index 75ae89c9d..710cf8a85 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureLogicSignalEmitter.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureLogicSignalEmitter.kt @@ -35,11 +35,13 @@ class FixtureLogicSignalEmitter(nameFun: () -> String) override fun update(delta: Float) { // set emit - worldBlockPos?.let { (x, y) -> + /*worldBlockPos?.let { (x, y) -> WireCodex.getAll().filter { it.accepts == "digital_bit" }.forEach { prop -> - world?.setWireEmitStateOf(x, y, prop.id, wireEmission[0]!!) + // only set a state of wire that actually exists on the world + if (world?.getWireGraphOf(x, y, prop.id) != null) + world?.setWireEmitStateOf(x, y, prop.id, wireEmission[0]!!) } - } + }*/ } }