Advertisement
Beafantles

compile_time_maze_jason_turner.cpp

Mar 2nd, 2018
126
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 10.99 KB | None | 0 0
  1. #include <cstdint>
  2. #include <cstddef>
  3. #include <limits>
  4. #include <utility>
  5. #include <iostream>
  6. #include <string>
  7.  
  8. constexpr auto seed()
  9. {
  10.   std::uint64_t shifted = 0;
  11.  
  12.   for( const auto c : __TIME__ )
  13.   {
  14.     shifted <<= 8;
  15.     shifted |= c;
  16.   }
  17.  
  18.   return shifted;
  19. }
  20.  
  21. struct PCG
  22. {
  23.   struct pcg32_random_t { std::uint64_t state=0;  std::uint64_t inc=seed(); };
  24.   pcg32_random_t rng;
  25.   typedef std::uint32_t result_type;
  26.  
  27.   constexpr result_type operator()()
  28.   {
  29.     return pcg32_random_r();
  30.   }
  31.  
  32.   static result_type constexpr min()
  33.   {
  34.     return std::numeric_limits<result_type>::min();
  35.   }
  36.  
  37.   static result_type constexpr max()
  38.   {
  39.     return std::numeric_limits<result_type>::min();
  40.   }
  41.  
  42.   private:
  43.   constexpr std::uint32_t pcg32_random_r()
  44.   {
  45.     std::uint64_t oldstate = rng.state;
  46.     // Advance internal state
  47.     rng.state = oldstate * 6364136223846793005ULL + (rng.inc|1);
  48.     // Calculate output function (XSH RR), uses old state for max ILP
  49.     std::uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
  50.     std::uint32_t rot = oldstate >> 59u;
  51.     return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
  52.   }
  53.  
  54. };
  55.  
  56. template<typename T, typename Gen>
  57. constexpr auto distribution(Gen &g, T min, T max)
  58. {
  59.   const auto range = max - min + 1;
  60.   const auto bias_remainder = std::numeric_limits<T>::max() % range;
  61.   const auto unbiased_max = std::numeric_limits<T>::max() - bias_remainder - 1;
  62.  
  63.   auto r = g();
  64.   for (; r > unbiased_max; r = g());
  65.  
  66.   return (r % range) + min;
  67. }
  68.  
  69.  
  70.  
  71.  
  72.  
  73. struct Cell
  74. {
  75.   bool left_open = false;
  76.   bool right_open = false;
  77.   bool up_open = false;
  78.   bool down_open = false;
  79.   bool visited = false;
  80. };
  81.  
  82. template<typename Data, std::size_t Cols, std::size_t Rows>
  83. struct Array2D
  84. {
  85.   constexpr static auto rows() { return Rows; }
  86.   constexpr static auto cols() { return Cols; }
  87.  
  88.   constexpr Data &operator()(const std::size_t col, const std::size_t row)
  89.   {
  90.     return m_data[col + (row * Cols)];
  91.   }
  92.  
  93.   constexpr const Data &operator()(const std::size_t col, const std::size_t row) const
  94.   {
  95.     return m_data[col + (row * Cols)];
  96.   }
  97.  
  98.   Data m_data[Cols * Rows];
  99. };
  100.  
  101. enum class WallType
  102. {
  103.   Empty,
  104.   UpperLeft,
  105.   Vertical,
  106.   Horizontal,
  107.   UpperRight,
  108.   LowerLeft,
  109.   LowerRight,
  110.   RightTee,
  111.   LeftTee,
  112.   UpTee,
  113.   DownTee,
  114.   FourWay,
  115.   Up,
  116.   Down,
  117.   Left,
  118.   Right,
  119.   Visited,
  120.   Used
  121. };
  122.  
  123. template<typename T, std::size_t Size>
  124. struct Stack
  125. {
  126.   T m_data[Size]{};
  127.   std::size_t pos = 0;
  128.  
  129.   template<typename ... Arg>
  130.     void constexpr emplace_back(Arg &&... arg)
  131.     {
  132.       m_data[pos] = T{std::forward<Arg>(arg)...};
  133.       ++pos;
  134.     }
  135.  
  136.   constexpr T pop_back()
  137.   {
  138.     --pos;
  139.     return m_data[pos];
  140.   }
  141.  
  142.   constexpr bool empty() const
  143.   {
  144.     return pos == 0;
  145.   }
  146.  
  147.   constexpr std::size_t size() const
  148.   {
  149.     return pos;
  150.   }
  151. };
  152.  
  153. struct Loc
  154. {
  155.   std::size_t col=0;
  156.   std::size_t row=0;
  157.  
  158.   constexpr Loc(const std::size_t t_col, const std::size_t t_row)
  159.     : col(t_col), row(t_row)
  160.   {
  161.   }
  162.  
  163.   constexpr Loc() = default;
  164. };
  165.  
  166. template<std::size_t num_cols, std::size_t num_rows>
  167. constexpr Array2D<Cell, num_cols, num_rows> make_maze()
  168. {
  169.  
  170.   PCG pcg;
  171.  
  172.   Array2D<Cell, num_cols, num_rows> M;
  173.  
  174.  
  175.  
  176.   // Set starting row and column
  177.   std::size_t c = 0;
  178.   std::size_t r = 0;
  179.   Stack<Loc, num_cols * num_rows> history;
  180.   history.emplace_back(c,r); // The history is the stack of visited locations
  181.  
  182.   // Trace a path though the cells of the maze and open walls along the path.
  183.   // We do this with a while loop, repeating the loop until there is no history,
  184.   // which would mean we backtracked to the initial start.
  185.   while (!history.empty())
  186.   {
  187.     M(c, r).visited = true;
  188.  
  189.     Stack<char, 4> check{};
  190.  
  191.     // check if the adjacent cells are valid for moving to
  192.     if (c > 0 && M(c-1, r).visited == false) {
  193.       check.emplace_back('L');
  194.     }
  195.     if (r > 0 && M(c, r-1).visited == false) {
  196.       check.emplace_back('U');
  197.     }
  198.     if (c < num_cols-1 && M(c+1, r).visited == false) {
  199.       check.emplace_back('R');
  200.     }
  201.     if (r < num_rows-1 && M(c, r+1).visited == false) {
  202.       check.emplace_back('D');
  203.     }
  204.  
  205.     if (!check.empty()) { // If there is a valid cell to move to.
  206.       // Mark the walls between cells as open if we move
  207.  
  208.       history.emplace_back(c,r);
  209.  
  210.       for (auto num_pops = distribution(pcg, std::size_t(0), check.size() - 1); num_pops > 0; --num_pops)
  211.       {
  212.         check.pop_back();
  213.       }
  214.  
  215.       switch (check.pop_back()) {
  216.         case 'L':
  217.           M(c, r).left_open = true;
  218.           --c;
  219.           M(c, r).right_open = true;
  220.           break;
  221.  
  222.         case 'U':
  223.           M(c, r).up_open = true;
  224.           --r;
  225.           M(c, r).down_open = true;
  226.           break;
  227.  
  228.         case 'R':
  229.           M(c, r).right_open = true;
  230.           ++c;
  231.           M(c, r).left_open = true;
  232.           break;
  233.  
  234.         case 'D':
  235.           M(c, r).down_open = true;
  236.           ++r;
  237.           M(c, r).up_open = true;
  238.           break;
  239.       }
  240.     } else {
  241.       // If there are no valid cells to move to.
  242.       // retrace one step back in history if no move is possible
  243.       const auto last = history.pop_back();
  244.       c = last.col;
  245.       r = last.row;
  246.     }
  247.   }
  248.  
  249.   // Open the walls at the start and finish
  250.   M(0,0).left_open = true;
  251.   M(num_cols-1, num_rows-1).right_open = true;
  252.  
  253.   return M;
  254. }
  255.  
  256. template<std::size_t num_cols, std::size_t num_rows>
  257. constexpr Array2D<WallType, num_cols*2+1, num_rows*2+1> render_maze(const Array2D<Cell, num_cols, num_rows> &maze_data)
  258. {
  259.   Array2D<WallType, num_cols*2+1, num_rows*2+1> result{};
  260.  
  261.  
  262.   for (std::size_t col = 0; col < num_cols; ++col)
  263.   {
  264.     for (std::size_t row = 0; row < num_rows; ++row)
  265.     {
  266.       const auto render_col = col * 2 + 1;
  267.       const auto render_row = row * 2 + 1;
  268.  
  269.       const auto &cell = maze_data(col, row);
  270.  
  271.       // upper
  272.       if (!cell.up_open) { result(render_col,render_row-1) = WallType::Horizontal; }
  273.  
  274.       // left
  275.       if (!cell.left_open) { result(render_col-1,render_row) = WallType::Vertical; }
  276.  
  277.       // right
  278.       if (!cell.right_open) { result(render_col+1,render_row) = WallType::Vertical; }
  279.  
  280.       // lower
  281.       if (!cell.down_open) { result(render_col,render_row+1) = WallType::Horizontal; }
  282.     }
  283.   }
  284.  
  285.   for (std::size_t col = 0; col < result.cols(); col += 2)
  286.   {
  287.     for (std::size_t row = 0; row < result.rows(); row += 2)
  288.     {
  289.       const auto up     = (row == 0)?false:result(col, row-1) != WallType::Empty;
  290.       const auto left   = (col == 0)?false:result(col-1, row) != WallType::Empty;
  291.       const auto right  = (col == num_cols * 2)?false:result(col+1, row) != WallType::Empty;
  292.       const auto down   = (row == num_rows * 2)?false:result(col, row+1) != WallType::Empty;
  293.  
  294.       if (up && right && down && left) {
  295.         result(col, row) = WallType::FourWay;
  296.       }
  297.       if (up && right && down && !left) {
  298.         result(col, row) = WallType::RightTee;
  299.       }
  300.       if (up && right && !down && left) {
  301.         result(col, row) = WallType::UpTee;
  302.       }
  303.       if (up && !right && down && left) {
  304.         result(col, row) = WallType::LeftTee;
  305.       }
  306.       if (!up && right && down && left) {
  307.         result(col, row) = WallType::DownTee;
  308.       }
  309.  
  310.       if (up && right && !down && !left) {
  311.         result(col, row) = WallType::LowerLeft;
  312.       }
  313.       if (up && !right && !down && left) {
  314.         result(col, row) = WallType::LowerRight;
  315.       }
  316.       if (!up && !right && down && left) {
  317.         result(col, row) = WallType::UpperRight;
  318.       }
  319.       if (!up && right && down && !left) {
  320.         result(col, row) = WallType::UpperLeft;
  321.       }
  322.       if (!up && right && !down && left) {
  323.         result(col, row) = WallType::Horizontal;
  324.       }
  325.       if (up && !right && down && !left) {
  326.         result(col, row) = WallType::Vertical;
  327.       }
  328.  
  329.  
  330.       if (up && !right && !down && !left) {
  331.         result(col, row) = WallType::Up;
  332.       }
  333.       if (!up && right && !down && !left) {
  334.         result(col, row) = WallType::Right;
  335.       }
  336.       if (!up && !right && down && !left) {
  337.         result(col, row) = WallType::Down;
  338.       }
  339.       if (!up && !right && !down && left) {
  340.         result(col, row) = WallType::Left;
  341.       }
  342.     }
  343.   }
  344.  
  345.  
  346.   return result;
  347. }
  348.  
  349. template<typename T>
  350. constexpr auto solve(T maze)
  351. {
  352.   constexpr auto num_cols = maze.cols();
  353.   constexpr auto num_rows = maze.rows();
  354.  
  355.   size_t row = 1;
  356.   size_t col = 0;
  357.  
  358.   Stack<Loc, num_cols * num_rows> history;
  359.  
  360.   history.emplace_back(col, row);
  361.   while (row != maze.rows() - 2 || col != maze.cols() - 2)
  362.   {
  363.     maze(col, row) = WallType::Visited;
  364.  
  365.     // check if the adjacent cells are valid for moving to
  366.     if (col < num_cols-1 && maze(col+1, row) == WallType::Empty) {
  367.       ++col;
  368.       history.emplace_back(col, row);
  369.     } else if (row < num_rows-1 && maze(col, row+1) == WallType::Empty) {
  370.       ++row;
  371.       history.emplace_back(col, row);
  372.     } else if (col > 0 && maze(col-1, row) == WallType::Empty) {
  373.       --col;
  374.       history.emplace_back(col, row);
  375.     } else if (row > 0 && maze(col, row-1) == WallType::Empty) {
  376.       --row;
  377.       history.emplace_back(col, row);
  378.     } else {
  379.       // If there are no valid cells to move to.
  380.       // retrace one step back in history if no move is possible
  381.       const auto last = history.pop_back();
  382.       col = last.col;
  383.       row = last.row;
  384.     }
  385.   }
  386.  
  387.   while (!history.empty())
  388.   {
  389.     const auto last = history.pop_back();
  390.     maze(last.col, last.row) = WallType::Used;
  391.   }
  392.  
  393.   return maze;
  394. }
  395.  
  396.  
  397. int main()
  398. {
  399.   constexpr const std::size_t num_cols = 20;
  400.   constexpr const std::size_t num_rows = 8;
  401.  
  402.   constexpr auto maze = make_maze<num_cols, num_rows>();
  403.   constexpr auto rendered_maze = solve(render_maze(maze));
  404.  
  405.   for (std::size_t row = 0; row < num_rows*2+1; ++row)
  406.   {
  407.     for (std::size_t col = 0; col < num_cols*2+1; ++col)
  408.     {
  409.       const auto square = [&](){
  410.         switch (rendered_maze(col, row)) {
  411.           case WallType::Empty:      return " ";
  412.           case WallType::UpperLeft:  return "┌";
  413.           case WallType::Vertical:   return "│";
  414.           case WallType::Horizontal: return "─";
  415.           case WallType::UpperRight: return "┐";
  416.           case WallType::LowerLeft:  return "└";
  417.           case WallType::LowerRight: return "┘";
  418.           case WallType::RightTee:   return "├";
  419.           case WallType::LeftTee:    return "┤";
  420.           case WallType::UpTee:      return "┴";
  421.           case WallType::DownTee:    return "┬";
  422.           case WallType::FourWay:    return "┼";
  423.           case WallType::Up:         return "╵";
  424.           case WallType::Down:       return "╷";
  425.           case WallType::Left:       return "╴";
  426.           case WallType::Right:      return "╶";
  427.           case WallType::Visited:    return "·";
  428.           case WallType::Used:       return "*";
  429.           default: throw 0;
  430.       }
  431.       }();
  432.  
  433.       std::cout << square;
  434.     }
  435.     std::cout << '\n';
  436.   }
  437. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement