Hole Game

v0.2-bots

Bots & leaderboard · Three AI rivals (Nova / GravityKid / VoidRider) compete for the same cubes. Bigger absorbs smaller. Defeated holes respawn after 1.4 s with brief ghost-blink invulnerability. Live leaderboard top-left.

Build notes

Personality (data-driven)

  • BotPersonalityDefinition ScriptableObject with six knobs per bot: speedMultiplier, valueBias, chaseBias, dangerThreshold (radius ratio, default 1.15× = 'bigger than me'), dangerAvoidanceRadius, chaseRadius. Plus a color for the disc tint.
  • Nova — greedy + cautious. High valueBias (chases big cubes), low chaseBias, normal danger.
  • GravityKid — fast + conservative. High speedMultiplier, low chaseBias, balanced everything else.
  • VoidRider — bully. High chaseBias (loves chasing smaller players), low dangerThreshold (overconfident).

Decision tree (re-evaluated every 0.35 s)

  • Flee — find any rival whose radius is ≥ myRadius * dangerThreshold within dangerAvoidanceRadius. If one exists, target a point opposite the threat, clamped to city bounds. Wins over everything else.
  • Chase — find the nearest smaller rival within chaseRadius. Roll random against chaseBias + 0.3; if it passes, target the rival's current position. The +0.3 means even cautious bots occasionally pounce.
  • Hunt cubes — score each eligible cube as cube.scoreValue * valueBias - distance * 2.5. Highest score wins.
  • Wander — random point within wanderAreaRadius, clamped.

Movement

  • Direct vector step toward target via Rigidbody.MovePosition in FixedUpdate.
  • Capped at (8 - radius * 0.4) * speedMultiplier units/sec — bigger holes are slower (built-in soft anti-snowball).
  • Cap clamps at 2.5 m/s minimum so a maxed-out hole still moves.

Hole-vs-hole absorption

  • Both holes' triggers overlap. The bigger one's OnTriggerEnter checks myRadius >= otherRadius * 1.15; if true, Growth.Consume(otherRadius * 0.6 mass, otherRadius * 200 score) and call other.Defeat().
  • Smaller hole's check fails — they get absorbed.
  • Invulnerable holes are ignored (no double-hit during respawn).

Respawn

  • 1. Hide all MeshRenderers + disable collider. 2. Wait 1.4 s. 3. Teleport to a random ring point 7–14 units from origin. 4. Growth.ResetTo(currentRadius * 0.85) — 15% size penalty. 5. Re-show + re-enable, set Invulnerable = true, blink renderers every 0.18 s. 6. After 1.5 s, stop blinking, drop invulnerability.
  • Score is *not* reset — bot keeps whatever it earned.

Known limitations (intentional, not bugs)

  • No pathfinding — bots move in straight lines and clip through buildings.
  • No memory between ticks — if a flee target moves out of dangerAvoidanceRadius, the bot stops fleeing immediately.
  • Bots don't coordinate — each runs its own decision tree independently.
  • Cube targets aren't reserved — two bots can both chase the same cube; whoever arrives first eats it.