Commit 4451e60b authored by Vury Leo's avatar Vury Leo

multi level

parent 7c961424
......@@ -3474,48 +3474,94 @@ function Synchro.AddSynchroProcedure(c,params)
c:RegisterEffect(e1)
end
--- DFS helper: check if partial selection + candidates can form valid Synchro material group
--- @param candidates table -- array of candidate cards
--- @param targetLevel integer -- required total level
--- Check if there exists a valid Synchro material selection from candidates (including current selected),
--- satisfying tuner/non-tuner counts and total level, supporting multiple possible levels per card.
---
--- @param candidates Card[] -- Array of candidate material cards.
--- @param targetLevel integer -- Required total level for Synchro Summon.
--- @param tunerFilter fun(card:Card,targetCard:Card):boolean
--- -- Function to identify tuner materials.
--- @param nonTunerFilter fun(card:Card,targetCard:Card):boolean
--- @param targetCard Card -- the Synchro monster being summoned
--- @param tunerMin integer
--- @param tunerMax integer|table
--- @param nonTunerMin integer
--- @param nonTunerMax integer|table
--- @param selected table -- array of currently selected cards
--- @param index integer -- current index in candidates array for DFS, default 1
--- @param currentSum integer -- current sum of levels, default 0
--- @return boolean
function Synchro.CanCompleteSelection(candidates,targetLevel,tunerFilter,nonTunerFilter,targetCard,tunerMin,tunerMax,nonTunerMin,nonTunerMax,selected,index,currentSum)
index=index or 1
currentSum=currentSum or 0
selected=selected or {}
if currentSum>targetLevel then return false end
--- -- Function to identify non-tuner materials.
--- @param targetCard Card -- The Synchro monster being summoned.
--- @param tunerMin integer -- Minimum number of tuner materials required.
--- @param tunerMax integer|table -- Maximum number of tuner materials allowed.
--- @param nonTunerMin integer -- Minimum number of non-tuner materials required.
--- @param nonTunerMax integer|table -- Maximum number of non-tuner materials allowed.
--- @param levelMapper fun(card:Card,targetCard:Card):integer[]
--- -- Function returning possible levels for a card.
--- @param selected? Card[] -- Currently selected material cards.
--- @param index? integer -- Current index in candidates for DFS (default 1).
--- @param state? table -- DFS state table containing:
--- -- sum (integer): current total level sum,
--- -- tunerCount (integer): current tuner count,
--- -- nonTunerCount (integer): current non-tuner count.
--- @return boolean -- True if a valid selection exists, false otherwise.
function Synchro.CanCompleteSelection(candidates,targetLevel,tunerFilter,nonTunerFilter,targetCard,
tunerMin,tunerMax,nonTunerMin,nonTunerMax,levelMapper,selected,index,state)
index = index or 1
selected = selected or {}
state = state or {
possibleSums = {[0]=true},
tunerCount = 0,
nonTunerCount = 0,
}
if Synchro.ExceedsMax(state.tunerCount,tunerMax) then return false end
if Synchro.ExceedsMax(state.nonTunerCount,nonTunerMax) then return false end
if index>#candidates then
return Synchro.IsSelectionValid(selected,targetLevel,tunerFilter,nonTunerFilter,targetCard,tunerMin,tunerMax,nonTunerMin,nonTunerMax)
-- check if targetLevel achievable exactly
if state.possibleSums[targetLevel] and
state.tunerCount>=tunerMin and
state.nonTunerCount>=nonTunerMin then
return true
end
return false
end
local card=candidates[index]
table.insert(selected,card)
if Synchro.CanCompleteSelection(candidates,targetLevel,tunerFilter,nonTunerFilter,targetCard,tunerMin,tunerMax,nonTunerMin,nonTunerMax,selected,index+1,currentSum+card:GetLevel()) then
local card = candidates[index]
local lvls = levelMapper(card,targetCard)
local tunerInc=tunerFilter(card,targetCard) and 1 or 0
local nonTunerInc=nonTunerFilter(card,targetCard) and 1 or 0
local newPossibleSums=Synchro.UpdatePossibleSums(state.possibleSums, lvls, targetLevel)
if next(newPossibleSums) then
-- push state
local prevPossibleSums = state.possibleSums
local prevTunerCount = state.tunerCount
local prevNonTunerCount = state.nonTunerCount
state.possibleSums = newPossibleSums
state.tunerCount = state.tunerCount + tunerInc
state.nonTunerCount = state.nonTunerCount + nonTunerInc
table.insert(selected, card)
if Synchro.CanCompleteSelection(candidates,targetLevel,tunerFilter,nonTunerFilter,targetCard,
tunerMin,tunerMax,nonTunerMin,nonTunerMax,levelMapper,selected,index+1,state) then
table.remove(selected)
-- pop state
state.possibleSums = prevPossibleSums
state.tunerCount = prevTunerCount
state.nonTunerCount = prevNonTunerCount
return true
end
table.remove(selected)
return true
-- pop state
state.possibleSums = prevPossibleSums
state.tunerCount = prevTunerCount
state.nonTunerCount = prevNonTunerCount
end
table.remove(selected)
if Synchro.CanCompleteSelection(candidates,targetLevel,tunerFilter,nonTunerFilter,targetCard,tunerMin,tunerMax,nonTunerMin,nonTunerMax,selected,index+1,currentSum) then
-- Option: exclude this card
if Synchro.CanCompleteSelection(candidates,targetLevel,tunerFilter,nonTunerFilter,targetCard,
tunerMin,tunerMax,nonTunerMin,nonTunerMax,levelMapper,selected,index+1,state) then
return true
end
return false
end
--- Synchro condition generator using tuner/non-tuner min/max counts and filters
function Synchro.SynCondition(tunerFilter,nonTunerFilter,tunerMin,tunerMax,nonTunerMin,nonTunerMax)
return function(e,c,smat,mg,min,max)
......@@ -3529,14 +3575,13 @@ function Synchro.SynCondition(tunerFilter,nonTunerFilter,tunerMin,tunerMax,nonTu
table.insert(candidates,card)
end
return Synchro.CanCompleteSelection(candidates,targetLevel,tunerFilter,nonTunerFilter,c,tunerMin,tunerMax,nonTunerMin,nonTunerMax,{},1,0)
return Synchro.CanCompleteSelection(candidates,targetLevel,tunerFilter,nonTunerFilter,c,tunerMin,tunerMax,nonTunerMin,nonTunerMax,Synchro.LevelMapper)
end
end
function Synchro.SynTarget(tunerFilter,nonTunerFilter,tunerMin,tunerMax,nonTunerMin,nonTunerMax)
return function(e,tp,eg,ep,ev,re,r,rp,chk,c,smat,mg,min,max)
local tc=c
local mg=Duel.GetMatchingGroup(function(mc) return mc:IsCanBeSynchroMaterial(mc,tc) end,tc:GetOwner(),LOCATION_MZONE+LOCATION_HAND,0,nil)
return function(e,tp,eg,ep,ev,re,r,rp,chk,tc,smat,mg,min,max)
mg=mg or Duel.GetMatchingGroup(function(mc) return mc:IsCanBeSynchroMaterial(mc,tc) end,tc:GetOwner(),LOCATION_MZONE+LOCATION_HAND,0,nil)
local sg=Group.CreateGroup()
local function GroupToArray(group)
......@@ -3556,16 +3601,16 @@ function Synchro.SynTarget(tunerFilter,nonTunerFilter,tunerMin,tunerMax,nonTuner
for _, card in ipairs(sg_arr) do table.insert(testSelection, card) end
table.insert(testSelection, candidate)
-- build testCandidates without candidate
local testCandidates = {}
-- build rest_candidates without candidate
local rest_candidates = {}
for _, other in ipairs(cg_arr) do
if other ~= candidate then
table.insert(testCandidates, other)
if other~=candidate then
table.insert(rest_candidates, other)
end
end
if Synchro.CanCompleteSelection(
testCandidates,
rest_candidates,
targetLevel,
tunerFilter,
nonTunerFilter,
......@@ -3574,9 +3619,10 @@ function Synchro.SynTarget(tunerFilter,nonTunerFilter,tunerMin,tunerMax,nonTuner
tunerMax,
nonTunerMin,
nonTunerMax,
Synchro.LevelMapper,
testSelection,
1,
0
Synchro.BuildStateFromSelection(testSelection,tunerFilter,nonTunerFilter,Synchro.LevelMapper,tc,targetLevel)
) then
addable:AddCard(candidate)
end
......@@ -3637,14 +3683,98 @@ function Synchro.GetDisplayMax(tunerMax,nonTunerMax)
end
function Synchro.IsSelectionValid(selection,targetLevel,tunerFilter,nonTunerFilter,targetCard,tunerMin,tunerMax,nonTunerMin,nonTunerMax)
local tunerCount,nonTunerCount,levelSum=0,0,0
for _,card in ipairs(selection) do
if tunerFilter(card,targetCard) then tunerCount=tunerCount+1
elseif nonTunerFilter(card,targetCard) then nonTunerCount=nonTunerCount+1
else return false end
levelSum=levelSum+card:GetLevel()
end
if tunerCount<tunerMin or (tunerMax~=Synchro.Infinite and tunerCount>tunerMax) then return false end
if nonTunerCount<nonTunerMin or (nonTunerMax~=Synchro.Infinite and nonTunerCount>nonTunerMax) then return false end
return levelSum==targetLevel
local state=Synchro.BuildStateFromSelection(selection,tunerFilter,nonTunerFilter,Synchro.LevelMapper,targetCard,targetLevel)
-- check if targetLevel achievable exactly
if Synchro.ExceedsMax(state.tunerCount,tunerMax) then return false end
if Synchro.ExceedsMax(state.nonTunerCount,nonTunerMax) then return false end
if state.possibleSums[targetLevel] and
state.tunerCount>=tunerMin and
state.nonTunerCount>=nonTunerMin then
return true
end
return false
end
--- Returns a list of possible levels this card can be treated as when used as Synchro material.
--- @param card Card -- The candidate material card.
--- @param targetCard Card -- The Synchro monster being summoned.
--- @return integer[] -- Array of possible level integers (e.g., {1} or {1,2}).
function Synchro.LevelMapper(card,targetCard)
Debug.Message(string.format("lvel mapper for %d",card:GetCode()))
local levelDict={}
local effs={card:IsHasEffect(EFFECT_SYNCHRO_LEVEL)}
Debug.Message(string.format("lvel mapper for %d, effs %d",card:GetCode(),#effs))
if #effs>0 then
local val=effs[1]:GetValue()(effs[1],targetCard)
Debug.Message(string.format("lvel mapper for %d, effs %d",card:GetCode(),#effs))
if type(val)=="table" then
for _, v in ipairs(val) do
levelDict[v]=true
end
elseif type(val)=="number" then
if val>=(1<<16) then -- compatibility
val=val>>16
end
Debug.Message(string.format("add additional level %d",val))
levelDict[val]=true
end
end
-- Always include original level
levelDict[card:GetLevel()]=true
-- Convert dictionary keys back to array
local levels={}
for lvl,_ in pairs(levelDict) do
table.insert(levels,lvl)
end
Debug.Message(string.format("lvel mapper result for %d, %d",card:GetCode(),#levels))
return levels
end
function Synchro.UpdatePossibleSums(possibleSums, cardLevels, targetLevel)
local newSums = {}
for sum, _ in pairs(possibleSums) do
for _, lvl in ipairs(cardLevels) do
local newSum = sum + lvl
if newSum <= targetLevel then
newSums[newSum] = true
end
end
end
return newSums
end
function Synchro.BuildStateFromSelection(sg_arr, tunerFilter, nonTunerFilter, levelMapper, targetCard, targetLevel)
local possibleSums={[0]=true}
local tunerCount=0
local nonTunerCount=0
for _,card in ipairs(sg_arr) do
local levels=levelMapper(card,targetCard)
possibleSums=Synchro.UpdatePossibleSums(possibleSums,levels,targetLevel)
if next(possibleSums)==nil then
-- no possible sums from current selection, invalid state
assert(false)
return {}
end
if tunerFilter(card,targetCard) then
tunerCount=tunerCount+1
elseif nonTunerFilter(card,targetCard) then
nonTunerCount=nonTunerCount+1
else
assert(false)
return {} -- invalid material type
end
end
return {
possibleSums=possibleSums,
tunerCount=tunerCount,
nonTunerCount=nonTunerCount,
}
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment