Hunter Micro AI: code cleanup
This commit is contained in:
parent
1928836333
commit
1ab3faca44
1 changed files with 29 additions and 38 deletions
|
@ -10,13 +10,11 @@ local function hunter_attack_weakest_adj_enemy(ai, hunter)
|
|||
-- 'killed': if a unit was killed
|
||||
-- 'no_attack': if no unit was attacked
|
||||
|
||||
-- First check that the hunter exists and has attacks left
|
||||
if (not hunter.valid) then return 'no_attack' end
|
||||
if (hunter.attacks_left == 0) then return 'no_attack' end
|
||||
|
||||
local min_hp, target = 9e99, {}
|
||||
for x, y in H.adjacent_tiles(hunter.x, hunter.y) do
|
||||
local enemy = wesnoth.get_unit(x, y)
|
||||
local min_hp, target = 9e99
|
||||
for xa,ya in H.adjacent_tiles(hunter.x, hunter.y) do
|
||||
local enemy = wesnoth.get_unit(xa, ya)
|
||||
if enemy and wesnoth.is_enemy(enemy.side, wesnoth.current.side) then
|
||||
if (enemy.hitpoints < min_hp) then
|
||||
min_hp, target = enemy.hitpoints, enemy
|
||||
|
@ -24,10 +22,9 @@ local function hunter_attack_weakest_adj_enemy(ai, hunter)
|
|||
end
|
||||
end
|
||||
|
||||
if target.id then
|
||||
--W.message { speaker = hunter.id, message = 'Attacking weakest adjacent enemy' }
|
||||
if target then
|
||||
AH.checked_attack(ai, hunter, target)
|
||||
if target.valid then
|
||||
if target and target.valid then
|
||||
return 'attacked'
|
||||
else
|
||||
return 'killed'
|
||||
|
@ -53,41 +50,38 @@ function ca_hunter:evaluation(ai, cfg)
|
|||
return 0
|
||||
end
|
||||
|
||||
-- cfg parameters: id, hunting_ground, home_x, home_y, rest_turns, show_messages
|
||||
function ca_hunter:execution(ai, cfg)
|
||||
-- Unit with the given ID goes on a hunt, doing a random wander in area given by
|
||||
-- hunting_ground, then retreats to
|
||||
-- position given by 'home_x,home_y' for 'rest_turns' turns, or until fully healed
|
||||
-- Hunter does a random wander in area given by @cfg.hunting_ground until it finds
|
||||
-- and kills an enemy unit, then retreats to position given by @cfg.home_x/y
|
||||
-- for @cfg.rest_turns turns, or until fully healed
|
||||
|
||||
local hunter = get_hunter(cfg)
|
||||
|
||||
-- If hunting_status is not set for the hunter -> default behavior -> hunting
|
||||
local hunter_vars = MAIUV.get_mai_unit_variables(hunter, cfg.ai_id)
|
||||
|
||||
-- If hunting_status is not set for the hunter -> default behavior -> random wander
|
||||
if (not hunter_vars.hunting_status) then
|
||||
-- Hunter gets a new goal if none exist or on any move with 10% random chance
|
||||
local r = math.random(10)
|
||||
if (not hunter_vars.goal_x) or (r <= 1) then
|
||||
local rand = math.random(10)
|
||||
if (not hunter_vars.goal_x) or (rand == 1) then
|
||||
-- 'locs' includes border hexes, but that does not matter here
|
||||
locs = AH.get_passable_locations((cfg.filter_location or {}), hunter)
|
||||
local rand = math.random(#locs)
|
||||
--print('#locs', #locs, rand)
|
||||
|
||||
hunter_vars.goal_x, hunter_vars.goal_y = locs[rand][1], locs[rand][2]
|
||||
MAIUV.set_mai_unit_variables(hunter, cfg.ai_id, hunter_vars)
|
||||
end
|
||||
--print('Hunter goto: ', hunter_vars.goal_x, hunter_vars.goal_y, r)
|
||||
|
||||
-- Hexes the hunter can reach
|
||||
local reach_map = AH.get_reachable_unocc(hunter)
|
||||
|
||||
-- Now find the one of these hexes that is closest to the goal
|
||||
local max_rating, best_hex = -9e99, {}
|
||||
local max_rating, best_hex = -9e99
|
||||
reach_map:iter( function(x, y, v)
|
||||
-- Distance from goal is first rating
|
||||
local rating = - H.distance_between(x, y, hunter_vars.goal_x, hunter_vars.goal_y)
|
||||
|
||||
-- Proximity to an enemy unit is a plus
|
||||
-- Huge rating bonus if this is next to an enemy
|
||||
local enemy_hp = 500
|
||||
for xa, ya in H.adjacent_tiles(x, y) do
|
||||
for xa,ya in H.adjacent_tiles(x, y) do
|
||||
local enemy = wesnoth.get_unit(xa, ya)
|
||||
if enemy and wesnoth.is_enemy(enemy.side, wesnoth.current.side) then
|
||||
if (enemy.hitpoints < enemy_hp) then enemy_hp = enemy.hitpoints end
|
||||
|
@ -95,13 +89,10 @@ function ca_hunter:execution(ai, cfg)
|
|||
end
|
||||
rating = rating + 500 - enemy_hp -- prefer attack on weakest enemy
|
||||
|
||||
reach_map:insert(x, y, rating)
|
||||
if (rating > max_rating) then
|
||||
max_rating, best_hex = rating, { x, y }
|
||||
end
|
||||
end)
|
||||
--print(' best_hex: ', best_hex[1], best_hex[2])
|
||||
--AH.put_labels(reach_map)
|
||||
|
||||
if (best_hex[1] ~= hunter.x) or (best_hex[2] ~= hunter.y) then
|
||||
AH.checked_move(ai, hunter, best_hex[1], best_hex[2]) -- partial move only
|
||||
|
@ -113,7 +104,7 @@ function ca_hunter:execution(ai, cfg)
|
|||
MAIUV.set_mai_unit_variables(hunter, cfg.ai_id, hunter_vars)
|
||||
end
|
||||
|
||||
-- Or if this gets the hunter to the goal, we also delete the goal
|
||||
-- If this gets the hunter to the goal, we delete the goal
|
||||
if (hunter.x == hunter_vars.goal_x) and (hunter.y == hunter_vars.goal_y) then
|
||||
hunter_vars.goal_x, hunter_vars.goal_y = nil, nil
|
||||
MAIUV.set_mai_unit_variables(hunter, cfg.ai_id, hunter_vars)
|
||||
|
@ -123,27 +114,25 @@ function ca_hunter:execution(ai, cfg)
|
|||
local attack_status = hunter_attack_weakest_adj_enemy(ai, hunter)
|
||||
|
||||
-- If the enemy was killed, hunter returns home
|
||||
if hunter.valid and (attack_status == 'killed') then
|
||||
if hunter and hunter.valid and (attack_status == 'killed') then
|
||||
hunter_vars.goal_x, hunter_vars.goal_y = nil, nil
|
||||
hunter_vars.hunting_status = 'return'
|
||||
hunter_vars.hunting_status = 'returning'
|
||||
MAIUV.set_mai_unit_variables(hunter, cfg.ai_id, hunter_vars)
|
||||
|
||||
if cfg.show_messages then
|
||||
W.message { speaker = hunter.id, message = 'Now that I have eaten, I will go back home.' }
|
||||
end
|
||||
end
|
||||
|
||||
-- At this point, issue a 'return', so that no other action takes place this turn
|
||||
return
|
||||
end
|
||||
|
||||
-- If we got here, this means the hunter is either returning, or resting
|
||||
if (hunter_vars.hunting_status == 'return') then
|
||||
if (hunter_vars.hunting_status == 'returning') then
|
||||
goto_x, goto_y = wesnoth.find_vacant_tile(cfg.home_x, cfg.home_y)
|
||||
--print('Go home:', home_x, home_y, goto_x, goto_y)
|
||||
|
||||
local next_hop = AH.next_hop(hunter, goto_x, goto_y)
|
||||
if next_hop then
|
||||
--print(next_hop[1], next_hop[2])
|
||||
AH.movefull_stopunit(ai, hunter, next_hop)
|
||||
if (not hunter) or (not hunter.valid) then return end
|
||||
|
||||
|
@ -154,6 +143,7 @@ function ca_hunter:execution(ai, cfg)
|
|||
if cfg.show_messages then
|
||||
W.message { speaker = hunter.id, message = 'Get out of my home!' }
|
||||
end
|
||||
|
||||
AH.checked_attack(ai, hunter, enemy)
|
||||
if (not hunter) or (not hunter.valid) then return end
|
||||
end
|
||||
|
@ -169,30 +159,30 @@ function ca_hunter:execution(ai, cfg)
|
|||
hunter_vars.hunting_status = 'resting'
|
||||
hunter_vars.resting_until = wesnoth.current.turn + (cfg.rest_turns or 3)
|
||||
MAIUV.set_mai_unit_variables(hunter, cfg.ai_id, hunter_vars)
|
||||
|
||||
if cfg.show_messages then
|
||||
W.message { speaker = hunter.id, message = 'I made it home - resting now until the end of Turn ' .. hunter_vars.resting_until .. ' or until fully healed.' }
|
||||
end
|
||||
end
|
||||
|
||||
-- At this point, issue a 'return', so that no other action takes place this turn
|
||||
return
|
||||
end
|
||||
|
||||
-- If we got here, the only remaining action is resting
|
||||
-- If we got here, the only remaining action should be resting
|
||||
if (hunter_vars.hunting_status == 'resting') then
|
||||
-- So all we need to do is take moves away from the hunter
|
||||
AH.checked_stopunit_moves(ai, hunter)
|
||||
if (not hunter) or (not hunter.valid) then return end
|
||||
|
||||
-- However, we do also attack the weakest adjacent enemy, if still possible
|
||||
-- We also attack the weakest adjacent enemy
|
||||
hunter_attack_weakest_adj_enemy(ai, hunter)
|
||||
if (not hunter) or (not hunter.valid) then return end
|
||||
|
||||
-- If this is the last turn of resting, we also remove the status and turn variable
|
||||
-- If this is the last turn of resting, we remove the status and turn variable
|
||||
if (hunter.hitpoints >= hunter.max_hitpoints) and (hunter_vars.resting_until <= wesnoth.current.turn) then
|
||||
hunter_vars.hunting_status = nil
|
||||
hunter_vars.resting_until = nil
|
||||
MAIUV.set_mai_unit_variables(hunter, cfg.ai_id, hunter_vars)
|
||||
|
||||
if cfg.show_messages then
|
||||
W.message { speaker = hunter.id, message = 'I am done resting. It is time to go hunting again next turn.' }
|
||||
end
|
||||
|
@ -200,7 +190,8 @@ function ca_hunter:execution(ai, cfg)
|
|||
return
|
||||
end
|
||||
|
||||
-- In principle we should never get here, but just in case: reset variable, so that hunter goes hunting on next turn
|
||||
-- In principle we should never get here, but just in case something got changed in WML:
|
||||
-- Reset variable, so that hunter goes hunting on next turn
|
||||
hunter_vars.hunting_status = nil
|
||||
MAIUV.set_mai_unit_variables(hunter, cfg.ai_id, hunter_vars)
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue