termaze

maze generation and pathfinding visualizer
git clone git://git.yotsev.xyz/termaze.git
Log | Files | Refs | README | LICENSE

commit 7aeb451d3992f0c5c294101298058a9b27b7598b
parent 7f726056c97d57832700bab60ec2fa5e066fb348
Author: Petar Yotsev <petar@yotsev.xyz>
Date:   Sat, 14 Nov 2020 10:47:26 +0000

Add sidewinder and Eller's algorithm

Diffstat:
MREADME.md | 4++--
Mmaze_gen.cpp | 184++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mmaze_gen.hpp | 2++
Mstate.cpp | 2+-
4 files changed, 182 insertions(+), 10 deletions(-)

diff --git a/README.md b/README.md @@ -18,8 +18,8 @@ the terminal. * [ ] Growing tree * [X] Wilson's random walk algorithm * [X] Binary tree - * [ ] Eller's algorithm - * [ ] Sidewinder + * [X] Eller's algorithm + * [X] Sidewinder * Pathfinding algorithms * [X] A* * [X] Dijkstra's diff --git a/maze_gen.cpp b/maze_gen.cpp @@ -398,7 +398,7 @@ void maze_gen::PrimWeighted(bool* field, const int& width, const int& height) attron(COLOR_PAIR(red)); for (cell* n : current->neighbors) { n->parent = current; - n->weight = std::rand() % range; + n->weight = std::rand(); openSet.push_back(n); mvprintw(n->pos.y, n->pos.x * 2, " "); } @@ -916,17 +916,20 @@ void maze_gen::BinaryTree(bool* field, const int& width, const int& height) std::vector<vec2<int>> neighbors; vec2<int>* selected; timer cycle; + // goes through all positions for (int y = 1; y < height - 1; y += 2) { for (int x = 1; x < width - 1; x += 2) { + // checks if neighbors are within bounds if (x < width - 3) neighbors.push_back(vec2<int>(x + 2, y)); if (y < height - 3) neighbors.push_back(vec2<int>(x, y + 2)); if (neighbors.empty()) continue; + // chooses randomly between bottom and right neighbor and + // carves the wall to it selected = &neighbors[std::rand() % neighbors.size()]; field[(y * width) + x] = true; - field[(selected->y * width) + selected->x] = true; field[((selected->y + y) * width / 2) + (selected->x + x) / 2] = true; s::draw.lock(); mvprintw(y, x * 2, " "); @@ -940,6 +943,171 @@ void maze_gen::BinaryTree(bool* field, const int& width, const int& height) } } +void maze_gen::Eller(bool* field, const int& width, const int& height) +{ + struct cell { + vec2<int> pos; + int s = 0; + }; + + cell grid[width][height]; + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + grid[x][y].pos.x = x; + grid[x][y].pos.y = y; + } + } + + std::vector<cell*> local_set; + int last_set; + int next_set = 1; + timer cycle; + + for (int y = 1; y < height - 1; y += 2) { + // assigns sets to new cells and caries sets from previous row + if (y == 1) { + for (int x = 1; x < width - 1; x += 2) { + grid[x][y].s = next_set; + ++next_set; + } + } else { + for (int x = 1; x < width - 1; x += 2) { + if (field[(y - 1) * width + x]) { + grid[x][y].s = grid[x][y - 2].s; + } else { + grid[x][y].s = next_set; + ++next_set; + } + } + } + // randomly merges sets + for (int x = 1; x < width - 1; x += 2) { + // sets the field to path where it's guarantied to always be + // a path (see documentation) + field[(y * width) + x] = true; + s::draw.lock(); + mvprintw(y, x * 2, " "); + refresh(); + s::draw.unlock(); + // randomly chooses to merge sets + if (grid[x + 2][y].s != grid[x][y].s && std::rand() % 2 == 0 && x < width - 3) { + grid[x + 2][y].s = grid[x][y].s; + field[(y * width) + x + 1] = true; + s::draw.lock(); + mvprintw(y, (x + 1) * 2, " "); + mvprintw(y, (x + 2) * 2, " "); + refresh(); + s::draw.unlock(); + } + cycle.WaitInterval(s::time_hard_maze_gen); + } + // places the down walls + local_set.push_back(&grid[1][y]); + last_set = grid[1][y].s; + if (y < height - 3) { + for (int x = 3; x < width - 1; x += 2) { + if (grid[x][y].s != last_set || x > width - 2) { + // encounters next set, drills one hole & clears set + vec2<int>* pos = &local_set[std::rand() % local_set.size()]->pos; + field[(pos->y + 1) * width + pos->x] = true; + s::draw.lock(); + mvprintw(pos->y + 1, pos->x * 2, " "); + refresh(); + s::draw.unlock(); + local_set.clear(); + local_set.push_back(&grid[x][y]); + last_set = grid[x][y].s; + cycle.WaitInterval(s::time_hard_maze_gen); + } else { + // expands local set + local_set.push_back(&grid[x][y]); + } + } + vec2<int>* pos = &local_set[std::rand() % local_set.size()]->pos; + field[(pos->y + 1) * width + pos->x] = true; + s::draw.lock(); + mvprintw(pos->y + 1, pos->x * 2, " "); + refresh(); + s::draw.unlock(); + local_set.clear(); + } else { + // preform final merge + for (int x = 1; x < width - 1; x += 2) { + if (grid[x + 2][y].s != grid[x][y].s && x < width - 3) { + field[(y * width) + x + 1] = true; + s::draw.lock(); + mvprintw(y, (x + 1) * 2, " "); + mvprintw(y, (x + 2) * 2, " "); + refresh(); + s::draw.unlock(); + } + cycle.WaitInterval(s::time_hard_maze_gen); + } + } + } +} + +void maze_gen::Sidewinder(bool* field, const int& width, const int& height) +{ + std::vector<vec2<int>> run_set; + vec2<int>* selected; + timer cycle; + for (int y = 1; y < height - 1; y += 2) { + run_set.push_back(vec2<int>(1, y)); + for (int x = 1; x < width - 1; x += 2) { + // sets the field to path where it's guarantied to always be + // a path (see documentation) + field[(y * width) + x] = true; + s::draw.lock(); + mvprintw(y, x * 2, " "); + s::draw.unlock(); + // randomly chooses to carve up or right + // safeguards for the top row \/ \/ + if ((std::rand() % 2 == 0 || x == width - 2) && (y > 1)) { + // carve the up wall from random position in the run set + if (run_set.empty()) + run_set.push_back(vec2<int>(x, y)); + selected = &run_set[std::rand() % run_set.size()]; + field[((selected->y - 1) * width) + selected->x] = true; + s::draw.lock(); + mvprintw(selected->y - 1, selected->x * 2, " "); + mvprintw(selected->y - 2, selected->x * 2, " "); + refresh(); + s::draw.unlock(); + // clears the run set + run_set.clear(); + cycle.WaitInterval(s::time_hard_maze_gen); + } else if (x < width - 3) { + // adds this position if previously carved up + if (run_set.empty()) + run_set.push_back(vec2<int>(x, y)); + // adds right neighbor to the run set + run_set.push_back(vec2<int>(x + 2, y)); + // carves the right wall + field[(y * width) + x + 1] = true; + s::draw.lock(); + mvprintw(y, (x + 1) * 2, " "); + mvprintw(y, (x + 2) * 2, " "); + refresh(); + s::draw.unlock(); + cycle.WaitInterval(s::time_hard_maze_gen); + } + } + if (!run_set.empty() && y > 1) { + // carve the up wall from random position in the run set + selected = &run_set[std::rand() % run_set.size()]; + field[((selected->y - 1) * width) + selected->x] = true; + s::draw.lock(); + mvprintw(selected->y - 1, selected->x * 2, " "); + mvprintw(selected->y - 2, selected->x * 2, " "); + refresh(); + s::draw.unlock(); + cycle.WaitInterval(s::time_hard_maze_gen); + } + run_set.clear(); + } +} + std::map<std::string, void (*)( bool* field, @@ -948,11 +1116,13 @@ std::map<std::string, generators = { { "Backtracker", maze_gen::Backtracker }, { "Kruskal", maze_gen::Kruskal }, - { "PrimRandom", maze_gen::PrimRandom }, - { "PrimWeighted", maze_gen::PrimWeighted }, + { "Prim random edges", maze_gen::PrimRandom }, + { "Prim random weights", maze_gen::PrimWeighted }, { "Wilson", maze_gen::Wilson }, - { "RecursiveDivider", maze_gen::RecursiveDivider }, - { "RecursiveDividerSet", maze_gen::RecursiveDividerSet }, - { "BinaryTree", maze_gen::BinaryTree }, + { "Recursive divider", maze_gen::RecursiveDivider }, + { "Recursive divider (set based)", maze_gen::RecursiveDividerSet }, + { "Binary tree", maze_gen::BinaryTree }, + { "Eller", maze_gen::Eller }, + { "Sidewinder", maze_gen::Sidewinder }, }; diff --git a/maze_gen.hpp b/maze_gen.hpp @@ -27,6 +27,8 @@ void Wilson(bool* field, const int& width, const int& height); void RecursiveDividerSet(bool* field, const int& width, const int& height); void RecursiveDivider(bool* field, const int& width, const int& height); void BinaryTree(bool* field, const int& width, const int& height); +void Eller(bool* field, const int& width, const int& height); +void Sidewinder(bool* field, const int& width, const int& height); }; // namespace maze_gen #endif // MAZE_GEN_HPP diff --git a/state.cpp b/state.cpp @@ -16,5 +16,5 @@ bool render_pathfinding = true; // should you render pathfinding proccess? bool ui_is_open = false; // is ui open or waiting to be rendered? bool ui_render_is_safe = false; // is it safe to render ui? std::string pathfinder = "A*"; -std::string generator = "BinaryTree"; +std::string generator = "Prim random weights"; };