Refactor player classes and enhance game logic; add tie handling and player input management
This commit is contained in:
@@ -9,7 +9,11 @@ trait CardManager {
|
||||
def resetOrder(): Unit
|
||||
|
||||
def nextCard(): Card
|
||||
|
||||
def remainingCards: Int = cardContainer.size - currentIndx
|
||||
|
||||
def removeCards(amount: Int): List[Card]
|
||||
|
||||
def createHand(amount: Int = 7): Hand
|
||||
|
||||
def grabSpecificCard(card: Card): Card
|
||||
|
||||
@@ -46,6 +46,14 @@ class CardBaseManager extends CardManager {
|
||||
}
|
||||
}
|
||||
|
||||
override def removeCards(amount: Int): List[Card] = {
|
||||
val removedCards = ListBuffer[Card]()
|
||||
for (_ <- 0 to amount) {
|
||||
removedCards += nextCard()
|
||||
}
|
||||
removedCards.toList
|
||||
}
|
||||
|
||||
override def createHand(amount: Int = 7): Hand = {
|
||||
val hand = ListBuffer[Card]()
|
||||
for (_ <- 1 to amount) {
|
||||
|
||||
@@ -10,6 +10,8 @@ object StubCardManager extends CardManager {
|
||||
override def resetOrder(): Unit = {}
|
||||
|
||||
override def nextCard(): Card = Card(CardValue.Ace, Suit.Clubs)
|
||||
|
||||
override def removeCards(amount: Int): List[Card] = List(Card(CardValue.Ace, Suit.Clubs))
|
||||
|
||||
override def createHand(amount: Int): Hand = Hand(List(Card(CardValue.Ace, Suit.Clubs)))
|
||||
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
package de.knockoutwhist.control
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.cards.base.CardBaseManager
|
||||
import de.knockoutwhist.cards.{Card, Hand, Suit}
|
||||
import de.knockoutwhist.control.ControlHandler
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Round, Trick}
|
||||
|
||||
import scala.util.Random
|
||||
|
||||
case object AILogic {
|
||||
|
||||
def decideCard(ai: AbstractPlayer, round: Round, trick: Trick): Card = {
|
||||
if(trick.firstCard.isEmpty) return ai.currentHand().get.cards.maxBy(_.cardValue.ordinal)
|
||||
val firstCardSuit = trick.firstCard.get.suit
|
||||
val hand = ai.currentHand().get
|
||||
val cardsOfSuit = hand.cards.filter(_.suit == firstCardSuit)
|
||||
val trumpsInGame = trick.cards.keys.filter(_.suit == round.trumpSuit)
|
||||
if (cardsOfSuit.isEmpty) {
|
||||
val trumpCards = hand.cards.filter(_.suit == round.trumpSuit)
|
||||
if (trumpCards.isEmpty) hand.cards.minBy(_.cardValue.ordinal)
|
||||
else {
|
||||
val bestOption = decideWhichTrumpCard(hand, round, trick, trumpsInGame.toList)
|
||||
grabBestResult(bestOption, hand, round, trick)
|
||||
}
|
||||
} else {
|
||||
if(trumpsInGame.nonEmpty) cardsOfSuit.minBy(_.cardValue.ordinal)
|
||||
else cardsOfSuit.maxBy(_.cardValue.ordinal)
|
||||
}
|
||||
KnockOutWhist.config.cardManager.nextCard()
|
||||
}
|
||||
|
||||
|
||||
private def grabBestResult(bestOption: Option[Card], hand: Hand, round: Round, trick: Trick): Card = {
|
||||
bestOption match {
|
||||
case Some(card) => card
|
||||
case None =>
|
||||
val card = hand.cards.filter(_.suit != round.trumpSuit)
|
||||
if (card.isEmpty) hand.cards.minBy(_.cardValue.ordinal)
|
||||
else card.minBy(_.cardValue.ordinal)
|
||||
}
|
||||
}
|
||||
|
||||
private def decideWhichTrumpCard(hand: Hand, round: Round, trick: Trick, activeTrumps: List[Card]): Option[Card] = {
|
||||
val trumpCards = hand.cards.filter(_.suit == round.trumpSuit)
|
||||
if (round.playerQueue.size - trick.cards.size == 1 && activeTrumps.isEmpty) return Some(trumpCards.minBy(_.cardValue.ordinal))
|
||||
val highestTrump = trumpCards.maxBy(_.cardValue.ordinal)
|
||||
val activeTrump = activeTrumps.maxBy(_.cardValue.ordinal)
|
||||
if (highestTrump.cardValue.ordinal < activeTrump.cardValue.ordinal) None
|
||||
else {
|
||||
val higherTrumps = trumpCards.filter(_.cardValue.ordinal > activeTrump.cardValue.ordinal)
|
||||
if(round.playerQueue.size - trick.cards.size <= round.playersin.size * 0.5) Some(higherTrumps.minBy(_.cardValue.ordinal))
|
||||
else Some(highestTrump)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def decideTrumpSuit(ai: AbstractPlayer): Suit = {
|
||||
val hand = ai.currentHand().get
|
||||
hand.cards.groupBy(_.suit).maxBy(_._2.size)._1
|
||||
}
|
||||
|
||||
def decideTie(min: Int, max: Int): Int = {
|
||||
Random.between(min, max+1)
|
||||
}
|
||||
def decideDogCard(ai: AbstractPlayer, round: Round, trick: Trick, needstoplay: Boolean): Option[Card] = {
|
||||
val firstCardSuit = trick.firstCard.get.suit
|
||||
val hand = ai.currentHand().get
|
||||
val trumpsuit = round.trumpSuit
|
||||
val trumpsuitPlayed = trick.cards.keys.exists(_.suit == trumpsuit)
|
||||
if(needstoplay) {
|
||||
Some(hand.cards.head)
|
||||
} else if(trumpsuitPlayed) {
|
||||
sortbestcard(trick, trumpsuit, hand)
|
||||
} else {
|
||||
sortbestcard(trick, firstCardSuit, hand)
|
||||
}
|
||||
}
|
||||
|
||||
private def sortbestcard(trick: Trick, suit: Suit, hand: Hand): Option[Card] = {
|
||||
val highestCard = trick.cards.keys.filter(_.suit == suit).maxBy(_.cardValue.ordinal)
|
||||
if (hand.cards.head.suit == suit && hand.cards.head.cardValue.ordinal > highestCard.cardValue.ordinal) {
|
||||
return Some(hand.cards.head)
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.cards.CardManager
|
||||
import de.knockoutwhist.components.Configuration
|
||||
import de.knockoutwhist.control.controllerBaseImpl.sublogic.util.{MatchUtil, RoundResult, RoundUtil, TrickUtil}
|
||||
import de.knockoutwhist.control.controllerBaseImpl.sublogic.{PlayerInputLogic, PlayerTieLogic, UndoManager}
|
||||
import de.knockoutwhist.events.GLOBAL_STATUS.SHOW_FINISHED_MATCH
|
||||
import de.knockoutwhist.events.ShowGlobalStatus
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||
import de.knockoutwhist.undo.Command
|
||||
import de.knockoutwhist.utils.CustomPlayerQueue
|
||||
import de.knockoutwhist.utils.events.EventHandler
|
||||
import org.apache.pekko.io.dns.IdGenerator.Policy
|
||||
import org.apache.pekko.io.dns.IdGenerator.Policy.ThreadLocalRandom
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
/*
|
||||
Main game logic controller
|
||||
*/
|
||||
|
||||
final class GameLogic (val config: Configuration) extends EventHandler {
|
||||
//Constants
|
||||
val ID: UUID = UUID.randomUUID()
|
||||
|
||||
//Logics
|
||||
val playerTieLogic: PlayerTieLogic = PlayerTieLogic(this)
|
||||
val playerInputLogic: PlayerInputLogic = PlayerInputLogic(this)
|
||||
val undoManager: UndoManager = UndoManager(this)
|
||||
|
||||
//Variables
|
||||
private[control] var cardManager: Option[CardManager] = Some(config.cardManager)
|
||||
|
||||
private[control] var currentMatch: Option[Match] = None
|
||||
private[control] var currentRound: Option[Round] = None
|
||||
private[control] var currentTrick: Option[Trick] = None
|
||||
private[control] var currentPlayer: Option[AbstractPlayer] = None
|
||||
private[control] var playerQueue: Option[CustomPlayerQueue[AbstractPlayer]] = None
|
||||
|
||||
def createSession(): Unit = {
|
||||
cardManager = Some(config.cardManager)
|
||||
|
||||
currentMatch = None
|
||||
currentRound = None
|
||||
currentTrick = None
|
||||
currentPlayer = None
|
||||
playerQueue = None
|
||||
}
|
||||
|
||||
def createMatch(players: List[AbstractPlayer]): Match = {
|
||||
val matchImpl = Match(totalplayers = players)
|
||||
currentMatch = Some(matchImpl)
|
||||
matchImpl
|
||||
}
|
||||
|
||||
private[control] def controlMatch(): Unit = {
|
||||
if (currentMatch.isEmpty) throw new IllegalStateException("No current match set")
|
||||
val matchImpl = currentMatch.get
|
||||
|
||||
//TODO update persistence
|
||||
|
||||
if (matchImpl.isOver) {
|
||||
|
||||
//TODO inform observers
|
||||
|
||||
} else {
|
||||
if (matchImpl.roundlist.isEmpty) {
|
||||
if (cardManager.isEmpty) throw new IllegalStateException("No card manager set")
|
||||
val cardManagerImpl = cardManager.get
|
||||
val firstCard = cardManagerImpl.nextCard()
|
||||
val newRound = RoundUtil.createRound(firstCard.suit)
|
||||
|
||||
providePlayersWithCards()
|
||||
|
||||
val randomPlayer: Int = .nextInt(matchImpl.playersIn.size)
|
||||
playerQueue = Some(config.createRightQueue(matchImpl.playersIn.toArray, matchImpl.startingplayerindex))
|
||||
//TODO inform observers about first round and new cards
|
||||
|
||||
currentRound = Some(newRound)
|
||||
controlRound()
|
||||
return
|
||||
}
|
||||
currentMatch = Some(matchImpl.setNumberOfCards(matchImpl.numberofcards - 1))
|
||||
providePlayersWithCards()
|
||||
//TODO inform observers about new round and new cards
|
||||
|
||||
//Check if the last round had a winner
|
||||
val lastRound = matchImpl.roundlist.last
|
||||
if (lastRound.winner.isEmpty)
|
||||
throw new IllegalStateException("Last round had no winner")
|
||||
val lastWinner = lastRound.winner.get
|
||||
|
||||
//TODO inform observers about that a player needs to pick trump suit
|
||||
|
||||
playerInputLogic.requestTrumpsuit(lastWinner)
|
||||
}
|
||||
}
|
||||
|
||||
private[control] def controlRound(): Unit = {
|
||||
if (currentMatch.isEmpty) throw new IllegalStateException("No current match set")
|
||||
val matchImpl = currentMatch.get
|
||||
if (currentRound.isEmpty) throw new IllegalStateException("No current round set")
|
||||
val roundImpl = currentRound.get
|
||||
|
||||
//TODO update persistence
|
||||
|
||||
if (MatchUtil.isRoundOver(matchImpl, roundImpl)) {
|
||||
val roundResult: RoundResult = RoundUtil.finishRound(roundImpl, matchImpl)
|
||||
if (roundResult.isTie) {
|
||||
|
||||
//TODO inform observers about tie
|
||||
//TODO delay for a moment to show the tie
|
||||
|
||||
playerTieLogic.handleTie(roundResult)
|
||||
return
|
||||
}
|
||||
val newMatch = endRound(roundResult.winners.head, roundResult)
|
||||
currentMatch = Some(newMatch)
|
||||
controlMatch()
|
||||
} else {
|
||||
|
||||
//TODO Inform observers about new trick
|
||||
|
||||
val trick = Trick()
|
||||
currentTrick = Some(trick)
|
||||
controlTrick()
|
||||
}
|
||||
}
|
||||
|
||||
private def endRound(winner: AbstractPlayer, roundResult: RoundResult): Match = {
|
||||
if (currentMatch.isEmpty) throw new IllegalStateException("No current match set")
|
||||
var matchImpl = currentMatch.get
|
||||
if (currentRound.isEmpty) throw new IllegalStateException("No current round set")
|
||||
val roundImpl = currentRound.get
|
||||
//Create final round snapshot
|
||||
val resultingRound = Round(
|
||||
trumpSuit = roundImpl.trumpSuit,
|
||||
firstRound = roundImpl.firstRound,
|
||||
tricklist = roundImpl.tricklist,
|
||||
winner = Some(winner)
|
||||
)
|
||||
|
||||
//TODO show who won the round
|
||||
//TODO delay for a moment to show the round winner
|
||||
|
||||
if (roundResult.notTricked.nonEmpty) {
|
||||
if (matchImpl.dogLife) {
|
||||
// TODO show who is out
|
||||
// TODO delay for a moment to show who is out
|
||||
matchImpl = matchImpl.updatePlayersIn(matchImpl.playersIn.filterNot(roundResult.notTricked.contains(_)))
|
||||
} else {
|
||||
//TODO show who became dog
|
||||
//TODO delay for a moment to show who became dog
|
||||
matchImpl = matchImpl.setDogLife()
|
||||
// Make players dogs
|
||||
roundResult.notTricked.foreach(player => {
|
||||
player.setDogLife()
|
||||
})
|
||||
}
|
||||
}
|
||||
roundResult.tricked.foreach(player => {
|
||||
player.resetDogLife()
|
||||
})
|
||||
matchImpl.addRound(resultingRound)
|
||||
}
|
||||
|
||||
private[control] def controlTrick(): Unit = {
|
||||
if (currentMatch.isEmpty) throw new IllegalStateException("No current match set")
|
||||
val matchImpl = currentMatch.get
|
||||
if (playerQueue.isEmpty) throw new IllegalStateException("No player queue set")
|
||||
val queueImpl = playerQueue.get
|
||||
if (currentTrick.isEmpty) throw new IllegalStateException("No current trick set")
|
||||
val trickImpl = currentTrick.get
|
||||
|
||||
//TODO update persistence
|
||||
|
||||
if (TrickUtil.isOver(matchImpl, queueImpl)) {
|
||||
val newRound = endTrick()
|
||||
if (newRound.tricklist.isEmpty || newRound.tricklist.last.winner.isEmpty) throw new IllegalStateException("Trick has no winner after ending trick")
|
||||
val winner = newRound.tricklist.last.winner.get
|
||||
currentRound = Some(newRound)
|
||||
//TODO show who won the trick
|
||||
queueImpl.resetAndSetStart(winner)
|
||||
//TODO Delay for a moment to show the trick winner
|
||||
controlRound()
|
||||
} else {
|
||||
controlPlayerPlay()
|
||||
}
|
||||
}
|
||||
|
||||
private def endTrick(): Round = {
|
||||
if (currentTrick.isEmpty) throw new IllegalStateException("No current trick set")
|
||||
val trickImpl = currentTrick.get
|
||||
if (currentRound.isEmpty) throw new IllegalStateException("No current round set")
|
||||
val roundImpl = currentRound.get
|
||||
val resultTrick = TrickUtil.finishTrick(trickImpl, roundImpl)
|
||||
val resultingTrick = Trick(
|
||||
cards = trickImpl.cards,
|
||||
winner = Some(resultTrick.winner),
|
||||
firstCard = trickImpl.firstCard
|
||||
)
|
||||
roundImpl.addTrick(resultingTrick)
|
||||
}
|
||||
|
||||
private[control] def controlPlayerPlay(): Unit = {
|
||||
if (playerQueue.isEmpty) throw new IllegalStateException("No player queue set")
|
||||
val queueImpl = playerQueue.get
|
||||
val playerImpl = queueImpl.nextPlayer()
|
||||
currentPlayer = Some(playerImpl)
|
||||
if (playerImpl.currentHand().isEmpty) {
|
||||
controlTrick()
|
||||
return
|
||||
}
|
||||
val handImpl = playerImpl.currentHand().get
|
||||
if (handImpl.cards.isEmpty) {
|
||||
controlTrick()
|
||||
return
|
||||
}
|
||||
if (playerImpl.isInDoglife) {
|
||||
|
||||
//TODO inform observers about player's turn to play in dog_life
|
||||
|
||||
playerInputLogic.requestDog(playerImpl)
|
||||
} else {
|
||||
|
||||
//TODO inform observers about player's turn to play
|
||||
|
||||
playerInputLogic.requestCard(playerImpl)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
private[control] def providePlayersWithCards(): Unit = {
|
||||
if (currentMatch.isEmpty) throw new IllegalStateException("No current match set")
|
||||
val matchImpl = currentMatch.get
|
||||
if (cardManager.isEmpty) throw new IllegalStateException("No card manager set")
|
||||
val cardManagerImpl = cardManager.get
|
||||
|
||||
cardManagerImpl.shuffleAndReset()
|
||||
|
||||
val handSize = matchImpl.numberofcards
|
||||
|
||||
matchImpl.playersIn.foreach(player => {
|
||||
val hand = if (player.isInDoglife) {
|
||||
cardManagerImpl.createHand(handSize)
|
||||
} else {
|
||||
cardManagerImpl.createHand(1)
|
||||
}
|
||||
player.provideHand(hand)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.cards.Card
|
||||
import de.knockoutwhist.control.*
|
||||
import de.knockoutwhist.events.GLOBAL_STATUS.SHOW_FINISHED_MATCH
|
||||
import de.knockoutwhist.events.PLAYER_STATUS.SHOW_WON_PLAYER_TRICK
|
||||
import de.knockoutwhist.events.ROUND_STATUS.{PLAYERS_OUT, WON_ROUND}
|
||||
import de.knockoutwhist.events.round.ShowCurrentTrickEvent
|
||||
import de.knockoutwhist.events.ui.GameState.{MAIN_MENU, PLAYERS, TIE}
|
||||
import de.knockoutwhist.events.ui.GameStateUpdateEvent
|
||||
import de.knockoutwhist.events.util.DelayEvent
|
||||
import de.knockoutwhist.events.{ShowGlobalStatus, ShowPlayerStatus, ShowRoundStatus}
|
||||
import de.knockoutwhist.persistence.MethodEntryPoint.{ControlMatch, ControlRound, ControlTrick}
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||
import de.knockoutwhist.undo.UndoManager
|
||||
import de.knockoutwhist.undo.commands.EnterPlayersCommand
|
||||
import de.knockoutwhist.utils.Implicits.*
|
||||
|
||||
object MainLogic extends Maincomponent {
|
||||
|
||||
def startMatch(): Unit = {
|
||||
ControlHandler.invoke(GameStateUpdateEvent(PLAYERS))
|
||||
}
|
||||
|
||||
def enteredPlayers(players: List[AbstractPlayer]): Unit = {
|
||||
UndoManager.doStep(EnterPlayersCommand(players))
|
||||
}
|
||||
|
||||
def controlMatch(matchImpl: Match): Unit = {
|
||||
KnockOutWhist.config.persistenceManager.updateMatch(matchImpl)
|
||||
KnockOutWhist.config.persistenceManager.updateMethodEntryPoint(ControlMatch)
|
||||
if(KnockOutWhist.config.matchcomponent.isOver(matchImpl)) {
|
||||
ControlHandler.invoke(ShowGlobalStatus(SHOW_FINISHED_MATCH, KnockOutWhist.config.roundlogcomponent.remainingPlayers(matchImpl.roundlist.last).head))
|
||||
//ControlHandler.invoke(GameStateUpdateEvent(MAIN_MENU))
|
||||
} else {
|
||||
val remainingPlayer = matchImpl.roundlist.isEmpty ? matchImpl.totalplayers |: KnockOutWhist.config.roundlogcomponent.remainingPlayers(matchImpl.roundlist.last)
|
||||
val newMatch = KnockOutWhist.config.roundlogcomponent.provideCards(matchImpl, remainingPlayer)
|
||||
KnockOutWhist.config.playerlogcomponent.trumpsuitStep(newMatch._1, newMatch._2)
|
||||
}
|
||||
}
|
||||
|
||||
def controlRound(matchImpl: Match, round: Round): Unit = {
|
||||
KnockOutWhist.config.persistenceManager.updateMatch(matchImpl)
|
||||
KnockOutWhist.config.persistenceManager.updateRound(round)
|
||||
KnockOutWhist.config.persistenceManager.updateMethodEntryPoint(ControlRound)
|
||||
if(!KnockOutWhist.config.roundlogcomponent.isOver(round)) {
|
||||
val trick = Trick()
|
||||
controlTrick(matchImpl, round, trick)
|
||||
return
|
||||
}
|
||||
val result = KnockOutWhist.config.roundlogcomponent.finalizeRound(KnockOutWhist.config.roundlogcomponent.smashResults(round), matchImpl)
|
||||
if(result._3.size == 1) {
|
||||
endRound(result._1, result._2, result._3.head, result._4)
|
||||
} else {
|
||||
KnockOutWhist.config.playerlogcomponent.preSelect(result._3, result._1, result._2, result._4)
|
||||
}
|
||||
}
|
||||
|
||||
def endRound(matchImpl: Match, round: Round, winner: AbstractPlayer, playersOut: List[AbstractPlayer]): Unit = {
|
||||
val finalRound = Round(round.trumpSuit, round.tricklist, round.playersin, playersOut, round.startingPlayer, winner, firstRound = round.firstRound)
|
||||
val newMatch = matchImpl.addRound(finalRound)
|
||||
ControlHandler.invoke(ShowRoundStatus(WON_ROUND, finalRound, winner))
|
||||
ControlHandler.invoke(DelayEvent(2000L))
|
||||
if (finalRound.playersout.nonEmpty) {
|
||||
ControlHandler.invoke(ShowRoundStatus(PLAYERS_OUT, finalRound))
|
||||
}
|
||||
controlMatch(newMatch)
|
||||
}
|
||||
|
||||
def controlTrick(matchImpl: Match, round: Round, trick: Trick, currentIndex: Int = 0): Unit = {
|
||||
KnockOutWhist.config.persistenceManager.updateMatch(matchImpl)
|
||||
KnockOutWhist.config.persistenceManager.updateRound(round)
|
||||
KnockOutWhist.config.persistenceManager.updateTrick(trick)
|
||||
KnockOutWhist.config.persistenceManager.updateCurrentIndex(currentIndex)
|
||||
KnockOutWhist.config.persistenceManager.updateMethodEntryPoint(ControlTrick)
|
||||
if(currentIndex < round.playersin.size) {
|
||||
val player = round.playerQueue.nextPlayer()
|
||||
controlPlayer(matchImpl, round, trick, player, currentIndex)
|
||||
}else {
|
||||
val result = KnockOutWhist.config.trickcomponent.wonTrick(trick, round)
|
||||
val newRound = round.addTrick(result._2)
|
||||
ControlHandler.invoke(ShowPlayerStatus(SHOW_WON_PLAYER_TRICK, result._1, result._2))
|
||||
newRound.playerQueue.resetAndSetStart(result._1)
|
||||
ControlHandler.invoke(DelayEvent(1000L))
|
||||
controlRound(matchImpl, newRound)
|
||||
}
|
||||
}
|
||||
|
||||
def controlPlayer(matchImpl: Match, round: Round, trick: Trick, player: AbstractPlayer, currentIndex: Int): Unit = {
|
||||
ControlHandler.invoke(ShowCurrentTrickEvent(round, trick))
|
||||
if (!player.doglife) {
|
||||
KnockOutWhist.config.playeractrcomponent.playCard(matchImpl, player, round, trick, currentIndex)
|
||||
} else if (player.currentHand().exists(_.cards.nonEmpty)) {
|
||||
KnockOutWhist.config.playeractrcomponent.dogplayCard(matchImpl, player, round, trick, currentIndex)
|
||||
}else {
|
||||
controlTrick(matchImpl, round, trick, currentIndex+1)
|
||||
}
|
||||
}
|
||||
|
||||
def playCard(trick: Trick, card: Card, player: AbstractPlayer): Trick = {
|
||||
if (trick.firstCard.isEmpty) {
|
||||
trick.setfirstcard(card).addCard(card, player)
|
||||
} else {
|
||||
trick.addCard(card, player)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.control.{ControlHandler, Matchcomponent}
|
||||
import de.knockoutwhist.rounds.Match
|
||||
|
||||
object MatchLogic extends Matchcomponent {
|
||||
def isOver(matchImpl: Match): Boolean = {
|
||||
if (matchImpl.roundlist.isEmpty) {
|
||||
false
|
||||
} else {
|
||||
KnockOutWhist.config.roundlogcomponent.remainingPlayers(matchImpl.roundlist.last).size == 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.control.{ControlHandler, Playeractrcomponent}
|
||||
import de.knockoutwhist.events.PLAYER_STATUS.*
|
||||
import de.knockoutwhist.events.ROUND_STATUS.SHOW_TURN
|
||||
import de.knockoutwhist.events.{ShowPlayerStatus, ShowRoundStatus}
|
||||
import de.knockoutwhist.events.cards.RenderHandEvent
|
||||
import de.knockoutwhist.events.util.DelayEvent
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||
|
||||
object PlayerControl extends Playeractrcomponent {
|
||||
|
||||
|
||||
def playCard(matchImpl: Match, player: AbstractPlayer, round: Round, trick: Trick, currentIndex: Int): Unit = {
|
||||
ControlHandler.invoke(ShowRoundStatus(SHOW_TURN, round, player))
|
||||
ControlHandler.invoke(DelayEvent(500))
|
||||
ControlHandler.invoke(ShowPlayerStatus(SHOW_PLAY_CARD, player))
|
||||
ControlHandler.invoke(RenderHandEvent(player, player.currentHand().get, true))
|
||||
player.handlePlayCard(player.currentHand().get, matchImpl, round, trick, currentIndex)
|
||||
}
|
||||
|
||||
def dogplayCard(matchImpl: Match, player: AbstractPlayer, round: Round, trick: Trick, currentIndex: Int): Unit = {
|
||||
ControlHandler.invoke(ShowRoundStatus(SHOW_TURN, round, player))
|
||||
ControlHandler.invoke(DelayEvent(500))
|
||||
ControlHandler.invoke(ShowPlayerStatus(SHOW_DOG_PLAY_CARD, player, KnockOutWhist.config.roundlogcomponent.dogNeedsToPlay(round)))
|
||||
ControlHandler.invoke(RenderHandEvent(player, player.currentHand().get, false))
|
||||
player.handleDogPlayCard(player.currentHand().get, matchImpl, round, trick, currentIndex, KnockOutWhist.config.roundlogcomponent.dogNeedsToPlay(round))
|
||||
}
|
||||
|
||||
def pickNextTrumpsuit(matchImpl: Match, remaining_players: List[AbstractPlayer], firstRound: Boolean, player: AbstractPlayer): Unit = {
|
||||
ControlHandler.invoke(ShowPlayerStatus(SHOW_TRUMPSUIT_OPTIONS, player))
|
||||
ControlHandler.invoke(RenderHandEvent(player, player.currentHand().get, false))
|
||||
player.handlePickTrumpsuit(matchImpl, remaining_players, firstRound)
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.cards.{Card, Suit}
|
||||
import de.knockoutwhist.control.{ControlHandler, Playerlogcomponent}
|
||||
import de.knockoutwhist.events.ERROR_STATUS.{INVALID_NUMBER, NOT_A_NUMBER}
|
||||
import de.knockoutwhist.events.GLOBAL_STATUS.{SHOW_TIE, SHOW_TIE_TIE, SHOW_TIE_WINNER}
|
||||
import de.knockoutwhist.events.PLAYER_STATUS.SHOW_TIE_NUMBERS
|
||||
import de.knockoutwhist.events.cards.ShowTieCardsEvent
|
||||
import de.knockoutwhist.events.ui.GameState.{INGAME, TIE}
|
||||
import de.knockoutwhist.events.ui.GameStateUpdateEvent
|
||||
import de.knockoutwhist.events.util.DelayEvent
|
||||
import de.knockoutwhist.events.{ShowErrorStatus, ShowGlobalStatus, ShowPlayerStatus}
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round}
|
||||
import de.knockoutwhist.ui.gui.TieMenu
|
||||
import de.knockoutwhist.undo.UndoManager
|
||||
import de.knockoutwhist.undo.commands.{SelectTieCommand, TrumpSuitSelectedCommand}
|
||||
|
||||
import scala.collection.immutable
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.util.Try
|
||||
|
||||
object PlayerLogic extends Playerlogcomponent {
|
||||
|
||||
def trumpsuitStep(matchImpl: Match, remaining_players: List[AbstractPlayer]): Unit = {
|
||||
if (matchImpl.roundlist.isEmpty) {
|
||||
val randomTrumpsuit = matchImpl.cardManager.nextCard().suit
|
||||
val newMatchImpl = matchImpl.setNumberOfCards(matchImpl.numberofcards - 1)
|
||||
val round = new Round(randomTrumpsuit, remaining_players, true)
|
||||
KnockOutWhist.config.maincomponent.controlRound(newMatchImpl, round)
|
||||
} else {
|
||||
val winner = matchImpl.totalplayers.filter(matchImpl.roundlist.last.winner.name == _.name).head
|
||||
KnockOutWhist.config.playeractrcomponent.pickNextTrumpsuit(matchImpl, remaining_players, false, winner)
|
||||
}
|
||||
}
|
||||
|
||||
def trumpSuitSelected(matchImpl: Match, suit: Try[Suit], remaining_players: List[AbstractPlayer], firstRound: Boolean, decided: AbstractPlayer): Unit = {
|
||||
if (suit.isFailure) {
|
||||
ControlHandler.invoke(ShowErrorStatus(INVALID_NUMBER))
|
||||
KnockOutWhist.config.playeractrcomponent.pickNextTrumpsuit(matchImpl, remaining_players, firstRound, decided)
|
||||
return
|
||||
}
|
||||
ControlHandler.invoke(GameStateUpdateEvent(INGAME))
|
||||
UndoManager.doStep(TrumpSuitSelectedCommand(matchImpl, suit.get, remaining_players, false, decided))
|
||||
}
|
||||
|
||||
def preSelect(winners: List[AbstractPlayer], matchImpl: Match, round: Round, playersout: List[AbstractPlayer]): Unit = {
|
||||
if (!KnockOutWhist.debugmode) matchImpl.cardManager.shuffleAndReset()
|
||||
ControlHandler.invoke(ShowGlobalStatus(SHOW_TIE))
|
||||
ControlHandler.invoke(GameStateUpdateEvent(TIE))
|
||||
selectTie(winners, matchImpl, round, playersout, immutable.HashMap(), 0, matchImpl.cardManager.cardContainer.size - (winners.length - 1))
|
||||
}
|
||||
|
||||
def selectTie(winners: List[AbstractPlayer], matchImpl: Match, round: Round, playersout: List[AbstractPlayer], cut: immutable.HashMap[AbstractPlayer, Card], currentStep: Int, remaining: Int, currentIndex: Int = 0): Unit = {
|
||||
ControlHandler.invoke(GameStateUpdateEvent(TIE))
|
||||
if(currentIndex == winners.size) {
|
||||
evaluateTieWinner(matchImpl, round, playersout, cut)
|
||||
} else {
|
||||
val player = winners(currentIndex)
|
||||
ControlHandler.invoke(ShowPlayerStatus(SHOW_TIE_NUMBERS, player, remaining))
|
||||
|
||||
player.handlePickTieCard(winners, matchImpl, round, playersout, cut, currentStep, remaining, currentIndex)
|
||||
}
|
||||
}
|
||||
|
||||
def selectedTie(winner: List[AbstractPlayer],matchImpl: Match, round: Round, playersout: List[AbstractPlayer], cut: immutable.HashMap[AbstractPlayer, Card], value: Try[Int], currentStep: Int, remaining: Int, currentIndex: Int = 0): Unit = {
|
||||
if (value.isFailure) {
|
||||
ControlHandler.invoke(ShowErrorStatus(NOT_A_NUMBER))
|
||||
selectTie(winner, matchImpl, round, playersout, cut, currentStep, remaining, currentIndex)
|
||||
return
|
||||
}
|
||||
val selCard = matchImpl.cardManager.cardContainer(currentStep + (value.get - 1))
|
||||
UndoManager.doStep(SelectTieCommand(winner, matchImpl, round, playersout, cut, value.get, selCard, currentStep, remaining, currentIndex))
|
||||
}
|
||||
|
||||
def evaluateTieWinner(matchImpl: Match, round: Round, playersout: List[AbstractPlayer], cut: immutable.HashMap[AbstractPlayer, Card]): Unit = {
|
||||
ControlHandler.invoke(ShowTieCardsEvent(cut.toList))
|
||||
val winner: ListBuffer[AbstractPlayer] = ListBuffer()
|
||||
var currentHighest: Card = null
|
||||
for ((player, card) <- cut) {
|
||||
if (currentHighest == null) {
|
||||
currentHighest = card
|
||||
winner += player
|
||||
} else {
|
||||
val compared = card.cardValue.ordinal.compareTo(currentHighest.cardValue.ordinal)
|
||||
if (compared > 0) {
|
||||
currentHighest = card
|
||||
winner.clear()
|
||||
winner += player
|
||||
} else if (compared == 0) {
|
||||
winner += player
|
||||
}
|
||||
}
|
||||
}
|
||||
if (winner.size == 1) {
|
||||
ControlHandler.invoke(ShowGlobalStatus(SHOW_TIE_WINNER, winner.head))
|
||||
KnockOutWhist.config.maincomponent.endRound(matchImpl, round, winner.head, playersout)
|
||||
return
|
||||
}
|
||||
ControlHandler.invoke(ShowGlobalStatus(SHOW_TIE_TIE))
|
||||
ControlHandler.invoke(DelayEvent(2000))
|
||||
preSelect(winner.toList, matchImpl, round, playersout)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.control.{ControlHandler, Roundlogcomponent}
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||
import de.knockoutwhist.utils.Implicits.*
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
object RoundLogic extends Roundlogcomponent{
|
||||
|
||||
def isOver(round: Round): Boolean = {
|
||||
round.playersin.map(_.currentHand()).count(_.get.cards.isEmpty) == round.playersin.size
|
||||
}
|
||||
|
||||
def dogNeedsToPlay(round: Round): Boolean = {
|
||||
round.playersin.filter(!_.doglife).map(_.currentHand()).exists(_.get.cards.isEmpty)
|
||||
}
|
||||
|
||||
def finalizeRound(round: Round, matchImpl: Match, force: Boolean = false): (Match, Round, List[AbstractPlayer], List[AbstractPlayer]) = {
|
||||
if (!force && round.tricklist.isEmpty)
|
||||
throw new IllegalStateException("No tricks played in this round")
|
||||
if (!force && !isOver(round))
|
||||
throw new IllegalStateException("Not all tricks were played in this round")
|
||||
val tricksMapped = round.tricklist
|
||||
.map(t => t.winner)
|
||||
.groupBy(identity).map((p, l) => (p, l.size)) //l.size = Anzahl gewonnener Tricks
|
||||
val winners = tricksMapped
|
||||
.filter((p, i) => i == tricksMapped.values.max)
|
||||
.keys
|
||||
var playersOut = round.firstRound
|
||||
? List()
|
||||
|: round.playersin.filter(!tricksMapped.contains(_))
|
||||
|
||||
var newMatch = matchImpl
|
||||
var newRound = round
|
||||
if (playersOut.nonEmpty && !matchImpl.dogLife) {
|
||||
newMatch = matchImpl.setDogLife()
|
||||
|
||||
val playersUpdated = ListBuffer[AbstractPlayer]()
|
||||
playersUpdated ++= tricksMapped.keys
|
||||
playersOut.foreach(p => {
|
||||
playersUpdated += p.setDogLife()
|
||||
})
|
||||
newMatch = newMatch.updatePlayers(playersUpdated.toList)
|
||||
newRound = newRound.updatePlayersIn(playersUpdated.toList)
|
||||
playersOut = List()
|
||||
}
|
||||
(newMatch, newRound, winners.toList, playersOut)
|
||||
}
|
||||
|
||||
def remainingPlayers(round: Round): List[AbstractPlayer] = {
|
||||
if (round.playersout == null) {
|
||||
return round.playersin
|
||||
}
|
||||
round.playersin.filter(!round.playersout.contains(_))
|
||||
}
|
||||
|
||||
def provideCards(matchImpl: Match, players: List[AbstractPlayer]): (Match,List[AbstractPlayer]) = {
|
||||
if (!KnockOutWhist.debugmode) matchImpl.cardManager.shuffleAndReset()
|
||||
val listbuff = new ListBuffer[AbstractPlayer]()
|
||||
for (player <- players) {
|
||||
if (!player.doglife) {
|
||||
val newPlayer = player.provideHand(matchImpl.cardManager.createHand(matchImpl.numberofcards))
|
||||
listbuff.addOne(newPlayer)
|
||||
} else {
|
||||
val newPlayer = player.provideHand(matchImpl.cardManager.createHand(1))
|
||||
listbuff.addOne(newPlayer)
|
||||
}
|
||||
}
|
||||
val matchResult = matchImpl.totalplayers.appendedAll(listbuff.toList).filter(!players.contains(_))
|
||||
(matchImpl.updatePlayers(matchResult), listbuff.toList)
|
||||
}
|
||||
|
||||
def smashResults(round: Round): Round = {
|
||||
val correctPlayers = round.playersin.groupMapReduce(_.id)(identity)((a, *) => a)
|
||||
val newTricks = round.tricklist.map(t => Trick(t.cards, correctPlayers.getOrElse(t.winner.id, t.winner), t.finished, t.firstCard))
|
||||
Round(round.trumpSuit, newTricks, round.playersin, round.playersout, round.startingPlayer, round.winner, round.firstRound)
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.cards.{Card, Hand}
|
||||
import de.knockoutwhist.control.controllerBaseImpl.PlayerControl
|
||||
import de.knockoutwhist.control.{ControlHandler, Tricklogcomponent}
|
||||
import de.knockoutwhist.events.ERROR_STATUS.{INVALID_INPUT, INVALID_NUMBER, WRONG_CARD}
|
||||
import de.knockoutwhist.events.ShowErrorStatus
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||
import de.knockoutwhist.undo.UndoManager
|
||||
import de.knockoutwhist.undo.commands.{PlayerPlayCommand, PlayerPlayDogCommand}
|
||||
|
||||
import scala.util.Try
|
||||
|
||||
object TrickLogic extends Tricklogcomponent {
|
||||
|
||||
def controlSuitplayed(card: Try[Card], matchImpl: Match, round: Round, trick: Trick, currentIndex: Int, player: AbstractPlayer): Unit = {
|
||||
if (card.isFailure) {
|
||||
ControlHandler.invoke(ShowErrorStatus(INVALID_NUMBER))
|
||||
KnockOutWhist.config.playeractrcomponent.playCard(matchImpl, player, round, trick, currentIndex)
|
||||
return
|
||||
}
|
||||
val realCard = card.get
|
||||
if (trick.firstCard.isDefined) {
|
||||
val firstCard = trick.firstCard.get
|
||||
if (firstCard.suit != realCard.suit) {
|
||||
var hasSuit = false
|
||||
for (cardInHand <- player.currentHand().get.cards) {
|
||||
if (cardInHand.suit == firstCard.suit) {
|
||||
hasSuit = true
|
||||
}
|
||||
}
|
||||
if (hasSuit) {
|
||||
ControlHandler.invoke(ShowErrorStatus(WRONG_CARD, firstCard))
|
||||
KnockOutWhist.config.playeractrcomponent.playCard(matchImpl, player, round, trick, currentIndex)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
UndoManager.doStep(PlayerPlayCommand(matchImpl, round, trick, player, realCard, currentIndex))
|
||||
}
|
||||
|
||||
def controlDogPlayed(card: Try[Option[Card]], matchImpl: Match, round: Round, trick: Trick, currentIndex: Int, player: AbstractPlayer): Unit = {
|
||||
if (card.isFailure) {
|
||||
ControlHandler.invoke(ShowErrorStatus(INVALID_INPUT))
|
||||
KnockOutWhist.config.playeractrcomponent.dogplayCard(matchImpl, player, round, trick, currentIndex)
|
||||
return
|
||||
}
|
||||
UndoManager.doStep(PlayerPlayDogCommand(matchImpl, round, trick, player, card.get, currentIndex))
|
||||
}
|
||||
|
||||
def alternativeCards(card: Card, round: Round, trick: Trick, player: AbstractPlayer): List[Card] = {
|
||||
if (trick.firstCard.isDefined) {
|
||||
val firstCard = trick.firstCard.get
|
||||
if (firstCard.suit != card.suit) {
|
||||
val alternatives: List[Card] = for cardInHand <- player.currentHand().get.cards
|
||||
if cardInHand.suit == firstCard.suit
|
||||
yield cardInHand
|
||||
if(round.trumpSuit == card.suit && alternatives.isEmpty) {
|
||||
return Nil
|
||||
}
|
||||
if (alternatives.nonEmpty) {
|
||||
return alternatives
|
||||
}
|
||||
}
|
||||
}
|
||||
Nil
|
||||
}
|
||||
|
||||
def wonTrick(trick: Trick, round: Round): (AbstractPlayer, Trick) = {
|
||||
val winningCard = {
|
||||
if (trick.cards.keys.exists(_.suit == round.trumpSuit)) {
|
||||
trick.cards.keys.filter(_.suit == round.trumpSuit).maxBy(_.cardValue.ordinal) //stream
|
||||
} else {
|
||||
trick.cards.keys.filter(_.suit == trick.firstCard.get.suit).maxBy(_.cardValue.ordinal) //stream
|
||||
}
|
||||
}
|
||||
val winningPlayer = trick.cards(winningCard)
|
||||
val finalTrick = Trick(trick.cards, winningPlayer, true)
|
||||
(winningPlayer, finalTrick)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl.snapshot
|
||||
|
||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||
|
||||
case class GameLogicSnapshot(
|
||||
matchImpl: Option[Match],
|
||||
roundImpl: Option[Round],
|
||||
trickImpl: Option[Trick],
|
||||
|
||||
|
||||
|
||||
)
|
||||
@@ -0,0 +1,65 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl.sublogic
|
||||
|
||||
import de.knockoutwhist.cards.{Card, Suit}
|
||||
import de.knockoutwhist.control.controllerBaseImpl.GameLogic
|
||||
import de.knockoutwhist.control.controllerBaseImpl.sublogic.util.RoundUtil
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
|
||||
final class PlayerInputLogic(gameLogic: GameLogic) {
|
||||
|
||||
|
||||
|
||||
def requestTrumpsuit(player: AbstractPlayer): Unit = {
|
||||
//TODO implement request trump suit logic
|
||||
}
|
||||
|
||||
def receivedTrumpSuit(player: AbstractPlayer, suit: Suit): Unit = {
|
||||
val newRound = RoundUtil.createRound(suit)
|
||||
gameLogic.currentRound = Some(newRound)
|
||||
gameLogic.controlRound()
|
||||
}
|
||||
|
||||
def requestCard(player: AbstractPlayer): Unit = {
|
||||
//TODO implement request card logic
|
||||
}
|
||||
|
||||
def receivedCard(player: AbstractPlayer, card: Card): Unit = {
|
||||
if (gameLogic.currentTrick.isEmpty) throw new IllegalStateException("No current trick set")
|
||||
val trickImpl = gameLogic.currentTrick.get
|
||||
|
||||
val newTrick = if (trickImpl.firstCard.isEmpty) {
|
||||
trickImpl
|
||||
.setfirstcard(card)
|
||||
.addCard(card, player)
|
||||
} else {
|
||||
trickImpl
|
||||
.addCard(card, player)
|
||||
}
|
||||
|
||||
gameLogic.currentTrick = Some(newTrick)
|
||||
gameLogic.controlTrick()
|
||||
}
|
||||
|
||||
def requestDog(player: AbstractPlayer): Unit = {
|
||||
//TODO implement request dog logic
|
||||
}
|
||||
|
||||
def receivedDog(player: AbstractPlayer, dog: Option[Card]): Unit = {
|
||||
if (gameLogic.currentTrick.isEmpty) throw new IllegalStateException("No current trick set")
|
||||
val trickImpl = gameLogic.currentTrick.get
|
||||
|
||||
if (dog.isDefined) {
|
||||
val newTrick = if (trickImpl.firstCard.isEmpty) {
|
||||
trickImpl
|
||||
.setfirstcard(dog.get)
|
||||
.addCard(dog.get, player)
|
||||
} else {
|
||||
trickImpl
|
||||
.addCard(dog.get, player)
|
||||
}
|
||||
gameLogic.currentTrick = Some(newTrick)
|
||||
}
|
||||
gameLogic.controlTrick()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl.sublogic
|
||||
|
||||
import de.knockoutwhist.cards.Card
|
||||
import de.knockoutwhist.control.controllerBaseImpl.GameLogic
|
||||
import de.knockoutwhist.control.controllerBaseImpl.sublogic.util.RoundResult
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
|
||||
final class PlayerTieLogic(gameLogic: GameLogic) {
|
||||
|
||||
private var roundResult: Option[RoundResult] = None
|
||||
private var tiedPlayers: List[AbstractPlayer] = Nil
|
||||
private var tieBreakerIndex: Int = -1
|
||||
private var lastNumber = -1
|
||||
private var selectedCard: Map[AbstractPlayer, Card] = Map.empty
|
||||
|
||||
def handleTie(roundResult: RoundResult): Unit = {
|
||||
this.roundResult = Some(roundResult)
|
||||
tiedPlayers = roundResult.winners
|
||||
tieBreakerIndex = 0
|
||||
lastNumber = 0
|
||||
selectedCard = Map.empty
|
||||
if (gameLogic.cardManager.isEmpty) throw new IllegalStateException("No card manager set")
|
||||
gameLogic.cardManager.get.shuffleAndReset()
|
||||
handleNextTieBreakerPlayer()
|
||||
}
|
||||
|
||||
private def handleNextTieBreakerPlayer(): Unit = {
|
||||
if(tieBreakerIndex >= 0 && tieBreakerIndex < tiedPlayers.size) {
|
||||
val player = tiedPlayers(tieBreakerIndex)
|
||||
tieBreakerIndex += 1
|
||||
// TODO Inform observer about next player to select tie-breaker card
|
||||
// TODO Request player to select a tie-breaker card
|
||||
} else {
|
||||
// All players have selected their tie-breaker cards
|
||||
// Find the highest card among selected cards
|
||||
//TODO Inform observer about selected tie-breaker cards
|
||||
//TODO Delay for a moment to let players see the selected cards
|
||||
|
||||
val winningEntry = selectedCard.values.maxBy(_.cardValue.ordinal)
|
||||
val winners = selectedCard.filter((_, card) => card == winningEntry).keySet
|
||||
|
||||
if (winners.size > 1) {
|
||||
//TODO Show tie-breaker tie result to players
|
||||
//TODO Delay for a moment to let players see the tie result
|
||||
|
||||
// Still a tie, handle again
|
||||
tiedPlayers = winners.toList
|
||||
tieBreakerIndex = 0
|
||||
lastNumber = 0
|
||||
selectedCard = Map.empty
|
||||
gameLogic.cardManager.get.shuffleAndReset()
|
||||
handleNextTieBreakerPlayer()
|
||||
return
|
||||
}
|
||||
|
||||
//TODO Show tie-breaker result to players
|
||||
|
||||
|
||||
// Tie-breaker resolved
|
||||
roundResult = None
|
||||
tiedPlayers = Nil
|
||||
lastNumber = -1
|
||||
tieBreakerIndex = -1
|
||||
selectedCard = Map.empty
|
||||
|
||||
val winner = winners.head
|
||||
// Inform game logic about the winner
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a player has selected a tie-breaker card
|
||||
* @param player the player who selected the card
|
||||
* @param number the index of the selected card
|
||||
*/
|
||||
def receivedTieBreakerCard(player: AbstractPlayer, number: Int): Unit = {
|
||||
val highestNumber = highestAllowedNumber()
|
||||
if (number < 0 || number > highestNumber)
|
||||
throw new IllegalArgumentException(s"Selected number $number is out of allowed range (1 to $highestNumber)")
|
||||
|
||||
if (gameLogic.cardManager.isEmpty) throw new IllegalStateException("No card manager set")
|
||||
val cardManager = gameLogic.cardManager.get
|
||||
val card = cardManager.removeCards(number).last
|
||||
selectedCard += (player -> card)
|
||||
handleNextTieBreakerPlayer()
|
||||
}
|
||||
|
||||
def highestAllowedNumber(): Int = {
|
||||
if (gameLogic.cardManager.isEmpty) throw new IllegalStateException("No card manager set")
|
||||
val remainingCards = gameLogic.cardManager.get.remainingCards
|
||||
|
||||
// The highest allowed number is total cards minus the number of tied players already selected
|
||||
// This ensures that each tied player can select a unique card
|
||||
remainingCards - (tiedPlayers.size - selectedCard.size - 1)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package de.knockoutwhist.undo
|
||||
package de.knockoutwhist.control.controllerBaseImpl.sublogic
|
||||
|
||||
import de.knockoutwhist.control.ControlThread
|
||||
import de.knockoutwhist.control.controllerBaseImpl.GameLogic
|
||||
import de.knockoutwhist.undo.{Command, UndoneException}
|
||||
|
||||
object UndoManager {
|
||||
class UndoManager(gameLogic: GameLogic) {
|
||||
|
||||
private var undoStack: List[Command] = Nil
|
||||
private var redoStack: List[Command] = Nil
|
||||
@@ -0,0 +1,21 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl.sublogic.util
|
||||
|
||||
import de.knockoutwhist.rounds.{Match, Round}
|
||||
|
||||
object MatchUtil {
|
||||
|
||||
private def remainingRounds(matchImpl: Match, roundImpl: Round): Int = {
|
||||
//Find player with the most cards
|
||||
matchImpl.numberofcards - roundImpl.tricklist.size
|
||||
}
|
||||
|
||||
def isRoundOver(matchImpl: Match, roundImpl: Round): Boolean = {
|
||||
//Find player with the most cards
|
||||
remainingRounds(matchImpl, roundImpl) == 0
|
||||
}
|
||||
|
||||
def dogNeedsToPlay(matchImpl: Match, roundImpl: Round): Boolean = {
|
||||
remainingRounds(matchImpl, roundImpl) == 1
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl.sublogic.util
|
||||
|
||||
object PersistenceUtil {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl.sublogic.util
|
||||
|
||||
import de.knockoutwhist.cards.Card
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Round, Trick}
|
||||
|
||||
object PlayerUtil {
|
||||
|
||||
def canPlayCard(card: Card, round: Round, trick: Trick, player: AbstractPlayer): Boolean = {
|
||||
if (trick.firstCard.isDefined) {
|
||||
val firstCard = trick.firstCard.get
|
||||
if (firstCard.suit != card.suit) {
|
||||
val alternatives: List[Card] = for cardInHand <- player.currentHand().get.cards
|
||||
if cardInHand.suit == firstCard.suit
|
||||
yield cardInHand
|
||||
if (round.trumpSuit == card.suit && alternatives.isEmpty) {
|
||||
return true
|
||||
}
|
||||
if (alternatives.nonEmpty) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
def alternativeCards(card: Card, round: Round, trick: Trick, player: AbstractPlayer): List[Card] = {
|
||||
if (trick.firstCard.isDefined) {
|
||||
val firstCard = trick.firstCard.get
|
||||
if (firstCard.suit != card.suit) {
|
||||
val alternatives: List[Card] = for cardInHand <- player.currentHand().get.cards
|
||||
if cardInHand.suit == firstCard.suit
|
||||
yield cardInHand
|
||||
if (round.trumpSuit == card.suit && alternatives.isEmpty) {
|
||||
return Nil
|
||||
}
|
||||
if (alternatives.nonEmpty) {
|
||||
return alternatives
|
||||
}
|
||||
}
|
||||
}
|
||||
Nil
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl.sublogic.util
|
||||
|
||||
import de.knockoutwhist.cards.Suit
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round}
|
||||
|
||||
object RoundUtil {
|
||||
|
||||
def createRound(trumpSuit: Suit): Round = {
|
||||
Round(trumpSuit, false)
|
||||
}
|
||||
|
||||
def finishRound(round: Round, matchImpl: Match): RoundResult = {
|
||||
val tricksMapped = round.tricklist
|
||||
.map(t => t.winner)
|
||||
.filter(t => t.isDefined)
|
||||
.map(t => t.get)
|
||||
.groupBy(identity).map((p, l) => (p, l.size))
|
||||
val maxTricks = if (tricksMapped.isEmpty) 0 else tricksMapped.values.max
|
||||
val winners = tricksMapped
|
||||
.filter((p, i) => i == maxTricks)
|
||||
.keys.toList
|
||||
val trickedPlayers = tricksMapped.keys.toList
|
||||
val notTrickedPlayers = matchImpl.playersIn.filterNot(trickedPlayers.contains)
|
||||
RoundResult(winners, trickedPlayers, notTrickedPlayers)
|
||||
}
|
||||
|
||||
def roundEndSnapshot(winner: AbstractPlayer, round: Round): Round = {
|
||||
Round(
|
||||
round.trumpSuit,
|
||||
round.firstRound,
|
||||
round.tricklist,
|
||||
Some(winner)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case class RoundResult(winners: List[AbstractPlayer], tricked: List[AbstractPlayer], notTricked: List[AbstractPlayer]) {
|
||||
def isTie: Boolean = winners.size > 1
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl.sublogic.util
|
||||
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||
import de.knockoutwhist.utils.CustomPlayerQueue
|
||||
|
||||
object TrickUtil {
|
||||
|
||||
def isOver(matchImpl: Match, queue: CustomPlayerQueue[AbstractPlayer]): Boolean = {
|
||||
queue.playersSinceLastReset() >= matchImpl.playersIn.length
|
||||
}
|
||||
|
||||
private def winningPlayer(trick: Trick, round: Round): AbstractPlayer = {
|
||||
val winningCard = {
|
||||
if (trick.cards.keys.exists(_.suit == round.trumpSuit)) {
|
||||
trick.cards.keys.filter(_.suit == round.trumpSuit).maxBy(_.cardValue.ordinal) //stream
|
||||
} else {
|
||||
trick.cards.keys.filter(_.suit == trick.firstCard.get.suit).maxBy(_.cardValue.ordinal) //stream
|
||||
}
|
||||
}
|
||||
val winningPlayer = trick.cards(winningCard)
|
||||
winningPlayer
|
||||
}
|
||||
|
||||
def finishTrick(trick: Trick, round: Round): TrickResult = {
|
||||
val winner = winningPlayer(trick, round)
|
||||
TrickResult(winner)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case class TrickResult(winner: AbstractPlayer)
|
||||
@@ -1,37 +0,0 @@
|
||||
package de.knockoutwhist.player
|
||||
import de.knockoutwhist.cards.{Card, Hand, Suit}
|
||||
import de.knockoutwhist.control.AILogic
|
||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||
|
||||
import java.util.UUID
|
||||
import scala.collection.immutable
|
||||
import scala.util.Try
|
||||
|
||||
class AIPlayer private[player](name: String, hand: Option[Hand], id: UUID = UUID.randomUUID(), doglife: Boolean = false) extends AbstractPlayer(name, hand, id, doglife) {
|
||||
override def provideHand(hand: Hand): AbstractPlayer = {
|
||||
AIPlayer(name, Some(hand), id, doglife)
|
||||
}
|
||||
|
||||
override def removeCard(card: Card): AbstractPlayer = {
|
||||
AIPlayer(name, Some(hand.get.removeCard(card)), id, doglife)
|
||||
}
|
||||
|
||||
override def setDogLife(): AbstractPlayer = AIPlayer(name, hand, id, true)
|
||||
|
||||
override def handlePlayCard(hand: Hand, matchImpl: Match, round: Round, trick: Trick, currentIndex: Int): Unit = {
|
||||
AILogic.decideCard(this, round, trick)
|
||||
}
|
||||
|
||||
override def handleDogPlayCard(hand: Hand, matchImpl: Match, round: Round, trick: Trick, currentIndex: Int, needstoplay: Boolean): Unit = {
|
||||
AILogic.decideDogCard(this, round, trick, needstoplay)
|
||||
}
|
||||
|
||||
override def handlePickTrumpsuit(matchImpl: Match, remaining_players: List[AbstractPlayer], firstRound: Boolean): Unit = {
|
||||
AILogic.decideTrumpSuit(this)
|
||||
}
|
||||
|
||||
override def handlePickTieCard(winner: List[AbstractPlayer], matchImpl: Match, round: Round, playersout: List[AbstractPlayer], cut: immutable.HashMap[AbstractPlayer, Card], currentStep: Int, remaining: Int, currentIndex: Int = 0): Unit = {
|
||||
AILogic.decideTie(1, remaining)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,13 +8,27 @@ import scala.collection.immutable
|
||||
import scala.util.Try
|
||||
|
||||
//If you get an uuid conflict, go play the lottery!!!
|
||||
abstract case class AbstractPlayer private[player](var name: String, hand: Option[Hand], id: UUID = UUID.randomUUID(), doglife: Boolean = false) {
|
||||
abstract case class AbstractPlayer private[player](name: String, id: UUID = UUID.randomUUID()) {
|
||||
|
||||
protected var hand: Option[Hand] = None
|
||||
protected var doglife: Boolean = false
|
||||
|
||||
def currentHand(): Option[Hand] = hand
|
||||
|
||||
def provideHand(hand: Hand): AbstractPlayer
|
||||
def setDogLife(): AbstractPlayer
|
||||
def removeCard(card: Card): AbstractPlayer
|
||||
def isInDoglife: Boolean = doglife
|
||||
|
||||
def provideHand(hand: Hand): Unit = {
|
||||
this.hand = Some(hand)
|
||||
}
|
||||
def setDogLife(): Unit = {
|
||||
this.doglife = true
|
||||
}
|
||||
def resetDogLife(): Unit = {
|
||||
this.doglife = false
|
||||
}
|
||||
def removeCard(card: Card): Unit = {
|
||||
this.hand = this.hand.map(_.removeCard(card))
|
||||
}
|
||||
|
||||
def handlePlayCard(hand: Hand, matchImpl: Match, round: Round, trick: Trick, currentIndex: Int): Unit
|
||||
def handleDogPlayCard(hand: Hand, matchImpl: Match, round: Round, trick: Trick, currentIndex: Int, needstoplay: Boolean): Unit
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package de.knockoutwhist.player
|
||||
|
||||
import de.knockoutwhist.player.Playertype.{AI, HUMAN, STUB}
|
||||
import de.knockoutwhist.player.Playertype.{HUMAN, STUB}
|
||||
import de.knockoutwhist.player.baseImpl.HumanPlayer
|
||||
import de.knockoutwhist.player.builder.*
|
||||
|
||||
@@ -8,7 +8,6 @@ import java.util.UUID
|
||||
|
||||
enum Playertype:
|
||||
case HUMAN
|
||||
case AI
|
||||
case STUB
|
||||
end Playertype
|
||||
|
||||
@@ -17,8 +16,6 @@ object PlayerFactory {
|
||||
val buildType: PlayerBuilder = playertype match {
|
||||
case HUMAN =>
|
||||
new HumanoidBuilder()
|
||||
case AI =>
|
||||
new AIPlayerBuilder()
|
||||
case STUB =>
|
||||
new StubPlayerBuilder
|
||||
}
|
||||
@@ -41,8 +38,6 @@ object PlayerFactory {
|
||||
player match {
|
||||
case _: HumanPlayer =>
|
||||
HUMAN
|
||||
case _: AIPlayer =>
|
||||
AI
|
||||
case _: StubPlayer =>
|
||||
STUB
|
||||
}
|
||||
|
||||
@@ -12,17 +12,7 @@ import java.util.UUID
|
||||
import scala.collection.immutable
|
||||
import scala.util.Try
|
||||
|
||||
class StubPlayer private[player](name: String, hand: Option[Hand], id: UUID = UUID.randomUUID(), doglife: Boolean = false) extends AbstractPlayer(name, hand, id, doglife) {
|
||||
|
||||
override def provideHand(hand: Hand): AbstractPlayer = {
|
||||
StubPlayer(name, Some(hand), id, doglife)
|
||||
}
|
||||
|
||||
override def removeCard(card: Card): AbstractPlayer = {
|
||||
StubPlayer(name, Some(hand.get.removeCard(card)), id, doglife)
|
||||
}
|
||||
|
||||
override def setDogLife(): AbstractPlayer = StubPlayer(name, hand, id, true)
|
||||
class StubPlayer private[player](name: String, id: UUID = UUID.randomUUID()) extends AbstractPlayer(name, id) {
|
||||
|
||||
override def handlePlayCard(hand: Hand, matchImpl: Match, round: Round, trick: Trick, currentIndex: Int): Unit = {
|
||||
KnockOutWhist.config.trickcomponent.controlSuitplayed(Try{Card(Ten, Spades)}, matchImpl, round, trick, currentIndex, this)
|
||||
|
||||
@@ -13,16 +13,7 @@ import scala.collection.immutable
|
||||
import scala.util.Try
|
||||
|
||||
|
||||
class HumanPlayer private[player](name: String, hand: Option[Hand], id: UUID = UUID.randomUUID(), doglife: Boolean = false) extends AbstractPlayer(name, hand, id, doglife) {
|
||||
override def provideHand(hand: Hand): AbstractPlayer = {
|
||||
HumanPlayer(name, Some(hand), id, doglife)
|
||||
}
|
||||
|
||||
override def setDogLife(): AbstractPlayer = HumanPlayer(name, hand, id, true)
|
||||
|
||||
override def removeCard(card: Card): AbstractPlayer = {
|
||||
HumanPlayer(name, Some(hand.get.removeCard(card)), id, doglife)
|
||||
}
|
||||
class HumanPlayer private[player](name: String, id: UUID = UUID.randomUUID()) extends AbstractPlayer(name, id) {
|
||||
|
||||
override def handlePlayCard(hand: Hand, matchImpl: Match, round: Round, trick: Trick, currentIndex: Int): Unit = {
|
||||
ControlHandler.invoke(RequestCardEvent(hand, matchImpl, round, trick, currentIndex, this))
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
package de.knockoutwhist.player.builder
|
||||
|
||||
import de.knockoutwhist.player.{AIPlayer, AbstractPlayer}
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
class AIPlayerBuilder extends PlayerBuilder {
|
||||
private var name: Option[String] = None
|
||||
private var id: Option[UUID] = Some(UUID.randomUUID())
|
||||
|
||||
override def setName(name: String): PlayerBuilder = {
|
||||
this.name = Some(name)
|
||||
this
|
||||
}
|
||||
|
||||
override def setID(id: UUID): PlayerBuilder = {
|
||||
this.id = Some(id)
|
||||
this
|
||||
}
|
||||
|
||||
override def reset(): PlayerBuilder = {
|
||||
this.name = None
|
||||
this.id = Some(UUID.randomUUID())
|
||||
this
|
||||
}
|
||||
|
||||
override def build(): AbstractPlayer = {
|
||||
if (this.name.isDefined && this.id.isDefined) {
|
||||
val player = new AIPlayer(this.name.get, None, id.get, false)
|
||||
reset()
|
||||
return player
|
||||
}
|
||||
throw new IllegalStateException("Trying to build non-existing AI")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,7 +28,7 @@ class HumanoidBuilder extends PlayerBuilder {
|
||||
|
||||
override def build(): AbstractPlayer = {
|
||||
if (this.name.isDefined && this.id.isDefined) {
|
||||
val player = new HumanPlayer(this.name.get, None, id.get, false)
|
||||
val player = new HumanPlayer(this.name.get, id.get)
|
||||
reset()
|
||||
return player
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class StubPlayerBuilder extends PlayerBuilder {
|
||||
|
||||
override def build(): AbstractPlayer = {
|
||||
if (this.name.isDefined && this.id.isDefined) {
|
||||
val player = new StubPlayer(this.name.get, None, id.get, false)
|
||||
val player = new StubPlayer(this.name.get, id.get)
|
||||
reset()
|
||||
return player
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import de.knockoutwhist.utils.Implicits.*
|
||||
import java.util.UUID
|
||||
import scala.collection.immutable.List
|
||||
|
||||
case class Match(totalplayers: List[AbstractPlayer], numberofcards: Int = 7, dogLife: Boolean = false, roundlist: List[Round] = List[Round](), cardManager: CardManager) {
|
||||
case class Match(totalplayers: List[AbstractPlayer], playersIn: List[AbstractPlayer] = List(), numberofcards: Int = 7, dogLife: Boolean = false, roundlist: List[Round] = List[Round]()) {
|
||||
|
||||
def addRound(round: Round): Match = {
|
||||
this.copy(roundlist = roundlist :+ round)
|
||||
@@ -21,8 +21,16 @@ case class Match(totalplayers: List[AbstractPlayer], numberofcards: Int = 7, dog
|
||||
this.copy(dogLife = true)
|
||||
}
|
||||
|
||||
def updatePlayers(totalplayers: List[AbstractPlayer]): Match = {
|
||||
this.copy(totalplayers = totalplayers)
|
||||
def updatePlayersIn(playersIn: List[AbstractPlayer]): Match = {
|
||||
this.copy(playersIn = playersIn)
|
||||
}
|
||||
|
||||
def playersOut: List[AbstractPlayer] = {
|
||||
totalplayers.diff(playersIn)
|
||||
}
|
||||
|
||||
def isOver: Boolean = {
|
||||
playersIn.size <= 1
|
||||
}
|
||||
|
||||
override def toString: String = {
|
||||
|
||||
@@ -10,26 +10,14 @@ import scala.collection.immutable
|
||||
import scala.collection.immutable.List
|
||||
import scala.util.Random
|
||||
|
||||
case class Round (trumpSuit: Suit, tricklist: List[Trick], playersin: List[AbstractPlayer], playersout: List[AbstractPlayer] = null, startingPlayer: Int = -1, winner: AbstractPlayer = null, firstRound: Boolean) {
|
||||
def this(trumpSuit: Suit, playersin: List[AbstractPlayer], firstRound: Boolean) = {
|
||||
this(trumpSuit, List[Trick](), playersin, firstRound = firstRound)
|
||||
}
|
||||
|
||||
val playerQueue: CustomPlayerQueue[AbstractPlayer] = KnockOutWhist.config.createRightQueue(
|
||||
playersin.toArray,
|
||||
(startingPlayer == -1) ? Random.nextInt(playersin.length) |: startingPlayer
|
||||
)
|
||||
case class Round (trumpSuit: Suit, firstRound: Boolean, tricklist: List[Trick] = List(), winner: Option[AbstractPlayer] = None) {
|
||||
|
||||
def addTrick(trick: Trick): Round = {
|
||||
Round(trumpSuit, tricklist :+ trick, playersin, playersout, playerQueue.currentIndex, winner, firstRound)
|
||||
}
|
||||
|
||||
def updatePlayersIn(playersin: List[AbstractPlayer]): Round = {
|
||||
Round(trumpSuit, tricklist, playersin, playersout, playerQueue.currentIndex, winner, firstRound)
|
||||
Round(trumpSuit, firstRound, tricklist :+ trick, winner)
|
||||
}
|
||||
|
||||
override def toString: String = {
|
||||
s"$trumpSuit, $tricklist, $playersin, $playersout, $winner, $firstRound"
|
||||
s"$trumpSuit, $tricklist, $winner, $firstRound"
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,18 +5,20 @@ import de.knockoutwhist.player.AbstractPlayer
|
||||
|
||||
import scala.collection.immutable
|
||||
|
||||
case class Trick (cards: immutable.HashMap[Card, AbstractPlayer] = immutable.HashMap[Card, AbstractPlayer](), winner: AbstractPlayer = null, finished: Boolean = false, firstCard: Option[Card] = None) {
|
||||
case class Trick (cards: immutable.HashMap[Card, AbstractPlayer] = immutable.HashMap[Card, AbstractPlayer](), winner: Option[AbstractPlayer] = None, firstCard: Option[Card] = None) {
|
||||
|
||||
def finished: Boolean = winner.isDefined
|
||||
|
||||
def addCard(card: Card, player: AbstractPlayer): Trick = {
|
||||
Trick(cards + (card -> player), winner, finished, firstCard)
|
||||
Trick(cards + (card -> player), winner, firstCard)
|
||||
}
|
||||
|
||||
def setfirstcard(card: Card): Trick = {
|
||||
Trick(cards, winner, finished, Some(card))
|
||||
Trick(cards, winner, Some(card))
|
||||
}
|
||||
|
||||
override def toString: String = {
|
||||
s"$cards, $winner, $finished"
|
||||
s"$cards, $winner"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ import atlantafx.base.theme.Styles
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.cards.{Card, Hand, Suit}
|
||||
import de.knockoutwhist.control.ControlThread
|
||||
import de.knockoutwhist.control.controllerBaseImpl.sublogic.UndoManager
|
||||
import de.knockoutwhist.events.ShowPlayerStatus
|
||||
import de.knockoutwhist.events.directional.{RequestCardEvent, RequestDogPlayCardEvent}
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Round, Trick}
|
||||
import de.knockoutwhist.undo.UndoManager
|
||||
import de.knockoutwhist.utils.CustomPlayerQueue
|
||||
import de.knockoutwhist.utils.Implicits.*
|
||||
import de.knockoutwhist.utils.gui.Animations
|
||||
@@ -355,7 +355,7 @@ object Game {
|
||||
def updateNextPlayer(queue: CustomPlayerQueue[AbstractPlayer], currendIndx: Int): Unit = {
|
||||
val queueDupli = queue.duplicate()
|
||||
nextPlayers.children = queueDupli.iteratorWithStart(currendIndx).map(player => new Label {
|
||||
text = !player.doglife ? player.name |: s"${player.name} (Doglife)"
|
||||
text = !player.isInDoglife ? player.name |: s"${player.name} (Doglife)"
|
||||
font = Font.font(20)
|
||||
}).toSeq
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package de.knockoutwhist.ui.tui
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.cards.{Card, CardValue, Hand, Suit}
|
||||
import de.knockoutwhist.control.controllerBaseImpl.sublogic.UndoManager
|
||||
import de.knockoutwhist.control.controllerBaseImpl.{PlayerLogic, TrickLogic}
|
||||
import de.knockoutwhist.control.{ControlHandler, ControlThread}
|
||||
import de.knockoutwhist.events.*
|
||||
@@ -18,7 +19,7 @@ import de.knockoutwhist.events.util.DelayEvent
|
||||
import de.knockoutwhist.player.Playertype.HUMAN
|
||||
import de.knockoutwhist.player.{AbstractPlayer, PlayerFactory}
|
||||
import de.knockoutwhist.ui.UI
|
||||
import de.knockoutwhist.undo.{UndoManager, UndoneException}
|
||||
import de.knockoutwhist.undo.UndoneException
|
||||
import de.knockoutwhist.utils.CustomThread
|
||||
import de.knockoutwhist.utils.events.{EventListener, SimpleEvent}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.knockoutwhist.undo.commands
|
||||
package de.knockoutwhist.undo.commands.old
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.cards.Card
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.knockoutwhist.undo.commands
|
||||
package de.knockoutwhist.undo.commands.old
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.cards.Card
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.knockoutwhist.undo.commands
|
||||
package de.knockoutwhist.undo.commands.old
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.cards.Card
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.knockoutwhist.undo.commands
|
||||
package de.knockoutwhist.undo.commands.old
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.cards.Suit
|
||||
@@ -27,6 +27,12 @@ trait CustomPlayerQueue[A] extends Iterable[A] {
|
||||
*/
|
||||
def resetAndSetStart(player: A): Boolean
|
||||
|
||||
/**
|
||||
* Retuns how many players have played since the last reset.
|
||||
* @return the number of players since the last reset.
|
||||
*/
|
||||
def playersSinceLastReset(): Int
|
||||
|
||||
/**
|
||||
* Let's the iterator start at a specific index.
|
||||
* @param start the index to start at.
|
||||
|
||||
@@ -5,10 +5,12 @@ import de.knockoutwhist.utils.CustomPlayerQueue
|
||||
class CustomPlayerBaseQueue[A](protected var players: Array[A], val start: Int = 0) extends CustomPlayerQueue[A] {
|
||||
|
||||
private var current = start
|
||||
private var lastReset = 0
|
||||
|
||||
def currentIndex: Int = current
|
||||
|
||||
def nextPlayer(): A = {
|
||||
lastReset += 1
|
||||
val player = players(current)
|
||||
current = (current + 1) % players.length
|
||||
player
|
||||
@@ -20,6 +22,7 @@ class CustomPlayerBaseQueue[A](protected var players: Array[A], val start: Int =
|
||||
}
|
||||
|
||||
def resetAndSetStart(player: A): Boolean = {
|
||||
lastReset = 0
|
||||
if(players.contains(player)) {
|
||||
current = players.indexOf(player)
|
||||
true
|
||||
@@ -28,6 +31,8 @@ class CustomPlayerBaseQueue[A](protected var players: Array[A], val start: Int =
|
||||
}
|
||||
}
|
||||
|
||||
override def playersSinceLastReset(): Int = lastReset
|
||||
|
||||
override def toList: List[A] = players.toList
|
||||
override def isEmpty: Boolean = players.isEmpty
|
||||
override def size: Int = players.length
|
||||
|
||||
@@ -17,6 +17,10 @@ class CustomPlayerQueueStub[A](protected var players: Array[A], val start: Int =
|
||||
def resetAndSetStart(player: A): Boolean = {
|
||||
false
|
||||
}
|
||||
|
||||
def playersSinceLastReset(): Int = {
|
||||
0
|
||||
}
|
||||
|
||||
override def toList: List[A] = List()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user