Example #1
0
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;
}
Example #2
0
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.;
}
Example #3
0
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 &current_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);
        }
    }
}