Tutorial on Dungeon Generation in Objective-C

So I am getting a Cellular Automator (CA) generator ready for this years 7DRL and hoping to create the same game in iOS and HTML5 – I am sure I am biting too much off. However, I have a basic implementation of a Dungeon generator and detailed how to create it below in X-Code using Objective-C. It’s a very simple implementation – so feel free to comment if you think we can clean the code up!!!!

In terms of CA there are some great references to get you going:

http://pcg.wikidot.com/pcg-algorithm:cellular-automata

http://www.futuredatalab.com/proceduraldungeon/

http://csharpcodewhisperer.blogspot.com.au/2013/07/Rouge-like-dungeon-generation.html

For this tutorial the dungeon is represented by 0′s as Rock and 1′s as  Path (or moveable space).

This is the output so far:

Screen Shot 2014-02-16 at 1.19.12 AM

So its a pretty simple set of steps to get us going.

Firstly create a OSX->Application->Command LineTool->Foundation project in Xcode

Then add a class called Dungeon

The header for Dungeon should look like this

#import <Foundation/Foundation.h>
 @interface Dungeon : NSObject
{
    int map[100][100];  //max size of dungeon
    int maxRows;        //size of this dungeon
    int maxCols;        //size of this dungeon
}
- (id)initWithRows:(int)rows AndCol:(int)cols;   
-(void)printMap;
@end

The implementation for this should look like

#import "Dungeon.h"

@implementation Dungeon
- (id)initWithRows:(int)rows AndCol:(int)cols
{
    self = [super init];
    if (self) {
        [self initMap];
        maxRows = rows;
        maxCols = cols;
        [self createCellularAutomatorDungeon:maxRows ForCols:maxCols];
    }

    return self;
}

//set all the map to rock
-(void)initMap
{
    for(int i=0;i<maxRows;i++)
        for(int j=0;j<maxCols;j++)
            map[i][j] = 0;
}
 //Loop through the 2 dim array and add space (not rock) if the wall has a specific minimum neighbourCount
- (void)createCellularAutomatorCellsForNeighbourCount:(int)neighbourCount
{
    //first pass CA
    for(int i=0;i<maxRows;i++)
    {
        for(int j=0;j<maxCols;j++)
        {
            if (map[i][j] == 0 && ([self getNeighboursForRow:i ForCol:j]>=neighbourCount))
                map[i][j] = 1;
        }
    }
}

//use a classic CA algorithm 
-(void)createCellularAutomatorDungeon:(int)rows ForCols:(int)cols
{
    //randomly initialize wih a few random spaces in the rock
    for(int i=1;i<maxRows;i++)
        for(int j=1;j<maxCols;j++)
            if (arc4random()%4==1)
                map[i][j] = 1;

    [self createCellularAutomatorCellsForNeighbourCount:4];//first pass
    [self createCellularAutomatorCellsForNeighbourCount:5];//second pass
}

//Count of the number of neighbours for this point
-(int)getNeighboursForRow:(int)row ForCol:(int)col
{
    int n = 0;

    //top neighbours
    if (row>0 && col>0)
    {
        for(int i=col-1;i<col+2;i++)
        {
            if (map[row-1][i]==1)
                n++;
        }
    }

    //middle neighbour left
    if (col>0 && col<maxCols)
        if (map[row][col-1]==1)
            n++;

    //middle neighbour right
    if (col>0 && col<maxCols)
        if (map[row][col+1]==1)
            n++;

    //bottom neighbours
    if (row<maxRows && col<maxCols)
    {
        for(int i=col-1;i<col+2;i++)
        {
            if (map[row+1][i]==1)
            {
                n++;
            }
        }
    }
    return n;
}

-(BOOL)ifValidCoordinateForRow:(int)row ForCol:(int)col
{
    return (map[col][row]==0 && row>0 && col > 0 && row<maxRows && col<maxCols);
}

-(void)printMap
{
    for(int i=0;i<maxRows;i++)
    {
        for(int j=0;j<maxCols;j++)
            printf("%u", map[i][j] );
        printf("\n");
    }
}

And make sure that main.m looks like

#import <Foundation/Foundation.h>
#import "Dungeon.h"
 int main(int argc, const char * argv[])
{
    @autoreleasepool {
        // insert code here...
        NSLog(@"Basic Dungeon CA Algorithm");
        Dungeon *dungeon  =[[Dungeon alloc]initWithRows:30 AndCol:50];
        [dungeon printMap];
            }
    return 0;

}

Ok so if you look at the code above in dungeon.m you should get a pretty good idea of how we are doing the generation of the dungeon using a CA algorithm. Read the comments as I have tried to explain anything a little unusual. If anything doesn’t make sense – leave a comment and I’ll get back to you.

Happy coding.