Both Recruiting MAIs: switch to using external CAs
This includes disabling the rush recruiter for the time being, as the engine files it uses do not yet use the external CAs. It will be re-enabled once those have been converted as well.
This commit is contained in:
parent
323595623b
commit
6fb7c46dbe
5 changed files with 138 additions and 133 deletions
|
@ -1,129 +0,0 @@
|
|||
return {
|
||||
init = function(ai, existing_engine)
|
||||
|
||||
local AH = wesnoth.require("ai/lua/ai_helper.lua")
|
||||
|
||||
local engine = existing_engine or {}
|
||||
local internal_recruit_cas = {}
|
||||
local internal_params = {}
|
||||
-- The following external engine creates the CA functions recruit_rushers_eval and recruit_rushers_exec
|
||||
-- It also exposes find_best_recruit and find_best_recruit_hex for use by other recruit engines
|
||||
wesnoth.require("ai/lua/generic_recruit_engine.lua").init(ai, internal_recruit_cas, internal_params)
|
||||
|
||||
function engine:mai_rusher_recruit_eval(cfg)
|
||||
internal_params.randomness = cfg.randomness
|
||||
return internal_recruit_cas:recruit_rushers_eval()
|
||||
end
|
||||
|
||||
function engine:mai_rusher_recruit_exec(cfg)
|
||||
return internal_recruit_cas:recruit_rushers_exec()
|
||||
end
|
||||
|
||||
local recruit
|
||||
|
||||
function engine:mai_random_recruit_eval(cfg)
|
||||
-- Random recruiting from all the units the side has
|
||||
|
||||
-- Check if leader is on keep
|
||||
local leader = wesnoth.get_units { side = wesnoth.current.side, canrecruit = 'yes' }[1]
|
||||
if (not leader) or (not wesnoth.get_terrain_info(wesnoth.get_terrain(leader.x, leader.y)).keep) then
|
||||
return 0
|
||||
end
|
||||
|
||||
-- Check if there is space left for recruiting
|
||||
local width, height, border = wesnoth.get_map_size()
|
||||
local castle = {
|
||||
locs = wesnoth.get_locations {
|
||||
x = "1-"..width, y = "1-"..height,
|
||||
{ "and", {
|
||||
x = leader.x, y = leader.y, radius = 200,
|
||||
{ "filter_radius", { terrain = 'C*,K*,C*^*,K*^*,*^K*,*^C*' } }
|
||||
}}
|
||||
}
|
||||
}
|
||||
local no_space = true
|
||||
for i,c in ipairs(castle.locs) do
|
||||
local unit = wesnoth.get_unit(c[1], c[2])
|
||||
if (not unit) then
|
||||
no_space = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if no_space then return 0 end
|
||||
|
||||
-- Set up the probability array
|
||||
local prob, prob_sum = {}, 0
|
||||
|
||||
-- Go through all the types listed in [probability] tags (which can be comma-separated lists)
|
||||
for i,tmp in ipairs(cfg.type) do
|
||||
tmp = AH.split(tmp, ",")
|
||||
for j,t in ipairs(tmp) do
|
||||
-- If this type is in the recruit list, add it
|
||||
for k,r in ipairs(wesnoth.sides[wesnoth.current.side].recruit) do
|
||||
if (r == t) then
|
||||
prob[t] = { value = cfg.prob[i] }
|
||||
prob_sum = prob_sum + cfg.prob[i]
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Now we add in all the unit types not listed in [probability] tags
|
||||
for i,r in ipairs(wesnoth.sides[wesnoth.current.side].recruit) do
|
||||
if (not prob[r]) then
|
||||
prob[r] = { value = 1 }
|
||||
prob_sum =prob_sum + 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Now eliminate all those that are too expensive (unless cfg.skip_low_gold_recruiting is set)
|
||||
if cfg.skip_low_gold_recruiting then
|
||||
for typ,pr in pairs(prob) do
|
||||
if (wesnoth.unit_types[typ].cost > wesnoth.sides[wesnoth.current.side].gold) then
|
||||
--print('Eliminating:', typ)
|
||||
prob_sum = prob_sum - pr.value
|
||||
prob[typ] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Now set up the min/max values for each type
|
||||
-- This needs to be done manually as the order of pairs() is not guaranteed
|
||||
local cum_prob, n_recruits = 0, 0
|
||||
for typ,pr in pairs(prob) do
|
||||
prob[typ].p_i = cum_prob
|
||||
cum_prob = cum_prob + pr.value / prob_sum * 1e6
|
||||
prob[typ].p_f = cum_prob
|
||||
n_recruits = n_recruits + 1
|
||||
end
|
||||
|
||||
-- Now we're ready to pick on of those
|
||||
-- We always call the exec function, no matter if the selected unit is affordable
|
||||
-- The point is that this will blacklist the CA if an unaffordable recruit was
|
||||
-- chosen -> no cheaper recruits will be selected in subsequent calls
|
||||
if (n_recruits > 0) then
|
||||
local rand_prob = AH.random(1e6)
|
||||
for typ,pr in pairs(prob) do
|
||||
if (pr.p_i <= rand_prob) and (rand_prob < pr.p_f) then
|
||||
recruit = typ
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
recruit = wesnoth.sides[wesnoth.current.side].recruit[1]
|
||||
end
|
||||
|
||||
return cfg.ca_score
|
||||
end
|
||||
|
||||
function engine:mai_random_recruit_exec()
|
||||
-- Let this function blacklist itself if the chosen recruit is too expensive
|
||||
if wesnoth.unit_types[recruit].cost <= wesnoth.sides[wesnoth.current.side].gold then
|
||||
ai.recruit(recruit)
|
||||
end
|
||||
end
|
||||
|
||||
return engine
|
||||
end
|
||||
}
|
|
@ -415,11 +415,13 @@ function wesnoth.wml_actions.micro_ai(cfg)
|
|||
elseif (cfg.ai_type == 'recruiting') then
|
||||
if (cfg.recruiting_type == 'rushers') then
|
||||
optional_keys = { "randomness" }
|
||||
CA_parms = { { ca_id = "mai_rusher_recruit", score = cfg.ca_score or 180000 } }
|
||||
CA_parms = { { ca_id = "mai_rusher_recruit", location = 'ai/micro_ais/cas/ca_recruit_rushers.lua', score = cfg.ca_score or 180000 } }
|
||||
wesnoth.message('The recruit_rushers Micro AI is currently diabled while we reorganize some things. Please check back later.')
|
||||
return
|
||||
|
||||
elseif (cfg.recruiting_type == 'random') then
|
||||
optional_keys = { "skip_low_gold_recruiting", "type", "prob" }
|
||||
CA_parms = { { ca_id = "mai_random_recruit", score = cfg.ca_score or 180000 } }
|
||||
CA_parms = { { ca_id = "mai_random_recruit", location = 'ai/micro_ais/cas/ca_recruit_random.lua', score = cfg.ca_score or 180000 } }
|
||||
|
||||
-- The 'probability' tags need to be handled separately here
|
||||
cfg.type, cfg.prob = {}, {}
|
||||
|
|
110
data/ai/micro_ais/cas/ca_recruit_random.lua
Normal file
110
data/ai/micro_ais/cas/ca_recruit_random.lua
Normal file
|
@ -0,0 +1,110 @@
|
|||
local AH = wesnoth.require("ai/lua/ai_helper.lua")
|
||||
|
||||
local ca_recruit_random = {}
|
||||
|
||||
local recruit
|
||||
|
||||
function ca_recruit_random:evaluation(ai, cfg)
|
||||
-- Random recruiting from all the units the side has
|
||||
|
||||
-- Check if leader is on keep
|
||||
local leader = wesnoth.get_units { side = wesnoth.current.side, canrecruit = 'yes' }[1]
|
||||
if (not leader) or (not wesnoth.get_terrain_info(wesnoth.get_terrain(leader.x, leader.y)).keep) then
|
||||
return 0
|
||||
end
|
||||
|
||||
-- Check if there is space left for recruiting
|
||||
local width, height, border = wesnoth.get_map_size()
|
||||
local castle = {
|
||||
locs = wesnoth.get_locations {
|
||||
x = "1-"..width, y = "1-"..height,
|
||||
{ "and", {
|
||||
x = leader.x, y = leader.y, radius = 200,
|
||||
{ "filter_radius", { terrain = 'C*,K*,C*^*,K*^*,*^K*,*^C*' } }
|
||||
}}
|
||||
}
|
||||
}
|
||||
local no_space = true
|
||||
for i,c in ipairs(castle.locs) do
|
||||
local unit = wesnoth.get_unit(c[1], c[2])
|
||||
if (not unit) then
|
||||
no_space = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if no_space then return 0 end
|
||||
|
||||
-- Set up the probability array
|
||||
local prob, prob_sum = {}, 0
|
||||
|
||||
-- Go through all the types listed in [probability] tags (which can be comma-separated lists)
|
||||
for i,tmp in ipairs(cfg.type) do
|
||||
tmp = AH.split(tmp, ",")
|
||||
for j,t in ipairs(tmp) do
|
||||
-- If this type is in the recruit list, add it
|
||||
for k,r in ipairs(wesnoth.sides[wesnoth.current.side].recruit) do
|
||||
if (r == t) then
|
||||
prob[t] = { value = cfg.prob[i] }
|
||||
prob_sum = prob_sum + cfg.prob[i]
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Now we add in all the unit types not listed in [probability] tags
|
||||
for i,r in ipairs(wesnoth.sides[wesnoth.current.side].recruit) do
|
||||
if (not prob[r]) then
|
||||
prob[r] = { value = 1 }
|
||||
prob_sum =prob_sum + 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Now eliminate all those that are too expensive (unless cfg.skip_low_gold_recruiting is set)
|
||||
if cfg.skip_low_gold_recruiting then
|
||||
for typ,pr in pairs(prob) do
|
||||
if (wesnoth.unit_types[typ].cost > wesnoth.sides[wesnoth.current.side].gold) then
|
||||
--print('Eliminating:', typ)
|
||||
prob_sum = prob_sum - pr.value
|
||||
prob[typ] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Now set up the min/max values for each type
|
||||
-- This needs to be done manually as the order of pairs() is not guaranteed
|
||||
local cum_prob, n_recruits = 0, 0
|
||||
for typ,pr in pairs(prob) do
|
||||
prob[typ].p_i = cum_prob
|
||||
cum_prob = cum_prob + pr.value / prob_sum * 1e6
|
||||
prob[typ].p_f = cum_prob
|
||||
n_recruits = n_recruits + 1
|
||||
end
|
||||
|
||||
-- Now we're ready to pick on of those
|
||||
-- We always call the exec function, no matter if the selected unit is affordable
|
||||
-- The point is that this will blacklist the CA if an unaffordable recruit was
|
||||
-- chosen -> no cheaper recruits will be selected in subsequent calls
|
||||
if (n_recruits > 0) then
|
||||
local rand_prob = AH.random(1e6)
|
||||
for typ,pr in pairs(prob) do
|
||||
if (pr.p_i <= rand_prob) and (rand_prob < pr.p_f) then
|
||||
recruit = typ
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
recruit = wesnoth.sides[wesnoth.current.side].recruit[1]
|
||||
end
|
||||
|
||||
return cfg.ca_score
|
||||
end
|
||||
|
||||
function ca_recruit_random:execution(ai, cfg)
|
||||
-- Let this function blacklist itself if the chosen recruit is too expensive
|
||||
if wesnoth.unit_types[recruit].cost <= wesnoth.sides[wesnoth.current.side].gold then
|
||||
ai.recruit(recruit)
|
||||
end
|
||||
end
|
||||
|
||||
return ca_recruit_random
|
24
data/ai/micro_ais/cas/ca_recruit_rushers.lua
Normal file
24
data/ai/micro_ais/cas/ca_recruit_rushers.lua
Normal file
|
@ -0,0 +1,24 @@
|
|||
local AH = wesnoth.require("ai/lua/ai_helper.lua")
|
||||
local DBG = wesnoth.require "~/add-ons/AI-demos/lua/debug.lua"
|
||||
|
||||
local ca_recruit_rushers = {}
|
||||
|
||||
local internal_recruit_cas = {}
|
||||
local internal_params = {}
|
||||
-- The following external engine creates the CA functions recruit_rushers_eval and recruit_rushers_exec
|
||||
-- It also exposes find_best_recruit and find_best_recruit_hex for use by other recruit engines
|
||||
|
||||
-- TODO: this does not currently work, as 'ai' is not defined in this context
|
||||
wesnoth.require("ai/lua/generic_recruit_engine.lua").init(ai, internal_recruit_cas, internal_params)
|
||||
|
||||
function ca_recruit_rushers:evaluation(ai, cfg)
|
||||
internal_params.randomness = cfg.randomness
|
||||
return internal_recruit_cas:recruit_rushers_eval()
|
||||
end
|
||||
|
||||
function ca_recruit_rushers:execution(ai)
|
||||
print('recruiting')
|
||||
return internal_recruit_cas:recruit_rushers_exec()
|
||||
end
|
||||
|
||||
return ca_recruit_rushers
|
|
@ -24,8 +24,6 @@
|
|||
recruit=Peasant,Spearman,Swordsman,Bowman,Longbowman,Cavalryman,Dragoon,Heavy Infantryman,Shock Trooper,Mage,White Mage
|
||||
|
||||
gold=1000
|
||||
|
||||
{MICRO_AI_RECRUITING}
|
||||
[/side]
|
||||
|
||||
[side]
|
||||
|
|
Loading…
Add table
Reference in a new issue