2021-12-09 11:11:17 +01:00
|
|
|
import Foundation
|
|
|
|
|
2021-12-18 15:08:43 +01:00
|
|
|
final class PlayingTable: AbstractTable<PlayingPlayer> {
|
2021-12-09 11:11:17 +01:00
|
|
|
|
2021-12-18 15:08:43 +01:00
|
|
|
let game: GameType
|
2021-12-09 11:11:17 +01:00
|
|
|
|
2021-12-18 15:08:43 +01:00
|
|
|
var indexOfTrickStarter = 0
|
|
|
|
|
|
|
|
var didDoubleInCurrentRound = false
|
|
|
|
|
|
|
|
var hasCompletedTrick: Bool {
|
|
|
|
!players.contains { $0.playedCard == nil }
|
|
|
|
}
|
|
|
|
|
|
|
|
var nextTrick: [Card] {
|
|
|
|
hasCompletedTrick ? [] : currentTrick
|
|
|
|
}
|
|
|
|
|
|
|
|
var currentTrick: [Card] {
|
|
|
|
players.rotated(toStartAt: indexOfTrickStarter).compactMap { $0.playedCard }
|
|
|
|
}
|
|
|
|
|
|
|
|
var completedTrick: Trick? {
|
|
|
|
let trick = currentTrick
|
|
|
|
guard trick.count == maximumPlayersPerTable else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return trick
|
2021-12-09 11:11:17 +01:00
|
|
|
}
|
|
|
|
|
2021-12-18 15:08:43 +01:00
|
|
|
var allCardsPlayed: Bool {
|
|
|
|
!players.contains { !$0.cards.isEmpty }
|
|
|
|
}
|
|
|
|
|
|
|
|
override var playedGame: GameType? {
|
|
|
|
game
|
|
|
|
}
|
2021-12-09 11:11:17 +01:00
|
|
|
|
2021-12-18 15:08:43 +01:00
|
|
|
convenience init(table: BiddingTable, game: GameType, playedBy player: BiddingPlayer) {
|
|
|
|
let calledAce = game.calledSuit?.ace
|
|
|
|
let players = table.players.map {
|
|
|
|
PlayingPlayer(player: $0, leads: $0 == player, calledAce: calledAce)
|
|
|
|
}
|
|
|
|
self.init(table: table, players: players, game: game)
|
2021-12-09 11:11:17 +01:00
|
|
|
}
|
|
|
|
|
2021-12-18 15:08:43 +01:00
|
|
|
convenience init(wedding table: WeddingTable, offeredBy offerer: WeddingPlayer, acceptedBy player: WeddingPlayer) {
|
|
|
|
let players = table.players.map {
|
|
|
|
PlayingPlayer(player: $0, leads: $0 == player || $0 == offerer, calledAce: nil)
|
|
|
|
}
|
|
|
|
self.init(table: table, players: players, game: .hochzeit)
|
2021-12-09 11:11:17 +01:00
|
|
|
}
|
|
|
|
|
2021-12-18 15:08:43 +01:00
|
|
|
private init(table: ManageableTable, players: [PlayingPlayer], game: GameType) {
|
|
|
|
self.game = game
|
|
|
|
super.init(table: table, players: players)
|
|
|
|
players.forEach { $0.sortCards(for: game) }
|
|
|
|
players.first!.isNextActor = true
|
2021-12-09 11:11:17 +01:00
|
|
|
}
|
|
|
|
|
2021-12-18 15:08:43 +01:00
|
|
|
override func cardStackPosition(ofPlayerAt index: Int) -> Int {
|
|
|
|
(4 + index - indexOfTrickStarter) % 4
|
2021-12-09 11:11:17 +01:00
|
|
|
}
|
|
|
|
|
2021-12-18 15:08:43 +01:00
|
|
|
override func perform(action: PlayerAction, forPlayer name: PlayerName) -> (result: PlayerActionResult, table: ManageableTable?) {
|
|
|
|
guard let player = players.player(named: name) else {
|
|
|
|
print("Player \(name) unexpectedly missing from playing table \(self.name)")
|
|
|
|
return (.tableStateInvalid, nil)
|
|
|
|
}
|
|
|
|
guard action == .doubleDuringGame else {
|
|
|
|
print("Player \(name) wants to perform action \(action) on playing table")
|
|
|
|
return (.tableStateInvalid, nil)
|
|
|
|
}
|
|
|
|
guard player.canPerform(.doubleDuringGame) else {
|
|
|
|
print("Player \(name) is not allowed to raise")
|
|
|
|
return (.tableStateInvalid, nil)
|
|
|
|
}
|
|
|
|
player.numberOfDoubles += 1
|
|
|
|
players.forEach { $0.switchLead() }
|
|
|
|
self.didDoubleInCurrentRound = true
|
|
|
|
return (.success, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
override func play(card: Card, player name: PlayerName) -> (result: PlayerActionResult, table: ManageableTable?) {
|
|
|
|
guard let player = players.player(named: name) else {
|
|
|
|
print("Player \(name) unexpectedly missing from playing table \(self.name)")
|
|
|
|
return (.tableStateInvalid, nil)
|
|
|
|
}
|
|
|
|
guard player.isNextActor else {
|
|
|
|
print("Player \(name) wants to play card but is not active")
|
|
|
|
return (.tableStateInvalid, nil)
|
|
|
|
}
|
|
|
|
guard player.canPlay(card: card, for: nextTrick, in: game) else {
|
|
|
|
return (.tableStateInvalid, nil)
|
|
|
|
}
|
|
|
|
if hasCompletedTrick {
|
|
|
|
players.forEach { $0.playedCard = nil }
|
|
|
|
indexOfTrickStarter = players.index(of: player)
|
|
|
|
}
|
|
|
|
player.play(card: card)
|
|
|
|
if let completedTrick = completedTrick {
|
|
|
|
return didFinish(trick: completedTrick, in: game)
|
|
|
|
} else {
|
|
|
|
let next = players.next(after: player)
|
|
|
|
next.isNextActor = true
|
|
|
|
player.isNextActor = false
|
|
|
|
return (.success, nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override func playerData(at index: Int) -> (actions: [PlayerAction], games: [GameConvertible], cards: [PlayableCard], selectsGame: Bool) {
|
|
|
|
let player = players[index]
|
|
|
|
let cards = player.playableCards(for: nextTrick, in: game)
|
|
|
|
return (actions: player.actions, games: [], cards: cards, selectsGame: false)
|
|
|
|
}
|
|
|
|
|
|
|
|
private func didFinish(trick: Trick, in game: GameType) -> (result: PlayerActionResult, table: ManageableTable?) {
|
|
|
|
let index = trick.highCardIndex(forGame: game)
|
|
|
|
let winner = players[(indexOfTrickStarter + index) % 4]
|
|
|
|
players.forEach {
|
|
|
|
$0.isNextActor = false
|
|
|
|
$0.canStillRaise = didDoubleInCurrentRound
|
|
|
|
}
|
|
|
|
winner.wonTricks.append(trick)
|
|
|
|
winner.isNextActor = true
|
|
|
|
|
|
|
|
if game == .bettel && winner.leadsGame {
|
|
|
|
// A bettel is lost if a single trick is won by the leader
|
|
|
|
return finishedGame()
|
|
|
|
}
|
|
|
|
|
|
|
|
didDoubleInCurrentRound = false
|
|
|
|
if allCardsPlayed {
|
|
|
|
return finishedGame()
|
|
|
|
}
|
|
|
|
return (.success, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
private func finishedGame() -> (result: PlayerActionResult, table: ManageableTable?) {
|
|
|
|
let table = FinishedTable(table: self)
|
|
|
|
print("\(table.winners) have won with \(table.winningPoints) to \(table.loosingPoints) points")
|
|
|
|
return (.success, table)
|
|
|
|
}
|
2021-12-09 11:11:17 +01:00
|
|
|
}
|