feat(engine): add zone escape as Priority 1 to all built-in bots
Per plan §3.7.1, the zone forces bots into contact. This change ensures all built-in bots escape the zone FIRST when threatened (dist to zone edge < 5 tiles), before any other action like energy collection or combat. Changes: - GuardianBot, SwarmBot, HunterBot: Added zone escape as Priority 1 - Phase 13 bots (Defender, Scout, Farmer, Pacifist, Phalanx, Raider, Nomad, Opportunist, Assassin, Kamikaze): Added zone escape as Priority 1 - RandomBot: Added zone escape before random movement The getZoneEscapeDirection function was already present and correctly implements toroidal distance calculation with 5-tile safety margin. Closes: bf-4m78q
This commit is contained in:
parent
64aa6aef40
commit
ccdec39c52
3 changed files with 141 additions and 14 deletions
|
|
@ -76,6 +76,16 @@ func (b *RandomBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
|
||||
for _, bot := range state.Bots {
|
||||
if bot.Owner == state.You.ID {
|
||||
// Priority 1: Escape zone if threatened
|
||||
if zoneDir := getZoneEscapeDirection(bot.Position, state); zoneDir != DirNone {
|
||||
moves = append(moves, Move{
|
||||
Position: bot.Position,
|
||||
Direction: zoneDir,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// Otherwise, move randomly
|
||||
moves = append(moves, Move{
|
||||
Position: bot.Position,
|
||||
Direction: directions[b.rng.Intn(len(directions))],
|
||||
|
|
|
|||
|
|
@ -623,7 +623,7 @@ func (b *GuardianBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
usedEnergy := make(map[Position]bool)
|
||||
|
||||
for _, bot := range myBots {
|
||||
move := b.computeBotMove(bot, myCores, enemyBots, enemyPositions, energyPositions, usedEnergy, wallPositions, config)
|
||||
move := b.computeBotMove(bot, myCores, enemyBots, enemyPositions, energyPositions, usedEnergy, wallPositions, config, state)
|
||||
if move != nil {
|
||||
moves = append(moves, *move)
|
||||
}
|
||||
|
|
@ -638,7 +638,13 @@ func (b *GuardianBot) computeBotMove(
|
|||
enemyBots []VisibleBot,
|
||||
enemyPositions, energyPositions, usedEnergy, wallPositions map[Position]bool,
|
||||
config Config,
|
||||
state *VisibleState,
|
||||
) *Move {
|
||||
// Priority 1: Escape zone if threatened
|
||||
if zoneDir := getZoneEscapeDirection(bot.Position, state); zoneDir != DirNone {
|
||||
return &Move{Position: bot.Position, Direction: zoneDir}
|
||||
}
|
||||
|
||||
const perimeterRadius = 5
|
||||
const safeZoneRadius = 10
|
||||
|
||||
|
|
@ -806,7 +812,7 @@ func (b *SwarmBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
claimed := make(map[Position]bool) // destinations already claimed by a friendly bot this turn
|
||||
|
||||
for _, bot := range myBots {
|
||||
move := b.computeBotMove(bot, myBotPositions, enemyPositions, energyPositions, swarmCenter, enemyCenter, wallPositions, claimed, config, len(myBots))
|
||||
move := b.computeBotMove(bot, myBotPositions, enemyPositions, energyPositions, swarmCenter, enemyCenter, wallPositions, claimed, config, len(myBots), state)
|
||||
if move != nil {
|
||||
dest := simulateMove(bot.Position, move.Direction, config.Rows, config.Cols)
|
||||
claimed[dest] = true
|
||||
|
|
@ -859,10 +865,16 @@ func (b *SwarmBot) computeBotMove(
|
|||
wallPositions, claimed map[Position]bool,
|
||||
config Config,
|
||||
friendlyCount int,
|
||||
state *VisibleState,
|
||||
) *Move {
|
||||
// Priority 1: Escape zone if threatened
|
||||
if zoneDir := getZoneEscapeDirection(bot.Position, state); zoneDir != DirNone {
|
||||
return &Move{Position: bot.Position, Direction: zoneDir}
|
||||
}
|
||||
|
||||
// Solo mode: when alone or with very few units, gather energy to build the swarm
|
||||
if friendlyCount <= 2 {
|
||||
return b.soloMove(bot, energyPositions, enemyPositions, wallPositions, config)
|
||||
return b.soloMove(bot, energyPositions, enemyPositions, wallPositions, config, state)
|
||||
}
|
||||
|
||||
// Target is enemy center if visible, otherwise map center
|
||||
|
|
@ -942,6 +954,7 @@ func (b *SwarmBot) soloMove(
|
|||
bot VisibleBot,
|
||||
energyPositions, enemyPositions, wallPositions map[Position]bool,
|
||||
config Config,
|
||||
state *VisibleState,
|
||||
) *Move {
|
||||
bestDir := DirNone
|
||||
bestScore := -math.MaxFloat64
|
||||
|
|
@ -1099,6 +1112,13 @@ func (b *HunterBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
break
|
||||
}
|
||||
|
||||
// Priority 1: Escape zone if threatened
|
||||
if zoneDir := getZoneEscapeDirection(bot.Position, state); zoneDir != DirNone {
|
||||
moves = append(moves, Move{Position: bot.Position, Direction: zoneDir})
|
||||
assignedHunters[bot.Position] = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if this bot is close enough to be a hunter
|
||||
dist := distance2(bot.Position, target.Position, config.Rows, config.Cols)
|
||||
if dist < 400 { // Within ~20 tiles
|
||||
|
|
@ -1120,6 +1140,12 @@ func (b *HunterBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
continue
|
||||
}
|
||||
|
||||
// Priority 1: Escape zone if threatened
|
||||
if zoneDir := getZoneEscapeDirection(bot.Position, state); zoneDir != DirNone {
|
||||
moves = append(moves, Move{Position: bot.Position, Direction: zoneDir})
|
||||
continue
|
||||
}
|
||||
|
||||
// Try to gather energy
|
||||
nearestEnergy, nearestDist := Position{}, math.MaxInt32
|
||||
for pos := range energyPositions {
|
||||
|
|
|
|||
|
|
@ -60,12 +60,17 @@ func (b *DefenderBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
|
||||
var dir Direction
|
||||
|
||||
// Priority 1: Intercept nearby enemies
|
||||
if nearestEnemy != nil && enemyDist <= 50 {
|
||||
// Priority 1: Escape zone if threatened
|
||||
if zoneDir := getZoneEscapeDirection(bot.Position, state); zoneDir != DirNone {
|
||||
dir = zoneDir
|
||||
}
|
||||
|
||||
// Priority 2: Intercept nearby enemies
|
||||
if dir == DirNone && nearestEnemy != nil && enemyDist <= 50 {
|
||||
dir = moveToward(bot.Position, *nearestEnemy, wallSet, claimed, config)
|
||||
}
|
||||
|
||||
// Priority 2: Return to core perimeter if too far
|
||||
// Priority 3: Return to core perimeter if too far
|
||||
if dir == DirNone && coreDist > perimeterRadius2 {
|
||||
nearestCore, _ := findNearestPos(bot.Position, coreSet, config)
|
||||
if nearestCore != nil {
|
||||
|
|
@ -73,7 +78,7 @@ func (b *DefenderBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Priority 3: Gather energy within perimeter
|
||||
// Priority 4: Gather energy within perimeter
|
||||
if dir == DirNone && len(energySet) > 0 {
|
||||
nearestEnergy, _ := findNearestPos(bot.Position, energySet, config)
|
||||
if nearestEnergy != nil {
|
||||
|
|
@ -81,7 +86,7 @@ func (b *DefenderBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Priority 4: Patrol near core
|
||||
// Priority 5: Patrol near core
|
||||
if dir == DirNone && len(coreSet) > 0 {
|
||||
nearestCore, _ := findNearestPos(bot.Position, coreSet, config)
|
||||
if nearestCore != nil {
|
||||
|
|
@ -149,6 +154,16 @@ func (b *ScoutBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
claimed := make(map[Position]bool)
|
||||
|
||||
for _, bot := range myBots {
|
||||
// Priority 1: Escape zone if threatened
|
||||
if zoneDir := getZoneEscapeDirection(bot.Position, state); zoneDir != DirNone {
|
||||
dest := simulateMove(bot.Position, zoneDir, config.Rows, config.Cols)
|
||||
if !claimed[dest] {
|
||||
claimed[dest] = true
|
||||
moves = append(moves, Move{Position: bot.Position, Direction: zoneDir})
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if shouldFleeFromEnemies(bot.Position, enemySet, config) {
|
||||
dir := fleeDirection(bot.Position, enemySet, wallSet, config)
|
||||
if dir != DirNone {
|
||||
|
|
@ -244,7 +259,12 @@ func (b *FarmerBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
for _, bot := range myBots {
|
||||
var dir Direction
|
||||
|
||||
if shouldFleeFromEnemies(bot.Position, enemySet, config) {
|
||||
// Priority 1: Escape zone if threatened
|
||||
if zoneDir := getZoneEscapeDirection(bot.Position, state); zoneDir != DirNone {
|
||||
dir = zoneDir
|
||||
}
|
||||
|
||||
if dir == DirNone && shouldFleeFromEnemies(bot.Position, enemySet, config) {
|
||||
dir = fleeDirection(bot.Position, enemySet, wallSet, config)
|
||||
}
|
||||
|
||||
|
|
@ -318,6 +338,18 @@ func (b *PacifistBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
sortBotsByEnemyDist(myBots, enemySet, config)
|
||||
|
||||
for _, bot := range myBots {
|
||||
// Priority 1: Escape zone if threatened
|
||||
if zoneDir := getZoneEscapeDirection(bot.Position, state); zoneDir != DirNone {
|
||||
dest := simulateMove(bot.Position, zoneDir, config.Rows, config.Cols)
|
||||
if !claimed[dest] && !wallSet[dest] {
|
||||
claimed[dest] = true
|
||||
moves = append(moves, Move{Position: bot.Position, Direction: zoneDir})
|
||||
} else {
|
||||
claimed[bot.Position] = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
bestDir := DirNone
|
||||
bestScore := float64(math.MinInt64)
|
||||
|
||||
|
|
@ -394,6 +426,18 @@ func (b *PhalanxBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
claimed := make(map[Position]bool)
|
||||
|
||||
for _, bot := range myBots {
|
||||
// Priority 1: Escape zone if threatened
|
||||
if zoneDir := getZoneEscapeDirection(bot.Position, state); zoneDir != DirNone {
|
||||
dest := simulateMove(bot.Position, zoneDir, config.Rows, config.Cols)
|
||||
if !claimed[dest] && !wallSet[dest] {
|
||||
claimed[dest] = true
|
||||
moves = append(moves, Move{Position: bot.Position, Direction: zoneDir})
|
||||
} else {
|
||||
claimed[bot.Position] = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
bestDir := DirNone
|
||||
bestScore := float64(math.MinInt64)
|
||||
|
||||
|
|
@ -472,6 +516,18 @@ func (b *RaiderBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
if assigned[bot.Position] {
|
||||
continue
|
||||
}
|
||||
|
||||
// Priority 1: Escape zone if threatened
|
||||
if zoneDir := getZoneEscapeDirection(bot.Position, state); zoneDir != DirNone {
|
||||
dest := simulateMove(bot.Position, zoneDir, config.Rows, config.Cols)
|
||||
if !claimed[dest] {
|
||||
claimed[dest] = true
|
||||
moves = append(moves, Move{Position: bot.Position, Direction: zoneDir})
|
||||
assigned[bot.Position] = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
d := distance2(bot.Position, target.Position, config.Rows, config.Cols)
|
||||
if d < 400 {
|
||||
turns := b.engagementTurns[bot.Position]
|
||||
|
|
@ -503,8 +559,15 @@ func (b *RaiderBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
if assigned[bot.Position] {
|
||||
continue
|
||||
}
|
||||
dir := DirNone
|
||||
if len(energySet) > 0 {
|
||||
|
||||
var dir Direction
|
||||
|
||||
// Priority 1: Escape zone if threatened
|
||||
if zoneDir := getZoneEscapeDirection(bot.Position, state); zoneDir != DirNone {
|
||||
dir = zoneDir
|
||||
}
|
||||
|
||||
if dir == DirNone && len(energySet) > 0 {
|
||||
nearest, _ := findNearestPos(bot.Position, energySet, config)
|
||||
if nearest != nil {
|
||||
dir = moveToward(bot.Position, *nearest, wallSet, claimed, config)
|
||||
|
|
@ -592,7 +655,12 @@ func (b *NomadBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
for _, bot := range myBots {
|
||||
var dir Direction
|
||||
|
||||
if shouldFleeFromEnemies(bot.Position, enemySet, config) {
|
||||
// Priority 1: Escape zone if threatened
|
||||
if zoneDir := getZoneEscapeDirection(bot.Position, state); zoneDir != DirNone {
|
||||
dir = zoneDir
|
||||
}
|
||||
|
||||
if dir == DirNone && shouldFleeFromEnemies(bot.Position, enemySet, config) {
|
||||
dir = fleeDirection(bot.Position, enemySet, wallSet, config)
|
||||
}
|
||||
|
||||
|
|
@ -698,7 +766,12 @@ func (b *OpportunistBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
for _, bot := range myBots {
|
||||
var dir Direction
|
||||
|
||||
if bestTarget != nil {
|
||||
// Priority 1: Escape zone if threatened
|
||||
if zoneDir := getZoneEscapeDirection(bot.Position, state); zoneDir != DirNone {
|
||||
dir = zoneDir
|
||||
}
|
||||
|
||||
if dir == DirNone && bestTarget != nil {
|
||||
dir = moveToward(bot.Position, *bestTarget, wallSet, claimed, config)
|
||||
}
|
||||
|
||||
|
|
@ -790,7 +863,13 @@ func (b *AssassinBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
|
||||
for _, bot := range myBots {
|
||||
var dir Direction
|
||||
if target != nil {
|
||||
|
||||
// Priority 1: Escape zone if threatened
|
||||
if zoneDir := getZoneEscapeDirection(bot.Position, state); zoneDir != DirNone {
|
||||
dir = zoneDir
|
||||
}
|
||||
|
||||
if dir == DirNone && target != nil {
|
||||
dir = moveToward(bot.Position, *target, wallSet, claimed, config)
|
||||
}
|
||||
if dir == DirNone {
|
||||
|
|
@ -840,6 +919,18 @@ func (b *KamikazeBot) GetMoves(state *VisibleState) ([]Move, error) {
|
|||
sortBotsByEnemyDist(myBots, enemySet, config)
|
||||
|
||||
for _, bot := range myBots {
|
||||
// Priority 1: Escape zone if threatened
|
||||
if zoneDir := getZoneEscapeDirection(bot.Position, state); zoneDir != DirNone {
|
||||
dest := simulateMove(bot.Position, zoneDir, config.Rows, config.Cols)
|
||||
if !claimed[dest] && !wallSet[dest] {
|
||||
claimed[dest] = true
|
||||
moves = append(moves, Move{Position: bot.Position, Direction: zoneDir})
|
||||
} else {
|
||||
claimed[bot.Position] = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
nearestEnemy, _ := findNearestPos(bot.Position, enemySet, config)
|
||||
|
||||
bestDir := DirNone
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue