import Foundation import Fluent /** Represents a table where players are still joining and leaving. */ final class WaitingTable: AbstractTable { /// The table contains enough players to start a game var isFull: Bool { players.count >= maximumPlayersPerTable } init(id: UUID, name: TableName, isPublic: Bool, players: [User]) { let players = players.map { WaitingPlayer(name: $0.name, points: $0.points) } players.first!.isNextActor = true super.init(id: id, name: name, isPublic: isPublic, players: players) if isFull { self.players.forEach { $0.canStartGame = true } } } /** Create a new table. - Parameter name: The name of the table - Parameter isPublic: The table is visible and joinable by everyone */ init(newTable object: Table) { let user = object.players[0] let player = WaitingPlayer(name: user.name, points: user.points) player.isNextActor = true super.init(id: object.id!, name: object.name, isPublic: object.isPublic, players: [player]) } /** Convert another table to a waiting table. This is needed when a player leaves an active table. - Parameter oldTable: The table to convert - Parameter player: The name of the player to remove from the table. */ init(oldTable: ManageableTable, removing player: PlayerName) { // TODO: End game and distribute points let players = oldTable.allPlayers .filter { guard $0.name == player else { return true } _ = $0.disconnect() return false } .map { WaitingPlayer(name: $0.name, points: $0.totalPoints, socket: $0.socket) } players.first!.isNextActor = true super.init(table: oldTable, players: players) } /** Convert another table to a waiting table. This is needed when a player leaves an active table. - Parameter oldTable: The table to convert */ init(oldTableAdvancedByOne table: ManageableTable) { let players = table.allPlayers .rotatedByOne() .map(WaitingPlayer.init) super.init(table: table, players: players) players.forEach { $0.canStartGame = true } players.first!.isNextActor = true } /** Add a player to the table. - Parameter player: The name of the player to add - Returns: `true`, if the player could be added, `false` if the table is full */ func add(player: PlayerName, points: Int) -> Bool { guard !isFull else { return false } let player = WaitingPlayer(name: player, points: points) players.append(player) // Allow dealing of cards if table is full if isFull { players.forEach { $0.canStartGame = true } } return true } /** Perform an action on the waiting table. Only dealing is a valid action (if the table is full) - Parameter action: The action to perform - Parameter player: The name of the player */ override func perform(action: PlayerAction, forPlayer name: PlayerName) -> (result: PlayerActionResult, table: ManageableTable?) { // Only dealing is allowed... guard action == .deal else { return (.tableStateInvalid, nil) } // and only when table is full guard isFull else { return (.tableStateInvalid, nil) } guard let player = players.player(named: name) else { print("Unexpected action \(action) for missing player \(name) at table \(self.name)") return (.tableStateInvalid, nil) } guard player.canPerform(.deal) else { print("Player \(name) cant perform deal, although table is full") return (.tableStateInvalid, nil) } let table = DealingTable(table: self) return (.success, table) } override func play(card: Card, player name: PlayerName) -> (result: PlayerActionResult, table: ManageableTable?) { // No cards playable while waiting (.tableStateInvalid, nil) } }