WIP Rules by guessing how it is expected to work

Rules
Mathieu GROUSSEAU 3 months ago
parent 397d0df2b1
commit e49690cde6

@ -16,7 +16,7 @@ public struct Board {
self.grid = grid
}
private func isInBounds(_ pos: Coords) -> Bool {
public func isInBounds(_ pos: Coords) -> Bool {
pos.col >= 0 && pos.col < self.columns && pos.row >= 0 && pos.row < self.rows
}

@ -0,0 +1,145 @@
public struct FourInARowRules: Rules {
private(set) public var state: GameState = .Playing(turn: .A)
private(set) public var history: [Move] = []
private let columns: Int, rows: Int, minAligned: Int
init?(columns: Int, rows: Int, minAligned: Int = 4) {
guard columns >= 3, rows >= 3 else {
return nil
}
self.columns = columns
self.rows = rows
self.minAligned = minAligned
}
public func createBoard() -> Board {
Board(columns: self.columns, rows: self.rows)!
}
public func isValid(board: Board, move: Move) -> Bool {
guard self.isValid(board: board) else { return false }
switch self.state {
case .Playing(let turn) where turn != move.player:
fallthrough
case .Finished(_):
return false
default:
break
}
if case .InsertOnSide(.Top, let offset) = move.action, offset >= 0 && offset < self.columns {
return board.fallCoordinates(
initialCoords: Coords(offset, 0),
direction: .Bottom
) != .Occupied
}
return false
}
public func isValid(board: Board) -> Bool {
for c in 0..<board.columns {
var had = false;
for r in 0..<board.rows {
switch board[c, r] != nil {
case false where had:
return false
case let has:
had = has
}
}
}
return true
}
public func validMoves(board: Board) -> [Move] {
return [ Player.A, Player.B ].flatMap({
player in self.validMoves(board: board, for_player: player).map({
action in Move(player: .A, action: action)
})
})
}
public func validMoves(board: Board, for_player player: Player) -> [Move.Action] {
var moves: [Move.Action] = [];
for c in 0..<board.columns {
switch board.fallCoordinates(initialCoords: Coords(c, 0), direction: .Bottom) {
case .Border, .Piece:
moves.append(.InsertOnSide(side: .Top, offset: c))
default:
break
}
}
return moves
}
public mutating func onMoveDone(move: Move, board: Board) -> Void {
self.history.append(move)
switch move.action {
case .InsertOnSide(side: .Top, let offset):
let initCoords = board.getInsertionCoordinates(from: .Top, offset: offset)
let pieceCoords = switch board.fallCoordinates(
initialCoords: initCoords,
direction: .Bottom
) {
case .Occupied:
initCoords
case .Piece(_, let touched):
touched
default:
fatalError("Illegal move \(move.action)")
}
if countMaxRow(center: pieceCoords, board: board) >= self.minAligned {
self.state = .Finished(winner: move.player)
} else if board.countPieces() == board.columns * board.rows {
self.state = .Finished(winner: nil)
} else {
let next: Player = switch move.player {
case .A: .B
case .B: .A
}
self.state = .Playing(turn: next)
}
default:
fatalError("Illegal move \(move.action)")
}
}
private func countMaxRow(center: Coords, board: Board) -> Int {
guard let of = board[center]?.owner else { return 0 }
var maxLength = 0
// For each "axis" (described as one direction)
for dir: (dc: Int, dr: Int) in [(1, 0), (0, 1), (1, 1)] {
var length = 1
// Run in the two opposite directions of the axis to sum the length
for (dc, dr) in [(dir.dc, dir.dr), (-dir.dc, -dir.dr)] {
var pos = center
while true {
pos = Coords(pos.col + dc, pos.row + dr)
if board.isInBounds(pos) && board[pos]?.owner != of { break }
length += 1
}
}
maxLength = max(maxLength, length)
}
return maxLength
}
}

@ -0,0 +1,9 @@
public struct Move {
let player: Player
let action: Action
public enum Action {
case InsertOnSide(side: Direction, offset: Int)
case InsertAt(where: Coords)
}
}

@ -0,0 +1,23 @@
public protocol Rules {
var state: GameState { get }
var history: [Move] { get }
mutating func createBoard() -> Board
func isValid(board: Board) -> Bool
func isValid(board: Board, move: Move) -> Bool
func validMoves(board: Board) -> [Move]
func validMoves(board: Board, for_player player: Player) -> [Move.Action]
mutating func onMoveDone(move: Move, board: Board) -> Void
}
public enum GameState {
case Playing(turn: Player)
case Finished(winner: Player?)
}
Loading…
Cancel
Save