mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-10 08:18:39 +00:00
279 lines
8.5 KiB
C++
279 lines
8.5 KiB
C++
/* $Id$ */
|
|
/*
|
|
Copyright (C) 2003 by David White <dave@whitevine.net>
|
|
Copyright (C) 2005 - 2007 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
|
|
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License version 2
|
|
or at your option any later version.
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY.
|
|
|
|
See the COPYING file for more details.
|
|
*/
|
|
|
|
#include "global.hpp"
|
|
|
|
#include "astarnode.hpp"
|
|
#include "log.hpp"
|
|
#include "pathfind.hpp"
|
|
#include "util.hpp"
|
|
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <iostream>
|
|
|
|
#define LOG_PF LOG_STREAM(info, engine)
|
|
#define DBG_PF LOG_STREAM(debug, engine)
|
|
|
|
typedef std::vector<gamemap::location> vector_location;
|
|
typedef std::vector<a_star_node*> vector_a_star_node;
|
|
typedef std::set<gamemap::location> set_location;
|
|
|
|
// heaps give the biggest element for free, so we want the biggest element to
|
|
// have the smallest cost
|
|
static bool compare_lt_a_star_node(const a_star_node* node1, const a_star_node* node2) {
|
|
return node1->g + node1->h > node2->g + node2->h;
|
|
}
|
|
|
|
static void a_star_init(gamemap::location const &src, gamemap::location const &dst,
|
|
vector_a_star_node &openList, a_star_world &aStarGameWorld,
|
|
const size_t parWidth, const size_t parHeight,
|
|
vector_location &vectLocation, std::set<gamemap::location> const *teleports,
|
|
size_t &parNbTeleport)
|
|
{
|
|
bool locIsCreated;
|
|
|
|
aStarGameWorld.resize_IFN(parWidth, parHeight);
|
|
a_star_node *locStartNode = aStarGameWorld.getNodeFromLocation(src, locIsCreated);
|
|
assert(locIsCreated);
|
|
locStartNode->initNode(src, dst, 0.0, NULL, teleports);
|
|
|
|
const size_t locValueH = size_t(locStartNode->h);
|
|
size_t locAllocSize;
|
|
|
|
if (locValueH < 16)
|
|
locAllocSize = 16;
|
|
else if (locValueH > 128)
|
|
locAllocSize = 128;
|
|
else
|
|
locAllocSize = locValueH;
|
|
openList.reserve(locAllocSize);
|
|
openList.push_back(locStartNode);
|
|
|
|
if (teleports != NULL)
|
|
parNbTeleport = teleports->size();
|
|
else
|
|
parNbTeleport = 0;
|
|
|
|
vectLocation.reserve(parNbTeleport + 6);
|
|
vectLocation.resize(parNbTeleport + 6);
|
|
|
|
if (parNbTeleport > 0)
|
|
std::copy(teleports->begin(), teleports->end(), &vectLocation[6]);
|
|
}
|
|
|
|
static void a_star_explore_neighbours(gamemap::location const &dst, const double stop_at,
|
|
cost_calculator const *costCalculator,
|
|
const size_t parWidth, const size_t parHeight,
|
|
std::set<gamemap::location> const *teleports,
|
|
vector_location &vectLocation, vector_a_star_node &openList,
|
|
a_star_world &aStarGameWorld,
|
|
a_star_node *parCurNode, const size_t parNbTeleport)
|
|
{
|
|
typedef std::pair<vector_a_star_node::iterator, vector_a_star_node::iterator> pair_node_iter;
|
|
|
|
a_star_node *locNextNode;
|
|
double locCost;
|
|
pair_node_iter locPlace;
|
|
size_t locSize;
|
|
bool locIsCreated;
|
|
const double locCostFather = parCurNode->g;
|
|
|
|
get_adjacent_tiles(parCurNode->loc, &vectLocation[0]);
|
|
|
|
if (parNbTeleport > 0 && teleports->count(parCurNode->loc) > 0)
|
|
locSize = parNbTeleport + 6;
|
|
else
|
|
locSize = 6;
|
|
|
|
bool broken_heap = false;
|
|
int locNbAdded = 0;
|
|
|
|
for (size_t i = 0; i != locSize; ++i)
|
|
{
|
|
const gamemap::location& locLocation = vectLocation[i];
|
|
|
|
if (locLocation.valid(int(parWidth), int(parHeight)) == false)
|
|
continue;
|
|
locNextNode = aStarGameWorld.getNodeFromLocation(locLocation, locIsCreated);
|
|
locCost = locCostFather + costCalculator->cost(parCurNode->loc,locLocation, locCostFather, locLocation == dst);
|
|
if (locIsCreated) {
|
|
locNextNode->initNode(locLocation, dst, locCost, parCurNode, teleports);
|
|
if (locNextNode->g + locNextNode->h < stop_at) {
|
|
openList.push_back(locNextNode);
|
|
++locNbAdded;
|
|
} else
|
|
locNextNode->isInCloseList = true;
|
|
|
|
} else if (locCost < locNextNode->g) {
|
|
|
|
if (locNextNode->isInCloseList) {
|
|
locNextNode->isInCloseList = false;
|
|
openList.push_back(locNextNode);
|
|
++locNbAdded;
|
|
} else
|
|
broken_heap = true;
|
|
|
|
locNextNode->g = locCost;
|
|
locNextNode->nodeParent = parCurNode;
|
|
}
|
|
}
|
|
|
|
vector_a_star_node::iterator openList_begin = openList.begin(),
|
|
openList_end = openList.end();
|
|
if (broken_heap)
|
|
std::make_heap(openList_begin, openList_end, compare_lt_a_star_node);
|
|
else
|
|
for(; locNbAdded > 0; --locNbAdded)
|
|
std::push_heap(openList_begin, openList_end - (locNbAdded - 1), compare_lt_a_star_node);
|
|
}
|
|
|
|
paths::route a_star_search(gamemap::location const &src, gamemap::location const &dst,
|
|
double stop_at, cost_calculator const *costCalculator, const size_t parWidth,
|
|
const size_t parHeight, std::set<gamemap::location> const *teleports)
|
|
{
|
|
//----------------- PRE_CONDITIONS ------------------
|
|
assert(src.valid(parWidth, parHeight));
|
|
assert(dst.valid(parWidth, parHeight));
|
|
assert(costCalculator != NULL);
|
|
assert(stop_at <= costCalculator->getNoPathValue());
|
|
//---------------------------------------------------
|
|
static a_star_world aStarGameWorld;
|
|
|
|
vector_a_star_node openList;
|
|
vector_location vectLocation;
|
|
paths::route locRoute;
|
|
size_t locNbTeleport;
|
|
a_star_node *locDestNode = NULL;
|
|
a_star_node *locCurNode = NULL;
|
|
|
|
DBG_PF << "A* search: " << src << " -> " << dst << '\n';
|
|
|
|
if (costCalculator->cost(src,dst, 0, true) >= stop_at) {
|
|
LOG_PF << "aborted A* search because Start or Dest is invalid\n";
|
|
locRoute.move_left = int(costCalculator->getNoPathValue());
|
|
return locRoute;
|
|
}
|
|
|
|
a_star_init(src, dst, openList, aStarGameWorld, parWidth, parHeight, vectLocation, teleports, locNbTeleport);
|
|
|
|
bool routeSolved = false;
|
|
while (!routeSolved && !openList.empty())
|
|
{
|
|
locCurNode = openList.front();
|
|
assert(locCurNode != NULL);
|
|
|
|
//if we have found a solution
|
|
if (locCurNode->loc == dst)
|
|
{
|
|
routeSolved = true;
|
|
} else {
|
|
std::pop_heap(openList.begin(), openList.end(), compare_lt_a_star_node);
|
|
openList.pop_back();
|
|
|
|
assert(locCurNode->isInCloseList == false);
|
|
locCurNode->isInCloseList = true;
|
|
|
|
a_star_explore_neighbours(dst, stop_at, costCalculator, parWidth, parHeight,
|
|
teleports, vectLocation, openList, aStarGameWorld, locCurNode, locNbTeleport);
|
|
}
|
|
}
|
|
if(routeSolved) {
|
|
locDestNode = locCurNode;
|
|
|
|
DBG_PF << "found solution; calculating it...\n";
|
|
while (locCurNode != NULL)
|
|
{
|
|
locRoute.steps.push_back(locCurNode->loc);
|
|
locCurNode = locCurNode->nodeParent;
|
|
}
|
|
std::reverse(locRoute.steps.begin(), locRoute.steps.end());
|
|
locRoute.move_left = int(locDestNode->g);
|
|
|
|
assert(locRoute.steps.front() == src);
|
|
assert(locRoute.steps.back() == dst);
|
|
|
|
DBG_PF << "exiting a* search (solved)\n";
|
|
} else {
|
|
//route not solved
|
|
LOG_PF << "aborted a* search\n";
|
|
locRoute.move_left = int(costCalculator->getNoPathValue());
|
|
}
|
|
openList.clear();
|
|
aStarGameWorld.clear();
|
|
return locRoute;
|
|
}
|
|
|
|
static void get_tiles_radius_internal(const gamemap::location& a, size_t radius,
|
|
std::set<gamemap::location>& res, std::map<gamemap::location,int>& visited)
|
|
{
|
|
visited[a] = radius;
|
|
res.insert(a);
|
|
|
|
if(radius == 0) {
|
|
return;
|
|
}
|
|
|
|
gamemap::location adj[6];
|
|
get_adjacent_tiles(a,adj);
|
|
for(size_t i = 0; i != 6; ++i) {
|
|
if(visited.count(adj[i]) == 0 || visited[adj[i]] < int(radius)-1) {
|
|
get_tiles_radius_internal(adj[i],radius-1,res,visited);
|
|
}
|
|
}
|
|
}
|
|
|
|
void get_tiles_radius(const gamemap::location& a, size_t radius,
|
|
std::set<gamemap::location>& res)
|
|
{
|
|
std::map<gamemap::location,int> visited;
|
|
get_tiles_radius_internal(a,radius,res,visited);
|
|
}
|
|
|
|
void get_tiles_radius(gamemap const &map, std::vector<gamemap::location> const &locs,
|
|
size_t radius, std::set<gamemap::location> &res, xy_pred *pred)
|
|
{
|
|
typedef std::set<gamemap::location> location_set;
|
|
location_set not_visited(locs.begin(), locs.end()), must_visit, filtered_out;
|
|
++radius;
|
|
|
|
for(;;) {
|
|
location_set::const_iterator it = not_visited.begin(), it_end = not_visited.end();
|
|
std::copy(it,it_end,std::inserter(res,res.end()));
|
|
for(; it != it_end; ++it) {
|
|
gamemap::location adj[6];
|
|
get_adjacent_tiles(*it, adj);
|
|
for(size_t i = 0; i != 6; ++i) {
|
|
gamemap::location const &loc = adj[i];
|
|
if(map.on_board(loc) && !res.count(loc) && !filtered_out.count(loc)) {
|
|
if(!pred || (*pred)(loc)) {
|
|
must_visit.insert(loc);
|
|
} else {
|
|
filtered_out.insert(loc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(--radius == 0 || must_visit.empty()) {
|
|
break;
|
|
}
|
|
|
|
not_visited.swap(must_visit);
|
|
must_visit.clear();
|
|
}
|
|
}
|
|
|