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:
mattsc 2013-10-26 17:26:37 -07:00
parent 323595623b
commit 6fb7c46dbe
5 changed files with 138 additions and 133 deletions

View file

@ -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
}

View file

@ -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 = {}, {}

View 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

View 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

View file

@ -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]