From 7de2352c6165469d925452af2915264a555f6239 Mon Sep 17 00:00:00 2001 From: Christoph Hagen Date: Tue, 21 Dec 2021 14:24:53 +0100 Subject: [PATCH] Show proper game summary --- .../GameSummary/EnglishGameSummarizer.swift | 68 +++++++++++--- Sources/App/GameSummary/GameSummarizer.swift | 4 +- .../GameSummary/GermanGameSummarizer.swift | 53 ++++++++--- Sources/App/Infos/GameSummary.swift | 20 ++-- Sources/App/Model/Players/PlayingPlayer.swift | 11 ++- Sources/App/Model/Tables/DealingTable.swift | 1 - Sources/App/Model/Tables/FinishedTable.swift | 93 ++++++++++++------- Sources/App/Model/Tables/PlayingTable.swift | 20 +++- 8 files changed, 192 insertions(+), 78 deletions(-) diff --git a/Sources/App/GameSummary/EnglishGameSummarizer.swift b/Sources/App/GameSummary/EnglishGameSummarizer.swift index 5484949..0aa0aaf 100644 --- a/Sources/App/GameSummary/EnglishGameSummarizer.swift +++ b/Sources/App/GameSummary/EnglishGameSummarizer.swift @@ -2,38 +2,84 @@ import Foundation struct EnglishGameSummarizer: GameSummarizer { - let game: GameSummary + let table: FinishedTable private var winText: String { - game.didWin ? "won" : "lost" + table.selectorDidWin ? "won" : "lost" } private var gameText: String { - "game" + switch table.game { + case .rufBlatt: + return "the call of Blatt" + case .rufEichel: + return "the call of Eichel" + case .rufSchelln: + return "the call of Schelln" + case .bettel: + return "the Bettel" + case .geier: + return "the Geier" + case .wenz: + return "the Wenz" + case .hochzeit: + return "the wedding" + case .soloBlatt: + return "the Solo Blatt" + case .soloEichel: + return "the Solo Eichel" + case .soloHerz: + return "the Solo Herz" + case .soloSchelln: + return "the Solo Schelln" + } } private var coPlayerNames: String { - switch game.coPlayers.count { + let coPlayers = table.coPlayers + switch coPlayers.count { case 0: return "" case 1: - return " with \(game.coPlayers[0])" + return " with \(coPlayers[0].name)" case 2: - return " with \(game.coPlayers[0]) and \(game.coPlayers[1])" + return " with \(coPlayers[0].name) and \(coPlayers[1].name)" default: return "" } } private var costText: String { - guard game.cost >= 100 else { - return "\(game.cost) cents" + let cost = table.cost + guard cost >= 100 else { + return "\(cost) cents" } - return String(format: "%d.%02d €", game.cost / 100, game.cost % 100) + return String(format: "%d.%02d €", cost / 100, cost % 100) + } + + private var costExplanation: String { + var components = [String]() + components.append("Game \(table.game.basicCost)") + if !table.isBettel { + if table.isSchwarz { + components.append("Schwarz") + } else if table.isSchneider { + components.append("Schneider") + } + if table.leadingTrumps > 0 { + components.append("\(table.leadingTrumps) Laufende") + } + } + components.append("\(table.totalNumberOfDoubles)x doubled") + return components.joined(separator: ", ") } var text: String { - "\(game.leader) \(winText) a \(gameText)\(coPlayerNames) collecting \(game.leaderPoints) points. " + - "The game cost \(costText)." + let start = "\(table.gameSelector.name) \(winText) the \(gameText)" + let cost = " The game costs \(costText) (\(costExplanation))." + guard table.game != .bettel else { + return start + cost + } + return start + "\(coPlayerNames) with \(table.selectorTeamPoints) points." + cost } } diff --git a/Sources/App/GameSummary/GameSummarizer.swift b/Sources/App/GameSummary/GameSummarizer.swift index 779bf79..e1a7c4b 100644 --- a/Sources/App/GameSummary/GameSummarizer.swift +++ b/Sources/App/GameSummary/GameSummarizer.swift @@ -2,7 +2,9 @@ import Foundation protocol GameSummarizer { - init(game: GameSummary) + var table: FinishedTable { get } + + init(table: FinishedTable) var text: String { get } } diff --git a/Sources/App/GameSummary/GermanGameSummarizer.swift b/Sources/App/GameSummary/GermanGameSummarizer.swift index 14ba19a..383e81c 100644 --- a/Sources/App/GameSummary/GermanGameSummarizer.swift +++ b/Sources/App/GameSummary/GermanGameSummarizer.swift @@ -2,14 +2,14 @@ import Foundation struct GermanGameSummarizer: GameSummarizer { - let game: GameSummary + let table: FinishedTable - var winText: String { - game.didWin ? "gewinnt" : "verliert" + private var winText: String { + table.selectorDidWin ? "gewinnt" : "verliert" } - var gameText: String { - switch GameType(id: game.game)! { + private var gameText: String { + switch table.game { case .rufBlatt: return "den Ruf Blatt" case .rufEichel: @@ -35,28 +35,51 @@ struct GermanGameSummarizer: GameSummarizer { } } - var coPlayerNames: String { - switch game.coPlayers.count { + private var coPlayerNames: String { + let coPlayers = table.coPlayers + switch coPlayers.count { case 0: return "" case 1: - return " mit \(game.coPlayers[0])" + return " mit \(coPlayers[0].name)" case 2: - return " mit \(game.coPlayers[0]) und \(game.coPlayers[1])" + return " mit \(coPlayers[0].name) und \(coPlayers[1].name)" default: return "" } } - var costText: String { - guard game.cost >= 100 else { - return "\(game.cost) Cent" + private var costText: String { + let cost = table.cost + guard cost >= 100 else { + return "\(cost) Cent" } - return String(format: "%d.%02d €", game.cost / 100, game.cost % 100) + return String(format: "%d.%02d €", cost / 100, cost % 100) + } + + private var costExplanation: String { + var components = [String]() + components.append("Grundspiel \(table.game.basicCost)") + if !table.isBettel { + if table.isSchwarz { + components.append("Schwarz") + } else if table.isSchneider { + components.append("Schneider") + } + if table.leadingTrumps > 0 { + components.append("\(table.leadingTrumps) Laufende") + } + } + components.append("\(table.totalNumberOfDoubles)x gedoppelt") + return components.joined(separator: ", ") } var text: String { - "\(game.leader) \(winText) \(gameText)\(coPlayerNames) mit \(game.leaderPoints) Punkten. " + - "Das Spiel kostet \(costText)." + let start = "\(table.gameSelector.name) \(winText) \(gameText)" + let cost = " Das Spiel kostet \(costText) (\(costExplanation))." + guard table.game != .bettel else { + return start + cost + } + return start + "\(coPlayerNames) mit \(table.selectorTeamPoints) Punkten." + cost } } diff --git a/Sources/App/Infos/GameSummary.swift b/Sources/App/Infos/GameSummary.swift index 60a1e2b..c578e14 100644 --- a/Sources/App/Infos/GameSummary.swift +++ b/Sources/App/Infos/GameSummary.swift @@ -2,7 +2,7 @@ import Foundation struct GameSummary: Codable, Equatable { - let leader: PlayerName + let selector: PlayerName let coPlayers: [PlayerName] @@ -10,24 +10,20 @@ struct GameSummary: Codable, Equatable { let game: GameId - let leaderPoints: Int + let points: Int let cost: Int var text: String = "" init(table: FinishedTable, language: SupportedLanguage) { - let leader = table.players.first { $0.selectedGame }! - self.coPlayers = table.players - .filter { $0 != leader && $0.leadsGame == leader.leadsGame } - .map { $0.name } - self.leader = leader.name + self.selector = table.gameSelector.name + self.coPlayers = table.coPlayers.map { $0.name } self.game = table.game.id - self.leaderPoints = table.leadingPoints - self.didWin = table.winners.contains(player: leader.name) - self.cost = table.game.basicCost - // TODO: Calculate cost correctly - self.text = language.gameSummarizer.init(game: self).text + self.points = table.selectorTeamPoints + self.didWin = table.selectorDidWin + self.cost = table.cost + self.text = language.gameSummarizer.init(table: table).text } } diff --git a/Sources/App/Model/Players/PlayingPlayer.swift b/Sources/App/Model/Players/PlayingPlayer.swift index d737c96..9ca947d 100644 --- a/Sources/App/Model/Players/PlayingPlayer.swift +++ b/Sources/App/Model/Players/PlayingPlayer.swift @@ -19,7 +19,7 @@ final class PlayingPlayer: CardHoldingPlayer { var leadsGame: Bool - var numberOfDoubles = 0 + var numberOfRaises = 0 /// All tricks won by the player in this game var wonTricks: [Trick] = [] @@ -34,8 +34,13 @@ final class PlayingPlayer: CardHoldingPlayer { } } + /// The players has been called in a call game + var isCallee: Bool { + isCalledWithAce != nil + } + private var isUnknownCallee: Bool { - isCalledWithAce != nil && !didPlayCalledAce + isCallee && !didPlayCalledAce } override var actions: [PlayerAction] { @@ -165,7 +170,7 @@ final class PlayingPlayer: CardHoldingPlayer { if leadsGame { states.append(.leadsGame) } - if numberOfDoubles > 0 { + if numberOfRaises > 0 { states.append(.didRaise) } return states diff --git a/Sources/App/Model/Tables/DealingTable.swift b/Sources/App/Model/Tables/DealingTable.swift index a72ed84..c3c12a3 100644 --- a/Sources/App/Model/Tables/DealingTable.swift +++ b/Sources/App/Model/Tables/DealingTable.swift @@ -9,7 +9,6 @@ final class DealingTable: AbstractTable { player.cards = cards[index] } super.init(table: table, players: players) - print("\(self.players[0].cards.count) cards") } /// All players either doubled or didn't double diff --git a/Sources/App/Model/Tables/FinishedTable.swift b/Sources/App/Model/Tables/FinishedTable.swift index 8228e18..01b3b2a 100644 --- a/Sources/App/Model/Tables/FinishedTable.swift +++ b/Sources/App/Model/Tables/FinishedTable.swift @@ -4,42 +4,60 @@ final class FinishedTable: AbstractTable { let game: GameType + let totalNumberOfDoubles: Int + + let leadingTrumps: Int + + var cost: Int { + guard !isBettel else { + return game.basicCost * 2^^totalNumberOfDoubles + } + var cost = game.basicCost + if isSchwarz { + cost += 10 + } else if isSchneider { + cost += 5 + } + cost += 5 * leadingTrumps + return cost * 2^^totalNumberOfDoubles + } + + var gameSelector: FinishedPlayer { + players.first { $0.selectedGame }! + } + + let selectorDidWin: Bool + + var coPlayers: [FinishedPlayer] { + let selector = gameSelector + return players.filter { $0 != selector && $0.leadsGame == selector.leadsGame } + } + var winners: [FinishedPlayer] { - leadersHaveWon ? leaders : opponents + let selectorLeads = gameSelector.leadsGame + return players.filter { $0.leadsGame == (selectorDidWin == selectorLeads) } } - var loosers: [FinishedPlayer] { - leadersHaveWon ? opponents : leaders + var selectorTeamPoints: Int { + gameSelector.points + coPlayers.map { $0.points }.reduce(0, +) } - var leaders: [FinishedPlayer] { - players.filter { $0.leadsGame } - } - - var opponents: [FinishedPlayer] { - players.filter { !$0.leadsGame } - } - - var winningPoints: Int { - leadersHaveWon ? leadingPoints : 120 - leadingPoints - } - - var loosingPoints: Int { - leadersHaveWon ? 120 - leadingPoints : leadingPoints - } - - let leadingPoints: Int - - var leadersHaveWon: Bool { - leadingPoints > 60 + var isBettel: Bool { + game == .bettel } var isSchwarz: Bool { - loosingPoints == 0 + !isBettel && (selectorTeamPoints == 0 || selectorTeamPoints == 120) } var isSchneider: Bool { - loosingPoints < (leadersHaveWon ? 30 : 31) + guard !isBettel else { + return false + } + let points = selectorTeamPoints + let leads = gameSelector.leadsGame + let limit = leads ? 31 : 30 + return points < limit || 120 - points < limit } override var playedGame: GameType? { @@ -47,16 +65,29 @@ final class FinishedTable: AbstractTable { } init(table: PlayingTable) { - let players = table.players.map(FinishedPlayer.init) + let selector = table.players.first { $0.selectsGame }! self.game = table.game - leadingPoints = players - .filter { $0.leadsGame } + self.totalNumberOfDoubles = table.totalNumberOfDoubles + defer { + for player in winners { + player.isNextActor = true + } + } + + guard table.game != .bettel else { + self.selectorDidWin = selector.wonTricks.isEmpty + self.leadingTrumps = 0 + super.init(table: table, players: players) + return + } + let selectorLeads = selector.leadsGame + let teamPoints = players + .filter { $0.leadsGame == selectorLeads } .map { $0.points } .reduce(0, +) - // TODO: Set isNextActor for winners - // TODO: Check for bettel - // TODO: Set schneider, schwarz, cost + self.selectorDidWin = teamPoints > (selectorLeads ? 60 : 59) + self.leadingTrumps = table.leadingTrumps super.init(table: table, players: players) } diff --git a/Sources/App/Model/Tables/PlayingTable.swift b/Sources/App/Model/Tables/PlayingTable.swift index 8518aa3..9dcbeda 100644 --- a/Sources/App/Model/Tables/PlayingTable.swift +++ b/Sources/App/Model/Tables/PlayingTable.swift @@ -4,6 +4,8 @@ final class PlayingTable: AbstractTable { let game: GameType + let leadingTrumps: Int + var indexOfTrickStarter = 0 var didDoubleInCurrentRound = false @@ -32,6 +34,12 @@ final class PlayingTable: AbstractTable { !players.contains { !$0.cards.isEmpty } } + var totalNumberOfDoubles: Int { + players.map { + $0.numberOfRaises + ($0.didDouble ? 1 : 0) + }.reduce(0,+) + } + override var playedGame: GameType? { game } @@ -53,6 +61,11 @@ final class PlayingTable: AbstractTable { private init(table: ManageableTable, players: [PlayingPlayer], game: GameType) { self.game = game + let selectorCards = players.filter { $0.leadsGame || $0.isCallee }.map { $0.cards }.reduce([], +) + let otherCards = players.filter { !$0.leadsGame && !$0.isCallee }.map { $0.cards }.reduce([], +) + let selectorTrumps = game.sortingType.consecutiveTrumps(selectorCards) + let otherTrumps = game.sortingType.consecutiveTrumps(otherCards) + self.leadingTrumps = max(selectorTrumps, otherTrumps) super.init(table: table, players: players) players.forEach { $0.sortCards(for: game) } players.first!.isNextActor = true @@ -75,7 +88,7 @@ final class PlayingTable: AbstractTable { print("Player \(name) is not allowed to raise") return (.tableStateInvalid, nil) } - player.numberOfDoubles += 1 + player.numberOfRaises += 1 players.forEach { $0.switchLead() } self.didDoubleInCurrentRound = true return (.success, nil) @@ -122,8 +135,8 @@ final class PlayingTable: AbstractTable { 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 + if game == .bettel && winner.selectsGame { + // A bettel is lost if a single trick is won by the game selector return finishedGame() } @@ -136,7 +149,6 @@ final class PlayingTable: AbstractTable { 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) } }