Schafkopf-Server/Sources/App/Model/Table.swift

234 lines
6.2 KiB
Swift
Raw Normal View History

2021-12-03 18:03:29 +01:00
import Foundation
import WebSocketKit
private extension Int {
mutating func advanceInTable() {
self = (self + 1) % maximumPlayersPerTable
}
}
final class Table {
let id: TableId
let name: TableName
let isPublic: Bool
var players: [Player] = []
var phase: GamePhase = .waitingForPlayers
var gameType: GameType? = nil
var minimumPlayableGame: GameType.GameClass = .ruf
/// Indicates if doubles are still allowed
var canDoubleDuringGame = false
/// Indicates if any player doubled during the current round, extending it to the next round
var didDoubleInCurrentRound = false
/// Indicates that all players acted after the first four cards
var allPlayersFinishedDoubling: Bool {
!players.contains { $0.didDoubleAfterFourCards == nil }
}
init(id: TableId, name: TableName, isPublic: Bool) {
self.id = id
self.name = name
self.isPublic = isPublic
}
init(newTable name: TableName, isPublic: Bool) {
self.id = .newToken()
self.name = name
self.isPublic = isPublic
}
func add(player: PlayerName) -> Bool {
guard !isFull else {
return false
}
let player = Player(name: player)
players.append(player)
if isFull {
prepareTableForFirstGame()
}
sendUpdateToAllPlayers()
return true
}
func contains(player: PlayerName) -> Bool {
players.contains { $0.name == player }
}
func select(player: PlayerName) -> Player? {
players.first { $0.name == player }
}
func player(leftOf index: Int) -> Player? {
player(at: (index + 1) % 4)
}
func player(acrossOf index: Int) -> Player? {
player(at: (index + 2) % 4)
}
func player(rightOf index: Int) -> Player? {
player(at: (index + 3) % 4)
}
func player(at index: Int) -> Player? {
guard index < players.count else {
return nil
}
return players[index]
}
func remove(player: PlayerName) {
guard contains(player: player) else {
return
}
players = players.filter { $0.name != player }
reset()
}
func connect(player name: PlayerName, using socket: WebSocket) -> Bool {
guard let player = select(player: name) else {
return false
}
player.connect(using: socket)
sendUpdateToAllPlayers()
return true
}
func disconnect(player name: PlayerName) {
guard let player = select(player: name) else {
return
}
guard player.disconnect() else {
return
}
sendUpdateToAllPlayers()
return
}
private func prepareTableForFirstGame() {
self.phase = .waitingForPlayers
self.gameType = nil
self.minimumPlayableGame = .ruf // Not relevant in this phase
self.canDoubleDuringGame = true // Not relevant in this phase
self.didDoubleInCurrentRound = false // Not relevant in this phase
let index = players.firstIndex { $0.playsFirstCard } ?? 0
for i in 0..<maximumPlayersPerTable {
players[i].prepareForFirstGame(isFirstPlayer: i == index)
}
}
private func sendUpdateToAllPlayers() {
players.enumerated().forEach { playerIndex, player in
guard player.isConnected else {
return
}
let info = TableInfo(self, forPlayerAt: playerIndex)
player.send(info)
}
}
// MARK: Player actions
func perform(action: Player.Action, forPlayer player: PlayerName) -> PlayerActionResult {
defer { sendUpdateToAllPlayers() }
switch action {
case .deal:
return dealInitialCards()
case .initialDoubleCost:
return perform(double: true, forPlayer: player)
case .noDoubleCost:
return perform(double: false, forPlayer: player)
case .offerWedding:
fatalError()
case .acceptWedding:
fatalError()
case .increaseOrMatchGame:
fatalError()
case .withdrawFromAuction:
fatalError()
case .doubleDuringGame:
fatalError()
}
}
private func dealInitialCards() -> PlayerActionResult {
guard isFull else {
return .tableNotFull
}
guard phase == .waitingForPlayers else {
return .tableStateInvalid
}
phase = .collectingDoubles
gameType = nil
minimumPlayableGame = .ruf
let cards = Dealer.dealFirstCards()
for (index, player) in players.enumerated() {
player.assignFirstCards(cards[index])
}
return .success
}
func perform(double: Bool, forPlayer name: PlayerName) -> PlayerActionResult {
let player = select(player: player)!
player.didDouble(double)
if allPlayersFinishedDoubling {
dealAdditionalCards()
}
return .success
}
private func dealAdditionalCards() {
let cards = Dealer.dealRemainingCards(of: players.map { $0.rawCards })
for (index, player) in players.enumerated() {
player.assignRemainingCards(cards[index])
}
return .success
}
private func startAuction() {
players.forEach { $0.startAuction() }
minimumPlayableGame = .ruf
}
private func reset() {
phase = .waitingForPlayers
gameType = nil
minimumPlayableGame = .ruf
}
}
extension Table {
var isFull: Bool {
players.count == maximumPlayersPerTable
}
var publicInfo: PublicTableInfo {
.init(id: id, name: name, players: playerNames)
}
var playerNames: [PlayerName] {
players.map { $0.name }
}
func compileInfo(for player: PlayerName) -> TableInfo? {
guard let index = players.firstIndex(where: { $0.name == player }) else {
return nil
}
return TableInfo(self, forPlayerAt: index)
}
}