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 }
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 }
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 }