It’s time to think about some game logic. The first thing a player will do is try to place a Tile on the Board. How can I tell if the move should be allowed?

To solve this problem I ended up creating a SequenceType and GeneratorType, which is the main focus of this part.

To see if a tile can go in a certain position on a board, I need to check each occupied square of the tile’s grid, and each square that matches on the board’s grid, and see if the spaces are all free.

Making a for…in loop

I could (and, indeed, did) do this by having an outer loop of rows and an inner loop of columns, but it felt a bit messy, and part of the point of this project is to look at some of the interesting things Swift permits you to do. A nicer way to work would be to be able to go something like:

for square in tile.squares {
	if square.occupied {
		... check the board at the relevant square
	}
}

The first step is to make a struct which can represent a square on a PlayingGrid:

public struct Square {
    let row: Int
    let column: Int
    let occupied: Bool?
}

occupied is an optional because sometimes I will want to create a Square and use it to query a PlayingGrid.

To allow a for…in loop to work, you need two things: a SequenceType, and a GeneratorType.

The sequence is the thing that is returned from, for example, tile.squares in the code above. Things like arrays also conform to SequenceType, which is how you can do for…in loops on them.

SequenceType has a single function you have to implement. Once you have that function, you can map, reduce, forEach and many others. The function is generate(), and that has to return a GeneratorType.

GeneratorType

GeneratorType is a simple protocol. It has one function to implement, next(), which returns whatever type your sequence is going to be made of. Swift can infer that from the way you declare the function.

In this case the generator is going to be a class, as it will need to hold some mutable state to track the last element that was generated. Here’s the setup:

public class GridSquareGenerator: GeneratorType {
    var currentRow: Int = 0
    var currentColumn: Int = -1
    
    let grid: PlayingGrid
    
    public init(grid: PlayingGrid) {
        self.grid = grid
    }
}

And here is the all-important next() method:

public func next() -> Square? {
    guard currentRow < grid.rows.count else { return nil }
    
    currentColumn += 1
    
    if currentColumn == grid[currentRow].count {
        currentColumn = 0
        currentRow += 1
    }
    if currentRow < grid.rows.count {
        return Square(row: currentRow, column: currentColumn, occupied: grid[currentRow][currentColumn])
    } else {
        return nil
    }
}

This moves along the columns and down the rows, creating and returning the squares. Generators can take many forms, you can even have ones that are theoretically infinite (for example, ones that generate mathematical series). It all depends on the state you track and the implementation of next().

SequenceType

Creating the sequence is simple:

public class GridSquareSequence: SequenceType {
    let grid: PlayingGrid
    
    public init(grid: PlayingGrid) {
        self.grid = grid
    }
    
    public func generate() -> GridSquareGenerator {
        return GridSquareGenerator(grid: grid)
    }
}

To make the sequence of squares of a PlayingGrid accessible, declare it like so:

extension PlayingGrid {
    public func squares() -> GridSquareSequence {
        return GridSquareSequence(grid: self)
    }
}

Placing a tile

With all that mess hidden away inside the PlayingGrid protocol, it is now quite nice to add placement checking logic to the board:

extension Board {
    public func canPositionTile(tile: Tile, atSquare: Square) -> Bool {
        
        for tileSquare in tile.squares() {
            let boardSquare = tileSquare.offsetBy(atSquare)
            if !squareWithinBoard(boardSquare) {
                return false
            }
            if tileSquare.occupied == true && squareOccupied(boardSquare) {
                return false
            }
        }
        return true
    }
}

I added a couple of utility functions to make this more readable, not shown here.

At this point, the blog posts have caught up to where I am in development. So from now on I’ll be giving commit links to the repo for the state at the end of each post. Here’s the first.

In the next post I talk about updating the board’s model when tiles are placed and removed.