mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-03 18:55:32 +00:00
141 lines
5.5 KiB
Lua
141 lines
5.5 KiB
Lua
------- Grab Villages CA --------------
|
|
|
|
local AH = wesnoth.require "ai/lua/ai_helper.lua"
|
|
local BC = wesnoth.require "ai/lua/battle_calcs.lua"
|
|
local M = wesnoth.map
|
|
|
|
local ca_grab_villages = {}
|
|
|
|
function ca_grab_villages:evaluation(cfg, data)
|
|
local start_time, ca_name = wesnoth.get_time_stamp() / 1000., 'grab_villages'
|
|
if AH.print_eval() then AH.print_ts(' - Evaluating grab_villages CA:') end
|
|
|
|
-- Check if there are units with moves left
|
|
local units = wesnoth.get_units { side = wesnoth.current.side, canrecruit = 'no',
|
|
formula = 'movement_left > 0'
|
|
}
|
|
if (not units[1]) then
|
|
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
|
return 0
|
|
end
|
|
|
|
local enemies = AH.get_attackable_enemies()
|
|
|
|
local villages = wesnoth.get_villages()
|
|
-- Just in case:
|
|
if (not villages[1]) then
|
|
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
|
return 0
|
|
end
|
|
|
|
-- First check if attacks are possible for any unit
|
|
local return_value = 200000
|
|
-- If one with > 50% chance of kill is possible, set return_value to lower than combat CA
|
|
local attacks = ai.get_attacks()
|
|
for i,a in ipairs(attacks) do
|
|
if (#a.movements == 1) and (a.chance_to_kill > 0.5) then
|
|
return_value = 90000
|
|
break
|
|
end
|
|
end
|
|
|
|
-- Also find which locations can be attacked by enemies
|
|
local enemy_attack_map = BC.get_attack_map(enemies).units
|
|
|
|
-- Now we go through the villages and units
|
|
local max_rating, best_village, best_unit = - math.huge
|
|
local village_ratings = {}
|
|
for j,v in ipairs(villages) do
|
|
-- First collect all information that only depends on the village
|
|
local village_rating = 0 -- This is the unit independent rating
|
|
|
|
local unit_in_way = wesnoth.get_unit(v[1], v[2])
|
|
|
|
-- If an enemy can get within one move of the village, we want to hold it
|
|
if enemy_attack_map:get(v[1], v[2]) then
|
|
village_rating = village_rating + 100
|
|
end
|
|
|
|
-- Unowned and enemy-owned villages get a large bonus
|
|
local owner = wesnoth.get_village_owner(v[1], v[2])
|
|
if (not owner) then
|
|
village_rating = village_rating + 10000
|
|
else
|
|
if wesnoth.is_enemy(owner, wesnoth.current.side) then village_rating = village_rating + 20000 end
|
|
end
|
|
|
|
local enemy_distance_from_village = AH.get_closest_enemy(v)
|
|
|
|
-- Now we go on to the unit-dependent rating
|
|
local best_unit_rating = - math.huge
|
|
local reachable = false
|
|
for i,u in ipairs(units) do
|
|
-- Skip villages that have units other than 'u' itself on them
|
|
local village_occupied = false
|
|
if AH.is_visible_unit(wesnoth.current.side, unit_in_way) and ((unit_in_way ~= u)) then
|
|
village_occupied = true
|
|
end
|
|
|
|
-- Rate all villages that can be reached and are unoccupied by other units
|
|
if (not village_occupied) then
|
|
-- Path finding is expensive, so we do a first cut simply by distance
|
|
-- There is no way a unit can get to the village if the distance is greater than its moves
|
|
local dist = M.distance_between(u.x, u.y, v[1], v[2])
|
|
if (dist <= u.moves) then
|
|
local path, cost = wesnoth.find_path(u, v[1], v[2])
|
|
if (cost <= u.moves) then
|
|
village_rating = village_rating - 1
|
|
reachable = true
|
|
local rating = 0
|
|
|
|
-- Prefer strong units if enemies can reach the village, injured units otherwise
|
|
if enemy_attack_map:get(v[1], v[2]) then
|
|
rating = rating + u.hitpoints
|
|
else
|
|
rating = rating + u.max_hitpoints - u.hitpoints
|
|
end
|
|
|
|
-- Prefer not backtracking and moving more distant units to capture villages
|
|
local enemy_distance_from_unit = AH.get_closest_enemy({u.x, u.y})
|
|
rating = rating - (enemy_distance_from_village + enemy_distance_from_unit)/5
|
|
|
|
if (rating > best_unit_rating) then
|
|
best_unit_rating, best_unit = rating, u
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
village_ratings[v] = {village_rating, best_unit, reachable}
|
|
end
|
|
for j,v in ipairs(villages) do
|
|
local rating = village_ratings[v][1]
|
|
if village_ratings[v][3] and rating > max_rating then
|
|
max_rating, best_village, best_unit = rating, v, village_ratings[v][2]
|
|
end
|
|
end
|
|
|
|
if best_village then
|
|
data.unit, data.village = best_unit, best_village
|
|
if (max_rating >= 1000) then
|
|
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
|
return return_value
|
|
else
|
|
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
|
return 0
|
|
end
|
|
end
|
|
if AH.print_eval() then AH.done_eval_messages(start_time, ca_name) end
|
|
return 0
|
|
end
|
|
|
|
function ca_grab_villages:execution(cfg, data)
|
|
if AH.print_exec() then AH.print_ts(' Executing grab_villages CA') end
|
|
if AH.show_messages() then wesnoth.wml_actions.message { speaker = data.unit.id, message = 'Grab villages' } end
|
|
|
|
AH.movefull_stopunit(ai, data.unit, data.village)
|
|
data.unit, data.village = nil, nil
|
|
end
|
|
|
|
return ca_grab_villages
|