From 9d9c8ad71fc30158bf36cc9e51712b926fd52c86 Mon Sep 17 00:00:00 2001 From: Christoph Hagen Date: Tue, 7 Dec 2021 09:09:51 +0100 Subject: [PATCH] Bug fixing --- Sources/App/Infos/TableInfo.swift | 2 +- Sources/App/Model/Player.swift | 86 ++++++++++++++++++--------- Sources/App/Model/Table.swift | 98 ++++++++++++++++++++++--------- 3 files changed, 131 insertions(+), 55 deletions(-) diff --git a/Sources/App/Infos/TableInfo.swift b/Sources/App/Infos/TableInfo.swift index 41e6831..c59523a 100644 --- a/Sources/App/Infos/TableInfo.swift +++ b/Sources/App/Infos/TableInfo.swift @@ -25,7 +25,7 @@ struct TableInfo: Codable { self.playerAcross = table.playerInfo(acrossOf: playerIndex, masked: true) self.playerRight = table.playerInfo(rightOf: playerIndex, masked: true) - if table.phase == .selectGame, player.selectsGame { + if table.phase == .bidding || table.phase == .selectGame { let games = table.minimumPlayableGame?.availableGames ?? GameType.allCases self.playableGames = games.filter(player.canPlay).map { $0.id } } else { diff --git a/Sources/App/Model/Player.swift b/Sources/App/Model/Player.swift index 5e66e94..fef4f9f 100644 --- a/Sources/App/Model/Player.swift +++ b/Sources/App/Model/Player.swift @@ -187,9 +187,12 @@ final class Player { actions = [] } - func requiresBid() { + func requiresBid(hasWedding: Bool) { isNextActor = true actions = [.increaseOrMatchGame, .withdrawFromAuction] + if hasWedding { + actions.append(.acceptWedding) + } } func acceptWedding() { @@ -262,6 +265,7 @@ final class Player { selectsGame = false actions = [.doubleDuringGame] isGameLeader = false + handCards = game.sortingType.sort(rawCards).map { .init(card: $0, isPlayable: false) } if playsFirstCard { setPlayableCardsForStarter(game: game) } @@ -282,21 +286,31 @@ final class Player { actions = [] } - func didFinishTrick(canDoubleInNextRound: Bool) { - isNextActor = false - playedCard = nil + func didFinish(trick: Trick, winner: Bool, canDoubleInNextRound: Bool) { + isNextActor = winner + //startedCurrentTrick = winner + if winner { + wonTricks.append(trick) + } if canDoubleInNextRound, !isGameLeader { actions = [.doubleDuringGame] } else { actions = [] } } - - func didWin(trick: Trick) { - self.wonTricks.append(trick) - self.isNextActor = true + func didFinishGame() { + actions = [.deal] } + func clearLastTrick() { + playedCard = nil + // This flag is not set until the last trick is cleared, because + // it would mess up the stacking of the cards on the table + // which relies on this property + startedCurrentTrick = isNextActor + } + + func setPlayableCards(forCurrentTrick trick: Trick, in game: GameType?) { guard let game = game, isNextActor else { for i in 0.. PlayerInfo? { - let position = players.firstIndex { $0.startedCurrentTrick }! - let layer = (index - position + 4) % 4 - // + let starter = indexOfTrickStarter + let layer = (4 - starter + index) % 4 return player(at: index)?.info(masked: masked, positionInTrick: layer) } @@ -220,37 +227,61 @@ final class Table { if phase == .selectWeddingCard { return selectedCardForWedding(card: card, player: player) } - guard let game = gameType, - player.hasPlayable(card: card) else { + guard let game = gameType, player.hasPlayable(card: card) else { + // Player only has playable cards when it is active return .invalidCard } + if hasCompletedTrick { + // Hide cards from last trick when next card is played + players.forEach { $0.clearLastTrick() } + } player.play(card: card) if let completedTrick = completedTrick { didFinish(trick: completedTrick, in: game) + // Update cards for empty trick + players.forEach { $0.setPlayableCards(forCurrentTrick: [], in: game) } } else { let next = nextPlayer(after: player) next.isNextActor = true player.isNextActor = false + // Update cards for empty trick + players.forEach { $0.setPlayableCards(forCurrentTrick: currentTrick, in: game) } + } + if didFinishGame { + finishedGame() } - updatePlayableCards() sendUpdateToAllPlayers() return .success } - private func updatePlayableCards() { - let playedCards = currentTrick - players.forEach { - $0.setPlayableCards(forCurrentTrick: playedCards, in: gameType) + private func finishedGame() { + phase = .gameFinished + players.forEach { $0.didFinishGame() } + guard didFinishGame else { + // Either no doubles or bids + return } + // TODO: Calculate winner, points, cost + } func didFinish(trick: Trick, in game: GameType) { // If trick is completed, calculate winner - let index = trick.highCardIndex(forGame: game) + let startIndex = indexOfTrickStarter + let rotated = trick.rotated(toStartAt: startIndex) + let index = rotated.highCardIndex(forGame: game) + print("Winner \(index) for \(rotated)") + let winner = players[(startIndex + index) % 4] players.forEach { - $0.didFinishTrick(canDoubleInNextRound: didDoubleInCurrentRound) + $0.didFinish(trick: trick, + winner: winner == $0, + canDoubleInNextRound: didDoubleInCurrentRound) + } + if game == .bettel && winner.isGameLeader { + // A bettel is lost if a single trick is won by the leader + finishedGame() + return } - players[index].didWin(trick: trick) didDoubleInCurrentRound = false } @@ -285,9 +316,12 @@ final class Table { guard isFull else { return .tableNotFull } - guard phase == .waitingForPlayers else { + guard phase == .waitingForPlayers || phase == .gameFinished else { return .tableStateInvalid } + if phase == .gameFinished { + prepareForNextGame() + } let cards = Dealer.dealFirstCards() for (index, player) in players.enumerated() { @@ -303,10 +337,11 @@ final class Table { guard allPlayersFinishedDoubling else { return .success } - guard initialDoubleExists else { - return dealNextGame() + if initialDoubleExists { + dealAdditionalCards() + } else { + finishedGame() } - dealAdditionalCards() return .success } @@ -329,7 +364,7 @@ final class Table { print("Invalid minimum game \(minimumPlayableGame!) for wedding call") return .tableStateInvalid } - guard player.offersWedding else { + guard player.canOfferWedding else { print("Player does not offer wedding") return .tableStateInvalid } @@ -365,8 +400,11 @@ final class Table { players.forEach { $0.weddingOutbid() } } player.didPerformBid() + if numberOfRemainingBidders == 1 { + selectGame(player: player) + } // Find next player to place bid - nextBidder(after: player).requiresBid() + nextBidder(after: player).requiresBid(hasWedding: false) return .success } @@ -376,7 +414,7 @@ final class Table { return .tableStateInvalid } players.forEach { $0.weddingOutbid() } - firstPlayer.requiresBid() + nextBidder(after: player).requiresBid(hasWedding: true) return .success } @@ -408,7 +446,7 @@ final class Table { $0.weddingAccepted() } player.acceptWedding() - nextBidder(after: player).requiresBid() + nextBidder(after: player).requiresBid(hasWedding: false) return .success } @@ -452,27 +490,32 @@ final class Table { player.withdrawFromBidding() switch numberOfRemainingBidders { case 1: - selectGame(player: auctionWinner) + if minimumPlayableGame != nil { + // Will only be called when at least one player placed a bid + selectGame(player: auctionWinner) + return .success + } case 0: // All players withdrawn, deal new cards - return dealNextGame() + finishedGame() + return .success default: - nextBidder(after: player).requiresBid() + break } + nextBidder(after: player).requiresBid(hasWedding: weddingOfferExists) return .success } - private func dealNextGame() -> PlayerActionResult { + private func prepareForNextGame() { let first = firstPlayer let newPlayer = self.nextBidder(after: first) first.playsFirstCard = false newPlayer.playsFirstCard = true + print("Made \(newPlayer.name) to new starter") prepareTableForFirstGame() - return dealInitialCards() } private func selectGame(player: Player) { - minimumPlayableGame = nil gameType = nil phase = .selectGame players.forEach { $0.auctionEnded() } @@ -518,6 +561,7 @@ final class Table { } player.numberOfRaises += 1 players.forEach { $0.switchLeadership() } + didDoubleInCurrentRound = true return .success }