Swarm MAI: switch to using external CAs
This commit is contained in:
parent
f308a26dcb
commit
d486c093ac
5 changed files with 146 additions and 146 deletions
|
@ -1,142 +0,0 @@
|
|||
return {
|
||||
init = function(ai, existing_engine)
|
||||
local engine = existing_engine or {}
|
||||
|
||||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
|
||||
function engine:mai_swarm_scatter_eval(cfg)
|
||||
local scatter_distance = cfg.scatter_distance or 3
|
||||
|
||||
-- Any enemy within "scatter_distance" hexes of a unit will cause swarm to scatter
|
||||
local enemies = wesnoth.get_units {
|
||||
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} },
|
||||
{ "filter_location",
|
||||
{ radius = scatter_distance, { "filter", { side = wesnoth.current.side } } }
|
||||
}
|
||||
}
|
||||
|
||||
if enemies[1] then -- don't use 'formula=' for moves, it's slow
|
||||
local units = wesnoth.get_units { side = wesnoth.current.side }
|
||||
for i,u in ipairs(units) do
|
||||
if (u.moves > 0) then return cfg.ca_score end
|
||||
end
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function engine:mai_swarm_scatter_exec(cfg)
|
||||
local scatter_distance = cfg.scatter_distance or 3
|
||||
local vision_distance = cfg.vision_distance or 12
|
||||
|
||||
-- Any enemy within "scatter_distance" hexes of a unit will cause swarm to scatter
|
||||
local units = wesnoth.get_units { side = wesnoth.current.side }
|
||||
for i = #units,1,-1 do
|
||||
if (units[i].moves == 0) then table.remove(units, i) end
|
||||
end
|
||||
|
||||
local enemies = wesnoth.get_units {
|
||||
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} },
|
||||
{ "filter_location",
|
||||
{ radius = scatter_distance, { "filter", { side = wesnoth.current.side } } }
|
||||
}
|
||||
}
|
||||
|
||||
-- In this case we simply maximize the distance from all these close enemies
|
||||
-- but only for units that are within 'vision_distance' of one of those enemies
|
||||
for i,unit in ipairs(units) do
|
||||
local unit_enemies = {}
|
||||
for i,e in ipairs(enemies) do
|
||||
if (H.distance_between(unit.x, unit.y, e.x, e.y) <= vision_distance) then
|
||||
table.insert(unit_enemies, e)
|
||||
end
|
||||
end
|
||||
|
||||
if unit_enemies[1] then
|
||||
local best_hex = AH.find_best_move(unit, function(x, y)
|
||||
local rating = 0
|
||||
for i,e in ipairs(unit_enemies) do
|
||||
rating = rating + H.distance_between(x, y, e.x, e.y)
|
||||
end
|
||||
return rating
|
||||
end)
|
||||
|
||||
AH.movefull_stopunit(ai, unit, best_hex)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function engine:mai_swarm_move_eval(cfg)
|
||||
local units = wesnoth.get_units { side = wesnoth.current.side }
|
||||
for i,u in ipairs(units) do
|
||||
if (u.moves > 0) then return cfg.ca_score end
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function engine:mai_swarm_move_exec(cfg)
|
||||
local enemy_distance = cfg.enemy_distance or 5
|
||||
local vision_distance = cfg.vision_distance or 12
|
||||
|
||||
-- If no close enemies, swarm will move semi-randomly, staying close together, but away from enemies
|
||||
local all_units = wesnoth.get_units { side = wesnoth.current.side }
|
||||
local units, units_no_moves = {}, {}
|
||||
for i,u in ipairs(all_units) do
|
||||
if (u.moves > 0) then
|
||||
table.insert(units, u)
|
||||
else
|
||||
table.insert(units_no_moves, u)
|
||||
end
|
||||
end
|
||||
|
||||
local enemies = wesnoth.get_units {
|
||||
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} }
|
||||
}
|
||||
--print('#units, #units_no_moves, #enemies', #units, #units_no_moves, #enemies)
|
||||
|
||||
-- pick a random unit and remove it from 'units'
|
||||
local rand = AH.random(#units)
|
||||
local unit = units[rand]
|
||||
table.remove(units, rand)
|
||||
|
||||
-- Find best place for that unit to move to
|
||||
local best_hex = AH.find_best_move(unit, function(x, y)
|
||||
local rating = 0
|
||||
|
||||
-- Only units within 'vision_distance' count for rejoining
|
||||
local close_units_no_moves = {}
|
||||
for i,u in ipairs(units_no_moves) do
|
||||
if (H.distance_between(unit.x, unit.y, u.x, u.y) <= vision_distance) then
|
||||
table.insert(close_units_no_moves, u)
|
||||
end
|
||||
end
|
||||
|
||||
-- If all units on the side have moves left, simply go to a hex far away
|
||||
if (not close_units_no_moves[1]) then
|
||||
rating = rating + H.distance_between(x, y, unit.x, unit.y)
|
||||
else -- otherwise, minimize distance from units that have already moved
|
||||
for i,u in ipairs(close_units_no_moves) do
|
||||
rating = rating - H.distance_between(x, y, u.x, u.y)
|
||||
end
|
||||
end
|
||||
|
||||
-- We also try to stay out of attack range of any enemy
|
||||
for i,e in ipairs(enemies) do
|
||||
local dist = H.distance_between(x, y, e.x, e.y)
|
||||
-- If enemy is within attack range, avoid those hexes
|
||||
if (dist < enemy_distance) then
|
||||
rating = rating - (enemy_distance - dist) * 10.
|
||||
end
|
||||
end
|
||||
|
||||
return rating
|
||||
end)
|
||||
|
||||
AH.movefull_stopunit(ai, unit, best_hex)
|
||||
end
|
||||
|
||||
return engine
|
||||
end
|
||||
}
|
|
@ -388,8 +388,8 @@ function wesnoth.wml_actions.micro_ai(cfg)
|
|||
optional_keys = { "scatter_distance", "vision_distance", "enemy_distance" }
|
||||
local score = cfg.ca_score or 300000
|
||||
CA_parms = {
|
||||
{ ca_id = "mai_swarm_scatter", score = score },
|
||||
{ ca_id = "mai_swarm_move", score = score - 1 }
|
||||
{ ca_id = "mai_swarm_scatter", location = 'ai/micro_ais/cas/ca_swarm_scatter.lua', score = score },
|
||||
{ ca_id = "mai_swarm_move", location = 'ai/micro_ais/cas/ca_swarm_move.lua', score = score - 1 }
|
||||
}
|
||||
|
||||
elseif (cfg.ai_type == 'wolves_multipacks') then
|
||||
|
|
76
data/ai/micro_ais/cas/ca_swarm_move.lua
Normal file
76
data/ai/micro_ais/cas/ca_swarm_move.lua
Normal file
|
@ -0,0 +1,76 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
|
||||
local swarm_move = {}
|
||||
|
||||
function swarm_move:evaluation(ai, cfg)
|
||||
local units = wesnoth.get_units { side = wesnoth.current.side }
|
||||
for i,u in ipairs(units) do
|
||||
if (u.moves > 0) then return cfg.ca_score end
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function swarm_move:execution(ai, cfg)
|
||||
local enemy_distance = cfg.enemy_distance or 5
|
||||
local vision_distance = cfg.vision_distance or 12
|
||||
|
||||
-- If no close enemies, swarm will move semi-randomly, staying close together, but away from enemies
|
||||
local all_units = wesnoth.get_units { side = wesnoth.current.side }
|
||||
local units, units_no_moves = {}, {}
|
||||
for i,u in ipairs(all_units) do
|
||||
if (u.moves > 0) then
|
||||
table.insert(units, u)
|
||||
else
|
||||
table.insert(units_no_moves, u)
|
||||
end
|
||||
end
|
||||
|
||||
local enemies = wesnoth.get_units {
|
||||
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} }
|
||||
}
|
||||
--print('#units, #units_no_moves, #enemies', #units, #units_no_moves, #enemies)
|
||||
|
||||
-- pick a random unit and remove it from 'units'
|
||||
local rand = AH.random(#units)
|
||||
local unit = units[rand]
|
||||
table.remove(units, rand)
|
||||
|
||||
-- Find best place for that unit to move to
|
||||
local best_hex = AH.find_best_move(unit, function(x, y)
|
||||
local rating = 0
|
||||
|
||||
-- Only units within 'vision_distance' count for rejoining
|
||||
local close_units_no_moves = {}
|
||||
for i,u in ipairs(units_no_moves) do
|
||||
if (H.distance_between(unit.x, unit.y, u.x, u.y) <= vision_distance) then
|
||||
table.insert(close_units_no_moves, u)
|
||||
end
|
||||
end
|
||||
|
||||
-- If all units on the side have moves left, simply go to a hex far away
|
||||
if (not close_units_no_moves[1]) then
|
||||
rating = rating + H.distance_between(x, y, unit.x, unit.y)
|
||||
else -- otherwise, minimize distance from units that have already moved
|
||||
for i,u in ipairs(close_units_no_moves) do
|
||||
rating = rating - H.distance_between(x, y, u.x, u.y)
|
||||
end
|
||||
end
|
||||
|
||||
-- We also try to stay out of attack range of any enemy
|
||||
for i,e in ipairs(enemies) do
|
||||
local dist = H.distance_between(x, y, e.x, e.y)
|
||||
-- If enemy is within attack range, avoid those hexes
|
||||
if (dist < enemy_distance) then
|
||||
rating = rating - (enemy_distance - dist) * 10.
|
||||
end
|
||||
end
|
||||
|
||||
return rating
|
||||
end)
|
||||
|
||||
AH.movefull_stopunit(ai, unit, best_hex)
|
||||
end
|
||||
|
||||
return swarm_move
|
68
data/ai/micro_ais/cas/ca_swarm_scatter.lua
Normal file
68
data/ai/micro_ais/cas/ca_swarm_scatter.lua
Normal file
|
@ -0,0 +1,68 @@
|
|||
local H = wesnoth.require "lua/helper.lua"
|
||||
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
||||
|
||||
local swarm_scatter = {}
|
||||
|
||||
function swarm_scatter:evaluation(ai, cfg)
|
||||
local scatter_distance = cfg.scatter_distance or 3
|
||||
|
||||
-- Any enemy within "scatter_distance" hexes of a unit will cause swarm to scatter
|
||||
local enemies = wesnoth.get_units {
|
||||
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} },
|
||||
{ "filter_location",
|
||||
{ radius = scatter_distance, { "filter", { side = wesnoth.current.side } } }
|
||||
}
|
||||
}
|
||||
|
||||
if enemies[1] then -- don't use 'formula=' for moves, it's slow
|
||||
local units = wesnoth.get_units { side = wesnoth.current.side }
|
||||
for i,u in ipairs(units) do
|
||||
if (u.moves > 0) then return cfg.ca_score end
|
||||
end
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function swarm_scatter:execution(ai, cfg)
|
||||
local scatter_distance = cfg.scatter_distance or 3
|
||||
local vision_distance = cfg.vision_distance or 12
|
||||
|
||||
-- Any enemy within "scatter_distance" hexes of a unit will cause swarm to scatter
|
||||
local units = wesnoth.get_units { side = wesnoth.current.side }
|
||||
for i = #units,1,-1 do
|
||||
if (units[i].moves == 0) then table.remove(units, i) end
|
||||
end
|
||||
|
||||
local enemies = wesnoth.get_units {
|
||||
{ "filter_side", {{"enemy_of", {side = wesnoth.current.side} }} },
|
||||
{ "filter_location",
|
||||
{ radius = scatter_distance, { "filter", { side = wesnoth.current.side } } }
|
||||
}
|
||||
}
|
||||
|
||||
-- In this case we simply maximize the distance from all these close enemies
|
||||
-- but only for units that are within 'vision_distance' of one of those enemies
|
||||
for i,unit in ipairs(units) do
|
||||
local unit_enemies = {}
|
||||
for i,e in ipairs(enemies) do
|
||||
if (H.distance_between(unit.x, unit.y, e.x, e.y) <= vision_distance) then
|
||||
table.insert(unit_enemies, e)
|
||||
end
|
||||
end
|
||||
|
||||
if unit_enemies[1] then
|
||||
local best_hex = AH.find_best_move(unit, function(x, y)
|
||||
local rating = 0
|
||||
for i,e in ipairs(unit_enemies) do
|
||||
rating = rating + H.distance_between(x, y, e.x, e.y)
|
||||
end
|
||||
return rating
|
||||
end)
|
||||
|
||||
AH.movefull_stopunit(ai, unit, best_hex)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return swarm_scatter
|
|
@ -41,8 +41,6 @@
|
|||
|
||||
gold=0
|
||||
income=-2
|
||||
|
||||
{MICRO_AI_SWARM}
|
||||
[/side]
|
||||
|
||||
[side] # This side is only here because we need one persistent side for the game to go on
|
||||
|
|
Loading…
Add table
Reference in a new issue