[[AI tuning]]

* Merged formula ai to default ai (formula makes moves first and
  handles recruiting if scenario includes formulas)

* Added AI configuration paramter for forced recruitment

* Added village extra penalty for leader village grapping if enemy can
  attack that village

* Fixed some other small issues with ai code

* Added recruiting formula to loyalists faction for testing
This commit is contained in:
Pauli Nieminen 2008-09-16 18:14:54 +00:00
parent 716247165b
commit 57ca8308ad
14 changed files with 250 additions and 120 deletions

View file

@ -11,5 +11,8 @@
terrain_liked=Gg, Ww, Wo
[ai]
recruitment_pattern=fighter,fighter,fighter,archer,mixed fighter,scout
[team_formula]
rulebase="{ai/formula/recruitment.fai}"
[/team_formula]
[/ai]
[/multiplayer_side]

View file

@ -174,7 +174,7 @@ protected:
}
}
void do_recruitment() {
bool do_recruitment() {
const std::set<std::string>& options = current_team().recruits();
if (!options.empty()) {
const int choice = (rand()%options.size());
@ -183,9 +183,11 @@ protected:
const bool res = recruit(*i);
if(res) {
do_recruitment();
return do_recruitment();
}
return true;
}
return false;
}
};
@ -288,6 +290,7 @@ ai::ai(ai_interface::info& info) :
state_(info.state),
consider_combat_(true),
additional_targets_(),
info_(info),
unit_movement_scores_(),
not_recommended_units_(),
unit_combat_scores_(),
@ -295,8 +298,11 @@ ai::ai(ai_interface::info& info) :
avoid_(),
unit_stats_cache_(),
attack_depth_(0),
recruiting_prefered_(false)
{}
recruiting_prefered_(false),
master_(info_.master)
{
info_.master = false;
}
void ai::new_turn()
{
@ -312,6 +318,7 @@ void ai::new_turn()
avoid_.clear();
unit_stats_cache_.clear();
attack_depth_ = 0;
ai_manager::get_ai("formula_ai", info_)->new_turn();
ai_interface::new_turn();
}
@ -332,17 +339,12 @@ bool ai::recruit_usage(const std::string& usage)
// Find an available unit that can be recruited,
// matches the desired usage type, and comes in under budget.
const std::set<std::string>& recruits = current_team().recruits();
for(std::map<std::string,unit_type>::const_iterator i =
unit_type_data::types().begin(); i != unit_type_data::types().end(); ++i)
for(std::set<std::string>::const_iterator recruit_item = recruits.begin(); recruit_item != recruits.end(); ++recruit_item)
{
std::map<std::string,unit_type>::const_iterator i = unit_type_data::types().find(*recruit_item);
const std::string& name = i->second.id();
// If usage is empty consider any unit.
// DBG_AI << name << " considered\n";
if (i->second.usage() == usage || usage == "") {
if (!recruits.count(name)) {
// DBG_AI << name << " rejected, not in recruitment list\n";
continue;
}
LOG_AI << name << " considered for " << usage << " recruitment\n";
found = true;
@ -799,8 +801,7 @@ void ai_interface::calculate_moves(const unit_map& units, std::map<location,path
continue;
}
// Discount incapacitated units
if(un_it->second.incapacitated()
|| un_it->second.movement_left() == 0) {
if(un_it->second.incapacitated()) {
continue;
}
@ -905,8 +906,8 @@ void ai::find_threats()
const unit_map::const_iterator leader = find_leader(units_,team_num_);
if(leader != units_.end()) {
items.push_back(protected_item(
lexical_cast_default<double>(parms["protect_leader"], 1.0),
lexical_cast_default<int>(parms["protect_leader_radius"], 20),
lexical_cast_default<double>(parms["protect_leader"], 2.0),
lexical_cast_default<int>(parms["protect_leader_radius"], 7),
leader->first));
}
@ -969,10 +970,28 @@ void ai::play_turn()
void ai::evaluate_recruiting_value(unit_map::iterator leader)
{
const int gold = current_team().gold();
// const int unit_price = current_team().average_recruit_price();
// recruiting_prefered_ = (gold/unit_price) > min_recruiting_value_to_force_recruit && !map_.is_keep();
recruiting_prefered_ = gold > min_recruiting_value_to_force_recruit && !map_.is_keep(leader->first);
ERR_AI << current_team().num_pos_recruits_to_force() << "\n";
if (current_team().num_pos_recruits_to_force()< 0.01f)
{
ERR_AI << "Recruiting force code disabled\n";
return;
}
float free_slots = 0.0f;
if (map_.is_keep(leader->first))
{
std::set<gamemap::location> checked_hexes;
checked_hexes.insert(leader->first);
free_slots = count_free_hexes_in_castle(leader->first, checked_hexes);
}
const float gold = current_team().gold();
const float unit_price = current_team().average_recruit_price();
recruiting_prefered_ = (gold/unit_price) - free_slots > current_team().num_pos_recruits_to_force();
ERR_AI << "recruiting prefered: " << (recruiting_prefered_?"yes":"no") <<
" units to recruit: " << (gold/unit_price) <<
" unit_price: " << unit_price <<
" free slots: " << free_slots <<
" limit: " << current_team().num_pos_recruits_to_force() << "\n";
}
void ai::do_move()
@ -983,6 +1002,11 @@ void ai::do_move()
raise_user_interact();
// Formula AI is first going to move everything that it can
if (master_)
static_cast<formula_ai*>(ai_manager::get_ai("formula_ai", info_).get())->play_turn();
typedef paths::route route;
typedef std::map<location,paths> moves_map;
@ -1109,7 +1133,14 @@ void ai::do_move()
}
if (map_.is_keep(leader->first))
{
do_recruitment();
if (recruiting_prefered_)
{
do_move();
return;
}
}
if(!passive_leader) {
move_leader_after_recruit(srcdst,dstsrc,enemy_dstsrc);
@ -1809,11 +1840,20 @@ void ai::analyze_potential_recruit_movements()
}
}
void ai::do_recruitment()
bool ai::do_recruitment()
{
const unit_map::const_iterator leader = find_leader(units_,team_num_);
if(leader == units_.end()) {
return;
return false;
}
// Let formula ai to do recruiting first
if (master_)
{
if(static_cast<formula_ai*>(ai_manager::get_ai("formula_ai",info_).get())->do_recruitment()) {
LOG_AI << "Recruitment done by formula_ai\n";
return true;
}
}
const location& start_pos = nearest_keep(leader->first);
@ -1893,6 +1933,7 @@ void ai::do_recruitment()
options.push_back("");
}
}
return true;
}
void ai::move_leader_to_goals( const move_map& enemy_dstsrc)
@ -2344,25 +2385,25 @@ void ai::attack_analysis::get_inputs(std::vector<game_logic::formula_input>* inp
ai_manager::AINameMap ai_manager::ais = ai_manager::AINameMap();
boost::intrusive_ptr<ai_interface> ai_manager::get_ai( std::string ai_algo,
boost::intrusive_ptr<ai_interface> ai_manager::get_ai(const std::string& ai_algo,
ai_interface::info& ai_info )
{
int ai_key = ai_info.team_num - 1 ;
const std::string ai_key = ai_algo + lexical_cast<std::string>(ai_info.team_num - 1);
boost::intrusive_ptr<ai_interface> ai_obj;
// If we are dealing with an AI - try to find it
if( ai_algo.size() )
{
AINameMap::const_iterator itor = ais.find(ai_key);
std::cout << "ai_manager::get_ai() for algorithm: " << ai_algo << std::endl ;
LOG_AI << "ai_manager::get_ai() for algorithm: " << ai_algo << std::endl ;
if(itor == ais.end())
{
std::cout << "manager did not find AI - creating..." << std::endl ;
ai_obj = create_ai(ai_algo, ai_info) ;
std::cout << "Asking newly created AI if it wants to be managed." << std::endl ;
LOG_AI << "manager did not find AI - creating..." << std::endl ;
ai_obj.reset(create_ai(ai_algo, ai_info));
LOG_AI << "Asking newly created AI if it wants to be managed." << std::endl ;
if( ai_obj->manager_manage_ai() )
{
std::cout << "AI has requested itself be managed: " << ai_algo << std::endl ;
LOG_AI << "AI has requested itself be managed: " << ai_algo << std::endl ;
AINameMap::value_type new_ai_pair( ai_key, ai_obj ) ;
itor = ais.insert(new_ai_pair).first ;
}
@ -2370,7 +2411,7 @@ boost::intrusive_ptr<ai_interface> ai_manager::get_ai( std::string ai_algo,
else
{
// AI was found - so return it
std::cout << "Reusing managed AI" << std::endl ;
LOG_AI << "Reusing managed AI" << std::endl ;
ai_obj = itor->second ;
}
}

View file

@ -91,7 +91,7 @@ protected:
const unit_map::const_iterator un, const move_map& srcdst,
const move_map& dstsrc, const move_map& enemy_dstsrc, double caution);
virtual void do_recruitment();
virtual bool do_recruitment();
virtual void move_leader_to_keep(const move_map& enemy_dstsrc);
virtual void move_leader_after_recruit(const move_map& srcdst,
@ -294,7 +294,8 @@ protected:
gamestatus& state_;
bool consider_combat_;
std::vector<target> additional_targets_;
ai_interface::info info_;
void add_target(const target& tgt) { additional_targets_.push_back(tgt); }
/**
@ -378,16 +379,17 @@ private:
const std::multimap<gamemap::location,gamemap::location>& enemy_dstsrc) const;
bool recruiting_prefered_;
static const int min_recruiting_value_to_force_recruit = 28;
protected:
bool master_;
};
class ai_manager {
public:
static boost::intrusive_ptr<ai_interface> get_ai( std::string, ai_interface::info& );
static boost::intrusive_ptr<ai_interface> get_ai(const std::string&, ai_interface::info& );
static int reap_ais() ;
private:
typedef std::map< int, boost::intrusive_ptr<ai_interface> > AINameMap ;
typedef std::map< std::string, boost::intrusive_ptr<ai_interface> > AINameMap ;
static AINameMap ais ;
};

View file

@ -28,6 +28,7 @@ class gamemap;
#include "pathfind.hpp"
#include "gamestatus.hpp"
#include "playturn.hpp"
#include <iostream>
class ai_interface : public game_logic::formula_callable {
public:
@ -47,7 +48,8 @@ public:
info(game_display& disp, gamemap& map, unit_map& units,
std::vector<team>& teams, unsigned int team_num, gamestatus& state, class turn_info& turn_data, class game_state& game_state)
: disp(disp), map(map), units(units), teams(teams),
team_num(team_num), state(state), turn_data_(turn_data), game_state_(game_state) {}
team_num(team_num), state(state), turn_data_(turn_data), game_state_(game_state), master(true)
{}
/** The display object, used to draw the moves the AI makes. */
game_display& disp;
@ -80,6 +82,7 @@ public:
/** The global game state, because we may set the completion field. */
class game_state& game_state_;
bool master;
};
/**

View file

@ -25,10 +25,9 @@
#include <cassert>
#include <iostream>
#include <queue>
#define LOG_AI LOG_STREAM(info, ai)
#define DBG_AI LOG_STREAM(debug, ai)
#define ERR_AI LOG_STREAM(err, ai)
struct move_cost_calculator : cost_calculator
{
@ -802,26 +801,12 @@ void ai::access_points(const move_map& srcdst, const location& u, const location
}
}
struct keep_value {
size_t value;
gamemap::location loc;
keep_value(size_t v, const gamemap::location& l) : value(v), loc(l)
{}
bool operator<(const keep_value& val) const
{
return value > val.value;
}
};
void ai::move_leader_to_keep(const move_map& enemy_dstsrc)
{
const unit_map::iterator leader = find_leader(units_,team_num_);
if(leader == units_.end()
|| leader->second.incapacitated()
|| leader->second.movement_left() == 0
|| !recruiting_prefered_) {
|| leader->second.movement_left() == 0) {
return;
}
@ -829,14 +814,15 @@ void ai::move_leader_to_keep(const move_map& enemy_dstsrc)
std::map<gamemap::location,paths>::iterator path_itor = possible_moves.insert(std::make_pair(leader->first, paths())).first;
// Guess how many units we want to recruit
const int number_of_recruit = current_team().gold() / 15;
const int number_of_recruit = current_team().gold() / current_team().average_recruit_price();
// If the leader is not on his starting location, move him there.
{
{
// Make a map of the possible locations the leader can move to,
// ordered by the distance from the keep.
std::priority_queue<keep_value> moves_toward_keep;
int best_value = INT_MAX;
gamemap::location best_target;
// The leader can't move to his keep, try to move to the closest location
// to the keep where there are no enemies in range.
@ -850,18 +836,32 @@ void ai::move_leader_to_keep(const move_map& enemy_dstsrc)
std::make_pair(*i,
a_star_search(leader->first, *i, 10000.0, &cost_calc,map_.w(), map_.h()))).first;
std::set<gamemap::location> checked_hexes;
const int distance = route->second.steps.size()-1;
std::set<gamemap::location> checked_hexes;
checked_hexes.insert(*i);
const int free_slots = count_free_hexes_in_castle(*i, checked_hexes);
const int tactical_value = leader->second.total_movement() * 0;
const int tactical_value = std::sqrt(std::pow(static_cast<float>(map_.w()/2 - i->x),2) +
std::pow(static_cast<float>(map_.h()/2 - i->y),2))*2;
const int empty_slots = leader->second.total_movement() * std::max(number_of_recruit - free_slots,0);
unit_map::const_iterator u = units_.find(*i);
const int reserved_penalty = leader->second.total_movement() *
(u != units_.end()?
(current_team().is_enemy(u->second.side())?4:2)
(u != units_.end()
&& &teams_[u->second.side()-1] != &current_team()
&& !u->second.invisible(u->first, units_, teams_)
?(current_team().is_enemy(u->second.side())?6:3)
:0);
const int enemy = leader->second.total_movement() * enemy_dstsrc.count(*i);
const int enemy = (power_projection(*i, enemy_dstsrc) * 8 * leader->second.total_movement())/leader->second.hitpoints();
int multiturn_move_penalty = 0;
if (recruiting_prefered_)
multiturn_move_penalty = 2;
const int distance_value = (distance > leader->second.movement_left()?
((distance - leader->second.movement_left())/leader->second.total_movement()+multiturn_move_penalty)*leader->second.total_movement() : 0);
const int value = distance_value + empty_slots + enemy + tactical_value + reserved_penalty;
if (value > best_value)
continue;
gamemap::location target;
if (distance > leader->second.movement_left())
{
@ -872,19 +872,23 @@ void ai::move_leader_to_keep(const move_map& enemy_dstsrc)
target = *i;
route->second.move_left = leader->second.movement_left() - distance;
}
DBG_AI << "Considiring keep: " << *i <<
best_value = value;
best_target = target;
ERR_AI << "Considiring keep: " << *i <<
" empty slots: " << empty_slots <<
" distance: " << distance <<
" enemy: " << enemy <<
" tactical_value: " << tactical_value <<
" reserved_penalty: " << reserved_penalty <<
" target: " << target <<
" value: " << value <<
" route: " << route->second.steps.size() << " " << route->second.move_left <<
"\n";
moves_toward_keep.push(keep_value(distance + empty_slots + enemy + tactical_value + reserved_penalty ,target));
}
}
// Find the location with the best value
if (leader->first != moves_toward_keep.top().loc)
move_unit(leader->first,moves_toward_keep.top().loc,possible_moves);
if (leader->first != best_target)
move_unit(leader->first,best_target,possible_moves);
}
}
}
@ -903,7 +907,9 @@ int ai::count_free_hexes_in_castle(const gamemap::location& loc, std::set<gamema
ret += count_free_hexes_in_castle(adj[n], checked_hexes);
if (u == units_.end()
|| (current_team().is_enemy(u->second.side())
&& u->second.invisible(adj[n], units_, teams_))) {
&& u->second.invisible(adj[n], units_, teams_))
|| ((&teams_[u->second.side()-1]) == &current_team()
&& u->second.movement_left() > 0)) {
ret += 1;
}
}

View file

@ -344,7 +344,8 @@ void ai::find_villages(
}
const unit& un = u->second;
if(un.hitpoints() < (threat*2*un.defense_modifier(map_.get_terrain(current_loc)))/100) {
const size_t threat_multipler = (current_loc == leader_loc?2:1) * current_team().caution() * 10;
if(un.hitpoints() < (threat_multipler*threat*2*un.defense_modifier(map_.get_terrain(current_loc)))/100) {
continue;
}

View file

@ -166,8 +166,7 @@ void get_player_info(const config& cfg, game_state& gamestate,
// If the game state specifies units that
// can be recruited for the player, add them.
if(player != NULL && player->can_recruit.empty() == false) {
std::copy(player->can_recruit.begin(),player->can_recruit.end(),
std::inserter(teams.back().recruits(),teams.back().recruits().end()));
teams.back().add_recruits(player->can_recruit);
}
if(player != NULL) {

View file

@ -22,9 +22,9 @@
#include "log.hpp"
#include "attack_prediction.hpp"
#define LOG_AI LOG_STREAM(info, ai)
#define WRN_AI LOG_STREAM(warn, ai)
#define ERR_AI LOG_STREAM(err, ai)
#define LOG_AI LOG_STREAM(info, formula_ai)
#define WRN_AI LOG_STREAM(warn, formula_ai)
#define ERR_AI LOG_STREAM(err, formula_ai)
namespace {
using namespace game_logic;
@ -467,7 +467,7 @@ private:
variant execute(const formula_callable& variables) const {
const gamemap::location src = convert_variant<location_callable>(args()[0]->evaluate(variables))->loc();
const gamemap::location dst = convert_variant<location_callable>(args()[1]->evaluate(variables))->loc();
std::cerr << "move(): " << src << ", " << dst << ")\n";
LOG_AI << "move(): " << src << ", " << dst << ")\n";
return variant(new move_callable(src, dst));
}
};
@ -571,7 +571,7 @@ private:
const gamemap::location dst = convert_variant<location_callable>(args()[2]->evaluate(variables))->loc();
const int weapon = args().size() == 4 ? args()[3]->evaluate(variables).as_int() : -1;
if(ai_.get_info().units.count(move_from) == 0 || ai_.get_info().units.count(dst) == 0) {
std::cerr << "AI ERROR: Formula produced illegal attack: " << move_from << " -> " << src << " -> " << dst << "\n";
LOG_AI << "AI ERROR: Formula produced illegal attack: " << move_from << " -> " << src << " -> " << dst << "\n";
return variant();
}
return variant(new attack_callable(ai_, move_from, src, dst, weapon));
@ -1281,14 +1281,14 @@ void formula_ai::handle_exception(game_logic::formula_error& e)
void formula_ai::handle_exception(game_logic::formula_error& e, const std::string& failed_operation)
{
std::cerr << failed_operation << ": " << e.formula_ << std::endl;
LOG_AI << failed_operation << ": " << e.formula_ << std::endl;
display_message(failed_operation + ": " + e.formula_);
//if line number = 0, don't display info about filename and line number
if (e.line_ != 0) {
std::cerr << e.type_ << " in " << e.filename_ << ":" << e.line_ << std::endl;
LOG_AI << e.type_ << " in " << e.filename_ << ":" << e.line_ << std::endl;
display_message(e.type_ + " in " + e.filename_ + ":" + boost::lexical_cast<std::string>(e.line_));
} else {
std::cerr << e.type_ << std::endl;
LOG_AI << e.type_ << std::endl;
display_message(e.type_);
}
}
@ -1348,8 +1348,11 @@ void formula_ai::make_candidate_moves() {
int best_score = (*best_move)->get_score();
// If no evals > 0, fallback
if(best_score < 0) {
ai_interface* fallback = create_ai("", get_info());
fallback->play_turn();
if (master_)
{
ai_interface* fallback = create_ai("", get_info());
fallback->play_turn();
}
return;
}
// Otherwise, make the best scoring move
@ -1369,8 +1372,11 @@ void formula_ai::make_candidate_moves() {
}
// After all candidate moves have been exhausted, fallback
ai_interface* fallback = create_ai("", get_info());
fallback->play_turn();
if (master_)
{
ai_interface* fallback = create_ai("", get_info());
fallback->play_turn();
}
}
@ -1448,15 +1454,17 @@ void formula_ai::prepare_move() const
bool formula_ai::make_move(game_logic::const_formula_ptr formula_, const game_logic::formula_callable& variables)
{
if(!formula_) {
ai_interface* fallback = create_ai("", get_info());
fallback->play_turn();
if(!formula_ ) {
if(master_) {
ai_interface* fallback = create_ai("", get_info());
fallback->play_turn();
}
return false;
}
move_maps_valid_ = false;
std::cerr << "do move...\n";
LOG_AI << "do move...\n";
const variant var = formula_->execute(variables);
return execute_variant(var);
@ -1490,7 +1498,7 @@ bool formula_ai::execute_variant(const variant& var, bool commandline)
prepare_move();
if(move) {
std::cerr << "MOVE: " << move->src().x << "," << move->src().y << " -> " << move->dst().x << "," << move->dst().y << "\n";
LOG_AI << "MOVE: " << move->src().x << "," << move->src().y << " -> " << move->dst().x << "," << move->dst().y << "\n";
unit_map::iterator i = units_.find(move->src());
if( (possible_moves_.count(move->src()) > 0) && (i->second.movement_left() != 0) ) {
move_unit(move->src(), move->dst(), possible_moves_);
@ -1507,7 +1515,7 @@ bool formula_ai::execute_variant(const variant& var, bool commandline)
if(attack->move_from() != attack->src()) {
move_unit(attack->move_from(), attack->src(), possible_moves_);
}
std::cerr << "ATTACK: " << attack->src() << " -> " << attack->dst() << " " << attack->weapon() << "\n";
LOG_AI << "ATTACK: " << attack->src() << " -> " << attack->dst() << " " << attack->weapon() << "\n";
attack_enemy(attack->src(), attack->dst(), attack->weapon(), attack->defender_weapon());
made_move = true;
} else if(attack_analysis) {
@ -1549,12 +1557,12 @@ bool formula_ai::execute_variant(const variant& var, bool commandline)
}
made_move = true;
} else if(recruit_command) {
std::cerr << "RECRUIT: '" << recruit_command->type() << "'\n";
LOG_AI << "RECRUIT: '" << recruit_command->type() << "'\n";
if(recruit(recruit_command->type(), recruit_command->loc())) {
made_move = true;
}
} else if(set_var_command) {
std::cerr << "setting var: " << set_var_command->key() << " -> " << set_var_command->value().to_debug_string() << "\n";
LOG_AI << "setting var: " << set_var_command->key() << " -> " << set_var_command->value().to_debug_string() << "\n";
vars_.add(set_var_command->key(), set_var_command->value());
made_move = true;
} else if(i->is_string() && i->as_string() == "recruit") {
@ -1563,22 +1571,25 @@ bool formula_ai::execute_variant(const variant& var, bool commandline)
} else if(i->is_string() && i->as_string() == "end_turn") {
return false;
} else if(fallback_command) {
if(fallback_command->key() == "human")
if (master_)
{
//we want give control of the side to human for the rest of this turn
throw fallback_ai_to_human_exception();
} else
{
ai_interface* fallback = create_ai(fallback_command->key(), get_info());
if(fallback) {
fallback->play_turn();
if(fallback_command->key() == "human")
{
//we want give control of the side to human for the rest of this turn
throw fallback_ai_to_human_exception();
} else
{
ai_interface* fallback = create_ai(fallback_command->key(), get_info());
if(fallback) {
fallback->play_turn();
}
}
}
return false;
} else {
//this information is unneded when evaluating formulas form commandline
if (!commandline)
std::cerr << "UNRECOGNIZED MOVE: " << i->to_debug_string() << "\n";
LOG_AI << "UNRECOGNIZED MOVE: " << i->to_debug_string() << "\n";
}
}
@ -1586,10 +1597,10 @@ bool formula_ai::execute_variant(const variant& var, bool commandline)
}
void formula_ai::do_recruitment()
bool formula_ai::do_recruitment()
{
if(!recruit_formula_) {
return;
return false;
}
variant var = recruit_formula_->execute(*this);
@ -1604,15 +1615,15 @@ void formula_ai::do_recruitment()
for(std::vector<variant>::const_iterator i = vars.begin(); i != vars.end(); ++i) {
if(!i->is_string()) {
return;
return false;
}
if(!recruit(i->as_string())) {
return;
return true;
}
}
do_recruitment();
return do_recruitment();
}
namespace {

View file

@ -151,7 +151,7 @@ private:
void handle_exception(game_logic::formula_error& e);
void handle_exception(game_logic::formula_error& e, const std::string& failed_operation);
void display_message(const std::string& msg);
void do_recruitment();
bool do_recruitment();
bool make_move(game_logic::const_formula_ptr formula_, const game_logic::formula_callable& variables);
bool execute_variant(const variant& var, bool commandline=false);
virtual variant get_value(const std::string& key) const;
@ -174,6 +174,7 @@ private:
game_logic::candidate_move_set candidate_moves_;
bool use_eval_lists_;
friend class ai;
};
#endif

View file

@ -535,8 +535,9 @@ namespace {
const std::string type = cfg["type"];
const std::vector<std::string>& types = utils::split(type);
const std::set<std::string> recruits(types.begin(), types.end());
(*teams)[index].add_recruits(recruits);
for(std::vector<std::string>::const_iterator i = types.begin(); i != types.end(); ++i) {
(*teams)[index].recruits().insert(*i);
preferences::encountered_units().insert(*i);
player_info *player=state_of_game->get_player((*teams)[index].save_id());
@ -559,7 +560,7 @@ namespace {
const std::string type = cfg["type"];
const std::vector<std::string>& types = utils::split(type);
for(std::vector<std::string>::const_iterator i = types.begin(); i != types.end(); ++i) {
(*teams)[index].recruits().erase(*i);
(*teams)[index].remove_recruit(*i);
player_info *player=state_of_game->get_player((*teams)[index].save_id());
if(player) {
@ -582,13 +583,11 @@ namespace {
if(recruit.size() == 1 && recruit.back() == "")
recruit.clear();
std::set<std::string>& can_recruit = (*teams)[index].recruits();
can_recruit.clear();
std::copy(recruit.begin(),recruit.end(),std::inserter(can_recruit,can_recruit.end()));
(*teams)[index].set_recruits(std::set<std::string>(recruit.begin(), recruit.end()));
player_info *player=state_of_game->get_player((*teams)[index].save_id());
if(player) {
player->can_recruit = can_recruit;
player->can_recruit = (*teams)[index].recruits();
}
}
@ -710,13 +709,10 @@ namespace {
if (recruit.size() == 1 && recruit.back() == "")
recruit.clear();
std::set<std::string>& rlist_set = (*teams)[team_index].recruits();
rlist_set.clear();
std::copy( recruit.begin(), recruit.end(), std::inserter(rlist_set,rlist_set.end()) );
(*teams)[team_index].set_recruits(std::set<std::string>(recruit.begin(),recruit.end()));
player_info *player = state_of_game->get_player((*teams)[team_index].save_id());
if (player) player->can_recruit = rlist_set;
if (player) player->can_recruit = (*teams)[team_index].recruits();
}
// Modify income
if(!income.empty()) {

View file

@ -51,7 +51,7 @@ std::vector< lg::logd > log_domains;
void timestamps(bool t) { timestamp = t; }
logger err("error", 0), warn("warning", 1), info("info", 2), debug("debug", 3);
log_domain general("general"), ai("ai"),cache("cache"), config("config"), display("display"),
log_domain general("general"), ai("ai"), formula_ai("formula_ai"), cache("cache"), config("config"), display("display"),
engine("engine"), network("network"), mp_server("server"),
filesystem("filesystem"), audio("audio"), notifs("notifs"),
replay("replay"), help("help"), gui("gui"), gui_parse("gui_parse"),

View file

@ -67,7 +67,7 @@ void timestamps(bool);
std::string get_timestamp(const time_t& t, const std::string& format="%Y%m%d %H:%M:%S ");
extern logger err, warn, info, debug;
extern log_domain general, ai,cache, config, display, engine, network, mp_server,
extern log_domain general, ai, formula_ai, cache, config, display, engine, network, mp_server,
filesystem, audio, notifs, replay, help, gui, gui_parse, gui_draw,
gui_event, editor, wml, mp_user_handler;

View file

@ -85,6 +85,8 @@ team::team_info::team_info(const config& cfg) :
start_gold(),
income(cfg["income"]),
income_per_village(),
average_price(0),
number_of_possible_recruits_to_force_recruit(lexical_cast_default<float>(cfg["number_of_possible_recruits_to_force_recruit"],0.0)),
can_recruit(),
global_recruitment_pattern(utils::split(cfg["global_recruitment_pattern"])),
recruitment_pattern(utils::split(cfg["recruitment_pattern"])),
@ -204,6 +206,11 @@ team::team_info::team_info(const config& cfg) :
ai_algorithm = global_ai_params["ai_algorithm"];
}
if (!cfg.has_attribute("number_of_possible_recruits_to_force_recruit"))
{
number_of_possible_recruits_to_force_recruit = lexical_cast_default<float>(global_ai_params["number_of_possible_recruits_to_force_recruit"],3.1);
}
std::string scouts_val = cfg["villages_per_scout"];
if(scouts_val.empty()
@ -264,7 +271,7 @@ team::team_info::team_info(const config& cfg) :
}
if(caution_val.empty()) {
caution_ = 1.0;
caution_ = 0.25;
} else {
caution_ = atof(caution_val.c_str());
}
@ -343,6 +350,7 @@ void team::team_info::write(config& cfg) const
cfg["disallow_observers"] = disallow_observers ? "yes" : "no";
cfg["allow_player"] = allow_player ? "yes" : "no";
cfg["no_leader"] = no_leader ? "yes" : "no";
cfg["number_of_possible_recruits_to_force_recruit"] = lexical_cast<std::string>(number_of_possible_recruits_to_force_recruit);
std::stringstream enemies_str;
for(std::vector<int>::const_iterator en = enemies.begin(); en != enemies.end(); ++en) {
@ -491,6 +499,57 @@ void team::lose_village(const gamemap::location& loc)
}
}
void team::remove_recruit(const std::string& recruit)
{
info_.can_recruit.erase(recruit);
info_.average_price = 0;
}
void team::set_recruits(const std::set<std::string>& recruits)
{
info_.can_recruit = recruits;
info_.average_price = 0;
}
void team::add_recruits(const std::set<std::string>& recruits)
{
std::copy(recruits.begin(),recruits.end(),
std::inserter(info_.can_recruit, info_.can_recruit.end()));
info_.average_price = 0;
}
namespace {
struct count_average {
count_average(size_t& a) : a_(a), sum_(0), count_(0)
{}
~count_average() {
a_ = sum_/count_;
}
void operator()(const std::string& recruit)
{
unit_type_data::unit_type_map::const_iterator i = unit_type_data::types().find(recruit);
if (i == unit_type_data::types().end())
return;
++count_;
sum_ += i->second.cost();
}
private:
size_t& a_;
size_t sum_;
size_t count_;
};
}
size_t team::average_recruit_price()
{
if (info_.average_price)
return info_.average_price;
std::for_each(info_.can_recruit.begin(), info_.can_recruit.end(), count_average(info_.average_price));
return info_.average_price;
}
void team::set_time_of_day(int turn, const time_of_day& tod)
{
aiparams_.clear();
@ -529,8 +588,9 @@ void team::set_time_of_day(int turn, const time_of_day& tod)
info_.recruitment_pattern = utils::split(aiparams_["recruitment_pattern"]);
if (info_.recruitment_pattern.empty())
info_.recruitment_pattern = info_.global_recruitment_pattern;
info_.aggression_ = lexical_cast_default<double>(aiparams_["aggression"],0.5);
info_.caution_ = lexical_cast_default<double>(aiparams_["caution"],0.25);
info_.aggression_ = lexical_cast_default<double>(aiparams_["aggression"],info_.aggression_);
info_.caution_ = lexical_cast_default<double>(aiparams_["caution"],info_.caution_);
info_.number_of_possible_recruits_to_force_recruit = lexical_cast_default<float>(aiparams_["number_of_possible_recruits_to_force_recruit"],info_.number_of_possible_recruits_to_force_recruit);
}
bool team::calculate_enemies(size_t index) const

View file

@ -72,6 +72,8 @@ public:
std::string start_gold;
std::string income;
int income_per_village;
size_t average_price;
float number_of_possible_recruits_to_force_recruit;
std::set<std::string> can_recruit;
std::vector<std::string> global_recruitment_pattern;
std::vector<std::string> recruitment_pattern;
@ -157,9 +159,14 @@ public:
void set_current_player(const std::string player)
{ info_.current_player = player; }
size_t average_recruit_price();
float num_pos_recruits_to_force()
{ return info_.number_of_possible_recruits_to_force_recruit; }
const std::set<std::string>& recruits() const
{ return info_.can_recruit; }
std::set<std::string>& recruits() { return info_.can_recruit; }
void add_recruits(const std::set<std::string>& recruits);
void remove_recruit(const std::string& recruits);
void set_recruits(const std::set<std::string>& recruits);
const std::vector<std::string>& recruitment_pattern() const
{ return info_.recruitment_pattern; }
bool remove_recruitment_pattern_entry(const std::string& key)