feat: initial commit with M1-M4 implementation

This commit is contained in:
Vladimir V Maksimov
2026-05-12 10:54:09 +03:00
commit aece34fe73
24 changed files with 1770 additions and 0 deletions

View File

@@ -0,0 +1,102 @@
# Hockey Game Design Specification
## 1. Game Overview
A top-down autonomous hockey simulation where the user observes two teams playing. The game features 6v6 teams (1 goalie, 2 defenders, 3 forwards) plus 1 referee. The match duration is 10 minutes with goals, resets, and scoring.
## 2. Technical Stack
- **Language**: Go
- **Graphics Engine**: Ebitengine (2D rendering)
- **Physics**: Simple 2D collision detection and response
- **AI**: Steering Behaviors combined with tactical zone management
## 3. Game Rules & Setup
- **Team Composition**: 6v6 (1 goalie, 2 defenders, 3 forwards) per team, plus 1 referee
- **Match Duration**: 10 minutes
- **Objective**: Score more goals than the opponent
- **Reset**: After each goal, players and puck reset to starting positions
- **Scoring**: Goals scored when puck enters opponent's goal area
## 4. Architecture
### 4.1 Simulation Engine
- **World/Game State**: Manages all entities and game state
- **Physics**: Handles collisions, bounces, and movement
- **Game Timer**: Tracks match time and game state
### 4.2 Entities
- **Puck**: Position, velocity, friction
- **Player**: Role, team, steering behavior
- **Referee**: Follows puck with larger arrival radius
### 4.3 AI System
- **Zone Manager**: Defines defensive, middle, and attack zones
- **Steering Logic**: Weighted sum of vectors (Seek, Separation, Arrival, Avoidance)
- **Role-based Behaviors**: Specific AI for each player role
### 4.4 Rendering
- Ebitengine-based 2D rendering of the rink, players, and puck
## 5. File Structure
```
cmd/game/main.go
internal/game/engine.go
internal/game/world.go
internal/game/timer.go
internal/entities/player.go
internal/entities/puck.go
internal/entities/referee.go
internal/ai/steering.go
internal/ai/zones.go
internal/ai/brain.go
internal/physics/collisions.go
internal/render/renderer.go
```
## 6. Detailed Logic
### 6.1 Game Loop
1. AI Update
2. Physics Update
3. Game State Update (goals, timer)
4. Render
### 6.2 Steering Behaviors
- **Seek**: Calculate vector towards target
- **Separation**: Avoid nearby teammates
- **Arrival**: Slow down as target is reached
- **Avoidance**: Prevent sticking to boards
- **Weighted Sum**: Combine behaviors with tactical zone weights
### 6.3 Role-based AI
#### Forwards
- Aggressive Seek(Puck) in attack zone
- Return to home zone if puck is far
- Use Support behavior to create passing options
#### Defenders
- High Separation(Opponents)
- Seek(Puck) in defense/middle zones
- Use Support behavior to coordinate attacks and transitions
#### Goalie
- Restricted to goal area
- Seek(Puck) primarily on X-axis
- Maintain position in goal area
#### Referee
- Gentle Seek(Puck) to stay nearby
- Larger arrival radius than players
### 6.4 Puck Interaction
- **Possession**: When within radius R
- **Shooting**: Triggered by probability or position
- **Passing**: A player with the puck can pass to a teammate if they are in a favorable position (closer to the opponent's goal and not heavily blocked). A pass is a high-velocity impulse towards the teammate.
- **Control**: Radius-based capture and release
- **Friction**: Gradual velocity reduction over time
## 7. Physics
- **Collisions**: Simple 2D elastic collisions (player-player, player-puck, puck-boards)
- **Bounces**: Puck bounces off walls with energy loss
- **Friction**: Puck gradually slows down over time
- **Movement**: Velocity-based position updates

View File

@@ -0,0 +1,31 @@
# Design: Physics & Puck Dynamics (M2)
## Goal
Implement basic physical properties for the puck to make movement feel natural: gradual slowing down (friction) and energy loss upon hitting boundaries (restitution).
## Architecture
The physics logic will be integrated into the `World.Update` loop. Constants for physical properties will be defined to allow easy tuning of the game feel.
## Implementation Details
### 1. Physical Constants
The following constants will be introduced:
- `PuckFriction`: Multiplier applied to velocity every frame (e.g., `0.99`).
- `PuckRestitution`: Multiplier applied to velocity upon boundary collision (e.g., `0.8`).
- `StopThreshold`: Minimum velocity magnitude below which the puck is forced to a complete stop (e.g., `0.1`).
### 2. Movement Logic (World.Update)
The update loop for the puck will follow these steps:
1. **Apply Friction**: `velocity = velocity * PuckFriction`.
2. **Stop Check**: If `length(velocity) < StopThreshold`, then `velocity = (0, 0)`.
3. **Position Update**: `position = position + velocity`.
4. **Boundary Collision**:
- If the puck hits a boundary (considering its radius):
- Invert the velocity component perpendicular to the boundary.
- Multiply the resulting velocity by `PuckRestitution`.
- **Correction**: Snap the puck's position to be exactly on the boundary edge to prevent it from getting stuck inside the wall.
## Success Criteria
- The puck gradually slows down and eventually stops.
- The puck loses speed after bouncing off the walls.
- The puck does not get stuck in the boundaries.

View File

@@ -0,0 +1,49 @@
# Design: Player AI and Steering System
## Goal
Implement autonomous players that move organically on the ice, reacting to the puck and each other, with randomized individual characteristics.
## Architecture
The system is based on "Steering Behaviors", where players calculate a desired velocity based on multiple forces, which are then summed and applied to the player's physics.
### 1. Player Attributes
Each player is assigned a set of attributes that define their physical and mental capabilities.
- **Budget**: 10 points total.
- **Distribution**: Each attribute gets a minimum of 1 point; the remaining 6 points are distributed randomly.
- **Attributes**:
- **Speed**: Affects `MaxSpeed`. Higher speed allows faster movement.
- **Agility**: Affects `MaxForce`. Higher agility allows sharper turns and faster acceleration.
- **Strength**: Affects collision impulse. Stronger players push the puck and opponents further.
- **Tactics**: Affects the weight of steering behaviors (e.g., how effectively they seek the puck).
### 2. Steering Behaviors
The `internal/game/steering.go` module will provide the following forces:
- **Seek(target)**: Direct force towards the target (puck).
- **Arrive(target)**: Similar to Seek, but slows down as the player reaches the target to prevent orbiting.
- **Avoidance(others)**: A strong repulsive force to prevent players from overlapping.
- **ZoneConstraint(homeZone)**: A soft force pulling the player back to their half of the ice if they stray too far.
### 3. File Structure
- `internal/entities/player.go`: Defines `Player` and `Attributes` structs and the `NewPlayer` constructor with random attribute logic.
- `internal/game/steering.go`: Pure functions for calculating steering vectors.
- `internal/game/world.go`:
- Manages the list of players.
- Updates player positions using steering forces.
- Handles collisions (Player-Puck, Player-Player) using the `Strength` attribute.
- `internal/render/renderer.go`: Renders players as colored circles (Red/Blue), with size slightly scaled by `Strength`.
## Data Flow
1. **Per-Tick Update**:
- Calculate Steering Forces: `TotalForce = (Seek * Tactics) + Avoidance + ZoneConstraint`.
- Apply Physics: `Acceleration = TotalForce / Mass` (capped by `Agility`).
- Update Velocity: `Velocity += Acceleration` (capped by `Speed`).
- Update Position: `Position += Velocity`.
2. **Collision Resolution**:
- If Player touches Puck: Transfer momentum based on `Strength` and `Velocity`.
- If Player touches Player: Repel based on relative `Strength`.
3. **Rendering**: Draw players at their current positions.
## Edge Cases
- **Overlapping**: High-priority `Avoidance` force prevents players from stacking.
- **Boundary Control**: Players are clamped to the ice rink boundaries.
- **Damping**: A small friction coefficient is applied to velocity to prevent infinite sliding.

View File

@@ -0,0 +1,55 @@
# Design: Players & AI Behavior (M3)
## Goal
Implement autonomous players with role-based and zone-based AI that move realistically and maintain tactical positions without user input.
## Architecture
The system separates player data from behavior logic.
### 1. Components
- **`Player` Entity**: A data structure containing physical properties (position, velocity, radius), team identity (Red/Blue), role (Striker/Defender), and movement constraints (MaxSpeed, Acceleration).
- **`PlayerAI` Logic**: A set of rules that determine the "Target Point" based on the puck's position and the player's role.
- **`World` Integration**: The world manages a collection of players, updates their AI state, applies steering physics, and handles boundary clamping.
## Detailed Design
### 1. Player Roles & Behavior
Players operate based on a hybrid of roles and zones.
#### Defender
- **Home Zone**: Their own half of the field.
- **Behavior**:
- **Puck in opponent's half**: Return to `HomePosition` (positioned in front of their own goal).
- **Puck in their half**: Move to a point on the line between the `Puck` and `Their Goal` to block the path.
#### Striker
- **Home Zone**: Center of the field and opponent's half.
- **Behavior**:
- **Puck in their zone**: Actively chase the `Puck`.
- **Puck deep in their own half**: Maintain a position near the center or between the center and the opponent's goal, avoiding overcrowding the defender's zone.
### 2. Movement Physics (Steering)
To avoid "robotic" movement, players use a steering-like approach:
1. **Target Point**: AI determines where the player *wants* to be.
2. **Desired Velocity**: A vector from current position to target, normalized and scaled by `MaxSpeed`.
3. **Acceleration**: The difference between `Desired Velocity` and `Current Velocity` is applied as a force, capped by the `Acceleration` parameter.
4. **Integration**: `Position += Velocity`.
### 3. File Structure
- `internal/entities/player.go`: Defines `Team`, `Role`, and `Player` struct.
- `internal/game/world.go`:
- Adds `Players []*Player` to `World`.
- Defines goal coordinates (`GoalLeft`, `GoalRight`).
- Implements `updatePlayersAI()` and integrates it into `World.Update`.
- `internal/game/world_test.go`: Tests for AI target selection and movement.
### 4. Edge Cases & Polish
- **Anti-Clumping**: Small repulsive force between teammates to prevent them from overlapping perfectly.
- **Dead Zone**: A small radius around the target point where the player stops moving to prevent jittering.
- **Boundary Clamping**: Players are clamped to the field boundaries to prevent them from leaving the screen.
## Success Criteria
- Players are rendered on screen with distinct colors.
- Defenders stay back when the puck is far and block the goal when it's near.
- Strikers chase the puck but don't crowd their own defenders.
- Movement is smooth (accelerating/decelerating) rather than instant.

View File

@@ -0,0 +1,44 @@
# Design: Tactical AI and Goalie (M4)
## Goal
Implement a "smart" goalie that covers angles and a zone-based tactical system for players to make the game feel more professional and less like a simple "chase the puck" simulation.
## Architecture
The system extends the existing steering-based movement. Instead of a simple target, the target is now determined by a combination of the player's role and the current game zone.
### 1. Goalie AI (The Interceptor)
The goalie's primary objective is to block the path between the puck and the goal center.
- **Anchor Point**: The center of the goalie's own goal.
- **Positioning Logic**:
- Calculate the vector from the goal center to the puck.
- The goalie attempts to stay on this vector, effectively "cutting off the angle".
- **Constraint**: The goalie is restricted to a small semi-circle (radius ~100-150px) around the goal center to prevent them from wandering into the midfield.
- **Idle State**: When the puck is far away, the goalie returns to the center of the goal.
- **Movement**: Uses the existing steering physics for smooth transitions.
### 2. Zone-Based Tactics
The field is divided into three zones for each team: **Defensive**, **Middle**, and **Offensive**.
| Role | Defensive Zone | Middle Zone | Offensive Zone |
| :--- | :--- | :--- | :--- |
| **Striker** | Return to Middle/Offensive | Support attack | Aggressively pursue puck |
| **Defender** | Aggressively pursue puck | Maintain position/Support | Stay back (Insurance) |
| **Goalie** | Intercept puck | Stay in goal area | Stay in goal area |
### 3. Technical Details
- **Constants**: Define `WorldZoneWidth` (1/3 of field width) and `GoalieRadius` to avoid magic numbers.
- **Role Update**: Add `RoleGoalie` to `PlayerRole` enum.
- **Logic Flow**: `Puck Position` $\rightarrow$ `Zone Detection` $\rightarrow$ `Role-based Target Calculation` $\rightarrow$ `Steering` $\rightarrow$ `Movement`.
## File Changes
- `internal/entities/player.go`: Add `RoleGoalie`.
- `internal/game/world.go`:
- Add zone and goal constants.
- Implement `updateGoalieAI`.
- Refactor `updatePlayerAI` to use zone-based logic.
- `internal/game/world_test.go`: Add tests for goalie positioning and zone transitions.
## Edge Cases
- **Puck behind goal**: Goalie resets to center.
- **Crowding**: Existing anti-clumping logic prevents players from stacking on top of the goalie.
- **Jitter**: Implement a small distance threshold before updating the target to prevent micro-oscillations.

View File

@@ -0,0 +1,75 @@
# Design: Match Logic & Game Rules (M5)
**Goal:** Implement a high-level match management system to handle scoring, game timing, and state transitions (Playing, Goal Pause, Match Ended).
**Architecture:**
Introduction of a `MatchManager` component that acts as a controller over the `World`. The `World` remains responsible for physics and AI, while the `MatchManager` handles the "rules of the sport".
---
## 1. Components
### MatchManager
The central authority for the match state.
- **GameState**: `Playing`, `GoalPause`, `MatchEnded`.
- **Score**: `ScoreRed`, `ScoreBlue` (integers).
- **Match Timer**: `MatchDuration` (total time) and `CurrentTime` (elapsed).
- **Pause Timer**: `PauseDuration` (fixed duration for goal celebration) and `PauseTimer` (countdown).
- **Reference**: Pointer to `World`.
### World (Extensions)
- **`CheckGoal() (team entities.Team, scored bool)`**: Checks if the puck has crossed the goal lines (X < 0 or X > WorldWidth).
- **`ResetPositions()`**: Resets the puck to the center and all players to their `HomePosition`, zeroing out all velocities.
---
## 2. File Structure
- **`internal/game/match_manager.go` (New)**:
- Definition of `GameState` and `MatchManager` struct.
- `Update()` method to drive the match lifecycle.
- **`internal/game/world.go` (Modified)**:
- Implementation of `CheckGoal()` and `ResetPositions()`.
- **`cmd/game/main.go` (Modified)**:
- Replace `World` with `MatchManager` in the `Game` struct.
- Update `Update()` and `Draw()` calls to go through `MatchManager`.
---
## 3. Data Flow
### Main Loop $\rightarrow$ MatchManager
`Game.Update()` $\rightarrow$ `MatchManager.Update()`:
1. **If `Playing`**:
- Call `World.CheckGoal()`.
- If goal $\rightarrow$ transition to `GoalPause`, increment score, start `PauseTimer`.
- If no goal $\rightarrow$ call `World.Update()` (physics/AI) and increment `CurrentTime`.
- If `CurrentTime >= MatchDuration` $\rightarrow$ transition to `MatchEnded`.
2. **If `GoalPause`**:
- Decrement `PauseTimer`.
- If `PauseTimer <= 0` $\rightarrow$ call `World.ResetPositions()` and transition to `Playing`.
- `World.Update()` is skipped (game is frozen).
3. **If `MatchEnded`**:
- `World.Update()` is skipped.
### World $\rightarrow$ MatchManager (Goal Detection)
- `X < 0` $\rightarrow$ `(TeamBlue, true)`
- `X > WorldWidth` $\rightarrow$ `(TeamRed, true)`
- Otherwise $\rightarrow$ `(_, false)`
---
## 4. Edge Cases & Handling
- **Double Scoring**: The `GoalPause` state prevents `CheckGoal` from being called repeatedly until the world is reset.
- **Reset Collisions**: `ResetPositions` explicitly sets `Velocity = 0` for all entities to prevent immediate "explosions" from anti-clumping logic.
- **Boundary Stuck**: `CheckGoal` is evaluated before physics clamping to ensure goals are registered even at high speeds.
- **Last-second Goal**: Goals are processed before the match timer check, ensuring a goal on the final frame is counted.
---
## 5. Success Criteria
- Puck crossing the boundary increments the correct team's score.
- The game freezes for a few seconds after a goal.
- All entities return to starting positions after the pause.
- The match stops automatically when the timer reaches the limit.