Ejemplo n.º 1
0
static void find_routes(const gamemap& map, const unit_map& units,
		const unit& u, const map_location& loc,
		int move_left, paths::dest_vect &destinations,
		std::vector<team> const &teams,
		bool force_ignore_zocs, bool allow_teleport, int turns_left,
		const team &viewing_team,
		bool see_all, bool ignore_units)
{
	const team& current_team = teams[u.side() - 1];
	std::set<map_location> teleports;
	if (allow_teleport) {
		teleports = get_teleport_locations(u, units, viewing_team, see_all, ignore_units);
	}

	const int total_movement = u.total_movement();

	std::vector<map_location> locs(6 + teleports.size());
	std::copy(teleports.begin(), teleports.end(), locs.begin() + 6);

	search_counter += 2;
	if (search_counter == 0) search_counter = 2;

	static std::vector<node> nodes;
	nodes.resize(map.w() * map.h());

	indexer index(map.w(), map.h());
	comp node_comp(nodes);

	int xmin = loc.x, xmax = loc.x, ymin = loc.y, ymax = loc.y, nb_dest = 1;

	nodes[index(loc)] = node(move_left, turns_left, map_location::null_location, loc);
	std::vector<int> pq;
	pq.push_back(index(loc));

	while (!pq.empty()) {
		node& n = nodes[pq.front()];
		std::pop_heap(pq.begin(), pq.end(), node_comp);
		pq.pop_back();
		n.in = search_counter;

		get_adjacent_tiles(n.curr, &locs[0]);
		for (int i = teleports.count(n.curr) ? locs.size() : 6; i-- > 0; ) {
			if (!locs[i].valid(map.w(), map.h())) continue;

			node& next = nodes[index(locs[i])];

			bool next_visited = next.in - search_counter <= 1u;

			// Classic Dijkstra allow to skip chosen nodes (with next.in==search_counter)
			// But the cost function and hex grid allow to also skip visited nodes:
			// if next was visited, then we already have a path 'src-..-n2-next'
			// - n2 was chosen before n, meaning that it is nearer to src.
			// - the cost of 'n-next' can't be smaller than 'n2-next' because
			//   cost is independent of direction and we don't have more MP at n
			//   (important because more MP may allow to avoid waiting next turn)
			// Thus, 'src-..-n-next' can't be shorter.
			if (next_visited) continue;

			const int move_cost = u.movement_cost(map[locs[i]]);

			node t = node(n.movement_left, n.turns_left, n.curr, locs[i]);
			if (t.movement_left < move_cost) {
				t.movement_left = total_movement;
				t.turns_left--;
			}

			if (t.movement_left < move_cost || t.turns_left < 0) continue;

			t.movement_left -= move_cost;

			if (!ignore_units) {
				const unit *v =
					get_visible_unit(units, locs[i], viewing_team, see_all);
				if (v && current_team.is_enemy(v->side()))
					continue;

				if (!force_ignore_zocs && t.movement_left > 0
						&& enemy_zoc(units, teams, locs[i], viewing_team, u.side(), see_all)
						&& !u.get_ability_bool("skirmisher", locs[i])) {
					t.movement_left = 0;
				}
			}

			++nb_dest;
			int x = locs[i].x;
			if (x < xmin) xmin = x;
			if (xmax < x) xmax = x;
			int y = locs[i].y;
			if (y < ymin) ymin = y;
			if (ymax < y) ymax = y;

			bool in_list = next.in == search_counter + 1;
			t.in = search_counter + 1;
			next = t;

			// if already in the priority queue then we just update it, else push it.
			if (in_list) { // never happen see next_visited above
				std::push_heap(pq.begin(), std::find(pq.begin(), pq.end(), index(locs[i])) + 1, node_comp);
			} else {
				pq.push_back(index(locs[i]));
				std::push_heap(pq.begin(), pq.end(), node_comp);
			}
		}
	}

	// Build the routes for every map_location that we reached.
	// The ordering must be compatible with map_location::operator<.
	destinations.reserve(nb_dest);
	for (int x = xmin; x <= xmax; ++x) {
		for (int y = ymin; y <= ymax; ++y)
		{
			const node &n = nodes[index(map_location(x, y))];
			if (n.in - search_counter > 1u) continue;
			paths::step s =
				{ n.curr, n.prev, n.movement_left + n.turns_left * total_movement };
			destinations.push_back(s);
		}
	}
}
Ejemplo n.º 2
0
/**
 * Creates a list of routes that a unit can traverse from the provided location.
 * (This is called when creating pathfind::paths and descendant classes.)
 *
 * @param[in]  origin        The location at which to begin the routes.
 * @param[in]  costs         The costs to use for route finding.
 * @param[in]  slowed        Whether or not to use the slowed costs.
 * @param[in]  moves_left    The number of movement points left for the current turn.
 * @param[in]  max_moves     The number of movement points in each future turn.
 * @param[in]  turns_left    The number of future turns of movement to calculate.
 * @param[out] destinations  The traversable routes.
 * @param[out] edges         The hexes (possibly off-map) adjacent to those in
 *                           destinations. (It is permissible for this to contain
 *                           some hexes that are also in destinations.)
 *
 * @param[in]  teleporter    If not NULL, teleportaion will be considered, using
 *                           this unit's abilities.
 * @param[in]  current_team  If not NULL, enemies of this team can obstruct routes
 *                           both by occupying hexes and by exerting zones of control.
 *                           In addition, the presence of units can affect
 *                           teleportation options.
 * @param[in]  skirmisher    If not NULL, use this to determine where ZoC can and
 *                           cannot be ignored (due to this unit having or not
 *                           having the skirmisher ability).
 *                           If NULL, then ignore all zones of control.
 *                           (No effect if current_team is NULL).
 * @param[in]  viewing_team  If not NULL, use this team's vision when detecting
 *                           enemy units and teleport destinations.
 *                           If NULL, then "see all".
 *                           (No effect if teleporter and current_team are both NULL.)
 * @param[in]  jamming_map   The relevant "jamming" of the costs being used
 *                           (currently only used with vision costs).
 */
static void find_routes(
		const map_location & origin, const movetype::terrain_costs & costs,
		bool slowed, int moves_left, int max_moves, int turns_left,
		paths::dest_vect & destinations, std::set<map_location> * edges,
		const unit * teleporter, const team * current_team,
		const unit * skirmisher, const team * viewing_team,
		const std::map<map_location, int> * jamming_map=NULL)
{
	const gamemap& map = *resources::game_map;

	const bool see_all =  viewing_team == NULL;
	// When see_all is true, the viewing team never matters, but we still
	// need to supply one to some functions.
	if ( viewing_team == NULL )
		viewing_team = &resources::teams->front();

	// Build a teleport map, if needed.
	const teleport_map teleports = teleporter ?
			get_teleport_locations(*teleporter, *viewing_team, see_all, current_team == NULL) :
			teleport_map();

	// Since this is called so often, keep memory reserved for the node list.
	static std::vector<findroute_node> nodes;
	static unsigned search_counter = 0;
	// Incrementing search_counter means we ignore results from earlier searches.
	++search_counter;
	// Whenever the counter cycles, trash the contents of nodes and restart at 1.
	if ( search_counter == 0 ) {
		nodes.resize(0);
		search_counter = 1;
	}
	// Initialize the nodes for this search.
	nodes.resize(map.w() * map.h());
	findroute_comp node_comp(nodes);
	findroute_indexer index(map.w(), map.h());

	// Used to optimize the final collection of routes.
	int xmin = origin.x, xmax = origin.x, ymin = origin.y, ymax = origin.y;
	int nb_dest = 1;

	// Record the starting location.
	assert(index(origin) >= 0);
	nodes[index(origin)] = findroute_node(moves_left, turns_left,
	                                      map_location::null_location,
	                                      search_counter);
	// Begin the search at the starting location.
	std::vector<int> hexes_to_process(1, index(origin));  // Will be maintained as a heap.

	while ( !hexes_to_process.empty() ) {
		// Process the hex closest to the origin.
		const int cur_index = hexes_to_process.front();
		const map_location cur_hex = index(cur_index);
		const findroute_node& current = nodes[cur_index];
		// Remove from the heap.
		std::pop_heap(hexes_to_process.begin(), hexes_to_process.end(), node_comp);
		hexes_to_process.pop_back();

		// Get the locations adjacent to current.
		std::vector<map_location> adj_locs(6);
		get_adjacent_tiles(cur_hex, &adj_locs[0]);
		if ( teleporter ) {
			std::set<map_location> allowed_teleports;
			teleports.get_adjacents(allowed_teleports, cur_hex);
			adj_locs.insert(adj_locs.end(), allowed_teleports.begin(), allowed_teleports.end());
		}
		for ( int i = adj_locs.size()-1; i >= 0; --i ) {
			// Get the node associated with this location.
			const map_location & next_hex = adj_locs[i];
			const int next_index = index(next_hex);
			if ( next_index < 0 ) {
				// Off the map.
				if ( edges != NULL )
					edges->insert(next_hex);
				continue;
			}
			findroute_node & next = nodes[next_index];

			// Skip nodes we have already collected.
			// (Since no previously checked routes were longer than
			// the current one, the current route cannot be shorter.)
			// (Significant difference from classic Dijkstra: we have
			// vertex weights, not edge weights.)
			if ( next.search_num == search_counter )
				continue;

			// If we go to next, it will be from current.
			next.prev = cur_hex;

			// Calculate the cost of entering next_hex.
			int cost = costs.cost(map[next_hex], slowed);
			if ( jamming_map ) {
				const std::map<map_location, int>::const_iterator jam_it =
					jamming_map->find(next_hex);
				if ( jam_it != jamming_map->end() )
					cost += jam_it->second;
			}

			// Calculate movement remaining after entering next_hex.
			next.moves_left = current.moves_left - cost;
			next.turns_left = current.turns_left;
			if ( next.moves_left < 0 ) {
				// Have to delay until the next turn.
				next.turns_left--;
				next.moves_left = max_moves - cost;
			}
			if ( next.moves_left < 0 || next.turns_left < 0 ) {
				// Either can never enter this hex or out of turns.
				if ( edges != NULL )
					edges->insert(next_hex);
				continue;
			}

			if ( current_team ) {
				// Account for enemy units.
				const unit *v = get_visible_unit(next_hex, *viewing_team, see_all);
				if ( v && current_team->is_enemy(v->side()) ) {
					// Cannot enter enemy hexes.
					if ( edges != NULL )
						edges->insert(next_hex);
					continue;
				}

				if ( skirmisher  &&  next.moves_left > 0  &&
				     enemy_zoc(*current_team, next_hex, *viewing_team, see_all)  &&
				     !skirmisher->get_ability_bool("skirmisher", next_hex) ) {
					next.moves_left = 0;
				}
			}

			// Mark next as being collected.
			next.search_num = search_counter;

			// Add this node to the heap.
			hexes_to_process.push_back(next_index);
			std::push_heap(hexes_to_process.begin(), hexes_to_process.end(), node_comp);

			// Bookkeeping (for later).
			++nb_dest;
			if ( next_hex.x < xmin )
				xmin = next_hex.x;
			else if ( xmax < next_hex.x )
				xmax = next_hex.x;
			if ( next_hex.y < ymin )
				ymin = next_hex.y;
			else if ( ymax < next_hex.y )
				ymax = next_hex.y;
		}//for (i)
	}//while (hexes_to_process)

	// Build the routes for every map_location that we reached.
	// The ordering must be compatible with map_location::operator<.
	destinations.reserve(nb_dest);
	for (int x = xmin; x <= xmax; ++x) {
		for (int y = ymin; y <= ymax; ++y)
		{
			const findroute_node &n = nodes[index(x,y)];
			if ( n.search_num == search_counter ) {
				paths::step s =
					{ map_location(x,y), n.prev, n.moves_left + n.turns_left*max_moves };
				destinations.push_back(s);
			}
		}
	}
}