From 4a86a3e1ffc69121b7f31608c2e90ef57e1a4219 Mon Sep 17 00:00:00 2001 From: funman300 Date: Wed, 15 Jul 2026 19:46:41 -0700 Subject: [PATCH] Give Perkeo its consumable wants; make theme fragility a decision Perkeo duplicates a RANDOM held consumable (card.lua:2788), so Spectral and Planet supply feed it exactly like Tarot supply always did - and the spectral_gen theme gains the third member it needed (the Seance + Perkeo negative-spectral archetype, now discoverable three ways). The other five two-member themes (diamonds, hearts, four_kind, gold_gen, stone_gen) are vanilla reality: no third joker exists. They get FRAGILE_OK verdicts in audit.lua, rendered in AUDIT.md, and the audit now fails on a fragile theme with no verdict - or a stale verdict for a theme that grew - the same discipline as the silent-joker list. Co-Authored-By: Claude Fable 5 --- AUDIT.md | 16 +++++++++++++--- audit.lua | 44 ++++++++++++++++++++++++++++++++++++++++++++ synergies.lua | 6 +++++- 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/AUDIT.md b/AUDIT.md index f079085..f5989d5 100644 --- a/AUDIT.md +++ b/AUDIT.md @@ -85,7 +85,7 @@ A tag with two is fragile: drop either joker and the theme dies. | `joker_gen` | 1 | 3 | 4 | yes | | `money` | 15 | 3 | 17 | yes | | `pair` | 3 | 3 | 3 | yes | -| `planet` | 3 | 3 | 3 | yes | +| `planet` | 3 | 4 | 4 | yes | | `probability` | 1 | 7 | 8 | yes | | `retrig_faces` | 1 | 10 | 11 | yes | | `retrig_held` | 1 | 4 | 5 | yes | @@ -93,7 +93,7 @@ A tag with two is fragile: drop either joker and the theme dies. | `retrig_scoring` | 4 | 17 | 21 | yes | | `sell_value` | 2 | 2 | 4 | yes | | `spades` | 2 | 3 | 3 | yes | -| `spectral_gen` | 2 | 2 | **2** | yes | +| `spectral_gen` | 2 | 3 | 3 | yes | | `stone_gen` | 1 | 1 | **2** | yes | | `straight` | 7 | 6 | 8 | yes | | `suit_flex` | 1 | 12 | 13 | yes | @@ -101,6 +101,16 @@ A tag with two is fragile: drop either joker and the theme dies. | `three_kind` | 4 | 3 | 4 | yes | | `two_pair` | 3 | 3 | 3 | yes | +### Fragile by design + +Two-member themes reviewed and accepted: vanilla has no third joker to add. + +- **`diamonds`** — Greedy Joker and Rough Gem are the only Diamond payoffs in vanilla. +- **`four_kind`** — DNA enables it, The Family pays it; no other vanilla joker cares. +- **`gold_gen`** — Midas Mask makes Gold, Golden Ticket cashes it - the whole economy. +- **`hearts`** — Lusty Joker and Bloodstone are the only Heart payoffs in vanilla. +- **`stone_gen`** — Marble adds Stones, Stone Joker counts them; Vampire eats them rather than wanting them. + ## Load-bearing jokers The jokers that appear in the most scoring relationships. If one of these is @@ -116,8 +126,8 @@ mis-tagged, the error is spread across the whole database. | `j_to_the_moon` | 16 | | `j_bootstraps` | 15 | | `j_bull` | 15 | +| `j_seance` | 13 | | `j_four_fingers` | 12 | -| `j_seance` | 12 | | `j_sock_and_buskin` | 12 | | `j_blueprint` | 11 | diff --git a/audit.lua b/audit.lua index 64a14ea..1a46849 100644 --- a/audit.lua +++ b/audit.lua @@ -244,6 +244,18 @@ end w('') -------------------------------------------------------------------------------- +-- Two-member themes, reviewed and accepted: vanilla simply has no third joker. +-- A fragile theme NOT in this table fails the audit -- either find a member or +-- record why none exists. A stale entry (theme grew past two) also fails, so +-- the table never drifts from the data. +local FRAGILE_OK = { + diamonds = 'Greedy Joker and Rough Gem are the only Diamond payoffs in vanilla.', + hearts = 'Lusty Joker and Bloodstone are the only Heart payoffs in vanilla.', + four_kind = 'DNA enables it, The Family pays it; no other vanilla joker cares.', + gold_gen = 'Midas Mask makes Gold, Golden Ticket cashes it - the whole economy.', + stone_gen = 'Marble adds Stones, Stone Joker counts them; Vampire eats them rather than wanting them.', +} + w('## Tag health') w('') w('A tag with one member can never fire (it takes two jokers to make a synergy).') @@ -251,15 +263,34 @@ w('A tag with two is fragile: drop either joker and the theme dies.') w('') w('| tag | givers | wanters | jokers | theme? |') w('|---|---|---|---|---|') +local fragile_unreviewed, fragile_stale = {}, {} +local fragile_counts = {} for _, t in ipairs(sorted(members)) do if not ENGINE[t] then local n = 0 for _ in pairs(members[t]) do n = n + 1 end + fragile_counts[t] = n + if data.theme_info[t] and n <= 2 and not FRAGILE_OK[t] then + fragile_unreviewed[#fragile_unreviewed + 1] = t + end w('| `%s` | %d | %d | %s | %s |', t, givers[t] or 0, wanters[t] or 0, n <= 2 and ('**' .. n .. '**') or tostring(n), data.theme_info[t] and 'yes' or '-') end end +for t in pairs(FRAGILE_OK) do + if (fragile_counts[t] or 0) > 2 then fragile_stale[#fragile_stale + 1] = t end +end +w('') +w('### Fragile by design') +w('') +w('Two-member themes reviewed and accepted: vanilla has no third joker to add.') +w('') +for _, t in ipairs(sorted(FRAGILE_OK)) do + if (fragile_counts[t] or 0) <= 2 then + w('- **`%s`** — %s', t, FRAGILE_OK[t]) + end +end w('') -------------------------------------------------------------------------------- @@ -312,3 +343,16 @@ if #buckets.unreviewed > 0 then :format(#buckets.unreviewed, n_jokers, table.concat(buckets.unreviewed, '\n '))) os.exit(1) end + +-- A theme hanging on two jokers is a discovery achievement one rebalance away +-- from impossible; that has to be a decision too. +if #fragile_unreviewed > 0 then + io.stderr:write(('\n%d fragile theme(s) (two members) have no FRAGILE_OK verdict:\n %s\n') + :format(#fragile_unreviewed, table.concat(fragile_unreviewed, '\n '))) + os.exit(1) +end +if #fragile_stale > 0 then + io.stderr:write(('\nFRAGILE_OK verdict(s) for themes that are no longer fragile -- ' + .. 'delete them:\n %s\n'):format(table.concat(fragile_stale, '\n '))) + os.exit(1) +end diff --git a/synergies.lua b/synergies.lua index 83b80e8..fa5997d 100644 --- a/synergies.lua +++ b/synergies.lua @@ -201,7 +201,11 @@ j('j_caino', {'xmult','copy_target'}, {'faces'}) j('j_triboulet', {'xmult','copy_target'}, {'retrig_scoring','retrig_faces'}) j('j_yorick', {'xmult','copy_target'}, {'discards_up'}) j('j_chicot', {}) -j('j_perkeo', {'tarot_gen'}, {'tarot_gen'}) +-- Perkeo duplicates a RANDOM held consumable (pseudorandom_element over +-- G.consumeables.cards, card.lua:2788), so every consumable economy feeds it: +-- Tarot and Planet supply give it targets, and duplicating Spectrals is the +-- famous negative-spectral archetype with Seance. +j('j_perkeo', {'tarot_gen'}, {'tarot_gen','spectral_gen','planet'}) -- Famous explicit combos (flat bonus on top of tag matching), each with a -- short "why it works" line shown in the tooltip. Keep blurbs under ~34