// // Make a random maze using kD-tree space subdivision // // by Jarno van der Linden jvan006@cs.auckland.ac.nz // #include #include #include #include #include typedef short Coord; const Coord WORLD_SIZE = 256; // Grid size of the world // Position of a grid square struct GridPos { Coord x, y; }; // Information about a grid square struct Square { bool wall; // True if this square is a wall }; // The world is a 2D grid of squares Square world[WORLD_SIZE][WORLD_SIZE]; // // Create a random world // Walls are placed by recursivly subdividing with alternating // horizontal and vertical walls. // Walls can only be placed if there is a wall at both ends // and there is enough space for a corridor. // A door is placed in the wall. // The algorithm guarantees that every room is reachable from // every other room by exactly one path. // const int CORRIDOR_SIZE = 8; // Half the smallest width of a corridor void PlaceVerticalWall(const GridPos& minp, const GridPos& maxp); // Place a horizontal wall in box [minp, maxp] void PlaceHorizontalWall(const GridPos& minp, const GridPos& maxp) { // Try finding a place for a horizontal wall that joins // a wall at both ends. Stop after 8 attempts for(int t = 0; t < 8; t++) { // Pick random number in the range [miny+CORRIDOR_SIZE, maxy-CORRIDOR_SIZE] const Coord y = rand() % (maxp.y - minp.y - 2*CORRIDOR_SIZE + 1) + minp.y + CORRIDOR_SIZE; if(world[minp.x-1][y].wall && world[maxp.x+1][y].wall) { // Place a wall here for(int w = minp.x; w <= maxp.x; w++) world[w][y].wall = true; // Make a doorway of half the corridor width const Coord x = rand() % (maxp.x - minp.x - CORRIDOR_SIZE + 1) + minp.x; for(int w = x; w < x + CORRIDOR_SIZE; w++) world[w][y].wall = false; // If there is enough space, place a horizontal wall above if((y - minp.y) > 2*CORRIDOR_SIZE) { const GridPos topminp = { minp.x, minp.y }; const GridPos topmaxp = { maxp.x, y-1 }; PlaceVerticalWall(topminp, topmaxp); } // If there is enough space, place a horizontal wall below if((maxp.y - y) > 2*CORRIDOR_SIZE) { const GridPos bottomminp = { minp.x, y+1 }; const GridPos bottommaxp = { maxp.x, maxp.y }; PlaceVerticalWall(bottomminp, bottommaxp); } return; } } } // Place a vertical wall in box [minp, maxp] void PlaceVerticalWall(const GridPos& minp, const GridPos& maxp) { // Try finding a place for a vertical wall that joins // a wall at both ends for(int t = 0; t < 8; t++) { // Pick random number in the range [miny+CORRIDOR_SIZE, maxy-CORRIDOR_SIZE] const Coord x = rand() % (maxp.x - minp.x - 2*CORRIDOR_SIZE + 1) + minp.x + CORRIDOR_SIZE; if(world[x][minp.y-1].wall && world[x][maxp.y+1].wall) { // Place a wall here for(int w = minp.y; w <= maxp.y; w++) world[x][w].wall = true; // Make a doorway of half the corridor width const Coord y = rand() % (maxp.y - minp.y - CORRIDOR_SIZE + 1) + minp.y; for(int w = y; w < y + CORRIDOR_SIZE; w++) world[x][w].wall = false; // If there is enough space, place a horizontal wall on the left if((x - minp.x) > 2*CORRIDOR_SIZE) { const GridPos leftminp = { minp.x, minp.y }; const GridPos leftmaxp = { x-1, maxp.y }; PlaceHorizontalWall(leftminp, leftmaxp); } // If there is enough space, place a horizontal wall on the right if((maxp.x - x) > 2*CORRIDOR_SIZE) { const GridPos rightminp = { x+1, minp.y }; const GridPos rightmaxp = { maxp.x, maxp.y }; PlaceHorizontalWall(rightminp, rightmaxp); } return; } } } void InitWorld(void) { // Don't use the standard C library rand() random number generator // in a real game. It's almost always slow and very non-random. // But this is just example code, so we don't care. srand(time(NULL)); for(Coord x = 0; x < WORLD_SIZE; x++) { for(Coord y = 0; y < WORLD_SIZE; y++) { world[x][y].wall = false; } } // Place a wall around the world for(Coord x = 0; x < WORLD_SIZE; x++) world[x][0].wall = world[x][WORLD_SIZE-1].wall = true; for(Coord y = 0; y < WORLD_SIZE; y++) world[0][y].wall = world[WORLD_SIZE-1][y].wall = true; const GridPos minp = { 1, 1 }; const GridPos maxp = { WORLD_SIZE-2, WORLD_SIZE-2 }; if(rand() > RAND_MAX/2) PlaceHorizontalWall(minp, maxp); else PlaceVerticalWall(minp, maxp); } // // GLUT rendering and stuff // void Display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glBegin(GL_QUADS); for(Coord x = 0; x < WORLD_SIZE; x++) { for(Coord y = 0; y < WORLD_SIZE; y++) { if(world[x][y].wall) glColor3f(0, 0, 0); else glColor3f(1, 1, 1); glVertex2f(x, y); glVertex2f(x+1, y); glVertex2f(x+1, y+1); glVertex2f(x, y+1); } } glEnd(); glutSwapBuffers(); } void Reshape(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, WORLD_SIZE, 0, WORLD_SIZE, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void Key(unsigned char key, int x, int y) { switch(key) { case 27: case 'q': exit(EXIT_SUCCESS); default: return; } glutPostRedisplay(); } // // Little main // int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH); glutInitWindowSize(512, 512); glutCreateWindow("PVS Demo"); glutReshapeFunc(Reshape); glutKeyboardFunc(Key); glutDisplayFunc(Display); InitWorld(); glutMainLoop(); return EXIT_SUCCESS; }