Skip to content

Commit

Permalink
old fill
Browse files Browse the repository at this point in the history
  • Loading branch information
TakoTheDev committed Sep 13, 2024
1 parent 1ba2086 commit b597e09
Showing 1 changed file with 114 additions and 192 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,14 @@
*/
package gg.skytils.skytilsmod.features.impl.dungeons.solvers

import gg.essential.universal.UChat
import gg.essential.universal.UMatrixStack
import gg.skytils.skytilsmod.Skytils
import gg.skytils.skytilsmod.Skytils.Companion.mc
import gg.skytils.skytilsmod.core.tickTimer
import gg.skytils.skytilsmod.features.impl.funny.Funny
import gg.skytils.skytilsmod.listeners.DungeonListener
import gg.skytils.skytilsmod.utils.RenderUtil
import gg.skytils.skytilsmod.utils.SuperSecretSettings
import gg.skytils.skytilsmod.utils.Utils
import gg.skytils.skytilsmod.utils.ifNull
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import net.minecraft.client.renderer.GlStateManager
Expand All @@ -50,69 +47,54 @@ object IceFillSolver {
tickTimer(20, repeats = true) {
if (!Utils.inDungeons || !Skytils.config.iceFillSolver || mc.thePlayer == null) return@tickTimer
val world: World = mc.theWorld
if (DungeonListener.missingPuzzles.contains("Ice Fill") && puzzles == null && job?.isActive != true) {
if (DungeonListener.missingPuzzles.contains("Ice Fill") && puzzles == null && (job == null || job?.isActive == false)) {
job = Skytils.launch {
val playerX = mc.thePlayer.posX.toInt()
val playerZ = mc.thePlayer.posZ.toInt()
val xRange = playerX - 30..playerX + 30
val zRange = playerZ - 30..playerZ + 30
findChest@ for (te in world.loadedTileEntityList) {
val xRange = playerX - 25..playerX + 25
val zRange = playerZ - 25..playerZ + 25
findChest@ for (te in mc.theWorld.loadedTileEntityList) {
if (te.pos.y == 75 && te is TileEntityChest && te.numPlayersUsing == 0 && te.pos.x in xRange && te.pos.z in zRange
) {
val pos = te.pos
if (world.getBlockState(pos.down()).block == Blocks.stone) {
for (direction in EnumFacing.HORIZONTALS) {
fun checkChestTorches(dir: EnumFacing): Boolean {
return world.getBlockState(
pos.offset(
dir,
1
)
).block == Blocks.torch && world.getBlockState(
pos.offset(
dir.opposite,
3
)
).block == Blocks.torch
}

if (world.getBlockState(pos.offset(direction)).block == Blocks.cobblestone && world.getBlockState(
pos.offset(direction.opposite, 2)
).block == Blocks.iron_bars) {

val offsetDir = listOf(direction.rotateYCCW(), direction.rotateY()).find {
return@find world.getBlockState(
pos.offset(
it,
1
)
).block == Blocks.torch && world.getBlockState(
pos.offset(
it.opposite,
3
)
).block == Blocks.torch
}?.opposite ?: continue
val offsetDir: EnumFacing? = if (checkChestTorches(direction.rotateY())) {
direction.rotateYCCW()
} else if (checkChestTorches(direction.rotateYCCW())) {
direction.rotateY()
} else continue

if (world.getBlockState(
pos.offset(direction.opposite)
.offset(offsetDir)
.down(2)
).block == Blocks.stone_brick_stairs) {
//chestCenter: -11 75 -89; direction: east
val chestCenter = pos.offset(offsetDir)

val starts = Triple(
//three: -33 70 -89
chestCenter.down(5).offset(direction.opposite, 22),
//five: -28 71 -89
chestCenter.down(4).offset(direction.opposite, 17),
//seven: -21 72 -89
chestCenter.down(3).offset(direction.opposite, 10),
)
val ends = Triple(
//three: -29 70 -89
starts.first.offset(direction, 3),
//five: -23 71 -89
starts.second.offset(direction, 5),
//seven: -14 72 -89
starts.third.offset(direction, 7),
)

puzzles = Triple(
IceFillPuzzle(pos, world, starts.first, ends.first, direction),
IceFillPuzzle(pos, world, starts.second, ends.second, direction),
IceFillPuzzle(pos, world, starts.third, ends.third, direction)
IceFillPuzzle(world, 70, pos, direction),
IceFillPuzzle(world, 71, pos, direction),
IceFillPuzzle(world, 72, pos, direction)
)

println(
"An Ice Fill chest is at $pos, is facing $direction and is offset $offsetDir"
"An Ice Fill chest is at $pos and is facing $direction. Offset direction is $offsetDir."
)
break@findChest
}
Expand All @@ -126,9 +108,23 @@ object IceFillSolver {
}
}

private fun checkForStart(world: World, pos: BlockPos, facing: EnumFacing) =
world.getBlockState(pos).block === Blocks.air &&
world.getBlockState(pos.offset(facing.rotateY())).block === Blocks.cobblestone_wall &&
world.getBlockState(pos.offset(facing.rotateYCCW())).block === Blocks.cobblestone_wall

private fun generatePairs(world: World, positions: List<BlockPos>) =
positions.flatMap { pos -> getPossibleMoves(world, pos).map { Move(pos, it) } }

private fun getPossibleMoves(world: World, pos: BlockPos) =
EnumFacing.HORIZONTALS.map { pos.offset(it) }.filter { spot ->
val down = world.getBlockState(spot.down()).block
(down == Blocks.ice || down == Blocks.packed_ice) && world.getBlockState(spot).block != Blocks.stone
}

@SubscribeEvent
fun onWorldRender(event: RenderWorldLastEvent) {
if (!Utils.inDungeons || !Skytils.config.iceFillSolver || "Ice Fill" !in DungeonListener.missingPuzzles) return
if (!Skytils.config.iceFillSolver || "Ice Fill" !in DungeonListener.missingPuzzles) return
val (three, five, seven) = puzzles ?: return
val matrixStack = UMatrixStack.Compat.get()
three.draw(matrixStack, event.partialTicks)
Expand All @@ -138,174 +134,100 @@ object IceFillSolver {

@SubscribeEvent
fun onWorldChange(event: WorldEvent.Unload) {
//TODO: Will only stop the scan task, not currently running path finders
job?.cancel()
job = null
puzzles = null
job = null
}

private class IceFillPuzzle(
val chestCenter: BlockPos, val world: World, val start: BlockPos, val end: BlockPos, val facing: EnumFacing
) {
private val optimal = SuperSecretSettings.azooPuzzoo
private var path: List<Vec3>? = null

init {
Skytils.launch {
path = findPath().ifNull {
UChat.chat("${Skytils.failPrefix} §cFailed to find a solution for Ice Fill. Please report this on our Discord at discord.gg/skytils.")
println("Ice Fill Data: chestCenter=$chestCenter, start=$start, end=$end, facing=$facing, optimal=$optimal")
}
private class IceFillPuzzle(world: World, y: Int, chestPos: BlockPos, roomFacing: EnumFacing) {
private val spaces: MutableList<BlockPos> = ArrayList()
private lateinit var start: BlockPos
var paths: MutableSet<List<BlockPos>> = HashSet()
fun genPaths(world: World) {
// Generate paths
val moves = generatePairs(world, spaces)
val g = Graph(moves, world)
val path: MutableList<BlockPos> = ArrayList()
path.add(start)
try {
getPaths(g, start, mutableSetOf(start), path, spaces.size)
} catch (e: Exception) {
e.printStackTrace()
}
}

private fun findPath(): List<Vec3>? {
val spaces = getSpaces()

val moves = spaces.associate {
val neighbors = EnumFacing.HORIZONTALS.associateBy { direction -> it.offset(direction) }
.filterKeys { spot -> spot in spaces }
.mapKeys { (pos, _) -> spaces.indexOf(pos) }
Pair(spaces.indexOf(it), neighbors)
}

val startIndex = spaces.indexOf(start)
val n = spaces.size
val visited = BooleanArray(n).also { it[startIndex] = true }
val startPath = IntArray(n) { -1 }.also { it[0] = startIndex }

if (optimal) {
val optimizedMoves = Array(n) {
moves[it]!!.map { map ->
map.key + (map.value.ordinal.toLong() shl 32)
}.toLongArray()
}

return getOptimalPath(
optimizedMoves, n, startIndex, visited, startPath, 1, facing.ordinal, 0, Int.MAX_VALUE
)?.first?.map { Vec3(spaces.elementAt(it)).addVector(0.5, 0.01, 0.5) }
} else {
val simplifiedMoves = moves.mapValues { (_, y) -> y.map { it.key } }

return getFirstPath(
Array(n) { simplifiedMoves[it]!! }, n, startIndex, visited, startPath, 1
)?.map { Vec3(spaces.elementAt(it)).addVector(0.5, 0.01, 0.5) }
}
}

fun draw(matrixStack: UMatrixStack, partialTicks: Float) {
path?.let {
GlStateManager.pushMatrix()
fun draw(matrixStack: UMatrixStack, partialTicks: Float) =
paths.firstOrNull()?.zipWithNext { first, second ->
GlStateManager.disableCull()

it.zipWithNext { first, second ->
RenderUtil.draw3DLine(first, second, 5, Color.MAGENTA, partialTicks, matrixStack, Funny.alphaMult)
}
GlStateManager.popMatrix()
RenderUtil.draw3DLine(
Vec3(first).addVector(0.5, 0.01, 0.5),
Vec3(second).addVector(0.5, 0.01, 0.5),
5,
Color.RED,
partialTicks,
matrixStack,
Funny.alphaMult
)
GlStateManager.enableCull()
}
}

private fun getFirstPath(
moves: Array<List<Int>>,
n: Int,
visiting: Int,
visited: BooleanArray,
path: IntArray,
depth: Int,
): List<Int>? {
if (depth == n) {
return path.toList()
}
private fun getPaths(
g: Graph,
v: BlockPos,
visited: MutableSet<BlockPos>,
path: MutableList<BlockPos>,
n: Int
) {
if (path.size == n) {
val newPath: List<BlockPos> = path.toList()
paths.add(newPath)
return
} else {

val move = moves[visiting]
// Check if every move starting from position `v` leads
// to a solution or not
g.adjList[v]?.forEach { w ->
// Only check if we haven't been there before
if (!visited.contains(w)) {
visited.add(w)
path.add(w)

for (index in move) {
if (!visited[index]) {
visited[index] = true
path[depth] = index
// Continue checking down this path
getPaths(g, w, visited, path, n)

getFirstPath(moves, n, index, visited, path, depth + 1)?.let {
return it
// backtrack
visited.remove(w)
path.remove(w)
}

visited[index] = false
}
}

return null
}

private fun getOptimalPath(
moves: Array<LongArray>,
n: Int,
visiting: Int,
visited: BooleanArray,
path: IntArray,
depth: Int,
lastDirection: Int,
corners: Int,
knownLeastCorners: Int
): Pair<List<Int>, Int>? {
if (corners >= knownLeastCorners) {
return null
}

if (depth == n) {
return Pair(path.toList(), corners)
}

var bestPath: List<Int>? = null
var leastCorners = knownLeastCorners

val move = moves[visiting]

for (value in move) {
val index = value.toInt()
if (visited[index]) continue

val direction = (value shr 32).toInt()

visited[index] = true
path[depth] = index

val newCorners = if (lastDirection != direction) corners + 1 else corners

val newPath = getOptimalPath(
moves, n, index, visited, path, depth + 1, direction, newCorners, leastCorners
)

if (newPath != null) {
bestPath = newPath.first
leastCorners = newPath.second
init {
chestPos.offset(roomFacing.opposite, 11).run { Utils.getBlocksWithinRangeAtSameY(chestPos, 25, y) }
.forEach { pos ->
when (world.getBlockState(pos.down()).block) {
Blocks.ice, Blocks.packed_ice ->
if (world.getBlockState(pos).block === Blocks.air)
spaces.add(pos)

Blocks.stone_brick_stairs, Blocks.stone -> {
if (!::start.isInitialized && checkForStart(world, pos, roomFacing))
start = pos.offset(roomFacing)
}
}
}

visited[index] = false
}

return bestPath?.let { Pair(it, leastCorners) }
genPaths(world)
}
}

private fun getSpaces(): List<BlockPos> {
val spaces = mutableListOf(start)
val queue = mutableListOf(start)
private data class Move(var source: BlockPos, var dest: BlockPos)

while (queue.isNotEmpty()) {
val current = queue.removeLast()
EnumFacing.HORIZONTALS.forEach { direction ->
val next = current.offset(direction)
if (next !in spaces && world.getBlockState(next).block === Blocks.air && Utils.equalsOneOf(
world.getBlockState(
next.down()
).block, Blocks.ice, Blocks.packed_ice
)
) {
spaces.add(next)
queue.add(next)
}
}
private class Graph(moves: Collection<Move>, world: World) {
val adjList: Map<BlockPos, Collection<BlockPos>> = buildMap {
moves.forEach { (source, dest) ->
this[source] = getPossibleMoves(world, source)
this[dest] = getPossibleMoves(world, dest)
}

return spaces
}
}
}

0 comments on commit b597e09

Please sign in to comment.