diff --git a/bot/path/blocks.go b/bot/path/blocks.go new file mode 100644 index 0000000..b6a848f --- /dev/null +++ b/bot/path/blocks.go @@ -0,0 +1,49 @@ +package path + +import ( + "github.com/Tnze/go-mc/bot/world" + "github.com/Tnze/go-mc/data/block" +) + +var ( + safeStepBlocks = make(map[world.BlockStatus]struct{}, 1024) + blocks = []block.Block{ + block.Stone, + block.Granite, + block.PolishedGranite, + block.Diorite, + block.PolishedDiorite, + block.Andesite, + block.PolishedAndesite, + block.GrassBlock, + block.Dirt, + block.CoarseDirt, + block.Cobblestone, + block.OakPlanks, + block.SprucePlanks, + block.BirchPlanks, + block.JunglePlanks, + block.AcaciaPlanks, + block.DarkOakPlanks, + block.Bedrock, + block.GoldOre, + block.IronOre, + block.CoalOre, + block.Glass, + block.LapisOre, + block.Sandstone, + block.RedstoneOre, + } +) + +func init() { + for _, b := range blocks { + if b.MinStateID == b.MaxStateID { + safeStepBlocks[world.BlockStatus(b.MinStateID)] = struct{}{} + } else { + for i := b.MinStateID; i <= b.MaxStateID; i++ { + safeStepBlocks[world.BlockStatus(i)] = struct{}{} + } + } + } +} diff --git a/bot/path/path.go b/bot/path/path.go new file mode 100644 index 0000000..d55b9c1 --- /dev/null +++ b/bot/path/path.go @@ -0,0 +1,157 @@ +// Package path implements pathfinding. +package path + +import ( + "github.com/Tnze/go-mc/bot/world" + "github.com/Tnze/go-mc/data/block" + "github.com/beefsack/go-astar" +) + +type V3 struct { + X, Y, Z int +} + +func (v V3) Cost(other V3) float64 { + x, y, z := v.X-other.X, v.Y-other.Y, v.Z-other.Z + return float64(x*x+z*z) + 1.2*float64(y*y) +} + +// Nav represents a navigation to a position. +type Nav struct { + World *world.World + Start, Dest V3 +} + +func (n *Nav) Path() (path []astar.Pather, distance float64, found bool) { + return astar.Path( + Tile{ // Start point + Nav: n, + Movement: Waypoint, + Pos: n.Start, + }, + Tile{ // Destination point + Nav: n, + Movement: Waypoint, + Pos: n.Dest, + }) +} + +// Movement represents a single type of movement in a path. +type Movement uint8 + +var allMovements = []Movement{TraverseNorth, TraverseSouth, TraverseEast, TraverseWest} + +// Valid movement values. +const ( + Waypoint Movement = iota + TraverseNorth + TraverseSouth + TraverseEast + TraverseWest +) + +// Tile represents a point in a path. All tiles in a path are adjaceent their +// preceeding tiles. +type Tile struct { + Nav *Nav + + Movement Movement + Pos V3 +} + +func (t Tile) PathNeighborCost(to astar.Pather) float64 { + other := to.(Tile) + return 1 + other.Movement.BaseCost() +} + +func (t Tile) PathEstimatedCost(to astar.Pather) float64 { + other := to.(Tile) + cost := t.Pos.Cost(other.Pos) + return cost + other.Movement.BaseCost() +} + +func (t Tile) PathNeighbors() []astar.Pather { + possibles := make([]astar.Pather, 0, 8) + + if t.Pos == t.Nav.Dest && t.Movement != Waypoint { + dupe := t + dupe.Movement = Waypoint + return []astar.Pather{dupe} + } + + for _, m := range allMovements { + x, y, z := m.Offset() + pos := V3{X: t.Pos.X + x, Y: t.Pos.Y + y, Z: t.Pos.Z + z} + if m.Possible(t.Nav, pos.X, pos.Y, pos.Z) { + possibles = append(possibles, Tile{ + Nav: t.Nav, + Movement: m, + Pos: pos, + }) + } + } + + // fmt.Printf("%v.Neighbours(): %+v\n", t.Pos, possibles) + return possibles +} + +func (m Movement) Possible(nav *Nav, x, y, z int) bool { + // fmt.Printf("%s.Possible(%d,%d,%d)\n", m, x, y, z) + switch m { + case Waypoint, TraverseNorth, TraverseSouth, TraverseEast, TraverseWest: + b := nav.World.GetBlockStatus(x, y, z) + if _, safe := safeStepBlocks[b]; !safe { + return false + } + above1 := uint32(nav.World.GetBlockStatus(x, y+1, z)) + above2 := uint32(nav.World.GetBlockStatus(x, y+2, z)) + return above1 == block.Air.MinStateID && above2 == block.Air.MinStateID + default: + panic(m) + } +} + +func (m Movement) Offset() (x, y, z int) { + switch m { + case Waypoint: + return 0, 0, 0 + case TraverseNorth: + return 0, 0, -1 + case TraverseSouth: + return 0, 0, 1 + case TraverseEast: + return 1, 0, 0 + case TraverseWest: + return -1, 0, 0 + default: + panic(m) + } +} + +func (m Movement) BaseCost() float64 { + switch m { + case Waypoint: + return 0 + case TraverseNorth, TraverseSouth, TraverseEast, TraverseWest: + return 1 + default: + panic(m) + } +} + +func (m Movement) String() string { + switch m { + case Waypoint: + return "waypoint" + case TraverseNorth: + return "traverse-north" + case TraverseSouth: + return "traverse-south" + case TraverseEast: + return "traverse-east" + case TraverseWest: + return "traverse-west" + default: + panic(m) + } +} diff --git a/bot/phy/phy.go b/bot/phy/phy.go index 0d8288b..3d97490 100644 --- a/bot/phy/phy.go +++ b/bot/phy/phy.go @@ -15,7 +15,7 @@ const ( resetVel = 0.003 maxYawChange = 33 - maxPitchChange = 22 + maxPitchChange = 11 gravity = 0.08 drag = 0.98 @@ -91,6 +91,8 @@ func (s *State) surroundings(query AABB, w World) Surrounds { func (s *State) applyLookInputs(input Inputs) { errYaw := math.Min(math.Max(input.Yaw-s.Yaw, -maxYawChange), maxYawChange) s.Yaw += errYaw + errPitch := math.Min(math.Max(input.Pitch-s.Pitch, -maxPitchChange), maxPitchChange) + s.Pitch += errPitch } func (s *State) applyPosInputs(input Inputs, acceleration, inertia float64) { diff --git a/go.mod b/go.mod index 421f318..a982652 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/Tnze/go-mc go 1.13 require ( + github.com/beefsack/go-astar v0.0.0-20200827232313-4ecf9e304482 github.com/google/uuid v1.1.1 github.com/iancoleman/strcase v0.1.1 // indirect ) diff --git a/go.sum b/go.sum index 1134786..ac2d898 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/beefsack/go-astar v0.0.0-20200827232313-4ecf9e304482 h1:p4g4uok3+r6Tg6fxXEQUAcMAX/WdK6WhkQW9s0jaT7k= +github.com/beefsack/go-astar v0.0.0-20200827232313-4ecf9e304482/go.mod h1:Cu3t5VeqE8kXjUBeNXWQprfuaP5UCIc5ggGjgMx9KFc= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/iancoleman/strcase v0.1.1 h1:2I+LRClyCYB7JgZb9U0k75VHUiQe9RfknRqDyUfzp7k=