ai-code-battle/bots/farmer/grid.go
jedarden 5362b6c011 feat(bot): add Farmer bot (Go) — economy-maximizer archetype
Economy-maximizing bot that prioritizes energy collection and spawning
while avoiding combat entirely. Seeks nearest uncontested energy via BFS,
flees enemies within 3 cells, avoids contested energy tiles, and stays
near active cores for maximum spawn throughput. Includes 12 unit tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 16:30:26 -04:00

92 lines
1.9 KiB
Go

package main
func ToroidalManhattan(a, b Position, rows, cols int) int {
dr := abs(a.Row - b.Row)
dc := abs(a.Col - b.Col)
dr = min(dr, rows-dr)
dc = min(dc, cols-dc)
return dr + dc
}
func distance2(a, b Position, rows, cols int) int {
dr := abs(a.Row - b.Row)
dc := abs(a.Col - b.Col)
dr = min(dr, rows-dr)
dc = min(dc, cols-dc)
return dr*dr + dc*dc
}
type cardinalStep struct {
pos Position
dir string
}
func cardinalSteps(p Position, rows, cols int) []cardinalStep {
steps := []struct {
dr, dc int
dir string
}{{-1, 0, "N"}, {0, 1, "E"}, {1, 0, "S"}, {0, -1, "W"}}
result := make([]cardinalStep, 0, 4)
for _, s := range steps {
result = append(result, cardinalStep{
pos: Position{
Row: (p.Row + s.dr + rows) % rows,
Col: (p.Col + s.dc + cols) % cols,
},
dir: s.dir,
})
}
return result
}
// BFS finds the shortest path from start to goal on a toroidal grid
// using 4-directional (cardinal) movement. passable returns true if a cell can be entered.
// Returns the first direction to move, or "" if unreachable.
func BFS(start, goal Position, passable func(Position) bool, rows, cols int) string {
if start == goal {
return ""
}
type node struct {
pos Position
dir string
}
visited := map[Position]bool{start: true}
queue := []node{}
for _, step := range cardinalSteps(start, rows, cols) {
if step.pos == goal && passable(step.pos) {
return step.dir
}
if passable(step.pos) && !visited[step.pos] {
visited[step.pos] = true
queue = append(queue, node{step.pos, step.dir})
}
}
for len(queue) > 0 {
cur := queue[0]
queue = queue[1:]
if cur.pos == goal {
return cur.dir
}
for _, step := range cardinalSteps(cur.pos, rows, cols) {
if !visited[step.pos] && passable(step.pos) {
visited[step.pos] = true
queue = append(queue, node{step.pos, cur.dir})
}
}
}
return ""
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}