feat(state): scope card evidence and policy memory by reported run identity
This commit is contained in:
parent
484551047c
commit
ba519a850e
9 changed files with 343 additions and 82 deletions
39
facts.py
39
facts.py
|
|
@ -538,7 +538,7 @@ class CombatFacts:
|
|||
f"block_available={self.max_block_available} survives_with_cards={self.survives_with_cards}",
|
||||
f"potions={[p.get('name') for p in self.potions]}",
|
||||
f"piles: draw={self.draw_pile_count} discard={self.discard_pile_count} exhaust={self.exhaust_pile_count}",
|
||||
f"deck: {self.deck_counts}",
|
||||
f"visible combat piles: {self.deck_counts}",
|
||||
]
|
||||
for e in self.enemies:
|
||||
lines.append(
|
||||
|
|
@ -620,22 +620,12 @@ def enemy_status_names(enemy: EnemyFact) -> list:
|
|||
|
||||
|
||||
def deck_summary(player: dict) -> dict:
|
||||
"""
|
||||
Stable aggregates over ALL FOUR PILES, for macro decisions.
|
||||
"""Aggregate visible combat piles, not the persistent run deck.
|
||||
|
||||
The reward, shop and map states do NOT expose the deck, so this travels with
|
||||
the composition snapshot taken during combat. It is deliberately a
|
||||
*supplement* to the name->count map, never a replacement: card identities
|
||||
are the signal for synergy, redundancy and upgrades, and only counts can
|
||||
carry them. These aggregates are the part that is arithmetic, so it is
|
||||
computed here and never asked of the model.
|
||||
|
||||
Every field is computed over the whole deck and is stable across snapshots
|
||||
of the same deck. There is deliberately no cost aggregate: the state does
|
||||
not expose a card's BASE cost, and a temporary in-combat cost modifier
|
||||
applies to the copy in hand. Measured over 600 captures, one Strike read
|
||||
`cost: "0"` in hand while the same Strike read `cost: "1"` in the draw pile,
|
||||
so an average cost would differ between two snapshots of an identical deck.
|
||||
Generated cards, Status cards, temporary upgrades, and missing pile data
|
||||
limit this evidence. These counts can change during combat. Cost is omitted
|
||||
because temporary modifiers make displayed costs unsuitable for a base-cost
|
||||
aggregate. The historical function name is retained for existing callers.
|
||||
"""
|
||||
names: list[str] = []
|
||||
attacks = blocks = other = upgraded = 0
|
||||
|
|
@ -668,22 +658,19 @@ def deck_summary(player: dict) -> dict:
|
|||
|
||||
|
||||
def deck_context(deck: Any) -> Any:
|
||||
"""
|
||||
What a macro question receives as deck context: the full name->count map
|
||||
(card identities, which carry the synergy and redundancy signal) plus the
|
||||
stable aggregates. Tolerates a legacy flat name->count snapshot.
|
||||
"""
|
||||
"""Supply card evidence with its limits; unscoped legacy data is unknown."""
|
||||
if not deck:
|
||||
return "unknown"
|
||||
if isinstance(deck, dict) and "counts" in deck:
|
||||
counts = deck.get("counts") or {}
|
||||
if not counts:
|
||||
return "unknown"
|
||||
return {"cards": counts, **({"summary": deck["summary"]}
|
||||
if deck.get("summary") else {})}
|
||||
if isinstance(deck, dict):
|
||||
return {"cards": deck}
|
||||
return deck
|
||||
provenance = deck.get("provenance")
|
||||
if not isinstance(provenance, dict) or not provenance.get("run_id"):
|
||||
return "unknown"
|
||||
return {"cards": counts, "provenance": provenance,
|
||||
**({"summary": deck["summary"]} if deck.get("summary") else {})}
|
||||
return "unknown"
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue