new and sane graph traversal; only works for first of the multiple wires tho

This commit is contained in:
minjaesong
2021-08-13 14:39:20 +09:00
parent a6d082fb7d
commit 479dc5c3fb
3 changed files with 56 additions and 100 deletions

View File

@@ -78,8 +78,8 @@ open class GameWorld : Disposable {
private val wirings: HashMap<BlockAddress, WiringNode> private val wirings: HashMap<BlockAddress, WiringNode>
private val wiringGraph = HashMap<BlockAddress, HashMap<ItemID, WiringSimCell>>() private val wiringGraph = HashMap<BlockAddress, HashMap<ItemID, WiringSimCell>>()
private val WIRE_POS_MAP = byteArrayOf(1,2,4,8) private val WIRE_POS_MAP = intArrayOf(1,2,4,8)
private val WIRE_ANTIPOS_MAP = byteArrayOf(4,8,1,2) 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. * 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 // figure out wiring graphs
val matchingNeighbours = WireActor.WIRE_NEARBY.mapIndexed { index, (tx, ty) -> val matchingNeighbours = WireActor.WIRE_NEARBY.mapIndexed { index, (tx, ty) ->
(getAllWiresFrom(x + tx, y + ty)?.contains(tile) == true).toInt() shl index (getAllWiresFrom(x + tx, y + ty)?.contains(tile) == true).toInt() shl index
}.sum().toByte() }.sum()
// setup graph of mine // setup graph of mine
setWireGraphOfUnsafe(blockAddr, tile, matchingNeighbours) setWireGraphOfUnsafe(blockAddr, tile, matchingNeighbours)
// setup graph for neighbours // setup graph for neighbours
for (i in 0.toByte() .. 3.toByte()) { for (i in 0..3) {
if (matchingNeighbours and WIRE_POS_MAP[i] > 0) { if (matchingNeighbours and WIRE_POS_MAP[i] > 0) {
val (tx, ty) = WireActor.WIRE_NEARBY[i] val (tx, ty) = WireActor.WIRE_NEARBY[i]
val old = getWireGraphOf(x + tx, y + ty, tile) ?: 0 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 (x, y) = coerceXY(x, y)
val blockAddr = LandUtil.getBlockAddr(this, x, y) val blockAddr = LandUtil.getBlockAddr(this, x, y)
return getWireGraphUnsafe(blockAddr, itemID) return getWireGraphUnsafe(blockAddr, itemID)
} }
fun getWireGraphUnsafe(blockAddr: BlockAddress, itemID: ItemID): Byte? { fun getWireGraphUnsafe(blockAddr: BlockAddress, itemID: ItemID): Int? {
return wiringGraph[blockAddr]?.get(itemID)?.connections return wiringGraph[blockAddr]?.get(itemID)?.connections
} }
@@ -377,19 +377,19 @@ open class GameWorld : Disposable {
return wiringGraph[blockAddr]?.get(itemID)?.recvStates 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 (x, y) = coerceXY(x, y)
val blockAddr = LandUtil.getBlockAddr(this, 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) if (wiringGraph[blockAddr] == null)
wiringGraph[blockAddr] = HashMap() wiringGraph[blockAddr] = HashMap()
if (wiringGraph[blockAddr]!![itemID] == null) 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) { 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]() * These values must be updated by none other than [WorldSimulator]()
*/ */
data class WiringSimCell( 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 emitState: Vector2 = Vector2(0.0, 0.0), // i'm emitting this much power
var recvStates: ArrayList<WireRecvState> = ArrayList() // how far away are the power sources var recvStates: ArrayList<WireRecvState> = ArrayList() // how far away are the power sources
) )

View File

@@ -1,6 +1,7 @@
package net.torvald.terrarum.gameworld package net.torvald.terrarum.gameworld
import com.badlogic.gdx.Input import com.badlogic.gdx.Input
import com.badlogic.gdx.utils.Queue
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.AppLoader.printdbg import net.torvald.terrarum.AppLoader.printdbg
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
@@ -21,6 +22,7 @@ import org.dyn4j.geometry.Vector2
import org.khelekore.prtree.* import org.khelekore.prtree.*
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.experimental.and
import kotlin.math.roundToInt import kotlin.math.roundToInt
/** /**
@@ -480,105 +482,55 @@ object WorldSimulator {
} }
private fun traverseWireGraph(world: GameWorld, fixture: FixtureBase, wireType: String, bbi: BlockBoxIndex) { private fun traverseWireGraph(world: GameWorld, fixture: FixtureBase, wireType: String, bbi: BlockBoxIndex) {
fixture.worldBlockPos?.let { sourceBlockPos ->
val branchesVisited = ArrayList<WireGraphCursor>()
val points = ArrayList<WireGraphCursor>() // a queue, enqueued at the end
var point = WireGraphCursor(sourceBlockPos + fixture.blockBoxIndexToPoint2i(bbi))
var terminate = false
fun dequeue() { fun getAdjacent(cnx: Int, point: WireGraphCursor): List<WireGraphCursor> {
//printdbg(this, "points before deq: $points") val r = ArrayList<WireGraphCursor>()
points.removeAt(0) for (dir in intArrayOf(RIGHT,DOWN,LEFT,UP)) {
//printdbg(this, "points after deq: $points") if (cnx and dir != 0) r.add(point.copy().moveOneCell(dir))
if (points.size == 0) terminate = true
else {
point = points[0]
//printdbg(this, "new point: $point")
}
} }
return r
}
points.add(point.copy()) fixture.worldBlockPos?.let { sourceBlockPos ->
while (points.isNotEmpty() && !terminate) { val signal = (fixture as Electric).wireEmission[bbi] ?: Vector2(0.0, 0.0)
// break if there are no available wires underneath var point = WireGraphCursor(sourceBlockPos + fixture.blockBoxIndexToPoint2i(bbi))
if (world.getAllWiresFrom(point.x, point.y)?.filter { WireCodex[it].accepts == wireType }?.isEmpty() != false)
break
//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)
// get all wires that matches 'accepts' (such as Red/Green/Blue wire) and propagate signal for each of them val points = Queue<WireGraphCursor>() // a queue, enqueued at the end
world.getAllWiresFrom(point.x, point.y)?.filter { WireCodex[it].accepts == wireType }?.forEach { wire -> var marked = HashSet<Long>()
world.getAllWiringGraph(point.x, point.y)?.get(wire)?.let { node -> fun mark(point: WireGraphCursor) {
marked.add(point.longHash())
// do some signal action
world.setWireEmitStateOf(point.x, point.y, wire, signal)
}
val signal = Vector2(1.0, 0.0)//node.emitState // FIXME branching wires somehow fetches wrong signal value fun isMarked(point: WireGraphCursor) = marked.contains(point.longHash())
val cnx = node.connections.toInt() // 1-15 fun enq(point: WireGraphCursor) = points.addFirst(point.copy())
val nextDirBit = cnx and (15 - point.fromWhere) // cnx minus where the old cursur was; also 1-15 fun deq() = points.removeLast()
//printdbg(this, "(${point.x}, ${point.y}) from ${point.fromWhere} to $nextDirBit")
// mark current position as visited enq(point)
branchesVisited.add(point.copy()) mark(point)
// termination condition 1 while (points.notEmpty()) {
if (branchesVisited.linearSearch { it.x == point.x && it.y == point.y }!! < branchesVisited.lastIndex) { point = deq()
//printdbg(this, "(${point.x}, ${point.y}) was already visited") // TODO if we found a power receiver, do something to it
dequeue() for (x in getAdjacent(world.getWireGraphOf(point.x, point.y, wire)!!, point)) {
} if (!isMarked(x)) {
else { mark(x)
enq(x)
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)
}
} }
} }
} }
} }
}
//printdbg(this, "Point = $point") printdbg(this, "------------------------------------------")
} // end While
//printdbg(this, "------------------------------------------")
} }
} }
@@ -616,5 +568,7 @@ object WorldSimulator {
return this return this
} }
fun longHash() = x.toLong().shl(32) or y.toLong()
} }
} }

View File

@@ -35,11 +35,13 @@ class FixtureLogicSignalEmitter(nameFun: () -> String)
override fun update(delta: Float) { override fun update(delta: Float) {
// set emit // set emit
worldBlockPos?.let { (x, y) -> /*worldBlockPos?.let { (x, y) ->
WireCodex.getAll().filter { it.accepts == "digital_bit" }.forEach { prop -> 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]!!)
} }
} }*/
} }
} }