ai-code-battle/bots/guardian/game.php
jedarden 6f1b50384c Complete Phase 2: HTTP protocol and 6 strategy bots
Phase 2 Implementation:
- HMAC authentication for engine-to-bot communication
  - Request signing with timestamp anti-replay
  - Response signing for integrity verification
- HTTP bot client with timeout and crash detection
  - Per-turn 3s timeout, 10 consecutive failure crash threshold
  - Move validation (position ownership, direction validity)
- Integration tests for HTTP match execution
- 6 strategy bots in 6 languages:
  - RandomBot (Python): Random valid moves - rating floor
  - GathererBot (Go): Energy-focused with combat avoidance
  - RusherBot (Rust): Aggressive core rushing via BFS
  - GuardianBot (PHP): Defensive core protection
  - SwarmBot (TypeScript): Formation-based group combat
  - HunterBot (Java): Target isolation and hunting

All bots include:
- HMAC signature verification
- Dockerfile for containerization
- README documentation

All engine tests passing (32+ tests)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 07:00:38 -04:00

198 lines
4.9 KiB
PHP

<?php
/**
* Game state types for AI Code Battle protocol.
*/
/**
* Position on the grid
*/
class Position {
public int $row;
public int $col;
public function __construct(int $row, int $col) {
$this->row = $row;
$this->col = $col;
}
public static function fromArray(array $data): self {
return new self($data['row'], $data['col']);
}
public function toArray(): array {
return ['row' => $this->row, 'col' => $this->col];
}
/**
* Move in a direction with toroidal wrapping
*/
public function moveToward(string $dir, int $rows, int $cols): Position {
switch ($dir) {
case 'N':
return new Position(($this->row - 1 + $rows) % $rows, $this->col);
case 'E':
return new Position($this->row, ($this->col + 1) % $cols);
case 'S':
return new Position(($this->row + 1) % $rows, $this->col);
case 'W':
return new Position($this->row, ($this->col - 1 + $cols) % $cols);
default:
return clone $this;
}
}
/**
* Calculate squared distance with toroidal wrapping
*/
public function distance2(Position $other, int $rows, int $cols): int {
$dr = abs($this->row - $other->row);
$dc = abs($this->col - $other->col);
$dr = min($dr, $rows - $dr);
$dc = min($dc, $cols - $dc);
return $dr * $dr + $dc * $dc;
}
}
/**
* Game configuration
*/
class GameConfig {
public int $rows;
public int $cols;
public int $maxTurns;
public int $visionRadius2;
public int $attackRadius2;
public int $spawnCost;
public int $energyInterval;
public static function fromArray(array $data): self {
$config = new self();
$config->rows = $data['rows'];
$config->cols = $data['cols'];
$config->maxTurns = $data['max_turns'];
$config->visionRadius2 = $data['vision_radius2'];
$config->attackRadius2 = $data['attack_radius2'];
$config->spawnCost = $data['spawn_cost'];
$config->energyInterval = $data['energy_interval'];
return $config;
}
}
/**
* Player info
*/
class PlayerInfo {
public int $id;
public int $energy;
public int $score;
public static function fromArray(array $data): self {
$info = new self();
$info->id = $data['id'];
$info->energy = $data['energy'];
$info->score = $data['score'];
return $info;
}
}
/**
* Visible bot
*/
class VisibleBot {
public Position $position;
public int $owner;
public static function fromArray(array $data): self {
$bot = new self();
$bot->position = Position::fromArray($data['position']);
$bot->owner = $data['owner'];
return $bot;
}
}
/**
* Visible core
*/
class VisibleCore {
public Position $position;
public int $owner;
public bool $active;
public static function fromArray(array $data): self {
$core = new self();
$core->position = Position::fromArray($data['position']);
$core->owner = $data['owner'];
$core->active = $data['active'];
return $core;
}
}
/**
* Fog-filtered game state
*/
class GameState {
public string $matchId;
public int $turn;
public GameConfig $config;
public PlayerInfo $you;
/** @var VisibleBot[] */
public array $bots = [];
/** @var Position[] */
public array $energy = [];
/** @var VisibleCore[] */
public array $cores = [];
/** @var Position[] */
public array $walls = [];
/** @var VisibleBot[] */
public array $dead = [];
public static function fromArray(array $data): self {
$state = new self();
$state->matchId = $data['match_id'];
$state->turn = $data['turn'];
$state->config = GameConfig::fromArray($data['config']);
$state->you = PlayerInfo::fromArray($data['you']);
foreach ($data['bots'] ?? [] as $bot) {
$state->bots[] = VisibleBot::fromArray($bot);
}
foreach ($data['energy'] ?? [] as $pos) {
$state->energy[] = Position::fromArray($pos);
}
foreach ($data['cores'] ?? [] as $core) {
$state->cores[] = VisibleCore::fromArray($core);
}
foreach ($data['walls'] ?? [] as $pos) {
$state->walls[] = Position::fromArray($pos);
}
foreach ($data['dead'] ?? [] as $bot) {
$state->dead[] = VisibleBot::fromArray($bot);
}
return $state;
}
}
/**
* A single move command
*/
class Move {
public Position $position;
public string $direction;
public function __construct(Position $position, string $direction) {
$this->position = $position;
$this->direction = $direction;
}
public function toArray(): array {
return [
'position' => $this->position->toArray(),
'direction' => $this->direction
];
}
}