// // 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 } }