Advertisement
Guest User

maze generator example

a guest
Nov 23rd, 2016
119
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.10 KB | None | 0 0
  1. #include "surface.h"
  2.  
  3. #include <cstdlib>
  4. #include <vector>
  5. #include <stack>
  6. #include <cstring>
  7. using namespace std;
  8.  
  9. enum Dir {
  10.     NORTH,
  11.     EAST,
  12.     SOUTH,
  13.     WEST,
  14.    
  15.     DIR_COUNT
  16. };
  17.  
  18. struct MazeCell
  19. {
  20.     bool can_join[DIR_COUNT]; // true if the cells can be considered for passage
  21.     bool has_path[DIR_COUNT]; // true if there actually is a path in that dir
  22.    
  23.     bool visited; // prevents passing through a cell twice during mazegen
  24.    
  25.     bool special;   // if true, special color will be drawn over cell in output
  26.     RGB special_color;
  27.    
  28.     MazeCell()
  29.     {
  30.         visited = false;
  31.         special = false;
  32.        
  33.         for(int i = 0; i < DIR_COUNT; i++)
  34.         {
  35.             can_join[i] = true;  // by default, allow consideration of neighbors
  36.             has_path[i] = false; // but don't actually make any paths
  37.         }
  38.     }
  39. };
  40.  
  41. struct Point
  42. {
  43.     int x;
  44.     int y;
  45.    
  46.     Point() : x(0), y(0) {}
  47.     Point(int X, int Y) : x(X), y(Y) {}
  48. };
  49.  
  50. // gives the opposite direction (e.g. NORTH -> SOUTH)
  51. Dir mirror(int dir)
  52. {
  53.     switch(dir)
  54.     {
  55.         case NORTH:
  56.             return SOUTH;
  57.            
  58.         case EAST:
  59.             return WEST;
  60.            
  61.         case SOUTH:
  62.             return NORTH;
  63.            
  64.         case WEST:
  65.             return EAST;
  66.        
  67.         default:
  68.             fprintf(stderr, "FATAL: Invalid direction\n");
  69.             exit(1);
  70.     }
  71. }
  72.  
  73. // Returns the indicated point shifted by one step in the indicated direction
  74. Point point_offset(Point here, int dir)
  75. {
  76.     switch(dir)
  77.     {
  78.         case NORTH:
  79.             return Point(here.x, here.y-1);
  80.         case SOUTH:
  81.             return Point(here.x, here.y+1);
  82.         case EAST:
  83.             return Point(here.x+1, here.y);
  84.         case WEST:
  85.             return Point(here.x-1, here.y);
  86.        
  87.         default:
  88.             fprintf(stderr, "FATAL: Invalid direction in point_offset\n");
  89.             exit(1);
  90.     }
  91. }
  92.  
  93. // marks two cells such that a cell at (x,y) and its neighbor in the indicated
  94. // direction both have flags set to indicate that there is a path between them
  95. void break_wall(Surface<MazeCell>& maze, int x, int y, int dir)
  96. {
  97.     if(!maze.bounds_check(x,y))
  98.         return;
  99.    
  100.     Point here(x, y);
  101.     MazeCell& here_cell = maze.at(x,y);
  102.     here_cell.has_path[dir] = true;
  103.    
  104.     Point there = point_offset(here, dir);
  105.     int opposite = mirror(dir);
  106.    
  107.     if(!maze.bounds_check(there.x, there.y))
  108.         return;
  109.    
  110.     MazeCell& there_cell = maze.at(there.x, there.y);
  111.     there_cell.has_path[opposite] = true;
  112. }
  113.  
  114. // marks two cells to disconnect them from each other in the graph.
  115. // this will make it so that the maze generator cannot move from one cell
  116. // to its neighbor and vice-versa.
  117. void prevent_path(Surface<MazeCell>& maze, int x, int y, int dir)
  118. {
  119.     if(!maze.bounds_check(x,y))
  120.         return;
  121.    
  122.     Point here(x, y);
  123.     MazeCell& here_cell = maze.at(x,y);
  124.     here_cell.can_join[dir] = false;
  125.     here_cell.has_path[dir] = false;
  126.    
  127.     Point there = point_offset(here, dir);
  128.     int opposite = mirror(dir);
  129.    
  130.     MazeCell& there_cell = maze.at(there.x, there.y);
  131.     there_cell.can_join[opposite] = false;
  132.     there_cell.has_path[opposite] = false;
  133. }
  134.  
  135. // given a vector of directions, pick one at random
  136. // this is not a very good randomizer, but it was really simple to implement
  137. Dir pick_random(const vector<Dir>& v)
  138. {
  139.     return v[rand() % v.size()];
  140. }
  141.  
  142. void mazegen(Surface<MazeCell>& maze, Point start)
  143. {
  144.     stack<Point> s;
  145.     s.push(start);
  146.    
  147.     while(!s.empty())
  148.     {
  149.         Point here = s.top();
  150.        
  151.         MazeCell& here_cell = maze.at(here.x, here.y);
  152.         here_cell.visited = true; // mark it so we don't pass back through it
  153.        
  154.         // pick a random neighbor and traverse in that direction
  155.         vector<Dir> neighbors;
  156.        
  157.         for(int dir = 0; dir < DIR_COUNT; dir++)
  158.         {
  159.             Point there = point_offset(here, dir);
  160.            
  161.             if(!here_cell.can_join[dir])
  162.                 continue;   // user requested we don't travel that way
  163.            
  164.             if(!maze.bounds_check(there.x, there.y))
  165.                 continue;   // neighbor is out of bounds of map
  166.            
  167.             MazeCell& there_cell = maze.at(there.x, there.y);
  168.            
  169.             if(there_cell.visited)
  170.                 continue;   // we've already made a path through that cell
  171.            
  172.             neighbors.push_back((Dir)dir);
  173.         }
  174.        
  175.         // no where to go further on this path; go back and choose another way
  176.         if(neighbors.size() == 0)
  177.         {
  178.             s.pop();
  179.             continue;
  180.         }
  181.        
  182.         Dir go_dir = pick_random(neighbors);
  183.         break_wall(maze, here.x, here.y, go_dir);
  184.         Point there = point_offset(here, go_dir);
  185.        
  186.         s.push(there);
  187.     }
  188. }
  189.  
  190. void draw_maze(RGBSurface& output, const Surface<MazeCell>& input)
  191. {
  192.     RGB bgcolor(0,0,0x7F);
  193.    
  194.     RGBSurface space;   // image to draw an open space
  195.     RGBSurface wall;    // image to draw a wall
  196.     RGBSurface special; // image to highlight an area for special emphasis
  197.            
  198.    
  199.     if(!load_image(wall, "wall.png"))
  200.     {
  201.         fprintf(stderr, "Failed to load wall.png\n");
  202.         wall.resize(32, 32);
  203.         wall.fill(RGB(0,0,0));
  204.     }
  205.    
  206.     space.resize(wall.width(), wall.height());
  207.     space.fill(bgcolor);
  208.  
  209.     special.resize(wall.width(), wall.height());
  210.    
  211.     output.resize(
  212.         2*wall.width()*input.width() + wall.width(),
  213.         2*wall.height()*input.height() + wall.height()
  214.     );
  215.      
  216.     // convenience macro to calculate pixel positions relative to a maze cell
  217.     // and the local wall-scale offset.
  218.     // e.g. X(0,0) corresponds to the first * in the diagram below,
  219.     //      X(0,1) corresponds to the first N in the diagram below,
  220.     //      X(1,0) corresponds to the 2nd * in the diagram below, etc.
  221.     #define X(xx, off) (wall.width() * (2*xx + off))
  222.     #define Y(yy, off) (wall.height() * (2*yy + off))
  223.        
  224.     // fill walls in 2x2 block pattern:
  225.     //
  226.     //    * N  * N  * N
  227.     //    W _  W _  W _  ...
  228.     //
  229.     //    * N  * N  * N
  230.     //    W _  W _  W _
  231.     //
  232.     //    ...
  233.     //
  234.    
  235.     for(int iy = 0; iy < input.height(); iy++)
  236.     for(int ix = 0; ix < input.width(); ix++)
  237.     {
  238.         blit(output, wall, X(ix, 0), Y(iy, 0));
  239.         blit(output, wall, X(ix, 1), Y(iy, 0));
  240.         blit(output, wall, X(ix, 0), Y(iy, 1));
  241.         blit(output, wall, X(ix, 1), Y(iy, 1));
  242.     }
  243.    
  244.     // draw EAST/SOUTH extreme border walls as a special case step
  245.     for(int y = 0; y < input.height(); y++)
  246.     {
  247.         blit(output, wall, X(input.width(), 0), Y(y, 0));
  248.         blit(output, wall, X(input.width(), 0), Y(y, 1));
  249.     }
  250.    
  251.     for(int x = 0; x < input.width(); x++)
  252.     {
  253.         blit(output, wall, X(x, 0), Y(input.height(), 0));
  254.         blit(output, wall, X(x, 1), Y(input.height(), 0));
  255.     }
  256.    
  257.     // draw final corner on SE edge
  258.     blit(output, wall, X(input.width(), 0), Y(input.height(), 0));
  259.    
  260.    
  261.     // traverse the grid knocking down walls where open paths should be
  262.     for(int iy = 0; iy < input.height(); iy++)
  263.     for(int ix = 0; ix < input.width(); ix++)
  264.     {
  265.         // draw open space for cell
  266.         blit(output, space, X(ix,1), Y(iy, 1));
  267.    
  268.         // draw N/E/S/W paths
  269.         // yes, this does more work that strictly necessary...
  270.         const MazeCell& input_here = input.at(ix, iy);
  271.        
  272.         if(input_here.has_path[NORTH])
  273.             blit(output, space, X(ix,1), Y(iy,0));
  274.            
  275.         if(input_here.has_path[EAST])
  276.             blit(output, space, X(ix,2), Y(iy,1));
  277.            
  278.         if(input_here.has_path[SOUTH])
  279.             blit(output, space, X(ix,1), Y(iy,2));
  280.            
  281.         if(input_here.has_path[WEST])
  282.             blit(output, space, X(ix,0), Y(iy,1));
  283.            
  284.         // allow special coloring to override regular behavior
  285.         // bit of an ugly hack to allow adding emphasis
  286.         if(input_here.special)
  287.         {
  288.             special.fill(input_here.special_color);
  289.             blit(output, special, X(ix, 1), Y(iy, 1));
  290.         }
  291.     }
  292. }
  293.  
  294. int main()
  295. {
  296.     //srand(12345);
  297.     srand(554433);
  298.  
  299.     Surface<MazeCell> maze(15,15);
  300.     RGBSurface output_image;
  301.    
  302.     // build a square in the middle
  303.     for(int y = 5; y < 10; y++)
  304.     {
  305.         prevent_path(maze, 5, y, EAST);
  306.         prevent_path(maze, 10, y, WEST);
  307.     }
  308.    
  309.     for(int x = 5; x < 10; x++)
  310.     {
  311.         prevent_path(maze, x, 5, SOUTH);
  312.         prevent_path(maze, x, 10, NORTH);
  313.     }
  314.    
  315.     // emphasize the block in the middle by coloring it red
  316.     for(int y = 6; y < 10; y++)
  317.     for(int x = 6; x < 10; x++)
  318.     {
  319.         maze.at(x,y).special = true;
  320.         maze.at(x,y).special_color = RGB(0xFF,0,0);
  321.     }
  322.    
  323.     // generate a maze starting from the top-left corner
  324.     mazegen(maze, Point(0,0));
  325.    
  326.     draw_maze(output_image, maze);
  327.     save_png(output_image, "output.png");
  328. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement