194 lines
7.2 KiB
Swift
194 lines
7.2 KiB
Swift
import Foundation
|
|
|
|
final class BiddingTable: AbstractTable<BiddingPlayer> {
|
|
|
|
var gameToOutbid: GameType.GameClass = .none
|
|
|
|
var indexOfHighestBidder = 0
|
|
|
|
var remainingBidders: Int {
|
|
players.filter { $0.isStillBidding }.count
|
|
}
|
|
|
|
var isWaitingForGameSelection: Bool {
|
|
players.contains { $0.selectsGame }
|
|
}
|
|
|
|
init(table: DealingTable) {
|
|
// Add new cards to the players
|
|
let newCards = Dealer.dealRemainingCards(of: table.players.map { $0.cards })
|
|
let players: [BiddingPlayer] = table.players.enumerated().map { index, player in
|
|
player.cards = (player.cards + newCards[index])
|
|
.sortedCards(order: NormalCardOrder.self)
|
|
player.isNextActor = false
|
|
return BiddingPlayer(player: player)
|
|
}
|
|
players.first!.isNextActor = true
|
|
super.init(table: table, players: players)
|
|
}
|
|
|
|
init(wedding table: WeddingTable, outbidBy player: WeddingPlayer) {
|
|
gameToOutbid = .hochzeit
|
|
indexOfHighestBidder = table.players.index(of: player)
|
|
// All players can bid again, except the wedding offerer
|
|
let players = table.players.map(BiddingPlayer.init)
|
|
players[indexOfHighestBidder].isNextActor = true
|
|
super.init(table: table, players: players)
|
|
|
|
// Choose the player after the one who discarded the wedding
|
|
selectNextBidder()
|
|
}
|
|
|
|
func select(game: GameType, player name: PlayerName) -> (result: PlayerActionResult, table: ManageableTable?) {
|
|
guard let player = players.player(named: name) else {
|
|
log("Player \(name) unexpectedly missing from bidding table \(self.name)")
|
|
return (.tableStateInvalid, nil)
|
|
}
|
|
guard player.selectsGame else {
|
|
log("Player \(name) does not select the game")
|
|
return (.tableStateInvalid, nil)
|
|
}
|
|
guard gameToOutbid.allows(game: game) else {
|
|
log("Game \(game) not allowed for class \(gameToOutbid)")
|
|
return (.tableStateInvalid, nil)
|
|
}
|
|
guard player.canPlay(game: game) else {
|
|
log("Player \(game) can't play game \(game)")
|
|
return (.tableStateInvalid, nil)
|
|
}
|
|
|
|
let table = PlayingTable(table: self, game: game, playedBy: player)
|
|
return (.success, table)
|
|
}
|
|
|
|
@discardableResult
|
|
private func selectNextBidder() -> Bool {
|
|
guard let index = players.firstIndex(where: { $0.isNextActor }) else {
|
|
log("Bidding: No current actor found to select next bidder")
|
|
return false
|
|
}
|
|
players[index].isNextActor = false
|
|
let newActor = players.rotated(toStartAt: (index + 1) % 4).first(where: { $0.isStillBidding })!
|
|
newActor.isNextActor = true
|
|
return true
|
|
}
|
|
|
|
override func perform(action: PlayerAction, forPlayer name: PlayerName) -> (result: PlayerActionResult, table: ManageableTable?) {
|
|
guard let player = players.player(named: name) else {
|
|
log("Player \(name) unexpectedly missing from bidding table \(self.name)")
|
|
return (.tableStateInvalid, nil)
|
|
}
|
|
guard player.canPerform(action) else {
|
|
return (.tableStateInvalid, nil)
|
|
}
|
|
|
|
switch action {
|
|
case .offerWedding:
|
|
return performWeddingOffer(forPlayer: player)
|
|
case .increaseOrMatchGame:
|
|
return performBidIncrease(forPlayer: player)
|
|
case .withdrawFromAuction:
|
|
return performWithdrawl(forPlayer: player)
|
|
default:
|
|
return (.tableStateInvalid, nil)
|
|
}
|
|
}
|
|
|
|
private func performWeddingOffer(forPlayer player: BiddingPlayer) -> (result: PlayerActionResult, table: ManageableTable?) {
|
|
guard gameToOutbid.allowsWedding else {
|
|
return (.tableStateInvalid, nil)
|
|
}
|
|
let newTable = WeddingTable(table: self, offerer: player)
|
|
return (.success, newTable)
|
|
}
|
|
|
|
private func performBidIncrease(forPlayer player: BiddingPlayer) -> (result: PlayerActionResult, table: ManageableTable?) {
|
|
guard player.isNextActor, player.isStillBidding else {
|
|
return (.tableStateInvalid, nil)
|
|
}
|
|
let index = players.index(of: player)
|
|
if index < indexOfHighestBidder {
|
|
// Player sits before the current highest bidder, so only needs to match the game
|
|
indexOfHighestBidder = index
|
|
if gameToOutbid == .solo {
|
|
// Can't be outbid, so player selects game
|
|
players.forEach { $0.isStillBidding = false }
|
|
player.selectsGame = true
|
|
return (.success, nil)
|
|
}
|
|
// TODO: Check that wedding can be offered at the correct times
|
|
// There may be a case where a player sitting before the highest bidder
|
|
// can't offer a wedding anymore although it should be able to
|
|
if !gameToOutbid.allowsWedding {
|
|
players.forEach { $0.isAllowedToOfferWedding = false }
|
|
}
|
|
} else {
|
|
// Player sits after the highest bidder, so must outbid the game
|
|
// Also the case when first starting bidding
|
|
gameToOutbid.increase()
|
|
indexOfHighestBidder = index
|
|
if !gameToOutbid.allowsWedding {
|
|
players.forEach { $0.isAllowedToOfferWedding = false }
|
|
}
|
|
}
|
|
if remainingBidders == 1 {
|
|
moveToGameSelection()
|
|
} else {
|
|
selectNextBidder()
|
|
}
|
|
return (.success, nil)
|
|
}
|
|
|
|
private func performWithdrawl(forPlayer player: BiddingPlayer) -> (result: PlayerActionResult, table: ManageableTable?) {
|
|
guard player.isStillBidding else {
|
|
return (.tableStateInvalid, nil)
|
|
}
|
|
player.isStillBidding = false
|
|
switch remainingBidders {
|
|
case 0:
|
|
// Nobody wants to play something, so abort the game
|
|
// This case can only be reached when nobody has bid yet
|
|
let table = WaitingTable(oldTableAdvancedByOne: self)
|
|
return (.success, table)
|
|
case 1:
|
|
if gameToOutbid != .none {
|
|
// Last player must play
|
|
player.isNextActor = false
|
|
moveToGameSelection()
|
|
return (.success, nil)
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
selectNextBidder()
|
|
return (.success, nil)
|
|
}
|
|
|
|
private func moveToGameSelection() {
|
|
indexOfHighestBidder = players.firstIndex { $0.isStillBidding == true }!
|
|
let highestPlayer = players[indexOfHighestBidder]
|
|
highestPlayer.isStillBidding = false
|
|
highestPlayer.selectsGame = true
|
|
highestPlayer.isNextActor = true
|
|
}
|
|
|
|
override func cards(forPlayerAt index: Int) -> [PlayableCard] {
|
|
players[index].cards.unplayable
|
|
}
|
|
|
|
override func games(forPlayerAt index: Int) -> [GameConvertible] {
|
|
if isWaitingForGameSelection {
|
|
let player = players[index]
|
|
return gameToOutbid.availableGames.filter(player.canPlay)
|
|
}
|
|
if index <= indexOfHighestBidder {
|
|
return gameToOutbid.availableClasses
|
|
}
|
|
return gameToOutbid.classesWhenOutbidding
|
|
}
|
|
|
|
override func gameIsSelected(byPlayerAt index: Int) -> Bool {
|
|
players[index].selectsGame
|
|
}
|
|
}
|