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.
117 lines
3.5 KiB
117 lines
3.5 KiB
import Foundation
|
|
|
|
//TODO implement Equatable?
|
|
public struct Board : CustomStringConvertible {
|
|
|
|
public let nbRows: Int
|
|
public let nbCols: Int
|
|
|
|
|
|
/// Watch your indices -- in a empty 3x3 grid, if you insert a chip at column 0, it will fall down to grid[2][0]
|
|
/// 0 1 2
|
|
/// 0 - - -
|
|
/// 1 - - -
|
|
/// 2 x - -
|
|
///
|
|
public var grid : [[Int?]] { _grid }
|
|
var _nbFree: Int
|
|
var _grid: [[Int?]]
|
|
|
|
public init?(withRows nbRows: Int = 6, andWithCols nbCols: Int = 7) {
|
|
guard(nbRows >= 3 && nbCols >= 3) else { return nil }
|
|
self.nbRows = nbRows
|
|
self.nbCols = nbCols
|
|
self._nbFree = nbRows * nbCols
|
|
self._grid = Array(repeating: Array(repeating: nil, count: nbCols), count: nbRows)
|
|
}
|
|
|
|
public init?(withGrid grid: [[Int?]]) {
|
|
guard(grid.count >= 3
|
|
&& grid[0].count >= 3
|
|
&& grid.allSatisfy{ $0.count == grid[0].count }) else { return nil }
|
|
self.nbRows = grid.count
|
|
self.nbCols = grid[0].count
|
|
self._grid = grid
|
|
var nbFree = self.nbRows * self.nbCols
|
|
for row in grid {
|
|
for tile in row {
|
|
if tile != nil {
|
|
nbFree -= 1
|
|
if (tile != 1 && tile != 2) {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
self._nbFree = nbFree
|
|
}
|
|
|
|
func isWithinBounds(_ row: Int, and col: Int) -> Bool {
|
|
return 0 <= row && row < nbRows
|
|
&& 0 <= col && col < nbCols
|
|
}
|
|
|
|
static let descriptionMapper : [Int? : String] = [nil : "□", 1 : "X", 2: "O"]
|
|
|
|
public var description: String {
|
|
var string = String()
|
|
for row in _grid {
|
|
for tile in row {
|
|
string.append("\(String(describing: Board.descriptionMapper[tile] ?? "@"))")
|
|
string.append(" ")
|
|
}
|
|
string.append("\n")
|
|
}
|
|
string.append("\n")
|
|
return string
|
|
}
|
|
|
|
public func displayVictory(fromTiles tiles: [(Int, Int)]) -> String {
|
|
var tmpGrid: [[Int?]] = Array(repeating: Array(repeating: nil, count: nbCols), count: nbRows)
|
|
for pair in tiles { tmpGrid[pair.0][pair.1] = 42 }
|
|
|
|
var string = String()
|
|
for row in tmpGrid {
|
|
for tile in row { string.append(tile == nil ? "□ " : "$ ") }
|
|
string.append("\n")
|
|
}
|
|
return string
|
|
}
|
|
|
|
private mutating func insertChip(from playerId: Int, atRow row: Int, atCol col: Int) -> Bool {
|
|
guard(isWithinBounds(row, and: col)) else { return false }
|
|
guard((_grid[row][col] == nil)) else { return false }
|
|
|
|
_grid[row][col] = playerId
|
|
_nbFree -= 1
|
|
return true
|
|
}
|
|
|
|
public mutating func insertChip(from playerId: Int, atCol col: Int) -> Bool {
|
|
guard(0 <= col && col < nbCols) else { return false }
|
|
guard(!isFull()) else { return false }
|
|
|
|
if _grid[0][col] != nil { return false }
|
|
|
|
for i in stride(from: nbRows - 1, through: 0, by: -1) {
|
|
if _grid[i][col] == nil {
|
|
return insertChip(from: playerId, atRow: i, atCol: col)
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// mutating func removeChip(fromRow row: Int, fromCol col: Int) {
|
|
// assert(isWithinBounds(row, and: col))
|
|
// _grid[row][col] = nil
|
|
// _nbFree += 1
|
|
// }
|
|
|
|
public func isFull() -> Bool {
|
|
return _nbFree <= 0
|
|
}
|
|
|
|
}
|
|
|