/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
//K-D Lab / Balmer
#pragma once
#include "common/multimap.h"
///////////////////////////AIAStar/////////////////////
//AIAStar::FindPath поиск пути из точки from
//в точку IsEndPoint.
//Боле-менее оптимизированный на случай квадратной сетки
/*
class Heuristic
{
float getH(int x,int y);//Предполагаемые затраты на продвижение из pos1 к окончанию
float getG(int x1,int y1,int x2,int y2);//Затраты на продвижение из pos1 в pos2
bool IsEndPoint(int x,int y);//Рекурсия должна окончиться здесь
//то есть класс AIAStar позволяет задавать несколько точек окончания поиска пути
};
*/
namespace QDEngine {
template
class AIAStar {
public:
typedef Common::MultiMap type_point_map;
struct OnePoint {
TypeH g;//Затраты на продвижение до этой точки
TypeH h;//Предполагаемые затраты на продвижение до финиша
int used;
OnePoint *parent;
bool is_open;
inline TypeH f() {
return g + h;
}
};
protected:
int _dx, _dy;
OnePoint *_chart;
type_point_map _open_map;
int _is_used_num;//Если _is_used_num==used, то ячейка используется
int _num_point_examine;//количество посещённых ячеек
int _num_find_erase;//Сколько суммарно искали ячейки для удаления
Heuristic *_heuristic;
public:
AIAStar();
~AIAStar();
void init(int dx, int dy);
bool findPath(Vect2i from, Heuristic *h, Std::vector &path, int directions_count = 8);
void getStatistic(int *num_point_examine, int *num_find_erase);
//Debug
OnePoint *getInternalBuffer() {
return _chart;
};
int getUsedNum() {
return _is_used_num;
}
protected:
void clear();
inline Vect2i posBy(OnePoint *p) {
int offset = p - _chart;
Vect2i pos;
pos.x = offset % _dx;
pos.y = offset / _dx;
return pos;
}
};
template
AIAStar::AIAStar() {
_chart = NULL;
_heuristic = NULL;
_num_find_erase = 0;
_dx = _dy = 0;
_is_used_num = 0;
_num_point_examine = 0;
}
template
void AIAStar::init(int dx_, int dy_) {
_dx = dx_;
_dy = dy_;
int size = _dx * _dy;
_chart = new OnePoint[size];
clear();
}
template
void AIAStar::clear() {
int size = _dx * _dy;
_is_used_num = 0;
for (int i = 0; i < size; i++)
_chart[i].used = 0;
}
template
AIAStar::~AIAStar() {
delete[] _chart;
}
template
bool AIAStar::findPath(Vect2i from, Heuristic *hr, Std::vector &path, int directions_count) {
_num_point_examine = 0;
_num_find_erase = 0;
_is_used_num++;
_open_map.clear();
path.clear();
if (_is_used_num == 0)
clear();//Для того, чтобы вызвалась эта строчка, необходимо гиганское время
assert(from.x >= 0 && from.x < _dx && from.y >= 0 && from.y < _dy);
_heuristic = hr;
OnePoint *p = _chart + from.y * _dx + from.x;
p->g = 0;
p->h = _heuristic->getH(from.x, from.y);
p->used = _is_used_num;
p->is_open = true;
p->parent = NULL;
_open_map.insert(typename type_point_map::value_type(p->f(), from));
const int sx[8] = { 0, -1, 0, +1, -1, +1, +1, -1,};
const int sy[8] = {-1, 0, +1, 0, -1, -1, +1, +1 };
// const int sx[size_child]={ 0,-1, 0,+1};
// const int sy[size_child]={-1, 0,+1, 0};
const int size_child = directions_count;
while (!_open_map.empty()) {
typename type_point_map::iterator low = _open_map.begin();
Vect2i pt = (*low).second;
OnePoint *parent = _chart + pt.y * _dx + pt.x;
parent->is_open = false;
_open_map.erase(low);
if (_heuristic->isEndPoint(pt.x, pt.y)) {
//сконструировать путь
Vect2i vp;
while (parent) {
vp = posBy(parent);;
path.push_back(vp);
if (parent->parent) {
Vect2i pp;
pp = posBy(parent->parent);
assert(abs(vp.x - pp.x) <= 1 &&
abs(vp.y - pp.y) <= 1);
}
parent = parent->parent;
}
assert(vp.x == from.x && vp.y == from.y);
Common::reverse(path.begin(), path.end());
return true;
}
//для каждого наследника child узла parent
for (int i = 0; i < size_child; i++) {
Vect2i child = Vect2i(pt.x + sx[i], pt.y + sy[i]);
_num_point_examine++;
if (child.x < 0 || child.y < 0 ||
child.x >= _dx || child.y >= _dy)continue;
p = _chart + child.y * _dx + child.x;
TypeH addg = _heuristic->getG(pt.x, pt.y, child.x, child.y);
TypeH newg = parent->g + addg;
if (p->used == _is_used_num) {
if (!p->is_open)continue;
if (p->g <= newg)continue;
//Удаляем элемент из _open_map
TypeH f = p->f();
typename type_point_map::iterator cur = _open_map.find(p->f());
bool erase = false;
while (cur != _open_map.end()) {
if ((*cur).first != f)break;
if ((*cur).second.x == child.x && (*cur).second.y == child.y) {
_open_map.erase(cur);
erase = true;
break;
}
_num_find_erase++;
cur++;
}
_num_find_erase++;
//assert(erase);
if (!erase)
continue;
}
p->parent = parent;
/*
{
Vect2i pp=posBy(parent);
Vect2i pc=posBy(p);
assert(abs(pc.x-pp.x)<=1 &&
abs(pc.y-pp.y)<=1);
}
*/
p->g = newg;
p->h = _heuristic->getH(child.x, child.y);
_open_map.insert(typename type_point_map::value_type(p->f(), child));
p->is_open = true;
p->used = _is_used_num;
}
}
return false;
}
template
void AIAStar::getStatistic(
int *p_num_point_examine, int *p_num_find_erase) {
if (p_num_point_examine)
*p_num_point_examine = _num_point_examine;
if (p_num_find_erase)
*p_num_find_erase = _num_find_erase;
}
///////////////////////AIAStarGraph/////////////
//AIAStarGraph - Так-же поиск пути, но ориентированный на поиск в произвольном графе
/*
class Node
{
typedef ... iterator;
iterator begin();//Работа со списком связанных с этой Node нод.
iterator end();
void* AIAStarPointer;//Используется в AIAStarGraph
};
class Heuristic
{
float getH(Node* pos);//Предполагаемые затраты на продвижение из pos1 к окончанию
float getG(Node* pos1,Node* pos2);//Затраты на продвижение из pos1 в pos2
bool IsEndPoint(Node* pos);//Рекурсия должна окончиться здесь
//то есть класс AIAStar позволяет задавать несколько точек окончания поиска пути
};
*/
template
class AIAStarGraph {
public:
struct OnePoint;
typedef Common::MultiMap type_point_map;
struct OnePoint {
TypeH g;//Затраты на продвижение до этой точки
TypeH h;//Предполагаемые затраты на продвижение до финиша
int used;
OnePoint *parent;
bool is_open;
Node *node;
typename type_point_map::iterator self_it;
inline TypeH f() {
return g + h;
}
};
protected:
Std::vector _chart;
type_point_map _open_map;
int _is_used_num;//Если _is_used_num==used, то ячейка используется
int _num_point_examine;//количество посещённых ячеек
int _num_find_erase;//Сколько суммарно искали ячейки для удаления
Heuristic *_heuristic;
public:
AIAStarGraph();
~AIAStarGraph();
//Общее количество узлов. Константа, которая не должна меняться,
//пока существует класс, указывающий на неё.
void init(Std::vector &all_node);
bool findPath(Node *from, Heuristic *h, Std::vector &path);
void getStatistic(int *num_point_examine, int *num_find_erase);
//Debug
OnePoint *getInternalBuffer() {
return _chart;
};
int getUsedNum() {
return _is_used_num;
}
protected:
void clear();
inline Node *posBy(OnePoint *p) {
return p->node;
}
};
template
AIAStarGraph::AIAStarGraph() {
_heuristic = NULL;
_is_used_num = 0;
_num_point_examine = 0;
_num_find_erase = 0;
}
template
void AIAStarGraph::init(Std::vector &all_node) {
int size = all_node.size();
_chart.resize(size);
for (int i = 0; i < size; i++) {
OnePoint *c = &_chart[i];
c->node = &all_node[i];
c->node->AIAStarPointer = (void *)c;
}
clear();
}
template
void AIAStarGraph::clear() {
_is_used_num = 0;
for (auto &it : _chart) {
it.used = 0;
}
}
template
AIAStarGraph::~AIAStarGraph() {
}
template
bool AIAStarGraph::findPath(Node *from, Heuristic *hr, Std::vector &path) {
_num_point_examine = 0;
_num_find_erase = 0;
_is_used_num++;
_open_map.clear();
path.clear();
if (_is_used_num == 0)
clear();//Для того, чтобы вызвалась эта строчка, необходимо гиганское время
_heuristic = hr;
OnePoint *p = (OnePoint *)from->AIAStarPointer;
Node *from_node = p->node;
p->g = 0;
p->h = _heuristic->getH(p->node);
p->used = _is_used_num;
p->is_open = true;
p->parent = NULL;
p->self_it = _open_map.insert(type_point_map::value_type(p->f(), p));
while (!_open_map.empty()) {
typename type_point_map::iterator low = _open_map.begin();
OnePoint *parent = low->second;
Node *node = parent->node;
parent->is_open = false;
_open_map.erase(low);
if (_heuristic->IsEndPoint(node)) {
//сконструировать путь
Node *np;
while (parent) {
np = PosBy(parent);
assert(parent->used == _is_used_num);
path.push_back(np);
parent = parent->parent;
}
assert(np == from_node);
reverse(path.begin(), path.end());
return true;
}
//для каждого наследника child узла parent
for (auto &it : *node) {
Node *cur_node = *it;
OnePoint *op = (OnePoint *)cur_node->AIAStarPointer;
_num_point_examine++;
TypeH addg = _heuristic->getG(node, cur_node);
TypeH newg = parent->g + addg;
if (op->used == _is_used_num) {
if (!op->is_open)continue;
if (op->g <= newg)continue;
_open_map.erase(op->self_it);
_num_find_erase++;
}
op->parent = parent;
op->g = newg;
op->h = _heuristic->getH(cur_node);
op->self_it = _open_map.insert(type_point_map::value_type(op->f(), op));
op->is_open = true;
op->used = _is_used_num;
}
}
return false;
}
template
void AIAStarGraph::getStatistic(
int *p_num_point_examine, int *p_num_find_erase) {
if (p_num_point_examine)
*p_num_point_examine = _num_point_examine;
if (p_num_find_erase)
*p_num_find_erase = _num_find_erase;
}
///////////////////////AIFindMaxium/////////////
/*
struct Maps
{
//Значение чего нибудь в точке (x,y)
TypeH get(int x,int y);
//Дальнейшие поиски можно прекратить
bool IsOptiumGood(TypeH optium,int x,int y);
};
*/
//Ищет минимальное значение, но не по всей карте
template
Vect2i AIFindMinium(int x, int y,
Maps &maps,
int dx, int dy) {
typename Maps::TypeH optium = maps.get(x, y);
int optiumx = x, optiumy = y;
int maxi = MAX(MAX(x, dx - x), MAX(y, dy - y));
for (int i = 1; i < maxi; i++) {
int curx, cury;
int xmin = MAX(0, x - i), xmax = MIN(dx - 1, x + i);
int ymin = MAX(0, y - i), ymax = MIN(dy - 1, y + i);
//up
cury = y - i;
if (cury >= 0)
for (curx = xmin; curx <= xmax; curx++) {
typename Maps::TypeH o = maps.get(curx, cury);
if (o < optium) {
optium = o;
optiumx = curx;
optiumy = cury;
}
}
//down
cury = y + i;
if (cury < dy)
for (curx = xmin; curx <= xmax; curx++) {
typename Maps::TypeH o = maps.get(curx, cury);
if (o < optium) {
optium = o;
optiumx = curx;
optiumy = cury;
}
}
//left
curx = x - i;
if (curx >= 0)
for (cury = ymin; cury <= ymax; cury++) {
typename Maps::TypeH o = maps.get(curx, cury);
if (o < optium) {
optium = o;
optiumx = curx;
optiumy = cury;
}
}
//right
curx = x + i;
if (curx < dx)
for (cury = ymin; cury <= ymax; cury++) {
typename Maps::TypeH o = maps.get(curx, cury);
if (o < optium) {
optium = o;
optiumx = curx;
optiumy = cury;
}
}
if (maps.IsOptiumGood(optium, optiumx, optiumy))
break;
}
Vect2i p = {optiumx, optiumy};
return p;
}
} // namespace QDEngine