Exemplo n.º 1
0
void run(const P& p0,
         const bool* blocked,
         int* out,
         const P& map_dims,
         int travel_lmt,
         const P& p1,
         const bool allow_diagonal)
{
    std::fill_n(out, map_dims.x * map_dims.y, 0);

    // List of positions to travel to
    std::vector<P> positions;

    // In the worst case we need to visit every position, reserve the elements
    positions.reserve(map_dims.x * map_dims.y);

    // Instead of removing evaluated positions from the vector, we track which
    // index to try next (cheaper than erasing front elements).
    size_t next_p_idx = 0;

    int     val                 = 0;
    bool    path_exists         = true;
    bool    is_at_tgt           = false;
    bool    is_stopping_at_tgt  = p1.x != -1;

    const R bounds(P(1, 1), map_dims - 2);

    P p(p0);

    const auto& dirs = allow_diagonal ?
                       dir_utils::dir_list :
                       dir_utils::cardinal_list;

    bool done = false;

    while (!done)
    {
        // "Flood" around the current position, and add those to the list of
        // positions to travel to.
        for (const P& d : dirs)
        {
            const P new_p(p + d);

            if (
                !blocked[idx2(new_p, map_dims.y)]   &&
                bounds.is_p_inside(new_p)           &&
                out[idx2(new_p, map_dims.y)] == 0   &&
                new_p != p0)
            {
                val = out[idx2(p, map_dims.y)];

                if (travel_lmt == -1 || val < travel_lmt)
                {
                    out[idx2(new_p, map_dims.y)] = val + 1;
                }

                if (is_stopping_at_tgt && new_p == p1)
                {
                    is_at_tgt = true;
                    break;
                }

                if (!is_stopping_at_tgt || !is_at_tgt)
                {
                    positions.push_back(new_p);
                }
            }
        } // Offset loop

        if (is_stopping_at_tgt)
        {
            if (positions.size() == next_p_idx)
            {
                path_exists = false;
            }

            if (is_at_tgt || !path_exists)
            {
                done = true;
            }
        }
        else if (positions.size() == next_p_idx)
        {
            done = true;
        }

        if (val == travel_lmt)
        {
            done = true;
        }

        if (!is_stopping_at_tgt || !is_at_tgt)
        {
            if (positions.size() == next_p_idx)
            {
                // No more positions to evaluate
                path_exists = false;
            }
            else // There are more positions to evaluate
            {
                p = positions[next_p_idx];

                ++next_p_idx;
            }
        }
    } // while
}
Exemplo n.º 2
0
void run(const P& p0,
         const P& p1,
         const bool* blocked,
         int* flood_buffer,
         const P& map_dims,
         std::vector<P>& out,
         const bool allow_diagonal,
         const bool randomize_steps)
{
    out.clear();

    if (p0 == p1)
    {
        // Origin and target is same cell
        return;
    }

    floodfill::run(p0,
                   blocked,
                   flood_buffer,
                   map_dims,
                   -1,
                   p1,
                   allow_diagonal);

    if (flood_buffer[idx2(p1, map_dims.y)] == 0)
    {
        // No path exists
        return;
    }

    const std::vector<P>& dirs = allow_diagonal ?
                                 dir_utils::dir_list :
                                 dir_utils::cardinal_list;

    const size_t nr_dirs = dirs.size();

    // Corresponds to the elements in "dirs"
    std::vector<bool> valid_offsets(nr_dirs, false);

    // The path length will be equal to the flood value at the target cell, so
    // we can reserve that many elements beforehand.
    out.reserve(flood_buffer[idx2(p1, map_dims.y)]);

    P p(p1);
    out.push_back(p);

    const R map_r(P(0, 0), map_dims - 1);

    while (true)
    {
        const int val = flood_buffer[idx2(p, map_dims.y)];

        P adj_p;

        // Find valid offsets, and check if origin is reached
        for (size_t i = 0; i < nr_dirs; ++i)
        {
            const P& d(dirs[i]);

            adj_p = p + d;

            if (adj_p == p0)
            {
                // Origin reached
                return;
            }

            // TODO: What is the purpose of this check? If the current value is
            // zero, doesn't that mean this cell is the target? Only the target
            // cell and unreachable cells should have values of zero(?)
            // Try removing the check and verify if the algorithm still works
            if (val != 0)
            {
                const bool is_inside_map = map_r.is_p_inside(adj_p);

                const int adj_val = is_inside_map ?
                                    flood_buffer[idx2(adj_p, map_dims.y)] : 0;

                // Mark this as a valid travel direction if it's fewer steps
                // from the target than the current cell
                valid_offsets[i] = adj_val < val;
            }
        }

        // Set the next position to one of the valid offsets - either pick one
        // randomly, or iterate over the list and pick the first valid choice.
        if (randomize_steps)
        {
            std::vector<P> adj_p_bucket;

            for (size_t i = 0; i < nr_dirs; ++i)
            {
                if (valid_offsets[i])
                {
                    adj_p_bucket.push_back(p + dirs[i]);
                }
            }

            ASSERT(!adj_p_bucket.empty());

            adj_p = rnd::element(adj_p_bucket);
        }
        else // Do not randomize step choices - iterate over offset list
        {
            for (size_t i = 0; i < nr_dirs; ++i)
            {
                if (valid_offsets[i])
                {
                    adj_p = P(p + dirs[i]);
                    break;
                }
            }
        }

        out.push_back(adj_p);

        p = adj_p;

    } //while
}
Exemplo n.º 3
0
void run(const P& p0,
         const P& p1,
         const bool blocked[map_w][map_h],
         std::vector<P>& out,
         const bool allow_diagonal,
         const bool randomize_steps)
{
    out.clear();

    if (p0 == p1)
    {
        // Origin and target is same cell
        return;
    }

    int flood_buffer[map_w][map_h];

    floodfill::run(p0,
                   blocked,
                   flood_buffer,
                   -1,
                   p1,
                   allow_diagonal);

    if (flood_buffer[p1.x][p1.y] == 0)
    {
        // No path exists
        return;
    }

    const std::vector<P>& dirs = allow_diagonal ?
                                 dir_utils::dir_list :
                                 dir_utils::cardinal_list;

    const size_t nr_dirs = dirs.size();

    // Corresponds to the elements in "dirs"
    std::vector<bool> valid_offsets(nr_dirs, false);

    // The path length will be equal to the flood value at the target cell, so
    // we can reserve that many elements beforehand.
    out.reserve(flood_buffer[p1.x][p1.y]);

    // We start at the target cell
    P p(p1);
    out.push_back(p);

    const R map_r(P(0, 0), P(map_w, map_h) - 1);

    while (true)
    {
        const int current_val = flood_buffer[p.x][p.y];

        P adj_p;

        // Find valid offsets, and check if origin is reached
        for (size_t i = 0; i < nr_dirs; ++i)
        {
            const P& d(dirs[i]);

            adj_p = p + d;

            if (adj_p == p0)
            {
                // Origin reached
                return;
            }

            if (map_r.is_p_inside(adj_p))
            {
                const int adj_val = flood_buffer[adj_p.x][adj_p.y];

                // Mark this as a valid travel direction if it is not blocked,
                // and is fewer steps from the target than the current cell.
                valid_offsets[i] =
                    (adj_val != 0) &&
                    (adj_val < current_val);
            }
        }

        // Set the next position to one of the valid offsets - either pick one
        // randomly, or iterate over the list and pick the first valid choice.
        if (randomize_steps)
        {
            std::vector<P> adj_p_bucket;

            for (size_t i = 0; i < nr_dirs; ++i)
            {
                if (valid_offsets[i])
                {
                    adj_p_bucket.push_back(p + dirs[i]);
                }
            }

            ASSERT(!adj_p_bucket.empty());

            adj_p = rnd::element(adj_p_bucket);
        }
        else // Do not randomize step choices - iterate over offset list
        {
            for (size_t i = 0; i < nr_dirs; ++i)
            {
                if (valid_offsets[i])
                {
                    adj_p = P(p + dirs[i]);
                    break;
                }
            }
        }

        out.push_back(adj_p);

        p = adj_p;

    } // while
}