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 wiringGraph = HashMap<BlockAddress, HashMap<ItemID, WiringSimCell>>()
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<WireRecvState> = ArrayList() // how far away are the power sources
)

View File

@@ -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<WireGraphCursor>()
val points = ArrayList<WireGraphCursor>() // 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<WireGraphCursor> {
val r = ArrayList<WireGraphCursor>()
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<WireGraphCursor>() // a queue, enqueued at the end
var marked = HashSet<Long>()
// 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()
}
}

View File

@@ -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]!!)
}
}
}*/
}
}