You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Connect4/Model/Sources/Model/Board.swift

121 lines
3.5 KiB

public struct Board {
private var grid: [[Piece?]]
public var columns: Int { return grid.count }
public var rows: Int { return grid.first!.count }
public init?(columns: Int, rows: Int) {
self.init(grid: Array(repeating: Array(repeating: nil, count: rows), count: columns))
}
public init?(grid: [[Piece?]]) {
guard !grid.isEmpty, !grid.first!.isEmpty else {
return nil
}
self.grid = grid
}
private func isInBounds(_ pos: Coords) -> Bool {
pos.col >= 0 && pos.col < self.columns && pos.row >= 0 && pos.row < self.rows
}
private func ensureBounds(_ pos: Coords) {
precondition(isInBounds(pos), "Coordinates out of bounds")
}
public subscript(column: Int, row: Int) -> Piece? {
get { self[Coords(column, row)] }
set { self[Coords(column, row)] = newValue }
}
public subscript(pos: Coords) -> Piece? {
get {
ensureBounds(pos)
return grid[pos.col][pos.row]
}
set(piece) {
ensureBounds(pos)
grid[pos.col][pos.row] = piece
}
}
public func countPieces(filter: (Piece) -> Bool = { piece in true }) -> Int {
var count = 0
for column in grid {
for piece in column {
if let piece = piece {
if filter(piece) {
count += 1
}
}
}
}
return count
}
// insertion coordinate computation is intentionally separated from actual insertion because the user may want to
// display a where the piece would end-up before doing the actual insertion
public func getInsertionCoordinates(from: Direction, offset: Int) -> Coords {
precondition(offset >= 0, "Offset out of bounds")
switch from {
case .Top:
precondition(offset < self.columns, "Offset (column) out of bounds")
return Coords(offset, 0)
case .Left:
precondition(offset < self.rows, "Offset (row) out of bounds")
return Coords(0, offset)
case .Bottom:
precondition(offset < self.columns, "Offset (column) out of bounds")
return Coords(offset, self.rows - 1)
case .Right:
precondition(offset < self.rows, "Offset (row) out of bounds")
return Coords(self.columns - 1, offset)
}
}
public enum FallResult: Equatable {
case Border(at: Coords)
case Piece(at: Coords, touched: Coords)
case Occupied
}
public func fallCoordinates(initialCoords: Coords, direction: Direction) -> FallResult {
ensureBounds(initialCoords)
let dir: (dc: Int, dr: Int) = switch direction {
case .Top: (0, -1)
case .Left: (-1, 0)
case .Bottom: (0, 1)
case .Right: (1, 0)
}
if (self[initialCoords] != nil) {
return FallResult.Occupied
}
var coords = initialCoords
var next: Coords
while true {
next = Coords(coords.col + dir.dc, coords.row + dir.dr)
if !isInBounds(next) {
return FallResult.Border(at: coords)
} else if self[next] != nil {
return FallResult.Piece(at: coords, touched: next)
}
coords.col = next.col
coords.row = next.row
}
}
// TODO push
}