diff --git a/bots/gatherer/main.go b/bots/gatherer/main.go index 1fba9b8..a794f23 100644 --- a/bots/gatherer/main.go +++ b/bots/gatherer/main.go @@ -51,6 +51,13 @@ type VisibleCore struct { Active bool `json:"active"` } +// ZoneBounds represents the active zone bounds. +type ZoneBounds struct { + Center Position `json:"center"` + Radius int `json:"radius"` + Active bool `json:"active"` +} + // GameState represents the fog-filtered state visible to this bot. type GameState struct { MatchID string `json:"match_id"` @@ -66,6 +73,7 @@ type GameState struct { Cores []VisibleCore `json:"cores"` Walls []Position `json:"walls"` Dead []VisibleBot `json:"dead"` + Zone *ZoneBounds `json:"zone,omitempty"` } // Direction represents a movement direction. diff --git a/bots/gatherer/strategy.go b/bots/gatherer/strategy.go index c8e6965..e9fff0f 100644 --- a/bots/gatherer/strategy.go +++ b/bots/gatherer/strategy.go @@ -52,7 +52,7 @@ func (s *GathererStrategy) ComputeMoves(state *GameState) []Move { for _, bot := range myBots { move := s.computeBotMove(bot, myBots, enemyBots, enemyPositions, - energyPositions, usedEnergy, config) + energyPositions, usedEnergy, config, state) if move != nil { moves = append(moves, *move) // Mark energy as targeted if bot will collect it @@ -71,7 +71,17 @@ func (s *GathererStrategy) computeBotMove( myBots, enemyBots []VisibleBot, enemyPositions, energyPositions, usedEnergy map[Position]bool, config GameConfig, + state *GameState, ) *Move { + // Zone awareness: if zone is active and bot is outside, move toward center immediately + if state.Zone != nil && state.Zone.Active { + dist2 := distance2(bot.Position, state.Zone.Center, config) + if dist2 > state.Zone.Radius*state.Zone.Radius { + // Bot is outside the zone - survival priority: move toward zone center + return s.moveTowardPosition(bot, state.Zone.Center, enemyPositions, config) + } + } + // First check if we should flee from enemies if s.shouldFlee(bot.Position, enemyBots, config) { fleeDir := s.getFleeDirection(bot.Position, enemyBots, config) @@ -296,3 +306,35 @@ func abs(x int) int { } return x } + +// moveTowardPosition returns a move that approaches the target position, avoiding walls and enemies. +func (s *GathererStrategy) moveTowardPosition( + bot VisibleBot, + target Position, + enemyPositions map[Position]bool, + config GameConfig, +) *Move { + directions := []Direction{DirN, DirE, DirS, DirW} + bestDir := DirN + bestDist2 := 999999 + + for _, dir := range directions { + newPos := simulateMove(bot.Position, dir, config) + + // Skip if moving towards enemy + if s.isNearEnemy(newPos, enemyPositions, config) { + continue + } + + dist2 := distance2(newPos, target, config) + if dist2 < bestDist2 { + bestDist2 = dist2 + bestDir = dir + } + } + + return &Move{ + Position: bot.Position, + Direction: bestDir, + } +} diff --git a/bots/rusher/src/game.rs b/bots/rusher/src/game.rs index 9bf8fda..90333cb 100644 --- a/bots/rusher/src/game.rs +++ b/bots/rusher/src/game.rs @@ -44,6 +44,14 @@ pub struct VisibleCore { pub active: bool, } +/// Zone bounds (shrinking storm) +#[derive(Debug, Clone, Deserialize)] +pub struct ZoneBounds { + pub center: Position, + pub radius: u32, + pub active: bool, +} + /// Fog-filtered game state visible to this bot #[derive(Debug, Clone, Deserialize)] pub struct GameState { @@ -61,6 +69,8 @@ pub struct GameState { pub walls: Vec, #[serde(default)] pub dead: Vec, + #[serde(default)] + pub zone: Option, } /// Movement direction diff --git a/bots/swarm/src/game.ts b/bots/swarm/src/game.ts index d0e3b2e..7a3107b 100644 --- a/bots/swarm/src/game.ts +++ b/bots/swarm/src/game.ts @@ -34,6 +34,12 @@ export interface VisibleCore { active: boolean; } +export interface ZoneBounds { + center: Position; + radius: number; + active: boolean; +} + export interface GameState { match_id: string; turn: number; @@ -44,6 +50,7 @@ export interface GameState { cores: VisibleCore[]; walls: Position[]; dead: VisibleBot[]; + zone?: ZoneBounds; } export type Direction = 'N' | 'E' | 'S' | 'W'; diff --git a/engine/bot_strategies.go b/engine/bot_strategies.go index 3de60c2..ddd09dc 100644 --- a/engine/bot_strategies.go +++ b/engine/bot_strategies.go @@ -39,7 +39,7 @@ func getZoneEscapeDirection(botPos Position, state *VisibleState) Direction { // Safety margin: move toward center if within 2 tiles of zone edge // This anticipates the shrinking zone and prevents getting caught outside safetyMargin2 := 4 // (2 tiles)^2 - if dist2 >= radius2 - safetyMargin2 { + if dist2 >= radius2-safetyMargin2 { // Move toward center: choose direction that reduces distance bestDir := DirNone bestReduction := 0 @@ -47,8 +47,8 @@ func getZoneEscapeDirection(botPos Position, state *VisibleState) Direction { for _, dir := range []Direction{DirN, DirE, DirS, DirW} { ddr, ddc := dir.Delta() newPos := Position{ - Row: ((botPos.Row + ddr) % rows + rows) % rows, - Col: ((botPos.Col + ddc) % cols + cols) % cols, + Row: ((botPos.Row+ddr)%rows + rows) % rows, + Col: ((botPos.Col+ddc)%cols + cols) % cols, } newDr := newPos.Row - center.Row diff --git a/starters/go/types.go b/starters/go/types.go index fa895a9..a1d8c17 100644 --- a/starters/go/types.go +++ b/starters/go/types.go @@ -62,15 +62,15 @@ type You struct { // VisibleState represents the fog-filtered game state visible to this player. type VisibleState struct { - MatchID string `json:"match_id"` - Turn int `json:"turn"` + MatchID string `json:"match_id"` + Turn int `json:"turn"` Config map[string]any `json:"config"` - You You `json:"you"` - Bots []VisibleBot `json:"bots"` - Energy []Position `json:"energy"` - Cores []VisibleCore `json:"cores"` - Walls []Position `json:"walls"` - Dead []VisibleBot `json:"dead"` + You You `json:"you"` + Bots []VisibleBot `json:"bots"` + Energy []Position `json:"energy"` + Cores []VisibleCore `json:"cores"` + Walls []Position `json:"walls"` + Dead []VisibleBot `json:"dead"` } // Move represents a bot's movement order.