поиск пути (в сетке) с библиотекой Boost Graph

Я переписываю своего бота для Google AI Challenge с Python на C++ и хочу использовать графическую библиотеку boost для обработки поиска пути, а не просто сворачивать свой собственный граф и поисковый код, как я делал раньше в Python.

Карта представляет собой простую квадратную сетку, огибающую края.

Я раньше не использовал boost или C++ (я достаточно хорошо знаю C), и мне очень трудно понять документацию по графику повышения, поэтому мне нужна небольшая помощь.

Конкретные документы, с которыми у меня возникли проблемы:

Вот фрагмент рабочего кода Python:

def do_turn(self, ants):
    # update path-finding graph
    for row in range(ants.rows):
        for col in range(ants.cols):
            key = (row, col)
            self.graph[key] = []

            def append_if_unoccupied(coord):
                if ants.passable(coord) and coord not in ants.my_hills():
                    self.graph[key].append(coord)

            if row - 1 >= 0:
                append_if_unoccupied((row - 1, col))
            else:
                append_if_unoccupied((ants.rows + (row - 1), col))

            if col - 1 >= 0:
                append_if_unoccupied((row, col - 1))
            else:
                append_if_unoccupied((row, ants.cols + (col - 1)))

            if row + 1 < ants.rows:
                append_if_unoccupied((row + 1, col))
            else:
                append_if_unoccupied((row + 1 - ants.rows, col))

            if col + 1 < ants.cols:
                append_if_unoccupied((row, col + 1))
            else:
                append_if_unoccupied((row, col + 1 - ants.cols))

Затем я использую shortest_path(self.graph, loc, dest) позже (поиск * с эвристикой Манхэттена), чтобы получить список, содержащий кратчайший путь куда-то еще. В С++ я хотел бы что-то подобное (вектор с кратчайшим путем). Вот код, который у меня есть до сих пор (мне просто нужна помощь, чтобы заставить его работать на базовой сетке без каких-либо препятствий, я могу сделать все остальное):

#include <vector>

#include <boost/config.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
//#include <boost/graph/astar_search.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>

// struct with .row and .col
#include "Location.h"

// might not be necessary
struct Edge {};

typedef boost::adjacency_list<
    boost::listS,       // container for edges
    boost::vecS,        // container for vertices
    boost::undirectedS, // directed or undirected edges
    Location,           // vertex type
    Edge                // edge type
> Graph;

typedef Graph::vertex_descriptor VertexID;
typedef Graph::edge_descriptor   EdgeID;

const int rows = 100;
const int cols = 100;

int main() {
    Graph graph;

    // this is probably useless since the graph stores everything
    // haven't looked for a way around it yet
    std::vector<std::vector<VertexID>> grid;

    // create the grid/graph
    for (int row = 0; row < rows; row++) {
        grid.resize(rows);
        for (int col = 0; col < cols; col++) {
            grid[row].resize(cols);

            VertexID vID = boost::add_vertex(graph);
            graph[vID].row = row;
            graph[vID].col = col;

            grid[row][col] = vID;
        }
    }

    // add the edges
    for (int row = 0; row < rows; row++) {
        for (int col = 0; col < cols; col++) {
            // add edges for the squares in the grid (it wraps around)
            // right now all the squares are traversable but that won't always
            // be the case
            int c_row, c_col;

            if (row - 1 >= 0) {
                c_row = row - 1;
                c_col = col;
            } else {
                c_row = rows + (row - 1);
                c_col = col;
            }
            boost::add_edge(grid[c_row][c_col], grid[row][col], graph);

            if (col - 1 >= 0) {
                c_row = row;
                c_col = col - 1;
            } else {
                c_row = row;
                c_col = cols + (col - 1);
            }
            boost::add_edge(grid[c_row][c_col], grid[row][col], graph);

            if (row + 1 < rows) {
                 c_row = row + 1;
                 c_col = col;
            } else {
                c_row = row + 1 - rows;
                c_col = col;
            }
            boost::add_edge(grid[c_row][c_col], grid[row][col], graph);

            if (col + 1 < cols) {
                c_row = row;
                c_col = col + 1;
            } else {
                c_row = row;
                c_col = col + 1 - cols;
            }
            boost::add_edge(grid[c_row][c_col], grid[row][col], graph);
        }
        // having trouble figuring out use these
        //boost::astar_search(graph, grid[c]
        //auto indexmap = get(vertex_index, graph);
        //dijkstra_shortest_paths(graph, grid[0][0], &p[0], &d[0], weightmap, indexmap, 
                          //std::less<int>(), closed_plus<int>(), 
                          //(std::numeric_limits<int>::max)(), 0,
                          //default_dijkstra_visitor());
    }
    return 0;
}

person Community    schedule 23.10.2011    source источник


Ответы (2)


Должно помочь:

    boost::astar_search
    (
        m_navmesh, start,
        distance_heuristic(m_navmesh, goal),
        boost::predecessor_map(&p[0])
        .distance_map(&d[0])
        .visitor(astar_goal_visitor(goal))
        .weight_map(get(&NavMeshConnection::dist, m_navmesh))
    );

Эта функция использует эвристику расстояния, которая представляет собой функтор, который вы должны создать сами:

// euclidean distance heuristic
class distance_heuristic : public boost::astar_heuristic <NavMeshGraph, float> {
public:
    distance_heuristic(const NavMeshGraph & l, TriangleDescriptor goal)
    : m_graph(l), m_goal(goal)
    {}

    float operator()(TriangleDescriptor u) {
        const NavMeshTriangle & U = m_graph[u];
        const NavMeshTriangle & V = m_graph[m_goal];
        float dx = U.pos.x - V.pos.x;
        float dy = U.pos.y - V.pos.y;
        return ::sqrt(dx * dx + dy * dy);
    }

private:
    const NavMeshGraph & m_graph;
    TriangleDescriptor m_goal;
};

Вам также нужен посетитель:

// visitor that terminates when we find the goal
class astar_goal_visitor : public boost::default_astar_visitor {
public:
    astar_goal_visitor(TriangleDescriptor goal) : m_goal(goal)
    {}

    void examine_vertex(TriangleDescriptor u, const NavMeshGraph & g)
    {
        if (u == m_goal)
            throw found_goal();
    }

private:
    TriangleDescriptor m_goal;
};

found_gloal может быть простой структурой или чем-то более сложным, в зависимости от того, что вы хотите вернуть:

struct found_goal {};

Такой объект передается посетителю, поэтому вам нужно обернуть boost::astar_search() в блок try/catch:

try {

    boost::astar_search
    (
      ...
     );


} catch (found_goal fg) { // found a path to the goal

Затем лучший способ извлекается в блоке catch:

    std::list<TriangleDescriptor> shortest_path;
    for (TriangleDescriptor v = goal;; v = p[v]) {
        shortest_path.push_front(v);
        if (p[v] == v)
            break;
    }

Вам потребуется серьезная адаптация, но, по крайней мере, этого должно быть достаточно, чтобы убрать Boost с дороги.

Кстати, Джикстра не совсем похож. Он возвращает все возможные пути от любого другого узла. Это плохо для скорости, но отлично подходит для предварительной обработки: если ваш мир статичен, вы можете предварительно построить матрицу поиска пути, которая вернет лучший следующий узел за O (1).

person Calvin1602    schedule 24.10.2011

Вам не нужно переключаться на C++, чтобы использовать BGL; есть действительно хорошая оболочка для python в виде graph_tool. Конечно, включает кратчайший путь.

person timday    schedule 23.10.2011