279 lines
8.6 KiB
Lua
279 lines
8.6 KiB
Lua
-- SPDX-License-Identifier: GPL-3.0-or-later
|
|
---@class RandomAI: AI
|
|
local RandomAI = AI:subclass("RandomAI")
|
|
|
|
---@param self RandomAI
|
|
---@param skill ActiveSkill
|
|
---@param card Card | nil
|
|
local function useActiveSkill(self, skill, card)
|
|
local room = self.room
|
|
local player = self.player
|
|
|
|
local filter_func = skill.cardFilter
|
|
if card then
|
|
filter_func = function()
|
|
return false
|
|
end
|
|
end
|
|
|
|
if self.command == "PlayCard" and (not skill:canUse(player, card) or player:prohibitUse(card)) then
|
|
return ""
|
|
end
|
|
|
|
local max_try_times = 100
|
|
local selected_targets = {}
|
|
local selected_cards = {}
|
|
local min = skill:getMinTargetNum()
|
|
local max = skill:getMaxTargetNum(player, card)
|
|
local min_card = skill:getMinCardNum()
|
|
local max_card = skill:getMaxCardNum()
|
|
for _ = 0, max_try_times do
|
|
if skill:feasible(selected_targets, selected_cards, self.player, card) then
|
|
break
|
|
end
|
|
local avail_targets = table.filter(self.room:getAlivePlayers(), function(p)
|
|
local ret = skill:targetFilter(p.id, selected_targets, selected_cards, card or Fk:cloneCard 'zixing')
|
|
if ret and card then
|
|
if player:prohibitUse(card) then
|
|
ret = false
|
|
end
|
|
end
|
|
return ret
|
|
end)
|
|
avail_targets = table.map(avail_targets, function(p)
|
|
return p.id
|
|
end)
|
|
local avail_cards = table.filter(player:getCardIds{Player.Hand, Player.Equip}, function(id)
|
|
return filter_func(skill, id, selected_cards, selected_targets)
|
|
end)
|
|
|
|
if #avail_targets == 0 and #avail_cards == 0 then
|
|
break
|
|
end
|
|
table.insertIfNeed(selected_targets, table.random(avail_targets))
|
|
table.insertIfNeed(selected_cards, table.random(avail_cards))
|
|
end
|
|
if skill:feasible(selected_targets, selected_cards, self.player, card) then
|
|
local ret = json.encode {
|
|
card = card and card.id or json.encode {
|
|
skill = skill.name,
|
|
subcards = selected_cards
|
|
},
|
|
targets = selected_targets
|
|
}
|
|
-- print(ret)
|
|
return ret
|
|
end
|
|
return ""
|
|
end
|
|
|
|
---@param self RandomAI
|
|
---@param skill ViewAsSkill
|
|
local function useVSSkill(self, skill, pattern, cancelable, extra_data)
|
|
local player = self.player
|
|
local room = self.room
|
|
local precondition
|
|
|
|
if self.command == "PlayCard" then
|
|
precondition = skill:enabledAtPlay(player)
|
|
if not precondition then
|
|
return nil
|
|
end
|
|
local exp = Exppattern:Parse(skill.pattern)
|
|
local cnames = {}
|
|
for _, m in ipairs(exp.matchers) do
|
|
if m.name then
|
|
table.insertTable(cnames, m.name)
|
|
end
|
|
end
|
|
for _, n in ipairs(cnames) do
|
|
local c = Fk:cloneCard(n)
|
|
precondition = c.skill:canUse(Self, c)
|
|
if precondition then
|
|
break
|
|
end
|
|
end
|
|
else
|
|
precondition = skill:enabledAtResponse(player)
|
|
if not precondition then
|
|
return nil
|
|
end
|
|
local exp = Exppattern:Parse(pattern)
|
|
precondition = exp:matchExp(skill.pattern)
|
|
end
|
|
|
|
if (not precondition) or math.random() < 0.2 then
|
|
return nil
|
|
end
|
|
|
|
local selected_cards = {}
|
|
local max_try_time = 100
|
|
|
|
for _ = 0, max_try_time do
|
|
local avail_cards = table.filter(player:getCardIds{Player.Hand, Player.Equip}, function(id)
|
|
return skill:cardFilter(id, selected_cards)
|
|
end)
|
|
if #avail_cards == 0 then
|
|
break
|
|
end
|
|
table.insert(selected_cards, table.random(avail_cards))
|
|
if skill:viewAs(selected_cards) then
|
|
return {
|
|
skill = skill.name,
|
|
subcards = selected_cards
|
|
}
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local random_cb = {}
|
|
|
|
random_cb.AskForUseActiveSkill = function(self, jsonData)
|
|
local data = json.decode(jsonData)
|
|
local skill = Fk.skills[data[1]]
|
|
local cancelable = data[3]
|
|
if cancelable and math.random() < 0.25 then
|
|
return ""
|
|
end
|
|
local extra_data = json.decode(data[4])
|
|
for k, v in pairs(extra_data) do
|
|
skill[k] = v
|
|
end
|
|
return useActiveSkill(self, skill)
|
|
end
|
|
|
|
random_cb.AskForSkillInvoke = function(self, jsonData)
|
|
return table.random {"1", ""}
|
|
end
|
|
|
|
random_cb.AskForUseCard = function(self, jsonData)
|
|
local player = self.player
|
|
local data = json.decode(jsonData)
|
|
local card_name = data[1]
|
|
local pattern = data[2] or card_name
|
|
local cancelable = data[4] or true
|
|
local exp = Exppattern:Parse(pattern)
|
|
|
|
local avail_cards = table.filter(player:getCardIds("he"), function(id)
|
|
return exp:match(Fk:getCardById(id)) and not player:prohibitUse(Fk:getCardById(id))
|
|
end)
|
|
if #avail_cards > 0 then
|
|
if math.random() < 0.25 then
|
|
return ""
|
|
end
|
|
avail_cards = table.map(avail_cards, function(id)
|
|
return Fk:getCardById(id)
|
|
end)
|
|
for _, card in ipairs(avail_cards) do
|
|
local skill = card.skill
|
|
local max_try_times = 100
|
|
local selected_targets = {}
|
|
local min = skill:getMinTargetNum()
|
|
local max = skill:getMaxTargetNum(player, card)
|
|
local min_card = skill:getMinCardNum()
|
|
local max_card = skill:getMaxCardNum()
|
|
for _ = 0, max_try_times do
|
|
if skill:feasible(selected_targets, {card.id}, self.player, card) then
|
|
break
|
|
end
|
|
local avail_targets = table.filter(self.room:getAlivePlayers(), function(p)
|
|
local ret = skill:targetFilter(p.id, selected_targets, {card}, card or Fk:cloneCard 'zixing')
|
|
return ret
|
|
end)
|
|
avail_targets = table.map(avail_targets, function(p)
|
|
return p.id
|
|
end)
|
|
|
|
if #avail_targets + #avail_cards < 1 then
|
|
break
|
|
end
|
|
table.insertIfNeed(selected_targets, table.random(avail_targets))
|
|
end
|
|
if skill:feasible(selected_targets, {card.id}, self.player, card) then
|
|
return json.encode {
|
|
card = table.random(avail_cards),
|
|
targets = selected_targets
|
|
}
|
|
end
|
|
end
|
|
end
|
|
return ""
|
|
end
|
|
|
|
---@param self RandomAI
|
|
random_cb.AskForResponseCard = function(self, jsonData)
|
|
local data = json.decode(jsonData)
|
|
local pattern = data[2]
|
|
local cancelable = true
|
|
local exp = Exppattern:Parse(pattern)
|
|
local avail_cards = table.filter(self.player:getCardIds{Player.Hand, Player.Equip}, function(id)
|
|
return exp:match(Fk:getCardById(id))
|
|
end)
|
|
if #avail_cards > 0 then
|
|
return json.encode {
|
|
card = table.random(avail_cards),
|
|
targets = {}
|
|
}
|
|
end
|
|
-- TODO: vs skill
|
|
return ""
|
|
end
|
|
|
|
---@param self RandomAI
|
|
random_cb.PlayCard = function(self, jsonData)
|
|
local cards = table.map(self.player:getCardIds(Player.Hand), function(id)
|
|
return Fk:getCardById(id)
|
|
end)
|
|
local actives = table.filter(self.player:getAllSkills(), function(s)
|
|
return s:isInstanceOf(ActiveSkill)
|
|
end)
|
|
local vss = table.filter(self.player:getAllSkills(), function(s)
|
|
return s:isInstanceOf(ViewAsSkill)
|
|
end)
|
|
table.insertTable(cards, actives)
|
|
table.insertTable(cards, vss)
|
|
|
|
while #cards > 0 do
|
|
local sth = table.random(cards)
|
|
if sth:isInstanceOf(Card) then
|
|
local card = sth
|
|
local skill = card.skill ---@type ActiveSkill
|
|
if math.random() > 0.15 then
|
|
local ret = useActiveSkill(self, skill, card)
|
|
if ret ~= "" then
|
|
return ret
|
|
end
|
|
table.removeOne(cards, card)
|
|
else
|
|
table.removeOne(cards, card)
|
|
end
|
|
elseif sth:isInstanceOf(ActiveSkill) then
|
|
local active = sth
|
|
if math.random() > 0.30 then
|
|
local ret = useActiveSkill(self, active, nil)
|
|
if ret ~= "" then
|
|
return ret
|
|
end
|
|
end
|
|
table.removeOne(cards, active)
|
|
else
|
|
local vs = sth
|
|
if math.random() > 0.20 then
|
|
local ret = useVSSkill(self, vs)
|
|
-- TODO: handle vs result
|
|
end
|
|
table.removeOne(cards, vs)
|
|
end
|
|
end
|
|
|
|
return ""
|
|
end
|
|
|
|
function RandomAI:initialize(player)
|
|
AI.initialize(self, player)
|
|
self.cb_table = random_cb
|
|
end
|
|
|
|
return RandomAI
|