changes to make AI smarter

This commit is contained in:
Dave White 2004-05-13 02:03:28 +00:00
parent 7975077f57
commit 47c8c39a7b
10 changed files with 235 additions and 50 deletions

View file

@ -158,6 +158,13 @@ void ai_interface::sync_network()
network::send_data(cfg);
info_.start_command = info_.recorder.ncommands();
cfg.clear();
while(network::connection res = network::receive_data(cfg)) {
std::deque<config> backlog;
info_.turn_data_.process_network_data(cfg,res,backlog);
cfg.clear();
}
}
}
@ -265,6 +272,76 @@ gamemap::location ai_interface::move_unit(location from, location to, std::map<l
return to;
}
gamemap::location ai::move_unit(location from, location to, std::map<location,paths>& possible_moves)
{
std::map<location,paths> temp_possible_moves;
std::map<location,paths>* possible_moves_ptr = &possible_moves;
const unit_map::const_iterator i = units_.find(from);
if(i != units_.end() && i->second.can_recruit()) {
//if the leader isn't on its keep, and we can move to the keep and still make our planned
//movement, then try doing that.
const gamemap::location& start_pos = map_.starting_position(i->second.side());
if(i->first != start_pos && to != start_pos && units_.count(start_pos) == 0) {
std::cerr << "when seeing if leader can move from " << (from.x+1) << "," << (from.y+1) << " -> " << (to.x+1) << "," << (to.y+1) << " seeing if can detour to keep at " << (start_pos.x+1) << "," << (start_pos.y+1) << "\n";
const std::map<location,paths>::const_iterator moves = possible_moves.find(from);
if(moves != possible_moves.end()) {
std::cerr << "found leader moves..\n";
bool can_make_it = false;
int move_left = 0;
//see if the leader can make it to the keep, and if it can, how much movement it
//will have left when it gets there.
const paths::routes_map::const_iterator itor = moves->second.routes.find(start_pos);
if(itor != moves->second.routes.end()) {
move_left = itor->second.move_left;
std::cerr << "can make it to keep with " << move_left << " movement left\n";
unit temp_unit(i->second);
temp_unit.set_movement(move_left);
const temporary_unit_placer unit_placer(units_,start_pos,temp_unit);
const paths leader_paths(map_,state_,gameinfo_,units_,start_pos,teams_,false,false);
std::cerr << "found " << leader_paths.routes.size() << " moves for temp leader\n";
//see if this leader could make it back to the keep
if(leader_paths.routes.count(to) != 0) {
std::cerr << "can make it back to the keep\n";
can_make_it = true;
}
}
//if we can make it back to the keep and then to our original destination, do so.
if(can_make_it) {
from = ai_interface::move_unit(from,start_pos,possible_moves);
if(from != start_pos) {
return from;
}
const unit_map::iterator itor = units_.find(from);
if(itor != units_.end()) {
itor->second.set_movement(move_left);
}
move_map srcdst, dstsrc;
calculate_possible_moves(temp_possible_moves,srcdst,dstsrc,false);
possible_moves_ptr = &temp_possible_moves;
}
}
}
do_recruitment();
}
if(units_.count(to) == 0) {
return ai_interface::move_unit(from,to,*possible_moves_ptr);
} else {
return from;
}
}
void ai_interface::calculate_possible_moves(std::map<location,paths>& res, move_map& srcdst, move_map& dstsrc, bool enemy, bool assume_full_movement)
{
for(std::map<gamemap::location,unit>::iterator un_it = info_.units.begin(); un_it != info_.units.end(); ++un_it) {
@ -278,7 +355,7 @@ void ai_interface::calculate_possible_moves(std::map<location,paths>& res, move_
}
//discount our own leader, and our units that have been turned to stone
if(!enemy && un_it->second.can_recruit() || un_it->second.stone()) {
if(/*!enemy && un_it->second.can_recruit() ||*/ un_it->second.stone()) {
continue;
}
@ -397,6 +474,17 @@ void ai::do_move()
return;
}
if(leader != units_.end()) {
srcdst.erase(leader->first);
for(move_map::iterator i = dstsrc.begin(); i != dstsrc.end(); ) {
if(i->second == leader->first) {
dstsrc.erase(i++);
} else {
++i;
}
}
}
const bool met_invisible_unit = move_to_targets(possible_moves,srcdst,dstsrc,enemy_srcdst,enemy_dstsrc,leader);
if(met_invisible_unit) {
do_move();
@ -530,18 +618,66 @@ void ai_interface::attack_enemy(const location& u, const location& target, int w
}
}
std::vector<std::pair<gamemap::location,gamemap::location> > ai::get_village_combinations(std::map<gamemap::location,paths>& possible_moves, const move_map& srcdst, const move_map& dstsrc,
const move_map& enemy_srcdst, const move_map& enemy_dstsrc, unit_map::const_iterator leader,
std::set<gamemap::location>& taken_villages, std::set<gamemap::location>& moved_units,
const std::vector<std::pair<gamemap::location,gamemap::location> >& village_moves,
std::vector<std::pair<gamemap::location,gamemap::location> >::const_iterator start_at)
{
const int leader_distance_from_keep = 10000;
std::vector<std::pair<location,location> > result;
for(std::vector<std::pair<location,location> >::const_iterator i = start_at; i != village_moves.end(); ++i) {
if(taken_villages.count(i->first) || moved_units.count(i->second)) {
continue;
}
int distance = -1;
if(leader != units_.end() && leader->first == i->second) {
const location& start_pos = map_.starting_position(leader->second.side());
distance = distance_between(start_pos,i->first);
}
taken_villages.insert(i->first);
moved_units.insert(i->second);
std::vector<std::pair<location,location> > res = get_village_combinations(possible_moves,srcdst,dstsrc,enemy_srcdst,enemy_dstsrc,leader,
taken_villages,moved_units,village_moves,i+1);
//the result is better if it results in getting more villages, or if it results in the same number of villages,
//but the leader ends closer to their keep
const bool result_better = res.size() >= result.size() || res.size()+1 == result.size() && distance != -1 && distance < leader_distance_from_keep;
if(result_better) {
result.swap(res);
result.push_back(*i);
}
taken_villages.erase(i->first);
moved_units.erase(i->second);
}
return result;
}
bool ai::get_villages(std::map<gamemap::location,paths>& possible_moves, const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc, unit_map::const_iterator leader)
{
//try to acquire villages
for(move_map::const_iterator i = dstsrc.begin(); i != dstsrc.end(); ++i) {
if(map_.is_village(i->first) == false) {
//we want to build up a list of possible moves we can make that will capture villages.
//limit the moves to 'max_village_moves' to make sure things don't get out of hand.
const size_t max_village_moves = 50;
std::vector<std::pair<location,location> > village_moves;
for(move_map::const_iterator j = dstsrc.begin(); j != dstsrc.end() && village_moves.size() < max_village_moves; ++j) {
if(map_.is_village(j->first) == false) {
continue;
}
bool want_village = true, owned = false;
for(size_t j = 0; j != teams_.size(); ++j) {
owned = teams_[j].owns_village(i->first);
if(owned && !current_team().is_enemy(j+1)) {
for(size_t n = 0; n != teams_.size(); ++n) {
owned = teams_[n].owns_village(j->first);
if(owned && !current_team().is_enemy(n+1)) {
want_village = false;
}
@ -552,28 +688,27 @@ bool ai::get_villages(std::map<gamemap::location,paths>& possible_moves, const m
//if it's a neutral village, and we have no leader, then the village is no use to us,
//and we don't want it.
if(!owned && leader == units_.end())
if(!owned && leader == units_.end()) {
want_village = false;
}
if(want_village) {
std::cerr << "trying to acquire village: " << i->first.x
<< ", " << i->first.y << "\n";
const std::map<location,unit>::iterator un = units_.find(i->second);
if(un == units_.end()) {
assert(false);
return false;
}
if(un->second.is_guardian())
continue;
move_unit(i->second,i->first,possible_moves);
return true;
village_moves.push_back(*j);
}
}
return false;
std::set<location> taken_villages, moved_units;
const int ticks = SDL_GetTicks();
std::cerr << "get_villages()..." << village_moves.size() << "\n";
const std::vector<std::pair<location,location> >& moves = get_village_combinations(possible_moves,srcdst,dstsrc,enemy_srcdst,enemy_dstsrc,leader,
taken_villages,moved_units,village_moves,village_moves.begin());
std::cerr << "get_villages() done: " << (SDL_GetTicks() - ticks) << "\n";
for(std::vector<std::pair<location,location> >::const_iterator i = moves.begin(); i != moves.end(); ++i) {
move_unit(i->second,i->first,possible_moves);
}
return moves.empty() == false;
}
bool ai::get_healing(std::map<gamemap::location,paths>& possible_moves, const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc)
@ -1077,6 +1212,28 @@ void ai::move_leader_to_keep(const move_map& enemy_dstsrc)
const paths::routes_map::const_iterator itor = leader_paths.routes.find(start_pos);
if(itor != leader_paths.routes.end() && units_.count(start_pos) == 0) {
move_unit(leader->first,start_pos,possible_moves);
} else {
//make a map of the possible locations the leader can move to, ordered by the
//distance from the keep
std::multimap<int,gamemap::location> moves_toward_keep;
//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.
const int current_distance = distance_between(leader->first,start_pos);
for(paths::routes_map::const_iterator i = leader_paths.routes.begin(); i != leader_paths.routes.end(); ++i) {
const int new_distance = distance_between(i->first,start_pos);
if(new_distance < current_distance) {
moves_toward_keep.insert(std::pair<int,gamemap::location>(new_distance,i->first));
}
}
//find the first location which we can move to without the threat of enemies
for(std::multimap<int,gamemap::location>::const_iterator j = moves_toward_keep.begin(); j != moves_toward_keep.end(); ++j) {
if(enemy_dstsrc.count(j->second) == 0) {
move_unit(leader->first,j->second,possible_moves);
break;
}
}
}
}
}

View file

@ -325,6 +325,18 @@ protected:
virtual bool recruit_usage(const std::string& usage);
//function which will calculate which movements should be made to get an optimal number of villages
std::vector<std::pair<gamemap::location,gamemap::location> > get_village_combinations(std::map<gamemap::location,paths>& possible_moves, const move_map& srcdst, const move_map& dstsrc,
const move_map& enemy_srcdst, const move_map& enemy_dstsrc, unit_map::const_iterator leader,
std::set<location>& taken_villages, std::set<location>& moved_units,
const std::vector<std::pair<gamemap::location,gamemap::location> >& village_moves,
std::vector<std::pair<gamemap::location,gamemap::location> >::const_iterator start_at);
//our own version of 'move_unit'. It is like the version in ai_interface, however if it is the leader
//moving, it will first attempt recruitment.
location move_unit(location from, location to, std::map<location,paths>& possible_moves);
struct attack_analysis
{
void analyze(const gamemap& map, std::map<location,unit>& units,

View file

@ -112,8 +112,9 @@ void ai::do_attack_analysis(
//check to see whether this move would be a backstab
int backstab_bonus = 1;
double surround_bonus = 1.0;
if(backstab && tiles[(j+3)%6] != current_unit) {
if(tiles[(j+3)%6] != current_unit) {
const unit_map::const_iterator itor = units_.find(tiles[(j+3)%6]);
//note that we *could* also check if a unit plans to move there before we're
@ -122,8 +123,14 @@ void ai::do_attack_analysis(
//make our analysis look *worse* instead of better.
//so we only check for 'concrete' backstab opportunities.
if(itor != units_.end() && itor->second.side() == unit_itor->second.side()) {
backstab_bonus = 2;
if(backstab) {
backstab_bonus = 2;
}
surround_bonus = 1.2;
}
}
//see if this position is the best rated we've seen so far
@ -142,14 +149,14 @@ void ai::do_attack_analysis(
//if this is a position with equal defense to another position, but more vulnerability
//then we don't want to use it
if(cur_position >= 0 && rating == best_rating && vulnerability - support >= best_vulnerability - best_support) {
if(cur_position >= 0 && rating == best_rating && vulnerability/surround_bonus - support*surround_bonus >= best_vulnerability - best_support) {
continue;
}
cur_position = j;
best_rating = rating;
best_vulnerability = vulnerability;
best_support = support;
best_vulnerability = vulnerability/surround_bonus;
best_support = support*surround_bonus;
}
if(cur_position != -1) {
@ -425,17 +432,17 @@ void ai::attack_analysis::analyze(const gamemap& map,
//if the attacker moved onto a village, reward it for doing so
else if(on_village) {
atthp += game_config::heal_amount*2; //double reward to emphasize getting onto villages
atthp += game_config::cure_amount*2; //double reward to emphasize getting onto villages
}
defenderxp += (atthp == 0 ? 8:1)*att->second.type().level();
defenderxp += (atthp == 0 ? game_config::kill_experience:1)*att->second.type().level();
avg_damage_taken += hitpoints[i] - atthp;
}
//penalty for allowing advancement is a 'negative' kill, and
//defender's hitpoints get restored to maximum
if(defend_it->second.experience() < defend_it->second.max_experience()&&
if(defend_it->second.experience() < defend_it->second.max_experience() &&
defend_it->second.experience() + defenderxp >=
defend_it->second.max_experience()) {
chance_to_kill -= 1.0;
@ -443,7 +450,7 @@ void ai::attack_analysis::analyze(const gamemap& map,
} else if(defhp == 0) {
chance_to_kill += 1.0;
} else if(map.is_village(defend_it->first)) {
defhp += game_config::heal_amount;
defhp += game_config::cure_amount;
if(defhp > target_hp)
defhp = target_hp;
}

View file

@ -99,26 +99,30 @@ std::vector<ai::target> ai::find_targets(unit_map::const_iterator leader, const
std::vector<target> targets;
//if enemy units are in range of the leader, then we rally to the leader's defense
//if enemy units are in range of the leader, then we target the enemies who are in range.
if(has_leader) {
const double threat = power_projection(leader->first,enemy_srcdst,enemy_dstsrc);
if(threat > 0.0) {
//find the specific tiles the enemy can reach, and set them as targets
std::vector<gamemap::location> threatened_tiles;
//find the location of enemy threats
std::set<gamemap::location> threats;
gamemap::location adj[6];
get_adjacent_tiles(leader->first,adj);
for(size_t n = 0; n != 6; ++n) {
if(enemy_dstsrc.count(adj[n]) > 0) {
threatened_tiles.push_back(adj[n]);
std::pair<move_map::const_iterator,move_map::const_iterator> itors = enemy_dstsrc.equal_range(adj[n]);
while(itors.first != itors.second) {
if(units_.count(itors.first->second)) {
threats.insert(itors.first->second);
}
++itors.first;
}
}
assert(threatened_tiles.size() > 0);
assert(threats.empty() == false);
//divide the threat into the tiles the enemy can reach, and try to
//get units to reach those tiles
const double value = threat/double(threatened_tiles.size());
for(std::vector<gamemap::location>::const_iterator i = threatened_tiles.begin(); i != threatened_tiles.end(); ++i) {
const double value = threat/double(threats.size());
for(std::set<gamemap::location>::const_iterator i = threats.begin(); i != threats.end(); ++i) {
targets.push_back(target(*i,value));
}
}

View file

@ -113,7 +113,7 @@ map_editor::~map_editor() {
try {
write_file(prefs_filename, prefs_.write());
}
catch (io_exception e) {
catch (io_exception& e) {
std::cerr << "Error when writing to " << prefs_filename << ": "
<< e.what() << std::endl;
}

View file

@ -64,7 +64,7 @@ void terrain_palette::adjust_size() {
const unsigned terrains_fitting =
(unsigned)(space_for_terrains / size_specs_.terrain_space) * 2;
const unsigned total_terrains = num_terrains();
nterrains_ = minimum(terrains_fitting, total_terrains);
nterrains_ = minimum<int>(terrains_fitting, total_terrains);
bot_button_y_ = size_specs_.palette_y + (nterrains_ / 2) * size_specs_.terrain_space +
button_palette_padding * 2 + button_height;
top_button_.set_location(button_x_, top_button_y_);

View file

@ -781,9 +781,7 @@ void floating_label::draw(SDL_Surface* screen)
SDL_Rect rect = {xpos(surf_->w),int(ypos_),surf_->w,surf_->h};
const clip_rect_setter clip_setter(screen,clip_rect_);
std::cerr << "blit a\n";
SDL_BlitSurface(screen,&rect,buf_,NULL);
std::cerr << "blit b\n";
SDL_BlitSurface(surf_,NULL,screen,&rect);
if(foreground_ != NULL) {

View file

@ -619,7 +619,6 @@ bool event_handler::handle_event_command(const queued_event& event_info, const s
else if(cmd == "unit_overlay") {
for(std::map<gamemap::location,unit>::iterator itor = units->begin(); itor != units->end(); ++itor) {
if(game_events::unit_matches_filter(itor,cfg)) {
std::cerr << "adding overlay '" << cfg["image"] << "' to '" << itor->second.description() << "'\n";
itor->second.add_overlay(cfg["image"]);
break;
}

View file

@ -99,7 +99,7 @@ void play_turn(game_data& gameinfo, game_state& state_of_game,
turn_data.move_unit_to_loc(ui,ui->second.get_goto(),false);
}
turn_data.start_interative_turn();
turn_data.start_interactive_turn();
while(!turn_data.turn_over()) {
@ -122,6 +122,11 @@ void play_turn(game_data& gameinfo, game_state& state_of_game,
start_command = turn_data.send_data(start_command);
}
//send one more time to make sure network is up-to-date.
start_command = turn_data.send_data(start_command);
assert(start_command == recorder.ncommands());
}
turn_info::turn_info(game_data& gameinfo, game_state& state_of_game,
@ -184,6 +189,8 @@ int turn_info::send_data(int first_command)
config cfg;
const config& obj = cfg.add_child("turn",recorder.get_data_range(first_command,recorder.ncommands()));
std::cerr << "sending commands " << first_command << "-" << recorder.ncommands() << ": '"
<< obj.write() << "'\n";
if(obj.empty() == false) {
network::send_data(cfg);
}
@ -888,7 +895,7 @@ void turn_info::move_unit_to_loc(const unit_map::const_iterator& ui, const gamem
gui_.invalidate_game_status();
}
void turn_info::start_interative_turn() {
void turn_info::start_interactive_turn() {
std::cerr << "done gotos\n";
start_ncmd_ = recorder.ncommands();
}
@ -1873,6 +1880,7 @@ void turn_info::do_speak(const std::string& message, bool allies_only)
cfg["team_name"] = teams_[gui_.viewing_team()].team_name();
}
std::cerr << "logging speech: '" << cfg.write() << "'\n";
recorder.speak(cfg);
gui_.add_chat_message(cfg["description"],side,message,
private_message ? display::MESSAGE_PRIVATE : display::MESSAGE_PUBLIC);

View file

@ -97,7 +97,7 @@ public:
bool in_context_menu(hotkey::HOTKEY_COMMAND command) const;
void move_unit_to_loc(const unit_map::const_iterator& ui, const gamemap::location& target, bool continue_move);
void start_interative_turn();
void start_interactive_turn();
void save_game(const std::string& message,gui::DIALOG_TYPE dialog_type);