• fullscreen
  • Dungeon.pde
  • DungeonMap.pde
  • // http://roguebasin.roguelikedevelopment.org/index.php?title=Java_Example_of_Dungeon-Building_Algorithm
    // Designed by Mike Andersen
    // Java version by "Solarnus"
    
    public class dungeon {
    
      //size of the map
      private int xsize = 0;
      private int ysize = 0;
    
      //number of "objects" to generate on the map
      private int objects = 0;
    
      //define the %chance to generate either a room or a corridor on the map
      //BTW, rooms are 1st priority so actually it's enough to just define the chance of generating a room
      private int chanceRoom = 75; 
      private int chanceCorridor = 25;
    
      //our map
      private int[] dungeon_map = {};
    
      //the old seed from the RNG is saved in this one
      private long oldseed = 0;
    
      //a list over tile types we're using
      final private int tileUnused = 0;
      final private int tileDirtWall = 1; //not in use
      final private int tileDirtFloor = 2;
      final private int tileStoneWall = 3;
      final private int tileCorridor = 4;
      final private int tileDoor = 5;
      final private int tileUpStairs = 6;
      final private int tileDownStairs = 7;
      final private int tileChest = 8;
    
      //misc. messages to print
      private String msgXSize = "X size of dungeon: \t";
      private String msgYSize = "Y size of dungeon: \t";
      private String msgMaxObjects = "max # of objects: \t";
      private String msgNumObjects = "# of objects made: \t";
      private String msgHelp = "";
      private String msgDetailedHelp = "";
    
      void createDungeon(int inx, int iny, int inobj) {
      /*******************************************************************************/
        // Here's the one generating the whole map
        if (inobj < 1) objects = 10;
        else objects = inobj;
    
        // Adjust the size of the map if it's too small
        if (inx < 3) xsize = 3;
        else xsize = inx;
    
        if (iny < 3) ysize = 3;
        else ysize = iny;
    
        System.out.println(msgXSize + xsize);
        System.out.println(msgYSize + ysize);
        System.out.println(msgMaxObjects + objects);
    
        //redefine the map var, so it's adjusted to our new map size
        dungeon_map = new int[xsize * ysize];
    
        //start with making the "standard stuff" on the map
        for (int y = 0; y < ysize; y++) {
          for (int x = 0; x < xsize; x++) {
            //ie, making the borders of unwalkable walls
            if (y == 0) setCell(x, y, tileStoneWall);
            else if (y == ysize-1) setCell(x, y, tileStoneWall);
            else if (x == 0) setCell(x, y, tileStoneWall);
            else if (x == xsize-1) setCell(x, y, tileStoneWall);
    
            //and fill the rest with dirt
            else setCell(x, y, tileUnused);
          }
        }
    
        /*******************************************************************************
         
        And now the code of the random-map-generation-algorithm begins!
         
        *******************************************************************************/
    
        //start with making a room in the middle, which we can start building upon
        makeRoom(xsize/2, ysize/2, 8, 6, getRand(0,3));
    
        //keep count of the number of "objects" we've made
        int currentFeatures = 1; //+1 for the first room we just made
    
        //then we start the main loop
        for (int countingTries = 0; countingTries < 1000; countingTries++) {
    
          //check if we've reached our quota
          if (currentFeatures == objects) {
            break;
          }
    
          //start with a random wall
          int newx = 0;
          int xmod = 0;
          int newy = 0;
          int ymod = 0;
          int validTile = -1;
    
          //1000 chances to find a suitable object (room or corridor)..
          //(yea, i know it's kinda ugly with a for-loop... -_-')
    
          for (int testing = 0; testing < 1000; testing++) {
            newx = getRand(1, xsize-1);
            newy = getRand(1, ysize-1);
            validTile = -1;
    
            //System.out.println("tempx: " + newx + "\ttempy: " + newy);
    
            if (getCell(newx, newy) == tileDirtWall || getCell(newx, newy) == tileCorridor) {
              //check if we can reach the place
              if (getCell(newx, newy+1) == tileDirtFloor || getCell(newx, newy+1) == tileCorridor) {
                validTile = 0; //
                xmod = 0;
                ymod = -1;
              }
              else if (getCell(newx-1, newy) == tileDirtFloor || getCell(newx-1, newy) == tileCorridor) {
                validTile = 1; //
                xmod = +1;
                ymod = 0;
              }
    
              else if (getCell(newx, newy-1) == tileDirtFloor || getCell(newx, newy-1) == tileCorridor) {
                validTile = 2; //
                xmod = 0;
                ymod = +1;
              }
    
              else if (getCell(newx+1, newy) == tileDirtFloor || getCell(newx+1, newy) == tileCorridor) {
                validTile = 3; //
                xmod = -1;
                ymod = 0;
              }
    
              //check that we haven't got another door nearby, so we won't get alot of openings besides each other
    
              if (validTile > -1) {
                if (getCell(newx, newy+1) == tileDoor) //north
                  validTile = -1;
                else if (getCell(newx-1, newy) == tileDoor)//east
                  validTile = -1;
                else if (getCell(newx, newy-1) == tileDoor)//south
                  validTile = -1;
                else if (getCell(newx+1, newy) == tileDoor)//west
                  validTile = -1;
              }
    
              //if we can, jump out of the loop and continue with the rest
              if (validTile > -1) break;
            }
          }
    
          if (validTile > -1) {
    
            //choose what to build now at our newly found place, and at what direction
            int feature = getRand(0, 100);
            if (feature <= chanceRoom) { //a new room
                if (makeRoom((newx+xmod), (newy+ymod), 8, 6, validTile)) {
                currentFeatures++; //add to our quota
    
                //then we mark the wall opening with a door
                setCell(newx, newy, tileDoor);
    
                //clean up infront of the door so we can reach it
                setCell((newx+xmod), (newy+ymod), tileDirtFloor);
              }
            }
    
            else if (feature >= chanceRoom) { //new corridor
              if (makeCorridor((newx+xmod), (newy+ymod), 6, validTile)) {
                //same thing here, add to the quota and a door
                currentFeatures++;
                setCell(newx, newy, tileDoor);
              }
            }
          }
        }
    
        /*******************************************************************************
         
        All done with the building, let's finish this one off
         
        *******************************************************************************/
    
        //sprinkle out the bonusstuff (stairs, chests etc.) over the map
        int newx = 0;
        int newy = 0;
        int ways = 0; //from how many directions we can reach the random spot from
        int state = 0; //the state the loop is in, start with the stairs
    
        while (state != 10) {
          for (int testing = 0; testing < 1000; testing++) {
            newx = getRand(1, xsize-1);
            newy = getRand(1, ysize-2); //cheap bugfix, pulls down newy to 0<y<24, from 0<y<25
            //System.out.println("x: " + newx + "\ty: " + newy);
            ways = 4; //the lower the better
    
            //check if we can reach the spot
            if (getCell(newx, newy+1) == tileDirtFloor || getCell(newx, newy+1) == tileCorridor) {
              //north
              if (getCell(newx, newy+1) != tileDoor)
                ways--;
            }
    
            if (getCell(newx-1, newy) == tileDirtFloor || getCell(newx-1, newy) == tileCorridor) {
              //east
              if (getCell(newx-1, newy) != tileDoor)
                ways--;
            }
    
            if (getCell(newx, newy-1) == tileDirtFloor || getCell(newx, newy-1) == tileCorridor) {
              //south
              if (getCell(newx, newy-1) != tileDoor)
                ways--;
            }
    
            if (getCell(newx+1, newy) == tileDirtFloor || getCell(newx+1, newy) == tileCorridor) {
              //west
              if (getCell(newx+1, newy) != tileDoor)
                ways--;
            }
    
            if (state == 0) {
              if (ways == 0) {
                //we're in state 0, let's place a "upstairs" thing
                setCell(newx, newy, tileUpStairs);
                state = 1;
                break;
              }
            }
    
            else if (state == 1) {
              if (ways == 0) {
                //state 1, place a "downstairs"
                setCell(newx, newy, tileDownStairs);
                state = 10;
                break;
              }
            }
          }
        }
    
        //all done with the map generation, tell the user about it and finish
        System.out.println(msgNumObjects + currentFeatures);
    
      }
    
      //setting a tile's type
      private void setCell(int x, int y, int celltype) {
        dungeon_map[x + xsize * y] = celltype;
      }
    
      //returns the type of a tile
      private int getCell(int x, int y) {
        return dungeon_map[x + xsize * y];
      }
    
      //The RNG. the seed is based on seconds from the "java epoch" ( I think..)
      //perhaps it's the same date as the unix epoch
      private int getRand(int min, int max) {
    
        //the seed is based on current date and the old, already used seed
        Date now = new Date();
        long seed = now.getTime() + oldseed;
        oldseed = seed;
        
        Random randomizer = new Random(seed);
        int n = max - min + 1;
        int i = randomizer.nextInt(n);
        if (i < 0) i = -i;
    
        //System.out.println("seed: " + seed + "\tnum:  " + (min + i));
        return min + i;
      }
    
    
      private boolean makeCorridor(int x, int y, int lenght, int direction) {
      /*******************************************************************************/
        //define the dimensions of the corridor (er.. only the width and height..)
        int len = getRand(2, lenght);
        int floor = tileCorridor;
        int dir = 0;
        if (direction > 0 && direction < 4) dir = direction;
    
        int xtemp = 0;
        int ytemp = 0;
    
        // reject corridors that are out of bounds
        if (x < 0 || x > xsize) return false;
        if (y < 0 || y > ysize) return false;
        
        switch(dir) {
          
          case 0: //north
            xtemp = x;
      
            // make sure it's not out of the boundaries
            for (ytemp = y; ytemp > (y-len); ytemp--) {
              if (ytemp < 0 || ytemp > ysize) return false; //oh boho, it was!
              if (getCell(xtemp, ytemp) != tileUnused) return false;
            }
      
            //if we're still here, let's start building
            for (ytemp = y; ytemp > (y-len); ytemp--) {
              setCell(xtemp, ytemp, floor);
            }
            break;
    
          case 1: //east
            ytemp = y;
      
            for (xtemp = x; xtemp < (x+len); xtemp++) {
              if (xtemp < 0 || xtemp > xsize) return false;
              if (getCell(xtemp, ytemp) != tileUnused) return false;
            }
      
            for (xtemp = x; xtemp < (x+len); xtemp++) {
              setCell(xtemp, ytemp, floor);
            }
            break;
      
          case 2: // south
            xtemp = x;
      
            for (ytemp = y; ytemp < (y+len); ytemp++) {
              if (ytemp < 0 || ytemp > ysize) return false;
              if (getCell(xtemp, ytemp) != tileUnused) return false;
            }
      
            for (ytemp = y; ytemp < (y+len); ytemp++) {
              setCell(xtemp, ytemp, floor);
            }
            break;
      
          case 3: // west
            ytemp = y;
      
            for (xtemp = x; xtemp > (x-len); xtemp--) {
              if (xtemp < 0 || xtemp > xsize) return false;
              if (getCell(xtemp, ytemp) != tileUnused) return false;
            }
      
            for (xtemp = x; xtemp > (x-len); xtemp--) {
              setCell(xtemp, ytemp, floor);
            }
            break;
          }
    
        //woot, we're still here! let's tell the other guys we're done!!
        return true;
      }
    
    
      
      private boolean makeRoom(int x, int y, int xlength, int ylength, int direction) {
      /*******************************************************************************/
    
        //define the dimensions of the room, it should be at least 4x4 tiles (2x2 for walking on, the rest is walls)
        int xlen = getRand(4, xlength);
        int ylen = getRand(4, ylength);
    
        //the tile type it's going to be filled with
        int floor = tileDirtFloor; //jordgolv..
        int wall = tileDirtWall; //jordv????gg
    
        //choose the way it's pointing at
        int dir = 0;
        if (direction > 0 && direction < 4) dir = direction;
    
        switch(dir) {
    
          case 0: // north
      
            //Check if there's enough space left for it
            for (int ytemp = y; ytemp > (y-ylen); ytemp--) {
              if (ytemp < 0 || ytemp > ysize) return false;
              for (int xtemp = (x-xlen/2); xtemp < (x+(xlen+1)/2); xtemp++) {
                if (xtemp < 0 || xtemp > xsize) return false;
                if (getCell(xtemp, ytemp) != tileUnused) return false; //no space left...
              }
            }
      
            //we're still here, build
            for (int ytemp = y; ytemp > (y-ylen); ytemp--) {
              for (int xtemp = (x-xlen/2); xtemp < (x+(xlen+1)/2); xtemp++) {
                //start with the walls
                if (xtemp == (x-xlen/2)) setCell(xtemp, ytemp, wall);
                else if (xtemp == (x+(xlen-1)/2)) setCell(xtemp, ytemp, wall);
                else if (ytemp == y) setCell(xtemp, ytemp, wall);
                else if (ytemp == (y-ylen+1)) setCell(xtemp, ytemp, wall);
                //and then fill with the floor
                else setCell(xtemp, ytemp, floor);
              }
            }
      
            break;
      
          case 1: // east
      
            for (int ytemp = (y-ylen/2); ytemp < (y+(ylen+1)/2); ytemp++) {
              if (ytemp < 0 || ytemp > ysize) return false;
              for (int xtemp = x; xtemp < (x+xlen); xtemp++) {
                if (xtemp < 0 || xtemp > xsize) return false;
                if (getCell(xtemp, ytemp) != tileUnused) return false;
              }
            }
      
            for (int ytemp = (y-ylen/2); ytemp < (y+(ylen+1)/2); ytemp++) {
              for (int xtemp = x; xtemp < (x+xlen); xtemp++) {
                if (xtemp == x) setCell(xtemp, ytemp, wall);
                else if (xtemp == (x+xlen-1)) setCell(xtemp, ytemp, wall);
                else if (ytemp == (y-ylen/2)) setCell(xtemp, ytemp, wall);
                else if (ytemp == (y+(ylen-1)/2)) setCell(xtemp, ytemp, wall);
                else setCell(xtemp, ytemp, floor);
              }
            }
      
            break;
      
          case 2: // south
      
            for (int ytemp = y; ytemp < (y+ylen); ytemp++) {
              if (ytemp < 0 || ytemp > ysize) return false;
              for (int xtemp = (x-xlen/2); xtemp < (x+(xlen+1)/2); xtemp++) {
                if (xtemp < 0 || xtemp > xsize) return false;
                if (getCell(xtemp, ytemp) != tileUnused) return false;
              }
            }
    
            for (int ytemp = y; ytemp < (y+ylen); ytemp++) {
              for (int xtemp = (x-xlen/2); xtemp < (x+(xlen+1)/2); xtemp++) {
                if (xtemp == (x-xlen/2)) setCell(xtemp, ytemp, wall);
                else if (xtemp == (x+(xlen-1)/2)) setCell(xtemp, ytemp, wall);
                else if (ytemp == y) setCell(xtemp, ytemp, wall);
                else if (ytemp == (y+ylen-1)) setCell(xtemp, ytemp, wall);
                else setCell(xtemp, ytemp, floor);
              }
            }
      
            break;
      
          case 3: // west
      
            for (int ytemp = (y-ylen/2); ytemp < (y+(ylen+1)/2); ytemp++) {
              if (ytemp < 0 || ytemp > ysize) return false;
              for (int xtemp = x; xtemp > (x-xlen); xtemp--) {
                if (xtemp < 0 || xtemp > xsize) return false;
                if (getCell(xtemp, ytemp) != tileUnused) return false;
              }
            }
      
            for (int ytemp = (y-ylen/2); ytemp < (y+(ylen+1)/2); ytemp++) {
              for (int xtemp = x; xtemp > (x-xlen); xtemp--) {
                if (xtemp == x) setCell(xtemp, ytemp, wall);
                else if (xtemp == (x-xlen+1)) setCell(xtemp, ytemp, wall);
                else if (ytemp == (y-ylen/2)) setCell(xtemp, ytemp, wall);
                else if (ytemp == (y+(ylen-1)/2)) setCell(xtemp, ytemp, wall);
                else setCell(xtemp, ytemp, floor);
              }
            }
      
            break;
          }
    
        //yay, all done
        return true;
      }
    
      String showDungeon() {
      /*******************************************************************************/
        //used to print the map on the screen
        String dungeonMap = "";
        for (int y = 0; y < ysize; y++) {
          for (int x = 0; x < xsize; x++) {
            switch(getCell(x, y)) {
              case tileUnused: dungeonMap += " "; break;
              case tileDirtWall: dungeonMap += "+"; break;
              case tileDirtFloor: dungeonMap += "."; break;
              case tileStoneWall: dungeonMap += "O"; break;
              case tileCorridor: dungeonMap += "#"; break;
              case tileDoor: dungeonMap += "D"; break;
              case tileUpStairs: dungeonMap += "<"; break;
              case tileDownStairs: dungeonMap += ">"; break;
              case tileChest: dungeonMap += "*"; break;
            }
          }
          dungeonMap += "\n";
        }
        return dungeonMap;
      }
    
      PGraphics generateDungeonImage() {
      /*******************************************************************************/
        PGraphics dungeonImage = createGraphics(xsize * TILE_SIZE, ysize * TILE_SIZE, JAVA2D);
        dungeonImage.beginDraw();
        dungeonImage.noStroke();
        
        for (int y = 0; y < ysize; y++) {
          for (int x = 0; x < xsize; x++) {
            switch (getCell(x, y)) {
              case 0: // Unused
                dungeonImage.fill(54, 47, 45);
                break;
              case 1: // Dirt Wall
                dungeonImage.fill(96, 57, 19);
                break;
              case 2: // Dirt Floor
                dungeonImage.fill(166, 124, 82);
                break;
              case 3: // Stone Wall
                dungeonImage.fill(115, 99, 87);
                break;
              case 4: // Corridor
                dungeonImage.fill(123, 46, 0);
                break;
              case 5: // Door
                dungeonImage.fill(0, 118, 163);
                break;
              case 6: // Stairs Up
                dungeonImage.fill(100, 200, 100);
                break;
              case 7: // Stairs Down
                dungeonImage.fill(200, 100, 100);
                break;
              case 8: // Treasure Chest
                dungeonImage.fill(255, 247, 153);
                break;
            }
            dungeonImage.rect(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE);
          }
        }
    
        dungeonImage.endDraw();
        
        return dungeonImage;
      }
    
    }
    
    
    // Taken from:
    // http://roguebasin.roguelikedevelopment.org/index.php?title=Java_Example_of_Dungeon-Building_Algorithm
    // 
    // Jan 25, 2011: Edited for clarity.  Added graphical representation.  Disabled width/height limits.
    
    // Next steps:
    // - See if it's possible to get the same sequence from the same seed
    // - Things seem pretty tight... is it possible to create new "room types" of varying sizes?
    // - Can we trim corridors with no cap?
    
    dungeon generator;
    PFont font;
    String mapDisplay;
    PImage dungeonMap;
    final static int TILE_SIZE = 8;
    int mapSizeX = 70;
    int mapSizeY = 70;
    int mapRoomObjects = 40;
    
    void setup() {
      size(mapSizeX * TILE_SIZE, mapSizeY * TILE_SIZE);
      font = createFont("Courier New", 12);
      textFont(font);
      fill(255);
      noSmooth();
      generator = new dungeon();
      getNewDungeonMap();
    }
    
    void draw() {
      background(0);
      image(dungeonMap, 0, 0);
      //text(generator.showDungeon(), 6, 36);
    }
    
    void getNewDungeonMap() {
      generator.createDungeon(mapSizeX, mapSizeY, mapRoomObjects);
      dungeonMap = generator.generateDungeonImage();
    }
    
    void keyReleased() {
      if (key == ' ') getNewDungeonMap();
    }
    
    

    code

    tweaks (0)

    about this sketch

    This sketch is running as Java applet, exported from Processing.

    license

    advertisement

    Ted Brown

    Dungeon Map Generator

    Add to Faves Me Likey@! 6
    You must login/register to add this sketch to your favorites.

    * Hit SPACEBAR to generate a new map.
    * Blue represents a door
    * Pink and Green are the up/down staircases

    This is a simple Processing port of the Dungeon Building Algorithm, designed by Mike Anderson and coded by Solarnus. I made some minor tweaks and added a graphical representation, but otherwise it's exactly as documented here:

    http://roguebasin.roguelikedevelopment.org/index.php?title=Dungeon-Building_Algorithm

    You need to login/register to comment.