Skip to content

Commit 48b9ee3

Browse files
committed
Simulation based goal finding
1 parent 8e0cdd5 commit 48b9ee3

File tree

12 files changed

+175
-51
lines changed

12 files changed

+175
-51
lines changed

common/src/main/kotlin/com/lambda/interaction/construction/Blueprint.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package com.lambda.interaction.construction
22

33
import com.lambda.context.SafeContext
44
import com.lambda.interaction.construction.verify.TargetState
5+
import com.lambda.module.modules.client.TaskFlow
56
import com.lambda.util.BlockUtils.blockPos
67
import com.lambda.util.BlockUtils.blockState
8+
import com.lambda.util.Communication.info
79
import com.lambda.util.extension.Structure
810
import net.minecraft.structure.StructureTemplate
911
import net.minecraft.util.math.BlockBox
@@ -16,7 +18,8 @@ abstract class Blueprint {
1618
open fun isDone(ctx: SafeContext) =
1719
structure.all { (pos, targetState) ->
1820
with(ctx) {
19-
targetState.matches(pos.blockState(world), pos, world)
21+
val state = pos.blockState(world)
22+
targetState.matches(state, pos, world) || state.block in TaskFlow.ignoredBlocks
2023
}
2124
}
2225

common/src/main/kotlin/com/lambda/interaction/construction/result/BuildResult.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.lambda.interaction.material.StackSelection.Companion.select
1010
import com.lambda.interaction.material.container.MainHandContainer
1111
import com.lambda.task.Task
1212
import com.lambda.task.Task.Companion.failTask
13+
import com.lambda.util.BlockUtils.blockState
1314
import net.minecraft.block.BlockState
1415
import net.minecraft.item.Item
1516
import net.minecraft.item.ItemStack
@@ -180,7 +181,11 @@ abstract class BuildResult : ComparableResult<Rank>, Task<Unit>() {
180181
}
181182

182183
override fun SafeContext.buildRenderer() {
183-
withBox(Box(blockPos), color)
184+
if (blockPos.blockState(world).isAir) {
185+
withBox(Box(blockPos), color)
186+
} else {
187+
withPos(blockPos, color)
188+
}
184189
}
185190

186191
override fun compareTo(other: ComparableResult<Rank>): Int {
@@ -215,7 +220,11 @@ abstract class BuildResult : ComparableResult<Rank>, Task<Unit>() {
215220
}
216221

217222
override fun SafeContext.buildRenderer() {
218-
withPos(blockPos, color)
223+
if (blockPos.blockState(world).isAir) {
224+
withBox(Box(blockPos), color)
225+
} else {
226+
withPos(blockPos, color)
227+
}
219228
}
220229

221230
override fun compareTo(other: ComparableResult<Rank>): Int {

common/src/main/kotlin/com/lambda/interaction/construction/result/Rank.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,8 @@ enum class Rank {
2929

3030
// not an issue
3131
DONE,
32-
IGNORED,
32+
IGNORED;
33+
34+
val solvable: Boolean
35+
get() = ordinal < OUT_OF_WORLD.ordinal
3336
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.lambda.interaction.construction.simulation
2+
3+
import baritone.api.pathing.goals.Goal
4+
import com.lambda.util.Communication.info
5+
import com.lambda.util.world.fastVectorOf
6+
7+
class BuildGoal(private val sim: Simulation) : Goal {
8+
override fun isInGoal(x: Int, y: Int, z: Int): Boolean {
9+
val vec = fastVectorOf(x, y, z)
10+
// info("Checking goal at $x, $y, $z")
11+
val simu = sim.simulate(vec)
12+
return simu.any { it.rank.ordinal < 4 }
13+
}
14+
15+
override fun heuristic(x: Int, y: Int, z: Int): Double {
16+
val bestRank = sim.simulate(fastVectorOf(x, y, z)).minOrNull()?.rank?.ordinal ?: 10000
17+
val score = 1 / (bestRank.toDouble() + 1)
18+
// info("Calculating heuristic at $x, $y, $z with score $score")
19+
return score
20+
}
21+
}

common/src/main/kotlin/com/lambda/interaction/construction/simulation/BuildSimulator.kt

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -39,25 +39,25 @@ import net.minecraft.util.math.Vec3d
3939
import kotlin.math.pow
4040

4141
object BuildSimulator {
42-
fun Blueprint.simulate(eye: Vec3d) =
42+
fun Blueprint.simulate(eye: Vec3d, reach: Double = TaskFlow.interact.reach) =
4343
runSafe {
4444
structure.entries.flatMap { (pos, target) ->
45-
checkRequirements(pos, target)?.let {
45+
checkRequirements(pos, target, reach)?.let {
4646
return@flatMap setOf(it)
4747
}
48-
checkPlaceResults(pos, target, eye).let {
48+
checkPlaceResults(pos, target, eye, reach).let {
4949
if (it.isEmpty()) return@let
5050
return@flatMap it
5151
}
52-
checkBreakResults(pos, eye).let {
52+
checkBreakResults(pos, eye, reach).let {
5353
if (it.isEmpty()) return@let
5454
return@flatMap it
5555
}
5656
emptySet()
5757
}.toSet()
5858
} ?: emptySet()
5959

60-
private fun SafeContext.checkRequirements(pos: BlockPos, target: TargetState): BuildResult? {
60+
private fun SafeContext.checkRequirements(pos: BlockPos, target: TargetState, reach: Double): BuildResult? {
6161
/* the chunk is not loaded */
6262
if (!world.isChunkLoaded(pos)) {
6363
return BuildResult.ChunkNotLoaded(pos)
@@ -101,7 +101,8 @@ object BuildSimulator {
101101
private fun SafeContext.checkPlaceResults(
102102
pos: BlockPos,
103103
target: TargetState,
104-
eye: Vec3d
104+
eye: Vec3d,
105+
reach: Double
105106
): Set<BuildResult> {
106107
val acc = mutableSetOf<BuildResult>()
107108

@@ -119,16 +120,16 @@ object BuildSimulator {
119120

120121
val boxes = voxelShape.boundingBoxes.map { it.offset(hitPos) }
121122

122-
if (boxes.all { it.center.distanceTo(eye) > interact.reach + 1 }) {
123-
acc.add(BuildResult.OutOfReach(pos, eye, hitPos.vecOf(hitSide), interact.reach, hitSide))
123+
if (boxes.all { it.center.distanceTo(eye) > reach + 1 }) {
124+
acc.add(BuildResult.OutOfReach(pos, eye, hitPos.vecOf(hitSide), reach, hitSide))
124125
return@forEach
125126
}
126127

127128
val verify: HitResult.() -> Boolean = {
128129
blockResult?.blockPos == hitPos && blockResult?.side == hitSide
129130
}
130131
val validHits = mutableMapOf<Vec3d, HitResult>()
131-
val reachSq = interact.reach.pow(2)
132+
val reachSq = reach.pow(2)
132133

133134
boxes.forEach { box ->
134135
val res = if (TaskFlow.interact.useRayCast) interact.resolution else 2
@@ -139,7 +140,7 @@ object BuildSimulator {
139140

140141
validHits[vec] = if (TaskFlow.interact.useRayCast) {
141142
val cast = eye.rotationTo(vec)
142-
.rayCast(interact.reach, eye) ?: return@scanVisibleSurfaces
143+
.rayCast(reach, eye) ?: return@scanVisibleSurfaces
143144
if (!cast.verify()) return@scanVisibleSurfaces
144145

145146
cast
@@ -257,7 +258,7 @@ object BuildSimulator {
257258
return acc
258259
}
259260

260-
private fun SafeContext.checkBreakResults(pos: BlockPos, eye: Vec3d): Set<BuildResult> {
261+
private fun SafeContext.checkBreakResults(pos: BlockPos, eye: Vec3d, reach: Double): Set<BuildResult> {
261262
val acc = mutableSetOf<BuildResult>()
262263
val state = pos.blockState(world)
263264

@@ -278,7 +279,7 @@ object BuildSimulator {
278279

279280
/* liquid needs to be submerged first to be broken */
280281
if (!state.fluidState.isEmpty && state.isReplaceable) {
281-
val submerge = checkPlaceResults(pos, TargetState.Solid, eye)
282+
val submerge = checkPlaceResults(pos, TargetState.Solid, eye, reach)
282283
acc.add(BreakResult.Submerge(pos, state, submerge))
283284
acc.addAll(submerge)
284285
return acc
@@ -292,7 +293,7 @@ object BuildSimulator {
292293
if (adjacentLiquids.isNotEmpty()) {
293294
acc.add(BreakResult.BlockedByLiquid(pos, state))
294295
adjacentLiquids.forEach {
295-
val submerge = checkPlaceResults(pos.offset(it), TargetState.Solid, eye)
296+
val submerge = checkPlaceResults(pos.offset(it), TargetState.Solid, eye, reach)
296297
acc.addAll(submerge)
297298
}
298299
return acc
@@ -310,16 +311,16 @@ object BuildSimulator {
310311
val interact = TaskFlow.interact
311312
val rotation = TaskFlow.rotation
312313
val currentRotation = RotationManager.currentRotation
313-
val currentCast = currentRotation.rayCast(interact.reach, eye)
314+
val currentCast = currentRotation.rayCast(reach, eye)
314315

315316
val voxelShape = state.getOutlineShape(world, pos)
316317
voxelShape.getClosestPointTo(eye).ifPresent {
317318
// ToDo: Use closest point of shape
318319
}
319320

320321
val boxes = voxelShape.boundingBoxes.map { it.offset(pos) }
321-
if (boxes.all { it.center.distanceTo(eye) > interact.reach + 1 }) {
322-
acc.add(BuildResult.OutOfReach(pos, eye, pos.toCenterPos(), interact.reach, Direction.UP))
322+
if (boxes.all { it.center.distanceTo(eye) > reach + 1 }) {
323+
acc.add(BuildResult.OutOfReach(pos, eye, pos.toCenterPos(), reach, Direction.UP))
323324
return acc
324325
}
325326

@@ -342,7 +343,7 @@ object BuildSimulator {
342343
}
343344

344345
val validHits = mutableMapOf<Vec3d, HitResult>()
345-
val reachSq = interact.reach.pow(2)
346+
val reachSq = reach.pow(2)
346347

347348
boxes.forEach { box ->
348349
val res = if (TaskFlow.interact.useRayCast) interact.resolution else 2
@@ -353,7 +354,7 @@ object BuildSimulator {
353354

354355
validHits[vec] = if (TaskFlow.interact.useRayCast) {
355356
val cast = eye.rotationTo(vec)
356-
.rayCast(interact.reach, eye) ?: return@scanVisibleSurfaces
357+
.rayCast(reach, eye) ?: return@scanVisibleSurfaces
357358
if (!cast.verify()) return@scanVisibleSurfaces
358359

359360
cast
@@ -405,10 +406,4 @@ object BuildSimulator {
405406

406407
return acc
407408
}
408-
409-
// private fun SafeContext.playerFitsIn(BlockPos pos) {
410-
// val pBox = player.boundingBox
411-
// val aabb = Box(pBox.minX, pBox.minY - 1.0E-6, pBox.minZ, pBox.maxX, pBox.minY, pBox.maxZ)
412-
// return world.findSupportingBlockPos(player, aabb).orElse(null)
413-
// }
414409
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.lambda.interaction.construction.simulation
2+
3+
import com.lambda.context.SafeContext
4+
import com.lambda.interaction.construction.Blueprint
5+
import com.lambda.interaction.construction.result.BuildResult
6+
import com.lambda.interaction.construction.simulation.BuildSimulator.simulate
7+
import com.lambda.threading.runSafe
8+
import com.lambda.util.Communication.info
9+
import com.lambda.util.world.FastVector
10+
import com.lambda.util.world.toBlockPos
11+
import com.lambda.util.world.toFastVec
12+
import com.lambda.util.world.toVec3d
13+
import net.minecraft.client.network.ClientPlayerEntity
14+
import net.minecraft.util.math.BlockPos
15+
import net.minecraft.util.math.Box
16+
import net.minecraft.util.math.Vec3d
17+
18+
data class Simulation(val blueprint: Blueprint) {
19+
private val cache: MutableMap<FastVector, Set<BuildResult>> = mutableMapOf()
20+
private fun FastVector.toView(): Vec3d = toVec3d().add(0.5, 0.62, 0.5)
21+
// private lateinit var player: ClientPlayerEntity
22+
23+
// init {
24+
// runSafe {
25+
// this@Simulation.player = player
26+
// BlockPos.iterateOutwards(player.blockPos, 5, 5, 5).forEach { pos ->
27+
// val vec = pos.toFastVec()
28+
// info("Preloading simulation at $vec")
29+
// simulate(vec)
30+
// }
31+
// }
32+
// }
33+
34+
// val best: FastVector? get() = cache.filter { (_, results) ->
35+
// results.isNotEmpty() && results.any { it.rank.ordinal < 4 }
36+
// }.keys.minByOrNull { it.toVec3d().distanceTo(player.pos) }
37+
38+
// fun best() = cache.filter { (_, results) ->
39+
// results.isNotEmpty() && results.any { it.rank.ordinal < 3 }
40+
// }.keys
41+
42+
fun simulate(pos: FastVector): Set<BuildResult> {
43+
// runSafe {
44+
// if (!playerFitsIn(Vec3d.ofBottomCenter(pos.toBlockPos()))) return emptySet()
45+
// }
46+
// return blueprint.simulate(pos.toView()).also { cache[pos] = it }
47+
// return cache.computeIfAbsent(pos) {
48+
//// runSafe {
49+
//// if (!playerFitsIn(Vec3d.ofBottomCenter(pos.toBlockPos()))) return@computeIfAbsent emptySet()
50+
//// }
51+
// blueprint.simulate(pos.toView())
52+
// }
53+
return blueprint.simulate(pos.toView(), reach = 3.5)
54+
}
55+
56+
private fun SafeContext.playerFitsIn(pos: Vec3d): Boolean {
57+
val pBox = player.boundingBox
58+
val aabb = Box(pBox.minX, pBox.minY - 1.0E-6, pBox.minZ, pBox.maxX, pBox.minY, pBox.maxZ)
59+
return world.isSpaceEmpty(aabb.offset(pos))
60+
}
61+
62+
companion object {
63+
fun Blueprint.simulation() = Simulation(this)
64+
}
65+
}

common/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.lambda.interaction.construction.verify
22

33
import com.lambda.interaction.material.ContainerManager.findDisposable
4+
import com.lambda.module.modules.client.TaskFlow
45
import com.lambda.util.BlockUtils.blockState
56
import com.lambda.util.item.ItemUtils.block
67
import net.minecraft.block.BlockState
@@ -17,32 +18,41 @@ sealed class TargetState : StateMatcher {
1718
override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack =
1819
ItemStack.EMPTY
1920
}
21+
2022
data object Solid : TargetState() {
2123
override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) =
2224
state.isSolidBlock(world, pos)
2325
override fun getStack(world: ClientWorld, pos: BlockPos) =
24-
findDisposable()?.stacks?.firstOrNull() ?: ItemStack(Items.NETHERRACK)
26+
findDisposable()?.stacks?.firstOrNull {
27+
it.item.block in TaskFlow.disposables
28+
} ?: ItemStack(Items.NETHERRACK)
2529
}
30+
2631
data class Support(val direction: Direction) : TargetState() {
2732
override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) =
2833
pos.offset(direction).blockState(world).isSolidBlock(world, pos.offset(direction))
2934
|| state.isSolidBlock(world, pos)
3035

3136
override fun getStack(world: ClientWorld, pos: BlockPos) =
32-
findDisposable()?.stacks?.firstOrNull() ?: ItemStack(Items.NETHERRACK)
37+
findDisposable()?.stacks?.firstOrNull {
38+
it.item.block in TaskFlow.disposables
39+
} ?: ItemStack(Items.NETHERRACK)
3340
}
41+
3442
data class State(val blockState: BlockState) : TargetState() {
3543
override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) =
3644
state == blockState
3745
override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack =
3846
blockState.block.getPickStack(world, pos, blockState)
3947
}
48+
4049
data class Block(val block: net.minecraft.block.Block) : TargetState() {
4150
override fun matches(state: BlockState, pos: BlockPos, world: ClientWorld) =
4251
state.block == block
4352
override fun getStack(world: ClientWorld, pos: BlockPos): ItemStack =
4453
block.getPickStack(world, pos, block.defaultState)
4554
}
55+
4656
data class Stack(val itemStack: ItemStack) : TargetState() {
4757
private val block = itemStack.item.block
4858

common/src/main/kotlin/com/lambda/interaction/material/container/EnderChestContainer.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ package com.lambda.interaction.material.container
33
import com.lambda.interaction.material.MaterialContainer
44
import com.lambda.interaction.material.StackSelection
55
import com.lambda.task.Task
6+
import com.lambda.task.Task.Companion.emptyTask
67
import com.lambda.task.Task.Companion.failTask
78
import com.lambda.task.tasks.OpenContainer.Companion.openContainer
9+
import com.lambda.util.Communication.info
810
import net.minecraft.item.ItemStack
911
import net.minecraft.screen.GenericContainerScreenHandler
1012
import net.minecraft.util.math.BlockPos
@@ -29,10 +31,12 @@ object EnderChestContainer : MaterialContainer(Rank.ENDER_CHEST) {
2931
// }
3032

3133
override fun withdraw(selection: StackSelection): Task<*> {
32-
TODO()
34+
info("Not yet implemented")
35+
return emptyTask()
3336
}
3437

3538
override fun deposit(selection: StackSelection): Task<*> {
36-
TODO()
39+
info("Not yet implemented")
40+
return emptyTask()
3741
}
3842
}

common/src/main/kotlin/com/lambda/module/modules/player/HighwayTools.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,7 @@ object HighwayTools : Module(
6363
structure = structure.plus(slice.map { it.key.add(currentPos) to it.value })
6464
}
6565

66-
runningTask = build {
67-
structure.toBlueprint()
68-
}.onSuccess { _, _ ->
66+
runningTask = structure.toBlueprint().build().onSuccess { _, _ ->
6967
if (distanceMoved < distance || distance < 0) {
7068
buildSlice()
7169
} else {

0 commit comments

Comments
 (0)