static void push_move_map(lua_State *L, const move_map& m) { move_map::const_iterator it = m.begin(); int index = 1; lua_createtable(L, 0, 0); // the main table do { map_location key = it->first; push_map_location(L, key); lua_createtable(L, 0, 0); while (key == it->first) { push_map_location(L, it->second); lua_rawseti(L, -2, index); ++index; ++it; } lua_settable(L, -3); index = 1; } while (it != m.end()); }
static void push_move_map(lua_State *L, const move_map& m) { lua_createtable(L, 0, 0); // the main table if (m.empty()) { return; } move_map::const_iterator it = m.begin(); int index = 1; do { map_location key = it->first; //push_map_location(L, key); // deprecated // This should be factored out. The same function is defined in data/lua/location_set.lua // At this point, it is not clear, where this(hashing) function can be placed // Implemented it this way, to test the new version of the data structure // as requested from the users of LuaAI <Nephro> int hashed_index = (key.x + 1) * 16384 + (key.y + 1) + 2000; lua_pushinteger(L, hashed_index); lua_createtable(L, 0, 0); while (key == it->first) { push_map_location(L, it->second); lua_rawseti(L, -2, index); ++index; ++it; } lua_settable(L, -3); index = 1; } while (it != m.end()); }
double aspect_attacks::power_projection2(const map_location& loc, const move_map& dstsrc) { map_location used_locs[6]; int ratings[6]; int num_used_locs = 0; map_location locs[6]; get_adjacent_tiles(loc,locs); const int lawful_bonus = resources::tod_manager->get_time_of_day().lawful_bonus; gamemap& map_ = *resources::game_map; unit_map& units_ = *resources::units; int res = 0; bool changed = false; for (int i = 0;; ++i) { if (i == 6) { if (!changed) break; // Loop once again, in case a unit found a better spot // and freed the place for another unit. changed = false; i = 0; } if (map_.on_board(locs[i]) == false) { continue; } const t_translation::t_terrain terrain = map_[locs[i]]; typedef move_map::const_iterator Itor; typedef std::pair<Itor,Itor> Range; Range its = dstsrc.equal_range(locs[i]); map_location* const beg_used = used_locs; map_location* end_used = used_locs + num_used_locs; int best_rating = 0; map_location best_unit; for(Itor it = its.first; it != its.second; ++it) { const unit_map::const_iterator u = units_.find(it->second); // Unit might have been killed, and no longer exist if(u == units_.end()) { continue; } const unit &un = *u; int tod_modifier = 0; if(un.alignment() == unit_type::LAWFUL) { tod_modifier = lawful_bonus; } else if(un.alignment() == unit_type::CHAOTIC) { tod_modifier = -lawful_bonus; } else if(un.alignment() == unit_type::LIMINAL) { tod_modifier = -(abs(lawful_bonus)); } // The 0.5 power avoids underestimating too much the damage of a wounded unit. int hp = int(sqrt(double(un.hitpoints()) / un.max_hitpoints()) * 1000); int most_damage = 0; BOOST_FOREACH(const attack_type &att, un.attacks()) { int damage = att.damage() * att.num_attacks() * (100 + tod_modifier); if (damage > most_damage) { most_damage = damage; } } int village_bonus = map_.is_village(terrain) ? 3 : 2; int defense = 100 - un.defense_modifier(terrain); int rating = hp * defense * most_damage * village_bonus / 200; if(rating > best_rating) { map_location *pos = std::find(beg_used, end_used, it->second); // Check if the spot is the same or better than an older one. if (pos == end_used || rating >= ratings[pos - beg_used]) { best_rating = rating; best_unit = it->second; } } } if (!best_unit.valid()) continue; map_location *pos = std::find(beg_used, end_used, best_unit); int index = pos - beg_used; if (index == num_used_locs) ++num_used_locs; else if (best_rating == ratings[index]) continue; else { // The unit was in another spot already, so remove its older rating // from the final result, and require a new run to fill its old spot. res -= ratings[index]; changed = true; } used_locs[index] = best_unit; ratings[index] = best_rating; res += best_rating; } return res / 100000.; }
std::vector<target> default_ai_context_impl::find_targets(const move_map& enemy_dstsrc) { log_scope2(log_ai, "finding targets..."); unit_map &units_ = resources::gameboard->units(); unit_map::iterator leader = units_.find_leader(get_side()); const gamemap &map_ = resources::gameboard->map(); std::vector<team> teams_ = resources::gameboard->teams(); const bool has_leader = leader != units_.end(); std::vector<target> targets; //=== start getting targets //if enemy units are in range of the leader, then we target the enemies who are in range. if(has_leader) { double threat = power_projection(leader->get_location(), enemy_dstsrc); if(threat > 0.0) { //find the location of enemy threats std::set<map_location> threats; map_location adj[6]; get_adjacent_tiles(leader->get_location(), adj); for(size_t n = 0; n != 6; ++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(threats.empty() == false); const double value = threat/double(threats.size()); for(std::set<map_location>::const_iterator i = threats.begin(); i != threats.end(); ++i) { LOG_AI << "found threat target... " << *i << " with value: " << value << "\n"; targets.push_back(target(*i,value,target::TYPE::THREAT)); } } } double corner_distance = distance_between(map_location::ZERO(), map_location(map_.w(),map_.h())); double village_value = get_village_value(); if(has_leader && village_value > 0.0) { std::map<map_location,pathfind::paths> friends_possible_moves; move_map friends_srcdst, friends_dstsrc; calculate_possible_moves(friends_possible_moves, friends_srcdst, friends_dstsrc, false, true); const std::vector<map_location>& villages = map_.villages(); for(std::vector<map_location>::const_iterator t = villages.begin(); t != villages.end(); ++t) { assert(map_.on_board(*t)); bool ally_village = false; for (size_t i = 0; i != teams_.size(); ++i) { if (!current_team().is_enemy(i + 1) && teams_[i].owns_village(*t)) { ally_village = true; break; } } if (ally_village) { //Support seems to cause the AI to just 'sit around' a lot, so //only turn it on if it's explicitly enabled. if(get_support_villages()) { double enemy = power_projection(*t, enemy_dstsrc); if (enemy > 0) { enemy *= 1.7; double our = power_projection(*t, friends_dstsrc); double value = village_value * our / enemy; add_target(target(*t, value, target::TYPE::SUPPORT)); } } } else { double leader_distance = distance_between(*t, leader->get_location()); double value = village_value * (1.0 - leader_distance / corner_distance); LOG_AI << "found village target... " << *t << " with value: " << value << " distance: " << leader_distance << '\n'; targets.push_back(target(*t,value,target::TYPE::VILLAGE)); } } } std::vector<goal_ptr>& goals = get_goals(); //find the enemy leaders and explicit targets unit_map::const_iterator u; if (get_leader_value()>0.0) { for(u = units_.begin(); u != units_.end(); ++u) { //is a visible enemy leader if (u->can_recruit() && current_team().is_enemy(u->side()) && !u->invisible(u->get_location(), *resources::gameboard)) { assert(map_.on_board(u->get_location())); LOG_AI << "found enemy leader (side: " << u->side() << ") target... " << u->get_location() << " with value: " << get_leader_value() << "\n"; targets.push_back(target(u->get_location(), get_leader_value(), target::TYPE::LEADER)); } } } //explicit targets for this team for(std::vector<goal_ptr>::iterator j = goals.begin(); j != goals.end(); ++j) { if (!(*j)->active()) { continue; } (*j)->add_targets(std::back_inserter(targets)); } //=== end getting targets std::vector<double> new_values; for(std::vector<target>::iterator i = targets.begin(); i != targets.end(); ++i) { new_values.push_back(i->value); for(std::vector<target>::const_iterator j = targets.begin(); j != targets.end(); ++j) { if(i->loc == j->loc) { continue; } const double distance = std::abs(j->loc.x - i->loc.x) + std::abs(j->loc.y - i->loc.y); new_values.back() += j->value/(distance*distance); } } assert(new_values.size() == targets.size()); for(size_t n = 0; n != new_values.size(); ++n) { LOG_AI << "target value: " << targets[n].value << " -> " << new_values[n] << "\n"; targets[n].value = new_values[n]; } return targets; }
void aspect_attacks::do_attack_analysis( const map_location& loc, const move_map& srcdst, const move_map& dstsrc, const move_map& fullmove_srcdst, const move_map& fullmove_dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc, const map_location* tiles, bool* used_locations, std::vector<map_location>& units, std::vector<attack_analysis>& result, attack_analysis& cur_analysis, const team ¤t_team ) const { // This function is called fairly frequently, so interact with the user here. ai::manager::raise_user_interact(); const int default_attack_depth = 5; if(cur_analysis.movements.size() >= size_t(default_attack_depth)) { //std::cerr << "ANALYSIS " << cur_analysis.movements.size() << " >= " << get_attack_depth() << "\n"; return; } gamemap &map_ = *resources::game_map; unit_map &units_ = *resources::units; std::vector<team> &teams_ = *resources::teams; const size_t max_positions = 1000; if(result.size() > max_positions && !cur_analysis.movements.empty()) { LOG_AI << "cut analysis short with number of positions\n"; return; } for(size_t i = 0; i != units.size(); ++i) { const map_location current_unit = units[i]; unit_map::iterator unit_itor = units_.find(current_unit); assert(unit_itor != units_.end()); // See if the unit has the backstab ability. // Units with backstab will want to try to have a // friendly unit opposite the position they move to. // // See if the unit has the slow ability -- units with slow only attack first. bool backstab = false, slow = false; std::vector<attack_type>& attacks = unit_itor->attacks(); for(std::vector<attack_type>::iterator a = attacks.begin(); a != attacks.end(); ++a) { a->set_specials_context(map_location(), map_location(), units_, true, NULL); if(a->get_special_bool("backstab")) { backstab = true; } if(a->get_special_bool("slow")) { slow = true; } } if(slow && cur_analysis.movements.empty() == false) { continue; } // Check if the friendly unit is surrounded, // A unit is surrounded if it is flanked by enemy units // and at least one other enemy unit is nearby // or if the unit is totaly surrounded by enemies // with max. one tile to escape. bool is_surrounded = false; bool is_flanked = false; int enemy_units_around = 0; int accessible_tiles = 0; map_location adj[6]; get_adjacent_tiles(current_unit, adj); size_t tile; for(tile = 0; tile != 3; ++tile) { const unit_map::const_iterator tmp_unit = units_.find(adj[tile]); bool possible_flanked = false; if(map_.on_board(adj[tile])) { accessible_tiles++; if (tmp_unit != units_.end() && current_team.is_enemy(tmp_unit->side())) { enemy_units_around++; possible_flanked = true; } } const unit_map::const_iterator tmp_opposite_unit = units_.find(adj[tile + 3]); if(map_.on_board(adj[tile + 3])) { accessible_tiles++; if (tmp_opposite_unit != units_.end() && current_team.is_enemy(tmp_opposite_unit->side())) { enemy_units_around++; if(possible_flanked) { is_flanked = true; } } } } if((is_flanked && enemy_units_around > 2) || enemy_units_around >= accessible_tiles - 1) is_surrounded = true; double best_vulnerability = 0.0, best_support = 0.0; int best_rating = 0; int cur_position = -1; // Iterate over positions adjacent to the unit, finding the best rated one. for(int j = 0; j != 6; ++j) { // If in this planned attack, a unit is already in this location. if(used_locations[j]) { continue; } // See if the current unit can reach that position. if (tiles[j] != current_unit) { typedef std::multimap<map_location,map_location>::const_iterator Itor; std::pair<Itor,Itor> its = dstsrc.equal_range(tiles[j]); while(its.first != its.second) { if(its.first->second == current_unit) break; ++its.first; } // If the unit can't move to this location. if(its.first == its.second || units_.find(tiles[j]) != units_.end()) { continue; } } unit_ability_list abil = unit_itor->get_abilities("leadership",tiles[j]); int best_leadership_bonus = abil.highest("value").first; double leadership_bonus = static_cast<double>(best_leadership_bonus+100)/100.0; if (leadership_bonus > 1.1) { LOG_AI << unit_itor->name() << " is getting leadership " << leadership_bonus << "\n"; } // Check to see whether this move would be a backstab. int backstab_bonus = 1; double surround_bonus = 1.0; 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 at this stage, but we don't because, since the // attack calculations don't actually take backstab into account (too complicated), // this could actually make our analysis look *worse* instead of better. // So we only check for 'concrete' backstab opportunities. // That would also break backstab_check, since it assumes // the defender is in place. if(itor != units_.end() && backstab_check(tiles[j], loc, units_, teams_)) { if(backstab) { backstab_bonus = 2; } // No surround bonus if target is skirmisher if (!itor->get_ability_bool("skirmisher")) surround_bonus = 1.2; } } // See if this position is the best rated we've seen so far. int rating = static_cast<int>(rate_terrain(*unit_itor, tiles[j]) * backstab_bonus * leadership_bonus); if(cur_position >= 0 && rating < best_rating) { continue; } // Find out how vulnerable we are to attack from enemy units in this hex. //FIXME: suokko's r29531 multiplied this by a constant 1.5. ? const double vulnerability = power_projection(tiles[j],enemy_dstsrc);//? // Calculate how much support we have on this hex from allies. const double support = power_projection(tiles[j], fullmove_dstsrc);//? // If this is a position with equal defense to another position, // but more vulnerability then we don't want to use it. #ifdef SUOKKO //FIXME: this code was in sukko's r29531 Correct? // scale vulnerability to 60 hp unit if(cur_position >= 0 && rating < best_rating && (vulnerability/surround_bonus*30.0)/unit_itor->second.hitpoints() - (support*surround_bonus*30.0)/unit_itor->second.max_hitpoints() > best_vulnerability - best_support) { continue; } #else if(cur_position >= 0 && rating == best_rating && vulnerability/surround_bonus - support*surround_bonus >= best_vulnerability - best_support) { continue; } #endif cur_position = j; best_rating = rating; #ifdef SUOKKO //FIXME: this code was in sukko's r29531 Correct? best_vulnerability = (vulnerability/surround_bonus*30.0)/unit_itor->second.hitpoints(); best_support = (support*surround_bonus*30.0)/unit_itor->second.max_hitpoints(); #else best_vulnerability = vulnerability/surround_bonus; best_support = support*surround_bonus; #endif } if(cur_position != -1) { units.erase(units.begin() + i); cur_analysis.movements.push_back(std::pair<map_location,map_location>(current_unit,tiles[cur_position])); cur_analysis.vulnerability += best_vulnerability; cur_analysis.support += best_support; cur_analysis.is_surrounded = is_surrounded; cur_analysis.analyze(map_, units_, *this, dstsrc, srcdst, enemy_dstsrc, get_aggression()); result.push_back(cur_analysis); used_locations[cur_position] = true; do_attack_analysis(loc,srcdst,dstsrc,fullmove_srcdst,fullmove_dstsrc,enemy_srcdst,enemy_dstsrc, tiles,used_locations, units,result,cur_analysis, current_team); used_locations[cur_position] = false; cur_analysis.vulnerability -= best_vulnerability; cur_analysis.support -= best_support; cur_analysis.movements.pop_back(); units.insert(units.begin() + i, current_unit); } } }