196 lines
5.6 KiB
Swift
196 lines
5.6 KiB
Swift
|
import Foundation
|
||
|
import WebSocketKit
|
||
|
import CloudKit
|
||
|
|
||
|
private let encoder = JSONEncoder()
|
||
|
|
||
|
final class Player {
|
||
|
|
||
|
let name: PlayerName
|
||
|
|
||
|
/// The player is the first to play a card in a new game
|
||
|
var playsFirstCard = false
|
||
|
|
||
|
/// The player is the next to perform an action (e.g. play a card)
|
||
|
var isNextActor = false
|
||
|
|
||
|
/// The player must select the game to play after winning the auction
|
||
|
var selectsGame = false
|
||
|
|
||
|
/// The players plays/played the first card for the current trick
|
||
|
var startedCurrentTrick = false
|
||
|
|
||
|
/// The action available to the player
|
||
|
var actions: [Action] = []
|
||
|
|
||
|
/// Indicates if the player doubled ("legen")
|
||
|
var didDoubleAfterFourCards: Bool? = nil
|
||
|
|
||
|
/// Indicates if the player is still involved in the bidding process
|
||
|
var isStillBidding = true
|
||
|
|
||
|
/// Indicates that the player leads the game ("Spieler")
|
||
|
var isGameLeader = false
|
||
|
|
||
|
/// Indicates the number of raises ("Schuss") of the player
|
||
|
var numberOfRaises = 0
|
||
|
|
||
|
/// The remaining cards of the player
|
||
|
var handCards: [PlayableCard] = []
|
||
|
|
||
|
/// The card played for the current trick
|
||
|
var playedCard: Card? = nil
|
||
|
|
||
|
/// All tricks won by the player in this game
|
||
|
var wonTricks: [Trick] = []
|
||
|
|
||
|
var socket: WebSocket? = nil
|
||
|
|
||
|
init(name: PlayerName) {
|
||
|
self.name = name
|
||
|
}
|
||
|
|
||
|
var rawCards: [Card] {
|
||
|
handCards.map { $0.card }
|
||
|
}
|
||
|
|
||
|
func connect(using socket: WebSocket) {
|
||
|
_ = self.socket?.close()
|
||
|
self.socket = socket
|
||
|
}
|
||
|
|
||
|
func send(_ info: TableInfo) {
|
||
|
try? socket?.send(encodeJSON(info))
|
||
|
}
|
||
|
|
||
|
func disconnect() -> Bool {
|
||
|
guard let socket = socket else {
|
||
|
return false
|
||
|
}
|
||
|
do {
|
||
|
try socket.close().wait()
|
||
|
} catch {
|
||
|
print("Failed to close socket for player: \(name): \(error)")
|
||
|
}
|
||
|
self.socket = nil
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func prepareForFirstGame(isFirstPlayer: Bool) {
|
||
|
playsFirstCard = isFirstPlayer
|
||
|
isNextActor = isFirstPlayer
|
||
|
selectsGame = false // Not relevant in this phase
|
||
|
startedCurrentTrick = isFirstPlayer
|
||
|
actions = [.deal]
|
||
|
didDoubleAfterFourCards = nil // Not relevant in this phase
|
||
|
isStillBidding = false // Not relevant in this phase
|
||
|
isGameLeader = false // Not relevant in this phase
|
||
|
numberOfRaises = 0 // Not relevant in this phase
|
||
|
handCards = []
|
||
|
playedCard = nil
|
||
|
wonTricks = []
|
||
|
}
|
||
|
|
||
|
func assignFirstCards(_ cards: Hand) {
|
||
|
selectsGame = false // Not relevant in this phase
|
||
|
actions = [.initialDoubleCost, .noDoubleCost]
|
||
|
didDoubleAfterFourCards = nil
|
||
|
isStillBidding = false // Not relevant in this phase
|
||
|
isGameLeader = false // Not relevant in this phase
|
||
|
numberOfRaises = 0 // Not relevant in this phase
|
||
|
handCards = cards.map { .init(card: $0, isPlayable: false) }
|
||
|
playedCard = nil
|
||
|
wonTricks = []
|
||
|
}
|
||
|
|
||
|
func didDouble(_ double: Bool) {
|
||
|
selectsGame = false // Not relevant in this phase
|
||
|
actions = []
|
||
|
didDoubleAfterFourCards = double
|
||
|
isStillBidding = false // Not relevant in this phase
|
||
|
isGameLeader = false // Not relevant in this phase
|
||
|
numberOfRaises = 0 // Not relevant in this phase
|
||
|
playedCard = nil
|
||
|
wonTricks = []
|
||
|
}
|
||
|
|
||
|
func assignRemainingCards(_ cards: Hand) {
|
||
|
isStillBidding = true
|
||
|
isGameLeader = false
|
||
|
numberOfRaises = 0
|
||
|
handCards = (rawCards + cards)
|
||
|
.sorted(CardOrderType: .normal)
|
||
|
.map { .init(card: $0, isPlayable: false) }
|
||
|
playedCard = nil
|
||
|
wonTricks = []
|
||
|
}
|
||
|
|
||
|
func startAuction() {
|
||
|
selectsGame = false // Not relevant in this phase
|
||
|
if playsFirstCard {
|
||
|
actions = [.withdrawFromAuction, .increaseOrMatchGame]
|
||
|
} else {
|
||
|
|
||
|
}
|
||
|
actions = []
|
||
|
isStillBidding = true
|
||
|
isGameLeader = false // Not relevant in this phase
|
||
|
numberOfRaises = 0 // Not relevant in this phase
|
||
|
playedCard = nil
|
||
|
wonTricks = []
|
||
|
}
|
||
|
|
||
|
func info(masked: Bool) -> PlayerInfo {
|
||
|
.init(player: self, isMasked: masked)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
extension Player {
|
||
|
|
||
|
/// Indicate that the player is connected when at a table
|
||
|
var isConnected: Bool {
|
||
|
guard let socket = socket else {
|
||
|
return false
|
||
|
}
|
||
|
guard !socket.isClosed else {
|
||
|
self.socket = nil
|
||
|
return false
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
extension Player {
|
||
|
|
||
|
enum Action: String, Codable {
|
||
|
/// The player can request cards to be dealt
|
||
|
case deal = "deal"
|
||
|
|
||
|
/// The player doubles on the initial four cards
|
||
|
case initialDoubleCost = "double"
|
||
|
|
||
|
/// The player does not double on the initial four cards
|
||
|
case noDoubleCost = "skip"
|
||
|
|
||
|
/// The player offers a wedding (one trump card)
|
||
|
case offerWedding = "wedding"
|
||
|
|
||
|
/// The player can choose to accept the wedding
|
||
|
case acceptWedding = "accept"
|
||
|
|
||
|
/// The player matches or increases the game during auction
|
||
|
case increaseOrMatchGame = "bid"
|
||
|
|
||
|
/// The player does not want to play
|
||
|
case withdrawFromAuction = "out"
|
||
|
|
||
|
/// The player claims to win and doubles the game cost ("schießen")
|
||
|
case doubleDuringGame = "raise"
|
||
|
|
||
|
/// The url path for the client to call (e.g. /player/deal)
|
||
|
var path: String {
|
||
|
rawValue
|
||
|
}
|
||
|
}
|
||
|
}
|