Search code examples
swiftsprite-kitcellular-automata

Procedural Level Generation With Cellular Automaton In Swift


Is there a easy way to create a procedural level with a cellular automaton in swift/SpriteKit(library?)? I want to create a 'cave' with 11 fields in the height and 22 width. These should be randomly created and every field without a wall should be reached. I just found a documentation using Objective-C, which I am not familiar with. I spend quite some time trying to understand the code and follow the example without success.

PS: If there is an easier way I appreciate some algorithms


Solution

  • I made a Playground where you can experiment

    //: Playground - noun: a place where people can play
    
    import UIKit
    import SpriteKit
    import XCPlayground
    
    
    class Cave {
    
        var cellmap:[[Bool]]
    
        let chanceToStartAlive = 35
        let deathLimit = 3
        let birthLimit = 4
        var xCell = 40 // number of cell in x axes
        var yCell = 20 // number of cell in y axes
        var wCell = 20 // cell width
        var hCell = 20 // cell height
    
        init(){
            cellmap = Array(count:yCell, repeatedValue:
                Array(count:xCell, repeatedValue:false))
    
            cellmap = self.initialiseMap(xCell, yIndex:yCell)
        }
    
        func initialiseMap(xIndex:Int, yIndex:Int) -> [[Bool]]{
            var map:[[Bool]] = Array(count:yIndex, repeatedValue:
                Array(count:xIndex, repeatedValue:false))
    
            for y in 0...(yIndex - 1) {
                for x in 0...(xIndex - 1) {
    
                    let diceRoll = Int(arc4random_uniform(100))
    
                    if diceRoll < chanceToStartAlive {
                        map[y][x] = true
                    } else {
                        map[y][x] = false
                    }
                }
            }
            return map
        }
    
        func addSprite(scene:SKScene){
            for (indexY, row) in cellmap.enumerate(){
                for (indexX, isWall) in row.enumerate(){
                    if isWall {
    
                        let wall = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: wCell, height: hCell))
                        wall.position = CGPoint(x: (indexX * wCell) + (wCell / 2) , y: (indexY * hCell) + (hCell / 2)  )
                        scene.addChild(wall)
                    }
                }
            }
        }
    
        func countAliveNeighbours(x:Int, y:Int) -> Int{
    
            var count = 0
            var neighbour_x = 0
            var neighbour_y = 0
    
            for i in -1...1 {
    
                for j in -1...1 {
    
                    neighbour_x = x + j
                    neighbour_y = y + i
    
    
                    if(i == 0 && j == 0){
                    } else if(neighbour_x < 0 || neighbour_y < 0 || neighbour_y >= cellmap.count || neighbour_x >= cellmap[0].count){
                        count = count + 1
                    } else if(cellmap[neighbour_y][neighbour_x]){
                        count = count + 1
                    }
    
                }
    
            }
    
            return count
    
        }
    
        func applyRules(){
    
            var newMap:[[Bool]] = Array(count:yCell, repeatedValue:
                Array(count:xCell, repeatedValue:false))
    
    
    
            for y in 0...(cellmap.count - 1) {
                for x in 0...(cellmap[0].count - 1) {
                    let nbs = countAliveNeighbours( x, y: y);
                    if(cellmap[y][x]){
                        if(nbs < deathLimit){
                            newMap[y][x] = false;
                        }
                        else{
                            newMap[y][x] = true;
                        }
                    } else{
                        if(nbs > birthLimit){
                            newMap[y][x] = true;
                        }
                        else{
                            newMap[y][x] = false;
                        }
                    }
                }
            }
            cellmap = newMap
        }
    }
    
    let view:SKView = SKView(frame: CGRectMake(0, 0, 1024, 768))
    
    XCPShowView("Live View", view: view)
    
    let scene:SKScene = SKScene(size: CGSizeMake(1024, 768))
    scene.scaleMode = SKSceneScaleMode.AspectFit
    
    let aCave = Cave()
    
    aCave.applyRules()
    aCave.applyRules()
    
    aCave.addSprite(scene)
    view.presentScene(scene)