Add crosspoll_state table to persist per-island generation counters across evolver restarts. Load state on startup and save after each cross-pollination check. Add persistence pattern and translation structure tests. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
81 lines
1.8 KiB
Go
81 lines
1.8 KiB
Go
package main
|
|
|
|
// ToroidalManhattan returns Manhattan distance with wrap-around.
|
|
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
|
|
}
|
|
|
|
// ToroidalChebyshev returns Chebyshev distance with wrap-around.
|
|
func ToroidalChebyshev(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 max(dr, dc)
|
|
}
|
|
|
|
// Neighbors returns 8-directional neighbors with wrap-around.
|
|
func Neighbors(p Position, rows, cols int) []Position {
|
|
offsets := [8][2]int{
|
|
{-1, -1}, {-1, 0}, {-1, 1},
|
|
{0, -1}, {0, 1},
|
|
{1, -1}, {1, 0}, {1, 1},
|
|
}
|
|
result := make([]Position, 0, 8)
|
|
for _, off := range offsets {
|
|
result = append(result, Position{
|
|
Row: (p.Row + off[0] + rows) % rows,
|
|
Col: (p.Col + off[1] + cols) % cols,
|
|
})
|
|
}
|
|
return result
|
|
}
|
|
|
|
// BFS finds the shortest path from start to goal on a toroidal grid.
|
|
// passable returns true if a cell can be entered.
|
|
// Returns the path (excluding start) or nil if unreachable.
|
|
func BFS(start, goal Position, passable func(Position) bool, rows, cols int) []Position {
|
|
if start == goal {
|
|
return []Position{}
|
|
}
|
|
|
|
type node struct {
|
|
pos Position
|
|
path []Position
|
|
}
|
|
|
|
visited := map[Position]bool{start: true}
|
|
queue := []node{{start, nil}}
|
|
|
|
for len(queue) > 0 {
|
|
cur := queue[0]
|
|
queue = queue[1:]
|
|
|
|
for _, n := range Neighbors(cur.pos, rows, cols) {
|
|
newPath := make([]Position, len(cur.path), len(cur.path)+1)
|
|
copy(newPath, cur.path)
|
|
newPath = append(newPath, n)
|
|
|
|
if n == goal {
|
|
return newPath
|
|
}
|
|
if !visited[n] && passable(n) {
|
|
visited[n] = true
|
|
queue = append(queue, node{n, newPath})
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func abs(x int) int {
|
|
if x < 0 {
|
|
return -x
|
|
}
|
|
return x
|
|
}
|