Add the shopping list: name the gaps the board wants filled
JCA.build_gaps() splits the board's tags into unmet wants (some joker wants it, no OTHER joker gives it) and untapped hooks (given, nothing pays it off). Rendered at the top of the in-run Combos overlay via TAG_LABEL; engine roles stay excluded because they hint nothing. Board slots are compared by index, not db-entry identity, so two copies of one cluster joker correctly feed each other. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -421,6 +421,46 @@ local function tag_labels(tag_set)
|
||||
return labels
|
||||
end
|
||||
|
||||
-- Build gaps: what the current board is shopping for. A tag is a gap when
|
||||
-- some owned joker wants it and no OTHER owned joker gives it (same rule as
|
||||
-- scoring and theme discovery: a joker cannot feed itself). The reverse -- a
|
||||
-- give no other owned joker wants -- is an untapped hook a future purchase
|
||||
-- could pay off. Engine roles (mult/chips/xmult) are skipped for the same
|
||||
-- reason they have no TAG_LABEL: they hint nothing specific. Returns two
|
||||
-- tag arrays in TAG_ORDER, so the display is stable across hovers.
|
||||
function JCA.build_gaps()
|
||||
local looking, untapped = {}, {}
|
||||
if not (G.jokers and G.jokers.cards) then return looking, untapped end
|
||||
local entries = {}
|
||||
for _, c in ipairs(G.jokers.cards) do
|
||||
if is_joker(c) then
|
||||
local e = JCA.db[c.config.center.key]
|
||||
if e then entries[#entries + 1] = e end
|
||||
end
|
||||
end
|
||||
-- Compare by index, not table identity: two copies of the same joker
|
||||
-- share one db entry, and they DO feed each other's cluster tags.
|
||||
local function others_have(side, tag, self_idx)
|
||||
for i, e in ipairs(entries) do
|
||||
if i ~= self_idx and e[side][tag] then return true end
|
||||
end
|
||||
end
|
||||
local want_gap, give_hook = {}, {}
|
||||
for i, e in ipairs(entries) do
|
||||
for t in pairs(e.wants) do
|
||||
if TAG_LABEL[t] and not others_have('gives', t, i) then want_gap[t] = true end
|
||||
end
|
||||
for t in pairs(e.gives) do
|
||||
if TAG_LABEL[t] and not others_have('wants', t, i) then give_hook[t] = true end
|
||||
end
|
||||
end
|
||||
for _, t in ipairs(data.tag_order) do
|
||||
if want_gap[t] then looking[#looking + 1] = t end
|
||||
if give_hook[t] then untapped[#untapped + 1] = t end
|
||||
end
|
||||
return looking, untapped
|
||||
end
|
||||
|
||||
local function tooltip_rows(card)
|
||||
-- Touch mode enlarges every advice row for phone screens (see config.lua).
|
||||
local S = JCA.config.touch_mode and 1.4 or 1
|
||||
@@ -1250,18 +1290,43 @@ end
|
||||
-- in an overlay. The overlay reuses catalog_tabs(); the page cyclers work
|
||||
-- unchanged because vanilla create_tabs also names its body 'tab_contents'.
|
||||
|
||||
-- Shopping list shown at the top of the in-run overlay: the tags the board
|
||||
-- wants but nothing else on it gives, and the hooks it gives that nothing
|
||||
-- pays off yet. Teaching content, so learning mode keeps it.
|
||||
local function build_gaps_rows()
|
||||
local looking, untapped = JCA.build_gaps()
|
||||
local nodes = {}
|
||||
local function line(prefix, tags, colour)
|
||||
if #tags == 0 then return end
|
||||
local labels = {}
|
||||
for _, t in ipairs(tags) do labels[#labels + 1] = {name = TAG_LABEL[t]} end
|
||||
for i, l in ipairs(wrap_names(labels, 58, 2)) do
|
||||
nodes[#nodes + 1] = {n = G.UIT.R, config = {align = 'cm', padding = 0.02}, nodes = {
|
||||
{n = G.UIT.T, config = {text = (i == 1 and prefix or ' ') .. l,
|
||||
colour = colour, scale = 0.3, shadow = true}},
|
||||
}}
|
||||
end
|
||||
end
|
||||
line('Your build is looking for: ', looking, G.C.GOLD)
|
||||
line('Untapped on your board: ', untapped, G.C.UI.TEXT_LIGHT)
|
||||
if #nodes == 0 then return nil end
|
||||
return {n = G.UIT.R, config = {align = 'cm', padding = 0.04}, nodes = nodes}
|
||||
end
|
||||
|
||||
G.FUNCS.jca_open_catalog = function()
|
||||
G.FUNCS.overlay_menu{definition = create_UIBox_generic_options({
|
||||
back_func = 'exit_overlay_menu',
|
||||
contents = {
|
||||
{n = G.UIT.R, config = {align = 'tm', padding = 0}, nodes = {
|
||||
local contents = {}
|
||||
local ok, gaps = pcall(build_gaps_rows)
|
||||
if ok and gaps then contents[#contents + 1] = gaps end
|
||||
contents[#contents + 1] = {n = G.UIT.R, config = {align = 'tm', padding = 0}, nodes = {
|
||||
create_tabs({
|
||||
tabs = catalog_tabs(),
|
||||
snap_to_nav = true,
|
||||
colour = G.C.BOOSTER,
|
||||
}),
|
||||
}},
|
||||
},
|
||||
}}
|
||||
G.FUNCS.overlay_menu{definition = create_UIBox_generic_options({
|
||||
back_func = 'exit_overlay_menu',
|
||||
contents = contents,
|
||||
})}
|
||||
end
|
||||
|
||||
|
||||
@@ -400,6 +400,35 @@ ok(cut == board[1] or cut == board[2],
|
||||
'the sell advisor flags one of the two clashing jokers',
|
||||
cut and cut.config.center.key or 'nil')
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
section('Engine: build gaps name what the board is shopping for')
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Baron alone: wants held-card retriggers and hand size, and nothing on the
|
||||
-- board provides either; its copy_target give has no copier to pay it off.
|
||||
field('j_baron')
|
||||
local looking, untapped = JCA.build_gaps()
|
||||
eq(table.concat(looking, ','), 'retrig_held,hand_size',
|
||||
'a lone Baron is shopping for its enablers, in TAG_ORDER')
|
||||
eq(table.concat(untapped, ','), 'copy_target',
|
||||
'its copy_target hook is untapped until a copier arrives')
|
||||
|
||||
-- Mime fills the retrig_held gap (and only that gap).
|
||||
field('j_baron', 'j_mime')
|
||||
looking = JCA.build_gaps()
|
||||
eq(table.concat(looking, ','), 'hand_size', 'Mime closes the retrig_held gap')
|
||||
|
||||
-- Two copies of the same cluster joker feed each other: the entries share one
|
||||
-- db table, so the check must compare board slots, not table identity.
|
||||
field('j_jolly', 'j_jolly')
|
||||
looking = JCA.build_gaps()
|
||||
eq(table.concat(looking, ','), '', 'two Jolly Jokers are not shopping for Pairs')
|
||||
|
||||
-- One alone is: a cluster member cannot feed itself.
|
||||
field('j_jolly')
|
||||
looking = JCA.build_gaps()
|
||||
eq(table.concat(looking, ','), 'pair', 'a lone cluster member still wants its cluster')
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
section('Data: themes are reachable')
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user