-
-
Notifications
You must be signed in to change notification settings - Fork 993
/
ca_wolves_move.lua
123 lines (101 loc) · 4.62 KB
/
ca_wolves_move.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local BC = wesnoth.require "ai/lua/battle_calcs.lua"
local M = wesnoth.map
local function get_wolves(cfg)
local wolves = AH.get_units_with_moves {
side = wesnoth.current.side,
{ "and", wml.get_child(cfg, "filter") }
}
return wolves
end
local function get_prey(cfg)
-- Note: we cannot pass wml.get_child() directly to AH.get_attackable_enemies()
-- as the former returns two values and the latter takes optional arguments
local filter_second = wml.get_child(cfg, "filter_second")
local prey = AH.get_attackable_enemies(filter_second)
return prey
end
local ca_wolves_move = {}
function ca_wolves_move:evaluation(cfg)
if (not get_wolves(cfg)[1]) then return 0 end
local avoid_map = AH.get_avoid_map(ai, nil, true)
local prey = get_prey(cfg)
local prey_found = false
for _,prey_unit in ipairs(prey) do
if (not avoid_map:get(prey_unit.x, prey_unit.y)) then
prey_found = true
break
end
end
if (not prey_found) then return 0 end
return cfg.ca_score
end
function ca_wolves_move:execution(cfg)
local wolves = get_wolves(cfg)
local prey = get_prey(cfg)
-- Only default AI [avoid] tag makes sense for the wolves since attacks are done by RCA AI
local avoid_map = AH.get_avoid_map(ai, nil, true)
local avoid_units = AH.get_attackable_enemies({ type = cfg.avoid_type })
local avoid_enemies_map = BC.get_attack_map(avoid_units).units
-- Find prey that is closest to the wolves
local min_dist, target = math.huge
for _,prey_unit in ipairs(prey) do
if (not avoid_map:get(prey_unit.x, prey_unit.y)) then
local dist = 0
for _,wolf in ipairs(wolves) do
dist = dist + M.distance_between(wolf.x, wolf.y, prey_unit.x, prey_unit.y)
end
if (dist < min_dist) then
min_dist, target = dist, prey_unit
end
end
end
-- Now sort wolf from farthest to closest
table.sort(wolves, function(a, b)
return M.distance_between(a.x, a.y, target.x, target.y) > M.distance_between(b.x, b.y, target.x, target.y)
end)
-- First wolf moves toward target, but tries to stay away from map edges
local width, height = wesnoth.get_map_size()
local wolf1 = AH.find_best_move(wolves[1], function(x, y)
local dist_1t = M.distance_between(x, y, target.x, target.y)
local rating = - dist_1t
if (x <= 5) then rating = rating - (6 - x) / 1.4 end
if (y <= 5) then rating = rating - (6 - y) / 1.4 end
if (width - x <= 5) then rating = rating - (6 - (width - x)) / 1.4 end
if (height - y <= 5) then rating = rating - (6 - (height - y)) / 1.4 end
-- Hexes that avoid_type units can reach get a massive penalty
if avoid_enemies_map:get(x, y) then rating = rating - 1000 end
return rating
end, { avoid_map = avoid_map })
local move_result = AH.movefull_stopunit(ai, wolves[1], wolf1 or { wolf1.x, wolf1.y })
-- If the wolf was ambushed, return and reconsider; also if an event removed a wolf
if (AH.is_incomplete_move(move_result)) then return end
for _,check_wolf in ipairs(wolves) do
if (not check_wolf) or (not check_wolf.valid) then return end
end
for i = 2,#wolves do
move = AH.find_best_move(wolves[i], function(x,y)
local rating = 0
-- We ideally want wolves to be 2-3 hexes from each other
-- but this requirement gets weaker and weaker with increasing wolf number
for j = 1,i-1 do
local dst = M.distance_between(x, y, wolves[j].x, wolves[j].y)
rating = rating - (dst - 2.7 * j)^2 / j
end
-- Same distance from Wolf 1 and target for all the wolves
local dist_t = M.distance_between(x, y, target.x, target.y)
local dist_1t = M.distance_between(wolf1[1], wolf1[2], target.x, target.y)
rating = rating - (dist_t - dist_1t)^2
-- Hexes that avoid_type units can reach get a massive penalty
if avoid_enemies_map:get(x, y) then rating = rating - 1000 end
return rating
end, { avoid_map = avoid_map })
local move_result = AH.movefull_stopunit(ai, wolves[i], move or { wolves[i].x, wolves[i].y })
-- If the wolf was ambushed, return and reconsider; also if an event removed a wolf
if (AH.is_incomplete_move(move_result)) then return end
for _,check_wolf in ipairs(wolves) do
if (not check_wolf) or (not check_wolf.valid) then return end
end
end
end
return ca_wolves_move