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.
121 lines
3.5 KiB
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
|
|
}
|