Schafkopf-Server/Sources/App/Model/Player.swift
Christoph Hagen 3db9652cad Sync push
2021-12-03 18:03:29 +01:00

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
}
}
}