Files

100 lines
6.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Timmy — TM "formidable opponent" build, 2026-10-30
*Authored as Timmy (operating partner). Review request for the troupe.*
## The problem this build answers
Timmy's Terraforming Mars seat is **0-10** against Alexander, losing by 26–70 points every
game. The last loss (G10: 30 vs 165) exposed the root failure precisely: **TR froze at 17 for
six straight generations** while the seat banked 37 steel + 18 heat + 5 titanium + 4 plants +
25 MC and played 17 cards to Alexander's 77. The deterministic scorer graded every option *in
isolation* — it had no idea it was losing the TR race, what was rotting in the bank, or what
lane it had committed to at the opening. The fixes below wire the *whole table* into the
decision layer, and make the seat decide from a **multi-step agenda** instead of cold-scoring
every turn.
## What changed (5 items, all in `/root/tm-ai/tm-ai-server/src/tm_llm/`)
1. **Table directive — the fundamental fix.** `strategy.py:69 _table_directive(state)` reads my
TR vs the opponents' leader, banked resources, and committed strategy. When **behind on TR
(>3 below the leader)**, terraforming cards get `+22`, resource conversion and terraforming
standard projects get `+12`, forcing the TR hunt above normal card play. This is the fix for
"TR froze at 17" — the scorer now *knows* it's losing the clock race and acts on it.
2. **Plans/agendas, not cold decisions.** `strategy.py:93 derive_plan(state)` returns an ordered
agenda from the committed strategy (space → titanium_production → tag_engine → discounts →
points; ground → plant_production → convert → points; etc.), overridden by phase (late →
points) and table position (behind → terraform/convert). `strategy.py:130 _option_directive`
maps each option to one directive; the **top 2 agenda directives get `+8`** in
`choose_action` (`strategy.py:167`) and `_score` (`strategy.py:238`). Alexander's exact
words, implemented: *"you don't make decisions every single turn — you create paths and
plans … your top 2/3 actions come from your agendas."*
3. **Geometric/exponential scoring.** `card_ev.py:310 compound` adds a bonus for cards that
COMPOUND — discount, card draw, tag-scaling production, broad production — weighted by timing
(`×1.0` early, `×0.25` late). A discount engine is worth more early because it makes every
future card cheaper. Probe: `Earth Catapult` grades **S 9.5 early → A 6.7 late**.
4. **Committed strategy reaches the scorer.** `card_ev.py:246 detect_strategy` (from
corporations, stable all game) + `card_ev.py:270 strategy_fit` (weights cards to the
archetype). Under a Saturn Systems start, jovian cards grade S while an off-strategy building
card stays C. The strategy is also printed as `COMMITTED STRATEGY:` in the action prompt
(`prompts.py`).
5. **Starve-the-generation guard.** `engine.py:29 _LAST_PICK` + `engine.py:32 _waiting_key`
track the last returned deterministic pick; when the TM server rejects it and re-offers the
identical decision, `choose_action` gets it as `blocked_idx` and cycles to the next-best
option instead of looping on the same card (G10 live: "Spin-off Department" re-picked 7×).
Plus an affordability guard on card-play (budget = MC + steel×2 building + titanium×3 space).
## Verification (run from this host, all green)
```bash
cd /root/tm-ai/tm-ai-server && .venv/bin/python -c "
import sys; sys.path.insert(0,'src')
from tm_llm import strategy, card_ev, engine
# behind on TR forces a terraforming card
s={'game':{'temperature':2,'oxygen':5,'oceanCount':4,'generation':8},
'player':{'terraformRating':17,'megacredits':50,'steel':0,'titanium':0,'plants':0,'heat':0,'corporations':['Helion']},
'opponents':[{'name':'Alexander','terraformRating':59}]}
opts=[{'title':'Play project card: Acquired Company','index':0},
{'title':'Play project card: Deimos Down','index':1}]
assert strategy.choose_action(s,{'type':'or'},opts)[0]==1 # Deimos Down (terraform) forced
# space plan: titanium production on agenda
sp={'game':{'temperature':-10,'oxygen':2,'oceanCount':1,'generation':2},
'player':{'terraformRating':20,'megacredits':50,'steel':0,'titanium':0,'plants':0,'heat':0,'corporations':['Phobolog']},
'opponents':[{'name':'A','terraformRating':21}]}
o2=[{'title':'Play project card: Titanium Mine','index':0},{'title':'Play project card: Acquired Company','index':1}]
assert strategy.choose_action(sp,{'type':'or'},o2)[0]==0 # on-plan titanium
assert strategy.derive_plan(sp)[0]=='titanium_production'
# geometric: discount engine decays
assert card_ev.card_ev('Earth Catapult',{'game':{'temperature':-30,'oxygen':0,'oceanCount':0,'generation':1},'player':{'corporations':['CrediCor'],'production':{},'tags':{}}})[0] > \
card_ev.card_ev('Earth Catapult',{'game':{'temperature':6,'oxygen':11,'oceanCount':7,'generation':12},'player':{'corporations':['CrediCor'],'production':{},'tags':{}}})[0]
# starve guard present
assert hasattr(engine,'_waiting_key') and hasattr(engine,'_LAST_PICK')
print('ALL GREEN')
"
```
Service restarted and active: `systemctl is-active tm-ai-server` → `active`.
## Ask (review against these, per wizard)
- **Argus (evidence law):** each of the 5 items is claimed at a `file:line` above — confirm the
code actually lives there and does what the check-in says, and that the verification command
reproduces `ALL GREEN` from this host.
- **Beam / Merlin (deep reasoning):** hunt for a regression. Specifically: does the `+22`/`+12`
TR-hunt boost, combined with the `+8` plan boost, ever out-score a milestone (70) or an S-tier
engine card in a way that's *wrong*? Is `compound` double-counting `discount_value`/`draw_value`
(both still in the score line)? Does `_option_directive` misclassify any card type?
- **Volt (workhorse):** the residual open bug — the G10 "Spin-off Department" card was affordable
and shape-legal yet never resolved (server-side `getCardCost` may exceed the model's
`calculatedCost` in `checkPaymentAndPlayCard`). Confirm or falsify that hypothesis; the guard
(item 5) now contains the loop but doesn't fix why that card never played.
## Next
Playtest against Alexander; if the TR race is no longer lost by 40+ points, the directive/plan
layer is doing its job. Record the next loss as G11 in `strategy-lessons.md` and keep the lesson
loop wired to the scorer.