星型算法由于指向父级的指针而无法工作,有助于修复它

时间:2015-06-23 23:28:03

标签: c++ algorithm a-star

我最近一直在研究a *算法的实现,但我遇到了问题。

在这里会发生什么:

std::list<Node> openList;
std::list<Node> closedList;

startNode.calc(map, endPos);

openList.push_back(startNode);

while (!openList.empty())
{
    auto min = std::min_element(openList.begin(), openList.end());
    auto current = Node(*min);

    current.calc(map, endPos);

    closedList.push_back(current);
    openList.remove(current);

我正在使current等于openList中的minimal元素。

稍后在代码中

        if (inOpen == openList.end())
        {
            successor.calc(map, endPos);
            successor.parent = &current;

            openList.push_back(successor);
        }

我将当前的地址设置为继任者的父母。

发生的事情是,在下一次迭代中,似乎电流在同一地址中定义,后继者的父级将等于新电流。

我应该使用什么而不是Node *,因为这似乎是让我的代码无效的原因。

完整算法代码:

bool Solver::aStar()
{

    if ((map.getDangerElement(startPos) == 'X') || map.getDangerElement(endPos) == 'X')
        return false;

    Node startNode(startPos);
    Node goalNode(Vector2(endPos.x - 1, endPos.y - 1));

    std::list<Node> openList;
    std::list<Node> closedList;

    startNode.calc(map, endPos);

    openList.push_back(startNode);

    while (!openList.empty())
    {
        auto current = Node(*std::min_element(openList.begin(), openList.end()));

        current.calc(map, endPos);

        closedList.push_back(current);
        openList.remove(current);

        for (auto& direction : directions)
        {
            Node successor(direction.position + current.position);

            if (map.getDangerElement(successor.position) == 'X' ||
                successor.position.x >= map.getSize() - 1 || successor.position.y >= map.getSize() - 1 ||
                successor.position.x < 0 || successor.position.y < 0 || 
                std::find(closedList.begin(), closedList.end(), successor) != closedList.end())
            {
                continue;
            }

            successor.calc(map, endPos);

            auto inOpen = std::find(openList.begin(), openList.end(), successor);

            if (inOpen == openList.end())
            {
                auto curr = &current;
                successor.parent = curr;
                successor.calc(map, endPos);

                openList.push_back(successor);
            }
            else
            {
                if (successor.G < inOpen->G)
                {
                    auto* curr = &current;
                    successor.parent = curr;
                }
            }
        }

#ifdef DEBUG
        for (auto a : closedList)
        {
            map.setElement(a.position, 'Y');
        }
#endif

        if (current == goalNode) break;
    }


    if (openList.size() == 0)
    {
        std::cout << "There's no solution to this map";
        return false;
    }

    map.display();


    return true;
}

完整代码:

Vector2.h

#pragma once

struct Vector2
{
    int x, y;
    Vector2(int, int);
    Vector2();
    Vector2 operator +(const Vector2&);
};

Vector2.cpp

#include "Vector2.h"

Vector2::Vector2(int _x, int _y) : x(_x), y(_y)
{ }

Vector2::Vector2()
{ }

Vector2 Vector2::operator+(const Vector2& other)
{
    Vector2 temp;
    temp.x = this->x + other.x;
    temp.y = this->y + other.y;
    return temp;
}

map.h

#pragma once

#include "vector2.h"

#include <vector>
#include <iostream>
#include <random>
#include <algorithm>

class Map
{
    Vector2 startPos, endPos;
    std::vector<char> data;
    std::vector<char> datad;
    int size;

    void setDangerElement(Vector2 position, char);
    void fillDangerMap();

    Vector2 clamp(int min, int max, Vector2 position) const;

public:
    Map(int size, Vector2 startPosition, Vector2 endPosition);
    Map();

    void setSize(int size);
    void fill(char, char, char, char, char);
    void display();

    char getElement(Vector2 position) const;
    char getDangerElement(Vector2 position) const;
    int  getSize() const;

    void setElement(Vector2 position, char element);
};

std::mt19937& getRandomEngine();

map.cpp

#include "map.h"

Map::Map(int _size, Vector2 _startPos, Vector2 _endPos) : size(_size), startPos(_startPos), endPos(_endPos)
{
    data.resize(size * size);
    datad.resize(size * size);
}

Map::Map()
{ }

Vector2 Map::clamp(int min, int max, Vector2 position) const
{
    if (position.y < 0) position.y = 0;
    if (position.x < 0) position.x = 0;
    if (position.y > size) position.y = size;
    if (position.x > size) position.x = size;

    return position;
}

void Map::fill(char fillStartWith, char fillEndWith, char fillGravWheelWith, char fillAsteroidWith, char fillElseWith)
{
    auto a = (size * size) * 0.1 / 3;
    auto b = (size * size) * 0.3 / 3;

    for(int i = 0; i < size * size; ++i){
        if(i < a)                       data[i] = fillGravWheelWith;
        else if(i < b)                  data[i] = fillAsteroidWith;
        else                            data[i] = fillElseWith;
    }
    std::shuffle(data.begin(), data.end(), getRandomEngine());
    setElement(startPos, fillStartWith);
    setElement(endPos, fillEndWith);
    fillDangerMap();
}

void Map::display()
{
    for(int i = 1; i <= size * size; ++i)
    {
        std::cout << data[i - 1] << " ";
        if (!(i % size))
            std::cout << "\n";
    }
}

void Map::setSize(int _size)
{
    size = _size;
    data.resize(size * size);
}

char Map::getElement(Vector2 position) const
{
    position = clamp(0, size, position);

    position.y *= size;
    return data[position.x + position.y];
}

char Map::getDangerElement(Vector2 position) const
{
    position = clamp(0, size, position);

    position.y *= size;
    return datad[position.x + position.y];
}

void Map::fillDangerMap()
{
    for (int i = 0; i < size * size; ++i) datad[i] = '.';

    for(int y = 0; y < size; ++y){
        for(int x = 0; x < size; ++x){
            Vector2 current(x,y);
            if      (getElement(current) == 'E') setDangerElement(current, 'E');
            else if (getElement(current) == 'S') setDangerElement(current, 'S');
            else if (getElement(current) == 'A') setDangerElement(current, 'X');
            if (getElement(current) == 'G' && x == 0){
                setDangerElement(current, 'X');
                setDangerElement(Vector2(x, y + 1), 'X');
                setDangerElement(Vector2(x, y - 1), 'X');
                setDangerElement(Vector2(x + 1, y + 1), 'X');
                setDangerElement(Vector2(x + 1, y), 'X');
                setDangerElement(Vector2(x + 1, y - 1), 'X');
            }
            else if (getElement(current) == 'G' && y == 0){
                setDangerElement(current, 'X');
                setDangerElement(Vector2(x, y + 1), 'X');
                setDangerElement(Vector2(x - 1, y), 'X');
                setDangerElement(Vector2(x - 1, y + 1), 'X');
                setDangerElement(Vector2(x + 1, y + 1), 'X');
                setDangerElement(Vector2(x + 1, y), 'X');
            }
            else if (getElement(current) == 'G' && x == size - 1){
                setDangerElement(Vector2(x, y), 'X');
                setDangerElement(Vector2(x, y + 1), 'X');
                setDangerElement(Vector2(x, y - 1), 'X');
                setDangerElement(Vector2(x - 1, y), 'X');
                setDangerElement(Vector2(x - 1, y - 1), 'X');
                setDangerElement(Vector2(x - 1, y + 1), 'X');
            }
            else if (getElement(current) == 'G' && y == size - 1){
                setDangerElement(current, 'X');
                setDangerElement(Vector2(x, y - 1), 'X');
                setDangerElement(Vector2(x - 1, y), 'X');
                setDangerElement(Vector2(x - 1, y - 1), 'X');
                setDangerElement(Vector2(x + 1, y), 'X');
                setDangerElement(Vector2(x + 1, y - 1), 'X');
            }
            else if (getElement(current) == 'G'){
                setDangerElement(current, 'X');
                setDangerElement(Vector2(x, y + 1), 'X');
                setDangerElement(Vector2(x, y - 1), 'X');
                setDangerElement(Vector2(x - 1, y), 'X');
                setDangerElement(Vector2(x - 1, y - 1), 'X');
                setDangerElement(Vector2(x - 1, y + 1), 'X');
                setDangerElement(Vector2(x + 1, y + 1), 'X');
                setDangerElement(Vector2(x + 1, y), 'X');
                setDangerElement(Vector2(x + 1, y - 1), 'X');
            }
        }
    }
}

void Map::setElement(Vector2 position, char elem)
{
    position = clamp(0, size, position);
    position.y *= size;
    data[position.x + position. y] = elem;
}

void Map::setDangerElement(Vector2 position, char elem)
{
    position = clamp(0, size, position);

    position.y *= size;
    datad[position.x + position.y] = elem;
}

int Map::getSize() const
{
    return size;
}

std::mt19937& getRandomEngine()
{
    static std::mt19937 randomEngine(std::random_device{}());
    return randomEngine;
}

node.h

#pragma once

#include "map.h"

#include <list>
#include <cmath>

struct Node
{
    Vector2 position;
    int G, H, F;
    Node* parent = nullptr;

    Node();
    Node(const Node& other) = default;
    Node(Vector2 pos);

    void calc(const Map&, const Vector2&);

    bool operator==(const Node&);
    bool operator<(const Node&);
};

node.cpp

#include "node.h"

Node::Node()
{ }

Node::Node(Vector2 pos) : position(pos)
{ }

void Node::calc(const Map& map, const Vector2& endPos)
{
    H = static_cast<int>((abs(static_cast<double>(position.x - endPos.x)) + abs(static_cast<double>(position.y - endPos.y))));
    G = parent ? parent->G + 1 : 1;
    F = G + H;
}

bool Node::operator==(const Node& other)
{
    return (position.x == other.position.x && position.y == other.position.y);
}

bool Node::operator<(const Node& other)
{
    return(F < other.F);
}

solver.h

#pragma once

#include "node.h"

class Solver
{
    Vector2 startPos, endPos;
    Map map;

    std::vector<Node> directions;

public:
    Solver(const int&, const Vector2&, const Vector2&);
    void displayMap();

    bool aStar();
};

solver.cpp

#include "solver.h"

#define DEBUG

Solver::Solver(const int& size, const Vector2& _startPos, const Vector2& _endPos) : startPos(_startPos), endPos(_endPos)
{
    Map temp(size, startPos, endPos);
    map = temp;

    map.fill('S', 'E', 'G', 'A', '.');
    map.display();

    directions.resize(8);

    directions[0] = Node(Vector2 (-1 ,1));
    directions[1] = Node(Vector2(-1, 0));
    directions[2] = Node(Vector2(-1, -1));
    directions[3] = Node(Vector2(0, 1));
    directions[4] = Node(Vector2(0, -1));
    directions[5] = Node(Vector2(1, 1));
    directions[6] = Node(Vector2(1, 0));
    directions[7] = Node(Vector2(1, -1));
}

void Solver::displayMap()
{
    map.display();
}

bool Solver::aStar()
{

    if ((map.getDangerElement(startPos) == 'X') || map.getDangerElement(endPos) == 'X')
        return false;

    Node startNode(startPos);
    Node goalNode(Vector2(endPos.x - 1, endPos.y - 1));

    std::list<Node> openList;
    std::list<Node> closedList;

    startNode.calc(map, endPos);

    openList.push_back(startNode);

    while (!openList.empty())
    {
        auto current = Node(*std::min_element(openList.begin(), openList.end()));

        current.calc(map, endPos);

        closedList.push_back(current);
        openList.remove(current);

        for (auto& direction : directions)
        {
            Node successor(direction.position + current.position);

            if (map.getDangerElement(successor.position) == 'X' ||
                successor.position.x >= map.getSize() - 1 || successor.position.y >= map.getSize() - 1 ||
                successor.position.x < 0 || successor.position.y < 0 || 
                std::find(closedList.begin(), closedList.end(), successor) != closedList.end())
            {
                continue;
            }

            successor.calc(map, endPos);

            auto inOpen = std::find(openList.begin(), openList.end(), successor);

            if (inOpen == openList.end())
            {
                auto curr = &current;
                successor.parent = curr;
                successor.calc(map, endPos);

                openList.push_back(successor);
            }
            else
            {
                if (successor.G < inOpen->G)
                {
                    auto* curr = &current;
                    successor.parent = curr;
                }
            }
        }

#ifdef DEBUG
        for (auto a : closedList)
        {
            map.setElement(a.position, 'Y');
        }
#endif

        if (current == goalNode) break;
    }


    if (openList.size() == 0)
    {
        std::cout << "There's no solution to this map";
        return false;
    }

    map.display();


    return true;
}

source.cpp

#include "solver.h"

int main()
{
    const int SIZE = 20;
    const Vector2 startPos(0, 0);
    const Vector2 endPos(SIZE - 1, SIZE - 1);

    Solver solve(SIZE, startPos, endPos);
    if (solve.aStar()){
        std::cout << "\nMap has been successfully solved.\nMap:\n";
        solve.displayMap();
    }
    else std::cout << "\nThere is no path from the start position to the end position in this map.\n";

    std::cin.get();
    return 0;
}

谢谢!

1 个答案:

答案 0 :(得分:1)

如评论所示,您将局部变量的地址存储在容器中。当本地超出范围时,您的代码将显示未定义的行为。

此外,您看到存储相同地址的原因是您正在创建current的新本地实例,并且偶然地,新本地实例具有与旧地址相同的地址(现在已销毁) current的实例。

但是,看一下有问题的功能,我发现您将current存储在std::list中。你可以通过一个非常小的改变来实现你所寻求的目标。

由于您在solver函数中执行此操作:

auto current = Node(*std::min_element(openList.begin(), openList.end()));
current.calc(map, endPos);
closedList.push_back(current);  // < -- This could be your lifeline

最后一行创建一个当前副本,然后将其放在std::list的背面。为什么这很有希望呢?这很有希望因为,

  1. 您确切知道current副本的位置(位于后面) 一个std::list)和
  2. 指向std::list条目的指针保证有效 范围等,即使std::list更改大小,条件仍然在列表中。
  3. 因此,对代码的修复应该非常简单。在你正在执行的功能中进一步向下:

            if (inOpen == openList.end())
            {
                auto curr = &current;
                successor.parent = curr;
                //...
    

    相反,这样做:

            if (inOpen == openList.end())
            {
                auto curr = &closedList.back();  
                successor.parent = curr;
                //...
    

    我们现在返回对推到列表中的最后一项的引用,并获取此项的地址。

    现在,需要注意的是closedList最好不要超出范围,并且当您持有该项目的地址时,您添加到列表中的项目不会被删除。

    这是否能解决您的所有问题,我不确定。但这是你应该尝试的。

    最重要的是,如果您使用std::list来存储对象,只要

    ,就可以获得指向列表中这些对象的指针。
    1. std::list在范围内
    2. 您的指针项目未从std::list
    3. 中移除