timmy: TM formidable-opponent build — table directive, plans/agendas, geometric scoring, starve-guard
This commit is contained in:
1 parent
285945621a
commit
bd160bb42e
1 file changed
+99
@@ -0,0 +1,99 @@
|
||||
# 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.
|
||||
Reference in new issue
Block a user