[[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:
parent
716247165b
commit
57ca8308ad
14 changed files with 250 additions and 120 deletions
|
@ -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]
|
||||
|
|
99
src/ai.cpp
99
src/ai.cpp
|
@ -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 ;
|
||||
}
|
||||
}
|
||||
|
|
12
src/ai.hpp
12
src/ai.hpp
|
@ -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 ;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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] != ¤t_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]) == ¤t_team()
|
||||
&& u->second.movement_left() > 0)) {
|
||||
ret += 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
66
src/team.cpp
66
src/team.cpp
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue