Bugfixes and improvements for experimental multiplayer AI.

Avoid crash near end of games with turn limits.

Properly identify poison immune units.

Better timing for keep switching.

Handle more weapon specials involving damage.
This commit is contained in:
Simon Forsyth 2012-12-28 21:49:35 +00:00
parent 3673f79c8a
commit 0f15ff94db
4 changed files with 73 additions and 9 deletions

View file

@ -2,8 +2,12 @@ Version 1.11.1+svn:
* AI:
* Experimental Multiplayer AI
* Improve recruitment, notably first turn choices and units with poison
and charge
* Improved selection of units for village stealing
* Remove dependency on AI-demos add-on
* Fix bug when playing on maps with a turn limit
* Fix bug handling regeneration
* Minor improvements in switching between castles
* Campaigns:
* Son of the Black Eye:
* Prevent infinite loop if fewer than two transport ships (bug #20389)

View file

@ -110,7 +110,7 @@ return {
local defense = defender_defense
local poison = false
local damage_multiplier = 1
-- TODO: handle more abilities (charge)
for special in H.child_range(attack, 'specials') do
local mod
if H.get_child(special, 'poison') and can_poison then
@ -130,13 +130,15 @@ return {
end
end
-- Handle backstab
-- Handle backstab, charge
mod = H.get_child(special, 'damage')
if mod then
if mod and mod.active_on ~= "defense" then
if mod.backstab then
-- Assume backstab happens on only 1/2 of attacks
-- TODO: find out what actual probability of getting to backstab is
damage_multiplier = damage_multiplier*(mod.multiply*0.5 + 0.5)
else
damage_multiplier = damage_multiplier*mod.multiply
end
end
end
@ -210,7 +212,7 @@ return {
name = "X",
random_gender = false
}
local can_poison = living(unit) or wesnoth.unit_ability(unit, 'regenerate')
local can_poison = living(unit) and (not wesnoth.unit_ability(unit, 'regenerate'))
local flat_defense = wesnoth.unit_defense(unit, "Gt")
local best_defense = get_best_defense(unit)
@ -644,7 +646,12 @@ return {
eta = eta + 1
end
-- divide the lawful bonus by eta before running it through the function because the function converts from 0 centered to 1 centered
local lawful_bonus = wesnoth.get_time_of_day(wesnoth.current.turn + eta).lawful_bonus / eta^2
local lawful_bonus = 1
local eta_turn = wesnoth.current.turn + eta
if eta_turn <= wesnoth.game_config.last_turn then
lawful_bonus = wesnoth.get_time_of_day(wesnoth.current.turn + eta).lawful_bonus / eta^2
end
local damage_bonus = AH.get_unit_time_of_day_bonus(recruit_unit.__cfg.alignment, lawful_bonus)
-- Estimate effectiveness on offense and defense
local offense_score =

View file

@ -74,7 +74,7 @@ return {
end
if self.data.leader_target then
return 290000
return self.data.leader_score
end
local width,height,border = wesnoth.get_map_size()
@ -88,7 +88,7 @@ return {
}} }}, -- That are not too close to an enemy leader
{ "not", {
x = leader.x, y = leader.y, terrain = "K*^*,*^Kov",
radius = 2,
radius = 3,
{ "filter_radius", { terrain = 'C*^*,K*^*,*^Kov,*^Cov' } }
}}, -- That are not close and connected to a keep the leader is on
{ "filter_adjacent_location", {
@ -115,7 +115,7 @@ return {
-- Prefer closer keeps to enemy
local turns = math.ceil(cost/leader.max_moves)
if turns <= 2 then
score = 1/(math.ceil(turns))
score = 1/turns
for j,e in ipairs(enemy_leaders) do
score = score + 1 / H.distance_between(loc[1], loc[2], e.x, e.y)
end
@ -128,6 +128,30 @@ return {
end
end
-- If we're on a keep,
-- don't move to another keep unless it's much better when uncaptured villages are present
if best_score > 0 and wesnoth.get_terrain_info(wesnoth.get_terrain(leader.x, leader.y)).keep then
local close_unowned_village = (wesnoth.get_villages {
{ "and", {
x = leader.x,
y = leader.y,
radius = leader.max_moves
}},
owner_side = 0
})[1]
if close_unowned_village then
local score = 1/best_turns
for j,e in ipairs(enemy_leaders) do
-- count all distances as three less than they actually are
score = score + 1 / (H.distance_between(leader.x, leader.y, e.x, e.y) - 3)
end
if score > best_score then
best_score = 0
end
end
end
if best_score > 0 then
local next_hop = AH.next_hop(leader, best_loc[1], best_loc[2])
@ -157,8 +181,34 @@ return {
end
self.data.leader_target = next_hop
-- if we're on a keep, wait until there are no movable units on the castle before moving off
self.data.leader_score = 290000
if wesnoth.get_terrain_info(wesnoth.get_terrain(leader.x, leader.y)).keep then
local castle = wesnoth.get_locations {
x = "1-"..width, y = "1-"..height,
{ "and", {
x = leader.x, y = leader.y, radius = 200,
{ "filter_radius", { terrain = 'C*^*,K*^*,*^Kov,*^Cov' } }
}}
}
local should_wait = false
for i,loc in ipairs(castle) do
local unit = wesnoth.get_unit(loc[1], loc[2])
if not unit then
should_wait = false
break
elseif unit.moves > 0 then
should_wait = true
end
end
if should_wait then
self.data.leader_score = 15000
end
end
AH.done_eval_messages(start_time, ca_name)
return 290000
return self.data.leader_score
end
AH.done_eval_messages(start_time, ca_name)

View file

@ -8,6 +8,9 @@ Version 1.11.1+svn:
* Improve recruitment, notably first turn choices and units with poison.
* Improve selection of units for village stealing.
* Remove dependency on AI-demos add-on.
* Fix bug when playing on maps with a turn limit.
* Fix bug handling regeneration.
* Minor improvements in switching between castles.
* Campaigns:
* Son of the Black Eye: