Tusker/Packages/TTTKit/Sources/TTTKit/Logic/GameController.swift

122 lines
3.5 KiB
Swift

//
// GameController.swift
// TTTKit
//
// Created by Shadowfacts on 12/21/22.
//
import Foundation
import Combine
public class GameController: ObservableObject {
@Published public private(set) var state: State
@Published public private(set) var board: SuperTicTacToeBoard
public init() {
self.state = .playAnywhere(.x)
self.board = SuperTicTacToeBoard()
}
init(state: State, board: SuperTicTacToeBoard) {
self.state = state
self.board = board
}
public func play(on subBoard: (column: Int, row: Int), column: Int, row: Int) {
guard board.getSubBoard(column: subBoard.column, row: subBoard.row)[column, row] == nil else {
return
}
let activePlayer: Mark
switch state {
case .playAnywhere(let mark):
activePlayer = mark
case .playSpecific(let mark, column: subBoard.column, row: subBoard.row):
activePlayer = mark
default:
return
}
board.play(mark: activePlayer, subBoard: subBoard, column: column, row: row)
if let win = board.win {
state = .end(.won(win))
} else if board.tied {
state = .end(.tie)
} else {
let nextSubBoard = board.getSubBoard(column: column, row: row)
if nextSubBoard.ended {
state = .playAnywhere(activePlayer.next)
} else {
state = .playSpecific(activePlayer.next, column: column, row: row)
}
}
}
private func canPlay(on subBoard: (column: Int, row: Int)) -> Bool {
switch state {
case .playAnywhere(_):
return true
case .playSpecific(_, column: subBoard.column, row: subBoard.row):
return true
default:
return false
}
}
}
public extension GameController {
enum State {
case playAnywhere(Mark)
case playSpecific(Mark, column: Int, row: Int)
case end(Result)
public var displayName: String {
switch self {
case .playAnywhere(_):
return "Play anywhere"
case .playSpecific(_, column: let col, row: let row):
switch (col, row) {
case (0, 0):
return "Play in the top left"
case (1, 0):
return "Play in the top middle"
case (2, 0):
return "Play in the top right"
case (0, 1):
return "Play in the middle left"
case (1, 1):
return "Play in the center"
case (2, 1):
return "Play in the middle right"
case (0, 2):
return "Play in the bottom left"
case (1, 2):
return "Play in the bottom middle"
case (2, 2):
return "Play in the bottom right"
default:
fatalError()
}
case .end(.tie):
return "It's a tie!"
case .end(.won(let win)):
switch win.mark {
case .x:
return "X wins!"
case .o:
return "O wins!"
}
}
}
}
}
public extension GameController {
enum Result {
case won(Win)
case tie
}
}