sim: deltoid-loop D3 pitch economy
This commit is contained in:
parent
8419b35ba7
commit
37460e86c1
4 changed files with 102 additions and 0 deletions
1
game/__init__.py
Normal file
1
game/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
"""Pitchwell game package (sim stubs; no Godot required)."""
|
||||
13
game/sim/__init__.py
Normal file
13
game/sim/__init__.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
"""Godot-less Pitchwell sim stubs."""
|
||||
|
||||
from .pitch_economy import Economy, SumpWell, Worker, START_PITCH, WORKER_LOAD, WELL_CAPACITY, SEEP_PER_SEC
|
||||
|
||||
__all__ = [
|
||||
"Economy",
|
||||
"SumpWell",
|
||||
"Worker",
|
||||
"START_PITCH",
|
||||
"WORKER_LOAD",
|
||||
"WELL_CAPACITY",
|
||||
"SEEP_PER_SEC",
|
||||
]
|
||||
67
game/sim/pitch_economy.py
Normal file
67
game/sim/pitch_economy.py
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
"""Pitch economy stub — numbers from docs/GAMEPLAY.md (Sump Wells / Start / Carry)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
# GAMEPLAY.md: Start 500 Pitch
|
||||
START_PITCH: float = 500.0
|
||||
# GAMEPLAY.md: Worker load 100
|
||||
WORKER_LOAD: int = 100
|
||||
# GAMEPLAY.md: Sump Wells finite 2500, seep +0.4/s while claimed
|
||||
WELL_CAPACITY: float = 2500.0
|
||||
SEEP_PER_SEC: float = 0.4
|
||||
|
||||
|
||||
@dataclass
|
||||
class SumpWell:
|
||||
"""Finite Pitch node. Claim via finished refinery in radius (GAMEPLAY.md)."""
|
||||
|
||||
remaining: float = WELL_CAPACITY
|
||||
claimed: bool = False
|
||||
has_refinery: bool = False
|
||||
|
||||
def claim_via_refinery(self) -> None:
|
||||
"""Claim well by finishing a refinery flag (GAMEPLAY.md)."""
|
||||
self.has_refinery = True
|
||||
self.claimed = True
|
||||
|
||||
def tick_seep(self, dt: float) -> float:
|
||||
"""Seep +0.4/s while claimed; depletes remaining. Returns Pitch extracted."""
|
||||
if not self.claimed or self.remaining <= 0.0 or dt <= 0.0:
|
||||
return 0.0
|
||||
extracted = min(self.remaining, SEEP_PER_SEC * dt)
|
||||
self.remaining -= extracted
|
||||
return extracted
|
||||
|
||||
|
||||
@dataclass
|
||||
class Worker:
|
||||
"""Carry load 100 (GAMEPLAY.md). Stub — load cap only for now."""
|
||||
|
||||
load: int = 0
|
||||
capacity: int = WORKER_LOAD
|
||||
|
||||
def can_pick(self, amount: int) -> int:
|
||||
return max(0, min(amount, self.capacity - self.load))
|
||||
|
||||
|
||||
@dataclass
|
||||
class Economy:
|
||||
"""Faction Pitch pool + claimed wells."""
|
||||
|
||||
pitch: float = START_PITCH
|
||||
wells: list[SumpWell] = field(default_factory=list)
|
||||
|
||||
def add_well(self, well: SumpWell | None = None) -> SumpWell:
|
||||
w = well if well is not None else SumpWell()
|
||||
self.wells.append(w)
|
||||
return w
|
||||
|
||||
def tick(self, dt: float) -> float:
|
||||
gained = 0.0
|
||||
for w in self.wells:
|
||||
gained += w.tick_seep(dt)
|
||||
self.pitch += gained
|
||||
return gained
|
||||
21
kitchen/patches/D3.md
Normal file
21
kitchen/patches/D3.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# D3 — `@deltoid-loop` Pitch economy stub
|
||||
|
||||
**When:** 2026-09-05 ~22:10:20Z
|
||||
**Scope:** Godot-less sim matching `docs/GAMEPLAY.md` Pitch numbers. No Nintendo/KKnD assets. No push.
|
||||
|
||||
## Files
|
||||
|
||||
| Path | Role |
|
||||
|------|------|
|
||||
| `game/sim/pitch_economy.py` | SumpWell (2500, seep +0.4/s claimed), Worker load 100, start Pitch 500, claim via refinery |
|
||||
| `game/sim/__init__.py` | exports |
|
||||
| `game/__init__.py` | package |
|
||||
| `scripts/smoke/economy_smoke.py` | 500 → claim → 10s seep → assert gain; exit 0/1 |
|
||||
| `scripts/smoke/run_all.sh` | runs economy_smoke (+ hooks if other smokes appear); no `D3-HOOK` in missing headless |
|
||||
|
||||
## Verify
|
||||
|
||||
```bash
|
||||
python3 scripts/smoke/economy_smoke.py
|
||||
# expect PASS, exit 0
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue