Merge branch 'master' into sdl2

This commit is contained in:
Andreas Löf 2015-09-07 21:25:22 +12:00
commit a4b957c347
8 changed files with 129 additions and 67 deletions

View file

@ -1,5 +1,6 @@
local H = wesnoth.require "lua/helper.lua"
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local LS = wesnoth.require "lua/location_set.lua"
-- Functions to perform fast evaluation of attacks and attack combinations.
-- The emphasis with all of this is on speed, not elegance.
@ -14,6 +15,37 @@ local AH = wesnoth.require "ai/lua/ai_helper.lua"
local ca_fast_attack_utils = {}
function ca_fast_attack_utils.get_avoid_map(cfg)
-- Get map of locations to be avoided.
-- Use [micro_ai][avoid] tag with priority over [ai][avoid].
-- If neither is given, return an empty location set.
-- Note that ai.get_avoid() cannot be used as it always returns an array,
-- even when the aspect is not set, and an empty array could also mean that
-- no hexes match the filter
local avoid_tag
if cfg.avoid then
avoid_tag = cfg.avoid
else
local ai_tag = H.get_child(wesnoth.sides[wesnoth.current.side].__cfg, 'ai')
for aspect in H.child_range(ai_tag, 'aspect') do
if (aspect.id == 'avoid') then
local facet = H.get_child(aspect, 'facet')
if facet then
avoid_tag = H.get_child(facet, 'value')
end
end
end
end
if avoid_tag then
return LS.of_pairs(wesnoth.get_locations(avoid_tag))
else
return LS.create()
end
end
function ca_fast_attack_utils.gamedata_setup()
-- Keep game data in a table for faster access.
-- This is currently re-done on every move. Could be optimized by only

View file

@ -9,10 +9,25 @@ function ca_fast_combat:evaluation(ai, cfg, self)
self.data.move_cache = { turn = wesnoth.current.turn }
self.data.gamedata = FAU.gamedata_setup()
local filter_own = cfg.filter
local filter_enemy = cfg.filter_second
if (not filter_own) and (not filter_enemy) then
local ai_tag = H.get_child(wesnoth.sides[wesnoth.current.side].__cfg, 'ai')
for aspect in H.child_range(ai_tag, 'aspect') do
if (aspect.id == 'attacks') then
local facet = H.get_child(aspect, 'facet')
if facet then
filter_own = H.get_child(facet, 'filter_own')
filter_enemy = H.get_child(facet, 'filter_enemy')
end
end
end
end
if (not self.data.fast_combat_units) or (not self.data.fast_combat_units[1]) then
self.data.fast_combat_units = wesnoth.get_units {
side = wesnoth.current.side,
{ "and", cfg.filter }
{ "and", filter_own }
}
if (not self.data.fast_combat_units[1]) then return 0 end
@ -26,11 +41,11 @@ function ca_fast_combat:evaluation(ai, cfg, self)
local excluded_enemies_map = LS.create()
-- Exclude enemies not matching [filter_second]
if (cfg.filter_second) then
-- Exclude enemies not matching [filter_enemy]
if filter_enemy then
local excluded_enemies = wesnoth.get_units {
{ "filter_side", { { "enemy_of", { side = wesnoth.current.side } } } },
{ "not", cfg.filter_second }
{ "not", filter_enemy }
}
for _,e in ipairs(excluded_enemies) do
@ -54,6 +69,9 @@ function ca_fast_combat:evaluation(ai, cfg, self)
if (aggression > 1) then aggression = 1 end
local own_value_weight = 1. - aggression
-- Get the locations to be avoided
local avoid_map = FAU.get_avoid_map(cfg)
for i = #self.data.fast_combat_units,1,-1 do
local unit = self.data.fast_combat_units[i]
local unit_info = FAU.get_unit_info(unit, self.data.gamedata)
@ -65,7 +83,9 @@ function ca_fast_combat:evaluation(ai, cfg, self)
if (#attacks > 0) then
local max_rating, best_target, best_dst = -9e99
for _,attack in ipairs(attacks) do
if (not excluded_enemies_map:get(attack.target.x, attack.target.y)) then
if (not excluded_enemies_map:get(attack.target.x, attack.target.y))
and (not avoid_map:get(attack.dst.x, attack.dst.y))
then
local target = wesnoth.get_unit(attack.target.x, attack.target.y)
local target_info = FAU.get_unit_info(target, self.data.gamedata)

View file

@ -1,5 +1,6 @@
local H = wesnoth.require "lua/helper.lua"
local AH = wesnoth.require "ai/lua/ai_helper.lua"
local FAU = wesnoth.require "ai/micro_ais/cas/ca_fast_attack_utils.lua"
local ca_fast_move = {}
@ -14,6 +15,9 @@ function ca_fast_move:execution(ai, cfg, self)
local move_cost_factor = cfg.move_cost_factor or 2.0
if (move_cost_factor < 1.1) then move_cost_factor = 1.1 end
-- Get the locations to be avoided
local avoid_map = FAU.get_avoid_map(cfg)
local all_units_MP = AH.get_units_with_moves { side = wesnoth.current.side, canrecruit = 'no' }
local units = {}
for _,unit in ipairs(all_units_MP) do
@ -29,16 +33,18 @@ function ca_fast_move:execution(ai, cfg, self)
if leader and (village_value > 0) then
local villages = wesnoth.get_villages()
-- Eliminate villages owned by a side that is not an enemy
-- Eliminate villages in avoid_map and those owned by an allied side
-- Also remove unowned villages if the AI has no leader
for i = #villages,1,-1 do
local owner = wesnoth.get_village_owner(villages[i][1], villages[i][2])
if owner and (not wesnoth.is_enemy(owner, wesnoth.current.side)) then
table.remove(villages, i)
end
if (not leader) and (not owner) then
if avoid_map:get(villages[i][1], villages[i][2]) then
table.remove(villages, i)
else
local owner = wesnoth.get_village_owner(villages[i][1], villages[i][2])
if owner and (not wesnoth.is_enemy(owner, wesnoth.current.side)) then
table.remove(villages, i)
elseif (not leader) and (not owner) then
table.remove(villages, i)
end
end
end
@ -110,8 +116,10 @@ function ca_fast_move:execution(ai, cfg, self)
end
for i_el,enemy_leader in ipairs(enemy_leaders) do
local goal = { x = enemy_leader.x, y = enemy_leader.y }
table.insert(goals, goal)
if (not avoid_map:get(enemy_leader.x, enemy_leader.y)) then
local goal = { x = enemy_leader.x, y = enemy_leader.y }
table.insert(goals, goal)
end
end
-- Putting information about all the units into the goals
@ -196,35 +204,37 @@ function ca_fast_move:execution(ai, cfg, self)
local pre_ratings = {}
local max_rating, best_hex = -9e99
for _,loc in ipairs(reach) do
local rating = - H.distance_between(loc[1], loc[2], short_goal[1], short_goal[2])
local other_rating = - H.distance_between(loc[1], loc[2], goal.x, goal.y) / 10.
rating = rating + other_rating
if (not avoid_map:get(loc[1], loc[2])) then
local rating = - H.distance_between(loc[1], loc[2], short_goal[1], short_goal[2])
local other_rating = - H.distance_between(loc[1], loc[2], goal.x, goal.y) / 10.
rating = rating + other_rating
local unit_in_way
if (rating > max_rating) then
unit_in_way = wesnoth.get_unit(loc[1], loc[2])
if (unit_in_way == unit) then unit_in_way = nil end
local unit_in_way
if (rating > max_rating) then
unit_in_way = wesnoth.get_unit(loc[1], loc[2])
if (unit_in_way == unit) then unit_in_way = nil end
if unit_in_way and (unit_in_way.side == unit.side) then
local reach = AH.get_reachable_unocc(unit_in_way)
if (reach:size() > 1) then
unit_in_way = nil
rating = rating - 0.01
other_rating = other_rating - 0.01
if unit_in_way and (unit_in_way.side == unit.side) then
local reach = AH.get_reachable_unocc(unit_in_way)
if (reach:size() > 1) then
unit_in_way = nil
rating = rating - 0.01
other_rating = other_rating - 0.01
end
end
end
end
if (not unit_in_way) then
if cfg.dungeon_mode then
table.insert(pre_ratings, {
rating = rating,
other_rating = other_rating,
x = loc[1], y = loc[2]
})
else
if (rating > max_rating) then
max_rating, best_hex = rating, { loc[1], loc[2] }
if (not unit_in_way) then
if cfg.dungeon_mode then
table.insert(pre_ratings, {
rating = rating,
other_rating = other_rating,
x = loc[1], y = loc[2]
})
else
if (rating > max_rating) then
max_rating, best_hex = rating, { loc[1], loc[2] }
end
end
end
end

View file

@ -460,7 +460,7 @@ function wesnoth.wml_actions.micro_ai(cfg)
elseif (cfg.ai_type == 'fast_ai') then
optional_keys = {
"attack_hidden_enemies", "dungeon_mode", "filter", "filter_second",
"attack_hidden_enemies", "avoid", "dungeon_mode", "filter", "filter_second",
"include_occupied_attack_hexes", "leader_weight", "move_cost_factor",
"weak_units_first", "skip_combat_ca", "skip_move_ca"
}

View file

@ -27,18 +27,18 @@ configure_engine::configure_engine(saved_game& state) :
}
set_use_map_settings(use_map_settings_default());
if(state_.classification().get_tagname() == "scenario") {
BOOST_FOREACH(const config& scenario,
game_config_manager::get()->game_config().child_range(state_.classification().get_tagname())) {
BOOST_FOREACH(const config& scenario,
game_config_manager::get()->game_config().child_range(state_.classification().get_tagname())) {
if (scenario["allow_new_game"].to_bool(true) || game_config::debug) {
if (!scenario["campaign_id"].empty() &&
(scenario["allow_new_game"].to_bool(true) || game_config::debug)) {
const std::string& title = (!scenario["new_game_title"].empty()) ?
scenario["new_game_title"] : scenario["name"];
const std::string& title = (!scenario["new_game_title"].empty()) ?
scenario["new_game_title"] : scenario["name"];
entry_points_.push_back(&scenario);
entry_point_titles_.push_back(title);
entry_points_.push_back(&scenario);
entry_point_titles_.push_back(title);
}
}
}
}

View file

@ -55,6 +55,7 @@ static void add_multiplayer_classification(config& multiplayer, saved_game& stat
config initial_level_config(saved_game& state)
{
const mp_game_settings& params = state.mp_settings();
state.set_defaults();
//Also impliers state.expand_scenario()
//We need to call this before expand_mp_events/options oterwise they might be overwritten
state.expand_random_scenario();

View file

@ -878,13 +878,6 @@ void terrain_builder::parse_config(const config &cfg, bool local)
if (const config::attribute_value *v = tc.get("y")) {
loc.y = *v;
}
if (const config::attribute_value *v = tc.get("loc")) {
std::vector<std::string> sloc = utils::split(*v);
if(sloc.size() == 2) {
loc.x = atoi(sloc[0].c_str());
loc.y = atoi(sloc[1].c_str());
}
}
if(loc.valid()) {
add_constraints(pbr.constraints, loc, tc, br);
}

View file

@ -1005,11 +1005,15 @@ namespace { // Helpers for set_config()
const boost::regex fai_identifier("[a-zA-Z_]+");
template<typename MoveT>
void patch_movetype(MoveT& mt, const std::string& new_key, const std::string& formula_str, int default_val) {
void patch_movetype(MoveT& mt, const std::string& new_key, const std::string& formula_str, int default_val, bool replace) {
config temp_cfg, original_cfg;
mt.write(original_cfg);
if (!replace && !original_cfg[new_key].blank()) {
// Don't replace if the key already exists in the config (even if empty).
return;
}
gui2::tformula<int> formula(formula_str);
game_logic::map_formula_callable original;
mt.write(original_cfg);
boost::sregex_iterator m(formula_str.begin(), formula_str.end(), fai_identifier);
BOOST_FOREACH(const boost::sregex_iterator::value_type& p, std::make_pair(m, boost::sregex_iterator())) {
const std::string var_name = p.str();
@ -1056,14 +1060,15 @@ void unit_type_data::set_config(config &cfg)
if (mt == "id" || mt == "default" || movement_types_.find(mt) == movement_types_.end()) {
continue;
}
patch_movetype(movement_types_[mt].get_resistances(), dmg_type, attr.second, 100);
patch_movetype(movement_types_[mt].get_resistances(), dmg_type, attr.second, 100, true);
}
if (r.has_attribute("default")) {
BOOST_FOREACH(movement_type_map::value_type &mt, movement_types_) {
// Don't apply a default if a value is explicitly specified.
if (r.has_attribute(mt.first)) {
continue;
}
patch_movetype(mt.second.get_resistances(), dmg_type, r["default"], 100);
patch_movetype(mt.second.get_resistances(), dmg_type, r["default"], 100, false);
}
}
}
@ -1085,28 +1090,29 @@ void unit_type_data::set_config(config &cfg)
continue;
}
if (tag == "defense") {
patch_movetype(movement_types_[mt].get_defense(), ter_type, attr.second, 100);
patch_movetype(movement_types_[mt].get_defense(), ter_type, attr.second, 100, true);
} else if (tag == "vision") {
patch_movetype(movement_types_[mt].get_vision(), ter_type, attr.second, 99);
patch_movetype(movement_types_[mt].get_vision(), ter_type, attr.second, 99, true);
} else if (tag == "movement") {
patch_movetype(movement_types_[mt].get_movement(), ter_type, attr.second, 99);
patch_movetype(movement_types_[mt].get_movement(), ter_type, attr.second, 99, true);
} else if (tag == "jamming") {
patch_movetype(movement_types_[mt].get_jamming(), ter_type, attr.second, 99);
patch_movetype(movement_types_[mt].get_jamming(), ter_type, attr.second, 99, true);
}
}
if (info.has_attribute("default")) {
BOOST_FOREACH(movement_type_map::value_type &mt, movement_types_) {
// Don't apply a default if a value is explicitly specified.
if (info.has_attribute(mt.first)) {
continue;
}
if (tag == "defense") {
patch_movetype(mt.second.get_defense(), ter_type, info["default"], 100);
patch_movetype(mt.second.get_defense(), ter_type, info["default"], 100, false);
} else if (tag == "vision") {
patch_movetype(mt.second.get_vision(), ter_type, info["default"], 99);
patch_movetype(mt.second.get_vision(), ter_type, info["default"], 99, false);
} else if (tag == "movement") {
patch_movetype(mt.second.get_movement(), ter_type, info["default"], 99);
patch_movetype(mt.second.get_movement(), ter_type, info["default"], 99, false);
} else if (tag == "jamming") {
patch_movetype(mt.second.get_jamming(), ter_type, info["default"], 99);
patch_movetype(mt.second.get_jamming(), ter_type, info["default"], 99, false);
}
}
}