fixed up sample AI to work properly

This commit is contained in:
Dave White 2004-02-27 23:22:36 +00:00
parent 9d05edaca2
commit 42d4cfe46b
5 changed files with 211 additions and 41 deletions

View file

@ -32,6 +32,9 @@ ai_interface* create_ai(const std::string& name, ai_interface::info& info)
// return new my_ai(info);
//at the top of this function
if(name == "sample_ai")
return new sample_ai(info);
return new ai(info);
}
@ -41,17 +44,14 @@ ai::ai(ai_interface::info& info)
consider_combat_(true)
{}
bool ai::recruit(const std::string& usage)
bool ai::recruit_usage(const std::string& usage)
{
const int min_gold = 0;
log_scope("recruiting troops");
std::cerr << "recruiting " << usage << "\n";
std::vector<std::map<std::string,unit_type>::const_iterator> options;
//record the number of the recruit for replay recording
std::vector<int> option_numbers;
std::vector<std::string> options;
//find an available unit that can be recruited, matches the desired
//usage type, and comes in under budget
@ -62,40 +62,56 @@ bool ai::recruit(const std::string& usage)
if(i->second.usage() == usage && recruits.count(i->second.name())
&& current_team().gold() - i->second.cost() > min_gold) {
options.push_back(i);
option_numbers.push_back(std::distance(recruits.begin(),
recruits.find(i->first)));
options.push_back(i->second.name());
}
}
//from the available options, choose one at random
if(options.empty() == false) {
const gamemap::location loc = gamemap::location::null_location;
const int option = rand()%options.size();
//add the recruit conditionally. If recruiting fails,
//we will undo it.
recorder.add_recruit(option_numbers[option],loc);
replay_undo replay_guard(recorder);
const unit_type& u = options[option]->second;
unit new_unit(&u,team_num_,true);
//see if we can actually recruit (i.e. have enough room etc)
if(recruit_unit(map_,team_num_,units_,new_unit,loc,&disp_).empty()) {
current_team().spend_gold(u.cost());
//confirm the transaction - i.e. don't undo recruitment
replay_guard.confirm_transaction();
return true;
} else {
return false;
}
return recruit(options[option]);
}
return false;
}
bool ai_interface::recruit(const std::string& unit_name, location loc)
{
const std::set<std::string>& recruits = current_team().recruits();
const std::set<std::string>::const_iterator i = std::find(recruits.begin(),recruits.end(),unit_name);
if(i == recruits.end()) {
return false;
}
const int num = std::distance(recruits.begin(),i);
recorder.add_recruit(num,loc);
replay_undo replay_guard(recorder);
game_data::unit_type_map::const_iterator u = info_.gameinfo.unit_types.find(unit_name);
if(u == info_.gameinfo.unit_types.end()) {
return false;
}
//check we have enough money
if(current_team().gold() < u->second.cost()) {
return false;
}
unit new_unit(&u->second,info_.team_num,true);
//see if we can actually recruit (i.e. have enough room etc)
if(recruit_unit(info_.map,info_.team_num,info_.units,new_unit,loc,&info_.disp).empty()) {
current_team().spend_gold(u->second.cost());
//confirm the transaction - i.e. don't undo recruitment
replay_guard.confirm_transaction();
return true;
} else {
return false;
}
}
team& ai_interface::current_team()
{
return info_.teams[info_.team_num-1];
@ -727,7 +743,7 @@ void ai::do_recruitment()
std::map<std::string,int> unit_types;
while(unit_types["scout"] < scouts_wanted) {
if(recruit("scout") == false)
if(recruit_usage("scout") == false)
break;
++unit_types["scout"];
@ -741,7 +757,7 @@ void ai::do_recruitment()
}
//buy units as long as we have room and can afford it
while(recruit(options[rand()%options.size()])) {
while(recruit_usage(options[rand()%options.size()])) {
}
}

View file

@ -85,7 +85,8 @@ protected:
///must be a valid weapon of the attacking unit
void attack_enemy(const location& attacking_unit, const location& target, int weapon);
///this function should be called to move a unit.
///this function should be called to move a unit. Once the unit has been moved, its
///movement allowance is set to 0.
///'from': the location of the unit being moved.
///'to': the location to be moved to. This must be a valid move for the unit
///'possible_moves': the map of possible moves, as obtained from 'calculate_possible_moves'
@ -98,11 +99,20 @@ protected:
///'srcdst': a map of units to all their possible destinations
///'dstsrc': a map of destinations to all the units that can move to that destination
///'enemy': if true, a map of possible moves for enemies will be calculated. If false,
///a map of possible moves for units on the AI's side will be calculated
///a map of possible moves for units on the AI's side will be calculated. The AI's own
///leader will not be included in this map.
///'assume_full_movement': if true, the function will operate on the assumption that all
///units can move their full movement allotment.
void calculate_possible_moves(std::map<location,paths>& possible_moves, move_map& srcdst, move_map& dstsrc, bool enemy, bool assume_full_movement=false);
///this function is used to recruit a unit. It will recruit the unit with the given name,
///at the given location, or at an available location to recruit units if 'loc' is not
///a valid recruiting location.
///
///if recruitment cannot be performed, because there are no available tiles, or not enough
///money, then the function will return false
bool recruit(const std::string& unit_name, location loc=location());
///functions to retrieve the 'info' object. Used by derived classes to discover all
///necessary game information
info& get_info() { return info_; }
@ -115,6 +125,143 @@ private:
///this function is used to create a new AI object with the specified algorithm name
ai_interface* create_ai(const std::string& algorithm_name, ai_interface::info& info);
class sample_ai : public ai_interface {
public:
sample_ai(info& i) : ai_interface(i) {}
void play_turn() {
do_attacks();
get_villages();
do_moves();
do_recruitment();
}
private:
void do_attacks() {
std::map<location,paths> possible_moves;
move_map srcdst, dstsrc;
calculate_possible_moves(possible_moves,srcdst,dstsrc,false);
for(unit_map::const_iterator i = get_info().units.begin(); i != get_info().units.end(); ++i) {
if(current_team().is_enemy(i->second.side())) {
location adjacent_tiles[6];
get_adjacent_tiles(i->first,adjacent_tiles);
int best_defense = -1;
std::pair<location,location> best_movement;
for(size_t n = 0; n != 6; ++n) {
typedef move_map::const_iterator Itor;
std::pair<Itor,Itor> range = dstsrc.equal_range(adjacent_tiles[n]);
while(range.first != range.second) {
const location& dst = range.first->first;
const location& src = range.first->second;
const unit_map::const_iterator un = get_info().units.find(src);
const gamemap::TERRAIN terrain = get_info().map.get_terrain(dst);
const int chance_to_hit = un->second.defense_modifier(get_info().map,terrain);
if(best_defense == -1 || chance_to_hit < best_defense) {
best_defense = chance_to_hit;
best_movement = *range.first;
}
++range.first;
}
}
if(best_defense != -1) {
move_unit(best_movement.second,best_movement.first,possible_moves);
const int weapon = choose_weapon(best_movement.first,i->first);
attack_enemy(best_movement.first,i->first,weapon);
do_attacks();
return;
}
}
}
}
int choose_weapon(const location& attacker, const location& defender) {
const unit_map::const_iterator att = get_info().units.find(attacker);
assert(att != get_info().units.end());
const std::vector<attack_type>& attacks = att->second.attacks();
int best_attack_rating = -1;
int best_attack = -1;
for(int n = 0; n != attacks.size(); ++n) {
const battle_stats stats = evaluate_battle_stats(get_info().map,attacker,defender,n,get_info().units,get_info().state,get_info().gameinfo,0,false);
const int attack_rating = stats.damage_defender_takes*stats.nattacks*stats.chance_to_hit_defender;
if(best_attack == -1 || attack_rating > best_attack_rating) {
best_attack = n;
best_attack_rating = attack_rating;
}
}
return best_attack;
}
void get_villages() {
std::map<location,paths> possible_moves;
move_map srcdst, dstsrc;
calculate_possible_moves(possible_moves,srcdst,dstsrc,false);
for(move_map::const_iterator i = dstsrc.begin(); i != dstsrc.end(); ++i) {
if(get_info().map.underlying_terrain(get_info().map.get_terrain(i->first)) == gamemap::TOWER &&
current_team().owns_tower(i->first) == false) {
move_unit(i->second,i->first,possible_moves);
get_villages();
return;
}
}
}
void do_moves() {
unit_map::const_iterator leader;
for(leader = get_info().units.begin(); leader != get_info().units.end(); ++leader) {
if(leader->second.can_recruit() && current_team().is_enemy(leader->second.side())) {
break;
}
}
if(leader == get_info().units.end())
return;
std::map<location,paths> possible_moves;
move_map srcdst, dstsrc;
calculate_possible_moves(possible_moves,srcdst,dstsrc,false);
int closest_distance = -1;
std::pair<location,location> closest_move;
for(move_map::const_iterator i = dstsrc.begin(); i != dstsrc.end(); ++i) {
const int distance = distance_between(i->first,leader->first);
if(closest_distance == -1 || distance < closest_distance) {
closest_distance = distance;
closest_move = *i;
}
}
if(closest_distance != -1) {
move_unit(closest_move.second,closest_move.first,possible_moves);
do_moves();
}
}
void do_recruitment() {
const std::set<std::string>& options = current_team().recruits();
const int choice = (rand()%options.size());
std::set<std::string>::const_iterator i = options.begin();
std::advance(i,choice);
const bool res = recruit(*i);
if(res) {
do_recruitment();
}
}
};
class ai : public ai_interface {
public:
@ -140,7 +287,7 @@ private:
void move_leader_after_recruit(const move_map& enemy_dstsrc);
void leader_attack();
bool recruit(const std::string& usage);
bool recruit_usage(const std::string& usage);
struct attack_analysis
{

View file

@ -379,6 +379,8 @@ int play_game(int argc, char** argv)
return 0;
}
//multiplayer mode skips straight into a multiplayer game, bypassing the main menu
//it is all handled inside this 'if' statement
if(multiplayer_mode) {
std::string scenario = "multiplayer_test";
@ -475,7 +477,7 @@ int play_game(int argc, char** argv)
}
if(algorithm != side_algorithms.end()) {
(*itors.first)->values["ai_algorithm"] = controller->second;
(*itors.first)->values["ai_algorithm"] = algorithm->second;
}
}

View file

@ -360,19 +360,24 @@ std::string client_type()
const std::string& theme()
{
std::string& res = prefs["theme"];
if(res.empty())
res = "Default";
if(non_interactive()) {
static const std::string null_theme = "null";
return null_theme;
}
if(non_interactive())
res = "null";
std::string& res = prefs["theme"];
if(res.empty()) {
res = "Default";
}
return res;
}
void set_theme(const std::string& theme)
{
prefs["theme"] = theme;
if(theme != "null") {
prefs["theme"] = theme;
}
}
void show_preferences_dialog(display& disp)

View file

@ -127,7 +127,7 @@ void update_rect(const SDL_Rect& rect_value)
SDL_Rect rect = rect_value;
SDL_Surface* const fb = get_video_surface();
SDL_Surface* const fb = SDL_GetVideoSurface();
if(fb != NULL) {
if(rect.x < 0) {
if(rect.x*-1 > int(rect.w))