Spec: Phantom Meteor Gravity Movement — bullet dodging via learned danger field #116

Open
opened 2026-08-29 17:54:27 +02:00 by SirStone · 0 comments
Owner

Problem Statement

EvoBot currently has no intelligent movement system. Against bots with statistical or evolved guns, standing still or moving randomly makes EvoBot an easy target. Traditional wave surfing is effective but discrete (pick bin A or B) and widely used. We want a continuous, physics-based movement system that learns the enemy's aim patterns and dodges accordingly.

Solution

Implement a phantom meteor gravity movement system. When the enemy fires, spawn a spread of phantom bullets ("meteors") across the possible escape angle arc, weighted by a learned danger histogram. The bot moves by summing repulsive forces from walls, phantom bullets, and the enemy bot. Over time, the danger histogram learns where the enemy aims, and the bot naturally gravitates toward safe GF zones.

This gives wave surfing's statistical learning with gravity movement's smooth, continuous, non-linear paths — harder to predict and aim at than discrete wave surfing.

User Stories

  1. As EvoBot, I want to detect when the enemy fires a bullet (via energy drop between scans), so that I can react to incoming threats.
  2. As EvoBot, I want to estimate the enemy bullet's speed from the detected energy drop, so that I know how fast the threat approaches.
  3. As EvoBot, I want to spawn 20-30 phantom bullets across the full escape angle arc (-MEA to +MEA) when a fire is detected, so that I model the spread of possible bullet trajectories.
  4. As EvoBot, I want each phantom bullet weighted by a danger histogram bin, so that likely enemy aim angles exert stronger repulsive force.
  5. As EvoBot, I want the danger histogram to start uniform (equal weight across all GF bins), so that initial movement is conservative against unknown guns.
  6. As EvoBot, I want phantom bullets to travel as moving points at the detected bullet speed, so that gravity intensifies as threats approach.
  7. As EvoBot, I want the gravity engine to compute repulsive force from each phantom bullet using inverse-square distance, so that nearby threats dominate movement decisions.
  8. As EvoBot, I want walls to exert repulsive force that grows as I approach them, so that I never drive into walls.
  9. As EvoBot, I want the enemy bot to exert a mild repulsive force, so that I maintain a preferred fighting distance.
  10. As EvoBot, I want all forces (phantoms, walls, enemy) summed into a single movement vector each tick, so that movement is smooth and continuous.
  11. As EvoBot, I want to move in the direction of the summed force vector each tick, so that my path is a natural response to the current threat field.
  12. As EvoBot, I want wall smoothing on the movement path, so that I curve away from walls instead of stopping or reversing abruptly.
  13. As EvoBot, I want to track real bullet waves (expanding circles at bullet speed from enemy fire position), so that I know when a wave passes my position.
  14. As EvoBot, I want to record which GF bin the real bullet occupied when its wave reaches me, so that I learn the enemy's aim pattern.
  15. As EvoBot, I want to increase the danger histogram weight for the GF bin where the real bullet was, so that future phantom spreads reflect learned patterns.
  16. As EvoBot, I want phantom bullets that pass beyond my position or exit the arena to be removed, so that resolved threats stop affecting movement.
  17. As EvoBot, I want the danger histogram to persist across rounds within a battle, so that learning accumulates over the full fight.
  18. As EvoBot, I want movement to work from tick 1 with uniform danger weights, so that there is no cold-start paralysis.
  19. As EvoBot, I want the gravity system to handle multiple simultaneous enemy bullet waves, so that overlapping threats create complex but navigable force fields.
  20. As EvoBot, I want the movement vector to respect Robocode's max velocity (8 units/tick) and max turn rate, so that movement commands are physically valid.
  21. As EvoBot, I want the bot to move perpendicular to the enemy (orbital movement) as a baseline when no bullets are detected, so that I'm not a stationary target between waves.
  22. As EvoBot, I want the danger histogram to use 30-50 bins across the GF range [-1, +1], so that aim pattern resolution is sufficient without overfitting.
  23. As EvoBot, I want the force magnitudes tunable (wall repulsion strength, phantom repulsion strength, enemy repulsion strength, preferred distance), so that the system can be calibrated.

Implementation Decisions

  • Fire detection. Enemy fire is detected by tracking enemy energy between scans. An energy drop of 0.1-3.0 (legal bullet power range) with no other explanation (wall hit, ram) indicates a bullet was fired. Store enemy position at fire time.
  • Phantom spread. On detected fire, spawn N phantom bullets (N=25 default) uniformly spaced from -MEA to +MEA relative to the enemy-to-bot bearing at fire time. Each phantom has: origin (enemy position), angle, speed (20 - 3 × detected power), and weight from the danger histogram bin it corresponds to.
  • Danger histogram. 41 bins covering GF [-1, +1] (bin width ~0.05). Initialized uniform (all 1.0). Updated when a wave passes: increment the bin matching the real bullet's GF. Decay: none initially — pure accumulation. Add decay when overfitting is observed.
  • Wave tracking. Each detected fire spawns a wave: expanding circle from enemy fire position at bullet speed. When wave radius ≥ distance to bot at fire time, the wave has "passed." At that point, check which GF bin the bot was in relative to the wave's origin and bearing. Record in histogram. Remove wave.
  • Gravity forces. All forces are 2D vectors summed per tick:
    • Phantom bullet repulsion: F = weight × K_bullet / dist² directed away from phantom position. K_bullet is a tunable constant.
    • Wall repulsion: F = K_wall / dist² directed away from nearest wall point. Applied from all four walls independently.
    • Enemy repulsion: F = K_enemy / dist² directed away from enemy. Optional mild attraction at long range to maintain fighting distance (spring force around preferred distance).
  • Movement execution. Sum all forces → normalize to direction → set bot velocity toward that direction, capped at max speed (8). Turn rate capped at max (10°/tick for body). If summed force is near zero, default to orbital movement (perpendicular to enemy bearing).
  • Wall smoothing. Before committing to a movement direction, project the bot's position forward N ticks. If it would hit a wall, rotate the movement vector away from the wall until the projection is safe. This creates smooth curves rather than abrupt stops.
  • No persistence across battles. Danger histogram resets each battle. Movement is reactive, not evolved — no weight files needed.
  • Module structure. New module libs/gravity.nim containing the gravity engine, phantom tracking, danger histogram, and force computation. EvoBot.nim calls it each tick from run() or onScannedBot.

Testing Decisions

What makes a good test

Tests assert external behavior: given a force configuration, does the bot move in the expected direction? No mocking internal force calculations.

Seam 1: Gravity engine (primary)

Feed known phantom positions, wall positions, and bot position into the gravity engine. Assert:

  • Force vector points away from nearest threat.
  • Wall repulsion dominates when bot is near wall.
  • Multiple opposing phantoms produce a force toward the gap between them.
  • Zero phantoms + no walls → zero force (or orbital default).

Seam 2: Danger histogram learning

Feed a sequence of wave-pass events with known GF bins. Assert:

  • Histogram bins increase for recorded GFs.
  • Phantom weights reflect histogram after update.
  • Uniform initial state produces equal weights.

Prior art

tests/handler_seam_v2.nim — assert-based self-check pattern. New tests follow the same style.

Out of Scope

  • Danger histogram persistence across battles (add when opponents are consistent across matches).
  • Adaptive force constants (K_bullet, K_wall, K_enemy) — start with hand-tuned values, evolve later if needed.
  • Anti-gravity from teammates (no team battles yet).
  • Movement prediction for the enemy (gun handles that separately).
  • Combining gravity movement with the GA (movement is physics-based, not evolved — keep concerns separate).

Further Notes

  • This system creates an implicit arms race with statistical enemy guns. As the enemy learns our movement pattern, our histogram learns their aim adjustment, and we shift again. This converges toward uniform GF distribution — maximum dodge difficulty.
  • The gravity approach produces smooth, non-linear curves that are inherently harder for pattern-matching guns to predict compared to discrete wave surfing's "pick a direction" reversals.
  • Force constants (K_bullet, K_wall, K_enemy) are the primary tuning knobs. Start conservative (strong wall repulsion, moderate phantom repulsion) and tune during testing.
  • The phantom meteor spread can be visualized using the existing SVG debug graphics — draw faint dots for each phantom, colored by danger weight. This helps tune the system visually.
## Problem Statement EvoBot currently has no intelligent movement system. Against bots with statistical or evolved guns, standing still or moving randomly makes EvoBot an easy target. Traditional wave surfing is effective but discrete (pick bin A or B) and widely used. We want a continuous, physics-based movement system that learns the enemy's aim patterns and dodges accordingly. ## Solution Implement a **phantom meteor gravity movement** system. When the enemy fires, spawn a spread of phantom bullets ("meteors") across the possible escape angle arc, weighted by a learned danger histogram. The bot moves by summing repulsive forces from walls, phantom bullets, and the enemy bot. Over time, the danger histogram learns where the enemy aims, and the bot naturally gravitates toward safe GF zones. This gives wave surfing's statistical learning with gravity movement's smooth, continuous, non-linear paths — harder to predict and aim at than discrete wave surfing. ## User Stories 1. As EvoBot, I want to detect when the enemy fires a bullet (via energy drop between scans), so that I can react to incoming threats. 2. As EvoBot, I want to estimate the enemy bullet's speed from the detected energy drop, so that I know how fast the threat approaches. 3. As EvoBot, I want to spawn 20-30 phantom bullets across the full escape angle arc (-MEA to +MEA) when a fire is detected, so that I model the spread of possible bullet trajectories. 4. As EvoBot, I want each phantom bullet weighted by a danger histogram bin, so that likely enemy aim angles exert stronger repulsive force. 5. As EvoBot, I want the danger histogram to start uniform (equal weight across all GF bins), so that initial movement is conservative against unknown guns. 6. As EvoBot, I want phantom bullets to travel as moving points at the detected bullet speed, so that gravity intensifies as threats approach. 7. As EvoBot, I want the gravity engine to compute repulsive force from each phantom bullet using inverse-square distance, so that nearby threats dominate movement decisions. 8. As EvoBot, I want walls to exert repulsive force that grows as I approach them, so that I never drive into walls. 9. As EvoBot, I want the enemy bot to exert a mild repulsive force, so that I maintain a preferred fighting distance. 10. As EvoBot, I want all forces (phantoms, walls, enemy) summed into a single movement vector each tick, so that movement is smooth and continuous. 11. As EvoBot, I want to move in the direction of the summed force vector each tick, so that my path is a natural response to the current threat field. 12. As EvoBot, I want wall smoothing on the movement path, so that I curve away from walls instead of stopping or reversing abruptly. 13. As EvoBot, I want to track real bullet waves (expanding circles at bullet speed from enemy fire position), so that I know when a wave passes my position. 14. As EvoBot, I want to record which GF bin the real bullet occupied when its wave reaches me, so that I learn the enemy's aim pattern. 15. As EvoBot, I want to increase the danger histogram weight for the GF bin where the real bullet was, so that future phantom spreads reflect learned patterns. 16. As EvoBot, I want phantom bullets that pass beyond my position or exit the arena to be removed, so that resolved threats stop affecting movement. 17. As EvoBot, I want the danger histogram to persist across rounds within a battle, so that learning accumulates over the full fight. 18. As EvoBot, I want movement to work from tick 1 with uniform danger weights, so that there is no cold-start paralysis. 19. As EvoBot, I want the gravity system to handle multiple simultaneous enemy bullet waves, so that overlapping threats create complex but navigable force fields. 20. As EvoBot, I want the movement vector to respect Robocode's max velocity (8 units/tick) and max turn rate, so that movement commands are physically valid. 21. As EvoBot, I want the bot to move perpendicular to the enemy (orbital movement) as a baseline when no bullets are detected, so that I'm not a stationary target between waves. 22. As EvoBot, I want the danger histogram to use 30-50 bins across the GF range [-1, +1], so that aim pattern resolution is sufficient without overfitting. 23. As EvoBot, I want the force magnitudes tunable (wall repulsion strength, phantom repulsion strength, enemy repulsion strength, preferred distance), so that the system can be calibrated. ## Implementation Decisions - **Fire detection.** Enemy fire is detected by tracking enemy energy between scans. An energy drop of 0.1-3.0 (legal bullet power range) with no other explanation (wall hit, ram) indicates a bullet was fired. Store enemy position at fire time. - **Phantom spread.** On detected fire, spawn N phantom bullets (N=25 default) uniformly spaced from -MEA to +MEA relative to the enemy-to-bot bearing at fire time. Each phantom has: origin (enemy position), angle, speed (20 - 3 × detected power), and weight from the danger histogram bin it corresponds to. - **Danger histogram.** 41 bins covering GF [-1, +1] (bin width ~0.05). Initialized uniform (all 1.0). Updated when a wave passes: increment the bin matching the real bullet's GF. Decay: none initially — pure accumulation. Add decay when overfitting is observed. - **Wave tracking.** Each detected fire spawns a wave: expanding circle from enemy fire position at bullet speed. When wave radius ≥ distance to bot at fire time, the wave has "passed." At that point, check which GF bin the bot was in relative to the wave's origin and bearing. Record in histogram. Remove wave. - **Gravity forces.** All forces are 2D vectors summed per tick: - Phantom bullet repulsion: `F = weight × K_bullet / dist²` directed away from phantom position. K_bullet is a tunable constant. - Wall repulsion: `F = K_wall / dist²` directed away from nearest wall point. Applied from all four walls independently. - Enemy repulsion: `F = K_enemy / dist²` directed away from enemy. Optional mild attraction at long range to maintain fighting distance (spring force around preferred distance). - **Movement execution.** Sum all forces → normalize to direction → set bot velocity toward that direction, capped at max speed (8). Turn rate capped at max (10°/tick for body). If summed force is near zero, default to orbital movement (perpendicular to enemy bearing). - **Wall smoothing.** Before committing to a movement direction, project the bot's position forward N ticks. If it would hit a wall, rotate the movement vector away from the wall until the projection is safe. This creates smooth curves rather than abrupt stops. - **No persistence across battles.** Danger histogram resets each battle. Movement is reactive, not evolved — no weight files needed. - **Module structure.** New module `libs/gravity.nim` containing the gravity engine, phantom tracking, danger histogram, and force computation. EvoBot.nim calls it each tick from `run()` or `onScannedBot`. ## Testing Decisions ### What makes a good test Tests assert external behavior: given a force configuration, does the bot move in the expected direction? No mocking internal force calculations. ### Seam 1: Gravity engine (primary) Feed known phantom positions, wall positions, and bot position into the gravity engine. Assert: - Force vector points away from nearest threat. - Wall repulsion dominates when bot is near wall. - Multiple opposing phantoms produce a force toward the gap between them. - Zero phantoms + no walls → zero force (or orbital default). ### Seam 2: Danger histogram learning Feed a sequence of wave-pass events with known GF bins. Assert: - Histogram bins increase for recorded GFs. - Phantom weights reflect histogram after update. - Uniform initial state produces equal weights. ### Prior art `tests/handler_seam_v2.nim` — assert-based self-check pattern. New tests follow the same style. ## Out of Scope - Danger histogram persistence across battles (add when opponents are consistent across matches). - Adaptive force constants (K_bullet, K_wall, K_enemy) — start with hand-tuned values, evolve later if needed. - Anti-gravity from teammates (no team battles yet). - Movement prediction for the enemy (gun handles that separately). - Combining gravity movement with the GA (movement is physics-based, not evolved — keep concerns separate). ## Further Notes - This system creates an implicit **arms race** with statistical enemy guns. As the enemy learns our movement pattern, our histogram learns their aim adjustment, and we shift again. This converges toward uniform GF distribution — maximum dodge difficulty. - The gravity approach produces smooth, non-linear curves that are inherently harder for pattern-matching guns to predict compared to discrete wave surfing's "pick a direction" reversals. - Force constants (K_bullet, K_wall, K_enemy) are the primary tuning knobs. Start conservative (strong wall repulsion, moderate phantom repulsion) and tune during testing. - The phantom meteor spread can be visualized using the existing SVG debug graphics — draw faint dots for each phantom, colored by danger weight. This helps tune the system visually.
SirStone added the ready-for-agent label 2026-08-29 17:54:30 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: SirStone/SirRoboGarage#116