📄 pathfinder.cpp
字号:
/*************************************************************************
"I Have No Tomatoes"
Copyright (c) 2004, Mika Halttunen
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute
it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
Mika Halttunen <lsoft@mbnet.fi>
*************************************************************************/
#include <stdlib.h>
#include "SDL.h"
#include "SDL_opengl.h"
#include "pathfinder.h"
#include "tilemap.h"
// Helper function which returns a pointer to the last node on a list
inline NODE *last_node(list <NODE> &l) {
return &(*(--l.end()));
}
// Helper function which returns the tile distance between two points on the map
int tile_dist(int px1, int py1, int px2, int py2) {
int dx = abs(px1 - px2);
int dy = abs(py1 - py2);
return ((dx + dy) * 10);
}
// Find the path between (sx,sy) and (dx,dy). Returns PATH_FAILED or PATH_OK.
int PATHFINDER::find_path(int sx_, int sy_, int dx_, int dy_) {
// Check that the target tile is reachable
if(map_solid(dx_, dy_))
return PATH_FAILED;
// Helper array which tells us if a tile is on the open nodes list, or
// on the closed nodes list.
// 0 == It isn't on the lists
// 1 == It is on the closed list
// 2 == It is on the open list
int map_nodes[MAP_W][MAP_H];
// Initialize the pathfinder
dx = dx_;
dy = dy_;
sx = sx_;
sy = sy_;
nodes_open.clear();
nodes_closed.clear();
path.clear();
for(int fyy = 0; fyy < MAP_H; fyy++)
for(int fxx = 0; fxx < MAP_W; fxx++)
map_nodes[fxx][fyy] = 0;
// Add the starting point to the open nodes
NODE st;
st.clear();
st.x = sx;
st.y = sy;
nodes_open.push_back(st);
map_nodes[sx][sy] = 2;
// Current node
NODE current;
current.clear();
// State of the search
int search_state = -1;
// Search the path
while(search_state == -1) {
// Sort the open list by F value
nodes_open.sort();
// Current tile shall be the one with the lowest F (the first after sorting)
current = *(nodes_open.begin());
// Move it to the closed list
nodes_open.pop_front();
nodes_closed.push_back(current);
map_nodes[current.x][current.y] = 1;
// Look for adjacent reachable tiles
for(int f=0; f < 4; f++) {
int mx,my;
switch(f) {
default:
case 0: mx = current.x; my = current.y - 1; break;
case 1: mx = current.x + 1; my = current.y; break;
case 2: mx = current.x; my = current.y + 1; break;
case 3: mx = current.x - 1; my = current.y; break;
}
// Check the tile
if(map_solid(mx, my))
continue; // Solid tile, skip it
// Check if the tile is on the closed nodes list
if(map_nodes[mx][my] == 1)
continue;
/* bool is_closed = false;
list<NODE>::iterator i;
for(i = nodes_open.begin(); i != nodes_open.end(); ++i) {
if((*i).x == mx && (*i).y == my) {
is_closed = true;
break;
}
}
if(is_closed)
continue; // It was on the closed nodes list, skip it
*/
// Check if the tile isn't on the open nodes list
if(map_nodes[mx][my] == 0) {
// Add a new node and make the current tile it's parent
NODE n;
n.clear();
n.x = mx;
n.y = my;
n.g = current.g + 10; // No diagonal movement
n.h = tile_dist(mx, my, dx, dy); // Manhattan distance
n.f = n.g + n.h; // F = G + H
n.parent = last_node(nodes_closed);
nodes_open.push_back(n);
map_nodes[mx][my] = 2;
// If we just added our target tile (dx, dy), we've found the path.
// Bail out.
if(mx == dx && my == dy) {
search_state = PATH_OK;
break;
}
}
}
// If the list of open nodes is empty, we can't find the path
if(nodes_open.empty()) {
search_state = PATH_FAILED;
break;
}
}
// At this point, the path is either found or failed
if(search_state == PATH_OK) {
// Path was found
// The destination node is the last node added to the open list
NODE *dest = last_node(nodes_open);
// Make sure it is so
if(dest->x != dx || dest->y != dy)
printf("find_path() error:\nThe path destination is (%d,%d) while it should be (%d,%d)!\n", dest->x, dest->y, dx, dy);
// Add the destination to the path
PATHPOINT point;
point.x = dx;
point.y = dy;
path.insert(path.begin(), point);
// Traverse from the destination along the path, and save the path points
NODE *p = dest->parent;
while(p) {
PATHPOINT point;
point.x = p->x;
point.y = p->y;
path.insert(path.begin(), point);
p = p->parent;
}
// Remove the start point from the path as it isn't needed.
path.erase(path.begin());
// Clear the nodes list as we don't need them any more
nodes_open.clear();
nodes_closed.clear();
return PATH_OK;
}
else {
// Path was not found
return PATH_FAILED;
}
}
// Operators for STL list
NODE &NODE::operator=(const NODE &other) {
this->x = other.x;
this->y = other.y;
this->f = other.f;
this->g = other.g;
this->h = other.h;
this->parent = other.parent;
return *this;
}
NODE *NODE::operator=(const NODE *other) {
this->x = other->x;
this->y = other->y;
this->f = other->f;
this->g = other->g;
this->h = other->h;
this->parent = other->parent;
return this;
}
int NODE::operator==(const NODE &other) const {
if(this->x != other.x || this->y != other.y) return 0;
if(this->f != other.f || this->g != other.g || this->h != other.h) return 0;
if(this->parent != other.parent) return 0;
return 1;
}
// For sorting
int NODE::operator<(const NODE &other) const {
if(this->f < other.f) return 1;
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -