forked from shadowfacts/Tusker
122 lines
3.5 KiB
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
|
|
}
|
|
}
|