28 Commits

Author SHA1 Message Date
Janis b9e896fc0b Merge pull request 'Reduced Test Time by a lot' (#34) from test-improvements into development
Build and Test (KnockOutWhist) TeamCity build finished
Reviewed-on: #34
2024-11-21 20:57:49 +01:00
Janis a0e2eadc93 Reduced Test Time by a lot
Build and Test (KnockOutWhist) TeamCity build finished
2024-11-21 19:42:14 +01:00
Janis 0b5bfc69e5 Merge pull request 'Coveralls' (#29) from coverall into development
Build and Test (KnockOutWhist) TeamCity build finished
Reviewed-on: #29
2024-11-18 10:45:23 +01:00
Janis ad0494f0ba Merge remote-tracking branch 'origin/development' into coverall
Build and Test (KnockOutWhist) TeamCity build finished
# Conflicts:
#	project/plugins.sbt
2024-11-18 10:42:48 +01:00
Janis 355addf8a5 Merge pull request 'eventsystem' (#28) from eventsystem into development
Build and Test (KnockOutWhist) TeamCity build finished
Reviewed-on: #28
2024-11-18 10:38:50 +01:00
Janis efd5e0fae6 Coveralls
Build and Test (KnockOutWhist) TeamCity build finished
2024-11-18 10:37:44 +01:00
Janis 8cb5012c8f Remove unnecessary method
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-15 12:35:55 +01:00
LQ63 6304ce44b9 Changed GameplayTests
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-15 12:35:39 +01:00
Janis ee44f6940b Added more tests
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-15 12:33:26 +01:00
LQ63 0c6de4b86f Added Tests for Events
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-15 12:00:48 +01:00
Janis 094afc5631 Added DelayHandlerTests
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-15 11:55:16 +01:00
Janis bb0ad578be Merge pull request 'Pipeline Test' (#26) from pipelineWorks into development
Build and Test (KnockOutWhist) TeamCity build failed
Reviewed-on: #26
2024-11-15 11:38:13 +01:00
Janis ce5efd7a05 Pipeline Test
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-15 11:35:26 +01:00
Janis 174fe3c993 Fixed Tests
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-15 11:17:34 +01:00
Janis 03ab3f3650 Improved Code Coverage
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-15 11:13:00 +01:00
LQ63 a7f839d01c Changed Tests according to the new structure.
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-14 20:35:12 +01:00
Janis a7e84b9815 Fixed DelayHandler
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-14 20:14:22 +01:00
Janis 4c7fb1c8bc Fixed EventHandler
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-14 20:09:11 +01:00
Janis ef1eb29fe3 Merge remote-tracking branch 'origin/eventsystem' into eventsystem
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-14 18:06:51 +01:00
LQ63 619cbcfaac Changed structure of Control
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-14 18:06:33 +01:00
Janis fe46690fb4 Added a clear console for the main menu 2024-11-14 17:31:32 +01:00
Janis 24bfd7925c Splitted Logic between UI and Logic
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-14 17:28:39 +01:00
Janis 11c0b61933 Rearranged everything Data Structure got no more logic
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-14 16:47:38 +01:00
Janis 0cc7756556 Switcher Handlers over to one
Build and Test (KnockOutWhist) TeamCity build failed
Added UI Trait
Renamed Controls
Deleted old Logic
2024-11-14 12:31:17 +01:00
Janis 153d028959 Added a delay handler
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-13 22:04:41 +01:00
LQ63 177ec3772e Added more Events, finished GenericPlayerControl. Startet with GenericMatchControl
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-13 18:07:28 +01:00
Janis c397d079bf Reworked Events
Build and Test (KnockOutWhist) TeamCity build failed
Created Events
2024-11-13 13:45:19 +01:00
Janis a99182beaf WIP - Event System
Build and Test (KnockOutWhist) TeamCity build failed
2024-11-11 23:14:56 +01:00
49 changed files with 1793 additions and 792 deletions
+9 -4
View File
@@ -1,5 +1,5 @@
ThisBuild / version := "0.1.0-SNAPSHOT"
ThisBuild / scalaVersion := "3.5.1"
Compile/mainClass := Some("de.knockoutwhist.KnockOutWhist") Compile/mainClass := Some("de.knockoutwhist.KnockOutWhist")
@@ -10,11 +10,16 @@ version := {
val buildNR = sys.env.getOrElse("BUI_COUNTER", "1") val buildNR = sys.env.getOrElse("BUI_COUNTER", "1")
s"$major.$minor.$buildNR" s"$major.$minor.$buildNR"
} }
organization := "de.knockoutwhist"
ThisBuild / organization := "de.knockoutwhist"
ThisBuild / version := version.value
ThisBuild / scalaVersion := "3.5.1"
lazy val root = (project in file(".")) lazy val root = (project in file("."))
.settings( .settings(
name := "Projekt-zu-SE" name := "Projekt-zu-SE",
assembly / mainClass := Some("de.knockoutwhist.KnockOutWhist"),
assembly / assemblyJarName := s"KnockOutWhist-${version.value}.jar",
) )
+3 -1
View File
@@ -1 +1,3 @@
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.2.1") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.2.1")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.0")
addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.3.1")
-5
View File
@@ -1,5 +0,0 @@
import de.knockoutwhist.cards.CardManager
val originalDeck = List(CardManager.cardContainer)
CardManager.shuffleAndReset()
val shuffledDeck = CardManager.cardContainer
@@ -2,23 +2,22 @@ package de.knockoutwhist
import de.knockoutwhist.control.MatchControl import de.knockoutwhist.control.MatchControl
import de.knockoutwhist.control.text.TextMatchControl import de.knockoutwhist.ui.tui.TUIMain
object KnockOutWhist { object KnockOutWhist {
val matchControl: MatchControl = TextMatchControl
/* /*
Debug mode: Debug mode:
- Disables the random shuffle of the cards - Disables the random shuffle of the cards
*/ */
private[knockoutwhist] var DEBUG_MODE_VAR: Boolean = true private[knockoutwhist] var DEBUG_MODE_VAR: Boolean = false
def DEBUG_MODE = DEBUG_MODE_VAR def DEBUG_MODE = DEBUG_MODE_VAR
def main(args: Array[String]): Unit = { def main(args: Array[String]): Unit = {
if(!matchControl.initial()) throw new IllegalStateException("Game could not be started.") if(!TUIMain.initial) throw new IllegalStateException("Game could not be started.")
} }
} }
@@ -1,8 +1,5 @@
package de.knockoutwhist.cards package de.knockoutwhist.cards
import de.knockoutwhist.cards.CardValue.Ten
import de.knockoutwhist.cards.Suit
enum Suit(identifier: String): enum Suit(identifier: String):
def cardType(): String = identifier def cardType(): String = identifier
@@ -34,34 +31,6 @@ enum CardValue(identifier: String):
end CardValue end CardValue
case class Card(cardValue: CardValue, suit: Suit) { case class Card(cardValue: CardValue, suit: Suit) {
def cardColour(suit: Suit): String = suit match { override def toString: String = s"$cardValue of $suit"
case Suit.Hearts | Suit.Diamonds => Console.RED
case Suit.Clubs | Suit.Spades => Console.BLACK
}
def renderAsString(): Vector[String] = {
if (cardValue == Ten) {
return Vector(
s"┌─────────┐",
s"│${cardColour(suit)}${Console.BOLD}${cardValue.cardType()}${Console.RESET} │",
"│ │",
s"│ ${cardColour(suit)}${Console.BOLD}${suit.cardType()}${Console.RESET} │",
"│ │",
s"│ ${cardColour(suit)}${Console.BOLD}${cardValue.cardType()}${Console.RESET}│",
s"└─────────┘"
)
}
Vector(
s"┌─────────┐",
s"│${cardColour(suit)}${Console.BOLD}${cardValue.cardType()}${Console.RESET} │",
"│ │",
s"│ ${cardColour(suit)}${Console.BOLD}${suit.cardType()}${Console.RESET} │",
"│ │",
s"│ ${cardColour(suit)}${Console.BOLD}${cardValue.cardType()}${Console.RESET}│",
s"└─────────┘"
)
}
override def toString: String = s"$cardValue of $suit"
//Combined String
} }
@@ -19,11 +19,5 @@ case class Hand(cards: List[Card]) {
def hasTrumpSuit(trumpSuit: Suit): Boolean = { def hasTrumpSuit(trumpSuit: Suit): Boolean = {
cards.exists(_.suit == trumpSuit) cards.exists(_.suit == trumpSuit)
} }
def renderAsString() : List[String] = {
val cardStrings = cards.map(_.renderAsString())
val zipped = cardStrings.transpose
zipped.map(_.mkString(" "))
}
} }
@@ -0,0 +1,12 @@
package de.knockoutwhist.control
import de.knockoutwhist.ui.tui.TUIMain
import de.knockoutwhist.utils.DelayHandler
import de.knockoutwhist.utils.events.EventHandler
object ControlHandler extends EventHandler {
addListener(TUIMain)
addListener(DelayHandler)
}
@@ -1,24 +1,77 @@
package de.knockoutwhist.control package de.knockoutwhist.control
import de.knockoutwhist.KnockOutWhist
import de.knockoutwhist.cards.{Card, CardManager}
import de.knockoutwhist.control.RoundControl.controlRound
import de.knockoutwhist.events.*
import de.knockoutwhist.events.ERROR_STATUS.{IDENTICAL_NAMES, INVALID_NAME_FORMAT, INVALID_NUMBER_OF_PLAYERS, WRONG_CARD}
import de.knockoutwhist.events.GLOBAL_STATUS.*
import de.knockoutwhist.events.PLAYER_STATUS.{SHOW_NOT_PLAYED, SHOW_WON_PLAYER_TRICK}
import de.knockoutwhist.events.ROUND_STATUS.{PLAYERS_OUT, SHOW_START_ROUND, WON_ROUND}
import de.knockoutwhist.events.round.ShowCurrentTrickEvent
import de.knockoutwhist.events.util.DelayEvent
import de.knockoutwhist.player.Player
import de.knockoutwhist.rounds.{Match, Round, Trick} import de.knockoutwhist.rounds.{Match, Round, Trick}
import de.knockoutwhist.utils.CustomPlayerQueue
import de.knockoutwhist.utils.Implicits.*
trait MatchControl { import scala.compiletime.uninitialized
import scala.io.StdIn
def initial(): Boolean import scala.util.Random
def start(): Unit
def playerControl: PlayerControl
/**
* Start the next round
* @return the next round or null if the match is over
*/
def nextRound(matchImpl: Match): Round
/** object MatchControl {
* Start the next trick
* @return the last trick or null if the round is over private[control] var playerQueue: CustomPlayerQueue[Player] = uninitialized
*/
def nextTrick(roundImpl: Round): Trick def startMatch(): Player = {
ControlHandler.invoke(ShowGlobalStatus(SHOW_START_MATCH))
val players = enterPlayers()
playerQueue = CustomPlayerQueue[Player](players, Random.nextInt(players.length))
controlMatch()
}
def enterPlayers(): Array[Player] = {
ControlHandler.invoke(ShowGlobalStatus(SHOW_TYPE_PLAYERS))
val names = StdIn.readLine().split(",")
if (names.length < 2) {
ControlHandler.invoke(ShowErrorStatus(INVALID_NUMBER_OF_PLAYERS))
return enterPlayers()
}
if (names.distinct.length != names.length) {
ControlHandler.invoke(ShowErrorStatus(IDENTICAL_NAMES))
return enterPlayers()
}
if (names.count(_.trim.isBlank) > 0 || names.count(_.trim.length <= 2) > 0 || names.count(_.trim.length > 10) > 0) {
ControlHandler.invoke(ShowErrorStatus(INVALID_NAME_FORMAT))
return enterPlayers()
}
names.map(s => Player(s))
}
def controlMatch(): Player = {
val matchImpl = Match(playerQueue.toList)
while (!isOver(matchImpl)) {
val roundImpl = controlRound(matchImpl)
}
val winner = finalizeMatch(matchImpl)
val playerwinner = winner.name
ControlHandler.invoke(ShowGlobalStatus(SHOW_FINISHED_MATCH, winner))
winner
}
def isOver(matchImpl: Match): Boolean = {
if (matchImpl.roundlist.isEmpty) {
false
} else {
RoundControl.remainingPlayers(matchImpl.roundlist.last).size == 1
}
}
def finalizeMatch(matchImpl: Match): Player = {
if (!isOver(matchImpl)) {
throw new IllegalStateException("Match is not over yet.")
}
RoundControl.remainingPlayers(matchImpl.roundlist.last).head
}
} }
@@ -1,16 +1,126 @@
package de.knockoutwhist.control package de.knockoutwhist.control
import de.knockoutwhist.cards.{Card, Suit} import de.knockoutwhist.KnockOutWhist
import de.knockoutwhist.cards.{Card, CardManager, Suit}
import de.knockoutwhist.control.PlayerControl
import de.knockoutwhist.events.ERROR_STATUS.{INVALID_INPUT, 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_DOG_PLAY_CARD, SHOW_PLAY_CARD, SHOW_TIE_NUMBERS, SHOW_TRUMPSUIT_OPTIONS, SHOW_TURN}
import de.knockoutwhist.events.cards.{RenderHandEvent, ShowTieCardsEvent}
import de.knockoutwhist.events.directional.{RequestCardEvent, RequestDogPlayCardEvent, RequestNumberEvent, RequestPickTrumpsuitEvent}
import de.knockoutwhist.events.util.DelayEvent
import de.knockoutwhist.events.{ShowErrorStatus, ShowGlobalStatus, ShowPlayerStatus}
import de.knockoutwhist.player.Player import de.knockoutwhist.player.Player
import de.knockoutwhist.rounds.Round import de.knockoutwhist.rounds.Round
import de.knockoutwhist.ui.tui.TUIMain
import de.knockoutwhist.utils.DelayHandler
import de.knockoutwhist.utils.events.EventHandler
trait PlayerControl { import scala.annotation.tailrec
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.io.StdIn.readLine
import scala.util.{Failure, Success}
import scala.util.control.Breaks.*
def playCard(player: Player): Card object PlayerControl {
def dogplayCard(player: Player, round: Round): Option[Card]
def determineWinnerTie(players: List[Player]): Player def playCard(player: Player): Card = {
def pickNextTrumpsuit(player: Player): Suit ControlHandler.invoke(ShowPlayerStatus(SHOW_TURN, player))
def showCards(player: Player): Boolean ControlHandler.invoke(DelayEvent(3000L))
def showWon(player: Player, round: Round): String ControlHandler.invoke(ShowPlayerStatus(SHOW_PLAY_CARD, player))
ControlHandler.invoke(RenderHandEvent(player.currentHand().get, true))
ControlHandler.invoke(RequestCardEvent(player.currentHand().get)) match {
case Success(value) => {
value
}
case Failure(exception) => {
ControlHandler.invoke(ShowErrorStatus(INVALID_NUMBER))
playCard(player)
}
}
}
def dogplayCard(player: Player, round: Round): Option[Card] = {
ControlHandler.invoke(ShowPlayerStatus(SHOW_TURN, player))
ControlHandler.invoke(DelayEvent(3000L))
ControlHandler.invoke(ShowPlayerStatus(SHOW_DOG_PLAY_CARD, player, RoundControl.dogNeedsToPlay(round)))
ControlHandler.invoke(RenderHandEvent(player.currentHand().get, false))
ControlHandler.invoke(RequestDogPlayCardEvent(player.currentHand().get, RoundControl.dogNeedsToPlay(round))) match {
case Success(value) => {
value
}
case Failure(exception) => {
ControlHandler.invoke(ShowErrorStatus(INVALID_INPUT))
dogplayCard(player, round)
}
}
}
def determineWinnerTie(players: List[Player]): Player = {
determineWinnerTieText(players, true)
}
@tailrec
private def determineWinnerTieText(players: List[Player], tieMessage: Boolean): Player = {
if (!KnockOutWhist.DEBUG_MODE) CardManager.shuffleAndReset()
if (tieMessage) ControlHandler.invoke(ShowGlobalStatus(SHOW_TIE))
var currentStep = 0
var remaining = CardManager.cardContainer.size - (players.length - 1)
val cut: mutable.HashMap[Player, Card] = mutable.HashMap()
for (player <- players) {
var selCard: Card = null
while (selCard == null) {
ControlHandler.invoke(ShowPlayerStatus(SHOW_TIE_NUMBERS, player, remaining))
ControlHandler.invoke(RequestNumberEvent(1, remaining)) match {
case Success(value) =>
selCard = CardManager.cardContainer(currentStep + (value-1))
cut.put(player, selCard)
currentStep += value
remaining -= (value-1)
case Failure(exception) =>
ControlHandler.invoke(ShowErrorStatus(NOT_A_NUMBER))
}
}
}
ControlHandler.invoke(ShowTieCardsEvent(cut.toList))
var currentHighest: Card = null
val winner: ListBuffer[Player] = ListBuffer()
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))
return winner.head
}
ControlHandler.invoke(ShowGlobalStatus(SHOW_TIE_TIE))
determineWinnerTieText(winner.toList, false)
}
def pickNextTrumpsuit(player: Player): Suit = {
ControlHandler.invoke(ShowPlayerStatus(SHOW_TRUMPSUIT_OPTIONS, player))
ControlHandler.invoke(RenderHandEvent(player.currentHand().get, false))
ControlHandler.invoke(RequestPickTrumpsuitEvent()) match {
case Success(value) => {
value
}
case Failure(exception) => {
ControlHandler.invoke(ShowErrorStatus(INVALID_NUMBER))
pickNextTrumpsuit(player)
}
}
}
} }
@@ -0,0 +1,124 @@
package de.knockoutwhist.control
import de.knockoutwhist.KnockOutWhist
import de.knockoutwhist.cards.CardManager
import de.knockoutwhist.control.MatchControl.playerQueue
import de.knockoutwhist.player.Player
import de.knockoutwhist.rounds.{Match, Round, Trick}
import de.knockoutwhist.utils.Implicits.*
import de.knockoutwhist.control.RoundControl
import de.knockoutwhist.control.TrickControl.controlTrick
import de.knockoutwhist.events.ROUND_STATUS.{PLAYERS_OUT, SHOW_START_ROUND, WON_ROUND}
import de.knockoutwhist.events.ShowRoundStatus
import de.knockoutwhist.events.util.DelayEvent
object RoundControl {
def isOver(round: Round): Boolean = {
round.players_in.map(_.currentHand()).count(_.get.cards.isEmpty) == round.players_in.size
}
def dogNeedsToPlay(round: Round): Boolean = {
round.players_in.filter(!_.doglife).map(_.currentHand()).exists(_.get.cards.isEmpty)
}
def finalizeRound(round: Round, matchImpl: Match,force: Boolean = false): (Player, Round) = {
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.players_in.filter(!tricksMapped.contains(_))
if (playersOut.nonEmpty && !matchImpl.dogLife) {
matchImpl.dogLife = true
playersOut.foreach(p => p.doglife = true)
playersOut = List()
}
tricksMapped.keys.foreach(p => {
p.doglife = false
})
val winner = (winners.size == 1)
? winners.head
|: PlayerControl.determineWinnerTie(winners.toList)
val finalRound = Round(round.trumpSuit, matchImpl, round.tricklist, round.players_in, playersOut, winner, round.firstRound)
matchImpl.roundlist += finalRound
(winner, finalRound)
}
def remainingPlayers(round: Round): List[Player] = {
if (round.players_out == null) {
return round.players_in
}
round.players_in.filter(!round.players_out.contains(_))
}
def create_round(matchImpl: Match): Round = {
val remainingPlayer = matchImpl.roundlist.isEmpty ? matchImpl.totalplayers |: RoundControl.remainingPlayers(matchImpl.roundlist.last)
provideCards(matchImpl, remainingPlayer)
if (matchImpl.roundlist.isEmpty) {
val random_trumpsuit = CardManager.nextCard().suit
matchImpl.current_round = Some(new Round(random_trumpsuit, matchImpl, remainingPlayer, true))
} else {
val winner = matchImpl.roundlist.last.winner
val trumpsuit = PlayerControl.pickNextTrumpsuit(winner)
matchImpl.current_round = Some(new Round(trumpsuit, matchImpl, remainingPlayer, false))
}
matchImpl.number_of_cards -= 1
matchImpl.current_round.get
}
def nextRound(matchImpl: Match): Round = {
if (MatchControl.isOver(matchImpl)) {
return null
}
create_round(matchImpl)
}
def controlRound(matchImpl: Match): Round = {
val roundImpl = nextRound(matchImpl)
ControlHandler.invoke(ShowRoundStatus(SHOW_START_ROUND, roundImpl))
while (!RoundControl.isOver(roundImpl)) {
TrickControl.controlTrick(roundImpl)
}
val (roundWinner, finalRound) = RoundControl.finalizeRound(roundImpl, matchImpl)
ControlHandler.invoke(ShowRoundStatus(WON_ROUND, finalRound, roundWinner))
ControlHandler.invoke(DelayEvent(5000L))
if (finalRound.players_out.nonEmpty) {
ControlHandler.invoke(ShowRoundStatus(PLAYERS_OUT, finalRound))
finalRound.players_out.foreach(p => {
playerQueue.remove(p)
})
}
playerQueue.resetAndSetStart(roundWinner)
finalRound
}
private def provideCards(matchImpl: Match, players: List[Player]): Int = {
if (!KnockOutWhist.DEBUG_MODE) CardManager.shuffleAndReset()
var hands = 0
for (player <- players) {
if (!player.doglife) {
player.provideHand(CardManager.createHand(matchImpl.number_of_cards))
} else {
player.provideHand(CardManager.createHand(1))
}
hands += 1
}
hands
}
}
@@ -0,0 +1,111 @@
package de.knockoutwhist.control
import de.knockoutwhist.KnockOutWhist
import de.knockoutwhist.cards.Card
import de.knockoutwhist.control.MatchControl.playerQueue
import de.knockoutwhist.events.ERROR_STATUS.WRONG_CARD
import de.knockoutwhist.events.PLAYER_STATUS.{SHOW_NOT_PLAYED, SHOW_WON_PLAYER_TRICK}
import de.knockoutwhist.events.{ShowErrorStatus, ShowPlayerStatus}
import de.knockoutwhist.events.round.ShowCurrentTrickEvent
import de.knockoutwhist.events.util.DelayEvent
import de.knockoutwhist.rounds.{Round, Trick}
import de.knockoutwhist.player.Player
object TrickControl {
def playCard(trick: Trick, round: Round, card: Card, player: Player): Boolean = {
if (trick.finished) {
throw new IllegalStateException("This trick is already finished")
} else {
if (trick.get_first_card().isEmpty) {
trick.set_first_card(card)
trick.cards += (card -> player)
true
} else if (card.suit == trick.get_first_card().getOrElse(card).suit) { // Wert aus Option extrahieren
trick.cards += (card -> player)
true
} else if (card.suit == round.trumpSuit) {
trick.cards += (card -> player)
true
} else {
trick.cards += (card -> player)
false
}
}
}
def wonTrick(trick: Trick, round: Round): (Player, 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.get_first_card().get.suit).maxBy(_.cardValue.ordinal) //stream
}
}
val winningPlayer = trick.cards(winningCard)
val finalTrick = Trick(round, trick.cards, winningPlayer, true)
round.tricklist += finalTrick
(winningPlayer, finalTrick)
}
def create_trick(round: Round): Trick = {
val trick = new Trick(round)
round.set_current_trick(trick)
trick
}
def controlTrick(round: Round): Trick = {
val trick = nextTrick(round)
for (player <- playerQueue) {
ControlHandler.invoke(ShowCurrentTrickEvent(round, trick))
if (!player.doglife) {
val rightCard = controlSuitplayed(trick, player)
player.removeCard(rightCard)
TrickControl.playCard(trick, round, rightCard, player)
} else if (player.currentHand().exists(_.cards.nonEmpty)) {
val card = PlayerControl.dogplayCard(player, round)
if (card.isEmpty) {
ControlHandler.invoke(ShowPlayerStatus(SHOW_NOT_PLAYED, player))
} else {
player.removeCard(card.get)
TrickControl.playCard(trick, round, card.get, player)
}
}
}
val (winner, finalTrick) = TrickControl.wonTrick(trick, round)
ControlHandler.invoke(ShowCurrentTrickEvent(round, finalTrick))
ControlHandler.invoke(ShowPlayerStatus(SHOW_WON_PLAYER_TRICK, winner))
playerQueue.resetAndSetStart(winner)
ControlHandler.invoke(DelayEvent(3000L))
finalTrick
}
def nextTrick(roundImpl: Round): Trick = {
if (RoundControl.isOver(roundImpl)) {
return null
}
create_trick(roundImpl)
}
private[control] def controlSuitplayed(trick: Trick, player: Player): Card = {
var card = PlayerControl.playCard(player)
if (trick.get_first_card().isDefined) {
val firstCard = trick.get_first_card().get
while (!(firstCard.suit == card.suit)) {
var hasSuit = false
for (cardInHand <- player.currentHand().get.cards) {
if (cardInHand.suit == firstCard.suit) {
hasSuit = true
}
}
if (!hasSuit) {
return card
} else {
ControlHandler.invoke(ShowErrorStatus(WRONG_CARD, firstCard))
card = PlayerControl.playCard(player)
}
}
}
card
}
}
@@ -1,205 +0,0 @@
package de.knockoutwhist.control.text
import de.knockoutwhist.KnockOutWhist
import de.knockoutwhist.cards.Card
import de.knockoutwhist.control.{MatchControl, PlayerControl}
import de.knockoutwhist.player.Player
import de.knockoutwhist.rounds.{Match, Round, Trick}
import de.knockoutwhist.utils.CustomPlayerQueue
import scala.compiletime.uninitialized
import scala.io.StdIn
import scala.util.Random
object TextMatchControl extends MatchControl {
private[control] var playerQueue: CustomPlayerQueue[Player] = uninitialized
private var init = false
override def initial(): Boolean = {
if(init) {
println("The game is already running.")
return false
}
init = true
println("Welcome to Knockout Whist!")
start()
true
}
override def start(): Unit = {
while(true) { //Main Gameplay Loop
val input = printMenu()
input match {
case "1" =>
startMatch()
case "2" =>
println("Exiting the game.")
return
case _ =>
println("Invalid input. Please try again.")
}
}
}
private[control] def startMatch(): Player = {
clearConsole()
println("Starting a new match.")
val players = enterPlayers()
playerQueue = CustomPlayerQueue[Player](players, Random.nextInt(players.length))
clearConsole()
controlMatch()
}
private[control] def enterPlayers(): Array[Player] = {
println("Please enter the names of the players, separated by a comma.")
val names = StdIn.readLine().split(",")
if(names.length < 2) {
println("Please enter at least two names.")
return enterPlayers()
}
if(names.distinct.length != names.length) {
println("Please enter unique names.")
return enterPlayers()
}
if(names.count(_.trim.isBlank) > 0 || names.count(_.trim.length <= 2) > 0 || names.count(_.trim.length > 10) > 0) {
println("Please enter valid names. Those can not be empty, shorter than 2 or longer then 10 characters.")
return enterPlayers()
}
names.map(s => Player(s))
}
private[control] def controlMatch(): Player = {
val matchImpl = Match(playerQueue.toList)
while (!matchImpl.isOver) {
val roundImpl = controlRound(matchImpl)
}
clearConsole()
println(s"The match is over. The winner is ${matchImpl.finalizeMatch().name}.")
matchImpl.finalizeMatch()
}
private[control] def controlRound(matchImpl: Match): Round = {
val roundImpl = nextRound(matchImpl)
clearConsole(10)
println(s"Starting a new round. The trump suit is ${roundImpl.trumpSuit}.")
clearConsole(2)
while (!roundImpl.isOver) {
controlTrick(roundImpl)
}
val (roundWinner, finalRound) = roundImpl.finalizeRound()
println(s"${roundWinner.name} won the round.")
if(!KnockOutWhist.DEBUG_MODE) Thread.sleep(5000L)
if(finalRound.players_out.nonEmpty) {
println("The following players are out of the game:")
finalRound.players_out.foreach(p => {
println(p.name)
playerQueue.remove(p)
})
}
playerQueue.resetAndSetStart(roundWinner)
finalRound
}
private[control] def controlTrick(round: Round): Trick = {
val trick = nextTrick(round)
for (player <- playerQueue) {
clearConsole()
println(printTrick(round))
if (!player.doglife) {
val rightCard = controlSuitplayed(trick, player)
player.removeCard(rightCard)
trick.playCard(rightCard, player)
} else if (player.currentHand().exists(_.cards.nonEmpty)) {
val card = playerControl.dogplayCard(player, round)
if (card.isEmpty) {
println(f"Player $player decided to not play his card")
} else {
player.removeCard(card.get)
trick.playCard(card.get, player)
}
}
}
val (winner, finalTrick) = trick.wonTrick()
clearConsole()
println(printTrick(round))
println(s"${winner.name} won the trick.")
clearConsole(2)
playerQueue.resetAndSetStart(winner)
if(!KnockOutWhist.DEBUG_MODE) Thread.sleep(3000L)
finalTrick
}
private[control] def controlSuitplayed(trick: Trick, player: Player): Card = {
var card = playerControl.playCard(player)
if (trick.get_first_card().isDefined) {
while (!(trick.get_first_card().get.suit == card.suit)) {
var hasSuit = false
for (cardInHand <- player.currentHand().get.cards) {
if (cardInHand.suit == trick.get_first_card().get.suit) {
hasSuit = true
}
}
if(!hasSuit) {
return card
}else {
println(f"You have to play a card of suit: ${trick.get_first_card().get.suit}\n")
card = playerControl.playCard(player)
}
}
}
card
}
private[control] def printMenu(): String = {
println("Please select an option:")
println("1. Start a new match")
println("2. Exit")
StdIn.readLine()
}
private[control] def printTrick(round: Round): String = {
val sb = new StringBuilder()
sb.append("Current Trick:\n")
sb.append("Trump-Suit: " + round.trumpSuit + "\n")
if(round.get_current_trick().get_first_card().isDefined) {
sb.append(s"Suit to play: ${round.get_current_trick().get_first_card().get.suit}\n")
}
for((card, player) <- round.get_current_trick().cards) {
sb.append(s"${player.name} played ${card.toString}\n")
}
sb.toString()
}
private def clearConsole(lines: Int = 32): Int = {
var l = 0
for(_ <- 0 until lines) {
println()
l += 1
}
l
}
override def playerControl: PlayerControl = {
TextPlayerControl
}
override def nextRound(matchImpl: Match): Round = {
if(matchImpl.isOver) {
println(s"The match is over. The winner is ${matchImpl.finalizeMatch().name}.")
return null
}
matchImpl.create_round()
}
override def nextTrick(roundImpl: Round): Trick = {
if(roundImpl.isOver) {
println("The round is over.")
return null
}
roundImpl.create_trick()
}
}
@@ -1,194 +0,0 @@
package de.knockoutwhist.control.text
import de.knockoutwhist.KnockOutWhist
import de.knockoutwhist.cards.{Card, CardManager, Suit}
import de.knockoutwhist.control.PlayerControl
import de.knockoutwhist.player.Player
import de.knockoutwhist.rounds.Round
import scala.annotation.tailrec
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.io.StdIn.readLine
import scala.util.control.Breaks.*
object TextPlayerControl extends PlayerControl {
override def playCard(player: Player): Card = {
println("It's your turn, " + player.name + ".")
if(!KnockOutWhist.DEBUG_MODE) Thread.sleep(3000L)
println("Which card do you want to play?")
showCards(player)
try {
val card = readLine().toInt-1
val handCard = player.currentHand()
if (handCard.isEmpty) {
println("You don't have any cards.")
throw new IllegalStateException("Trying to play a card without any cards.")
} else if(card < 0 || card >= handCard.get.cards.length) {
println("Please enter a valid number.")
playCard(player)
} else {
handCard.get.cards(card)
}
} catch {
case e: NumberFormatException =>
println("Please enter a valid number.")
playCard(player)
}
}
override def dogplayCard(player: Player, round: Round): Option[Card] = {
println("It's your turn, " + player.name + ".")
if (!KnockOutWhist.DEBUG_MODE) Thread.sleep(3000L)
println("You are using your dog life. Do you want to play your final card now?")
if(round.dogNeedsToPlay) {
println("You have to play your final card this round!")
println("Please enter y to play your final card.")
}else {
println("Please enter y/n to play your final card.")
}
showCards(player)
val card = readLine()
val handCard = player.currentHand()
if (handCard.isEmpty) {
println("You don't have any cards.")
throw new IllegalStateException("Trying to play a card without any cards.")
} else if(card.equalsIgnoreCase("y")) {
Some(handCard.get.cards.head)
} else if (card.equalsIgnoreCase("n") && !round.dogNeedsToPlay) {
None
} else {
println("Please enter y or n to play your final card.")
dogplayCard(player, round)
}
}
override def determineWinnerTie(players: List[Player]): Player = {
determineWinnerTieText(players, true)
}
@tailrec
private def determineWinnerTieText(players: List[Player], tieMessage: Boolean): Player = {
if (!KnockOutWhist.DEBUG_MODE) CardManager.shuffleAndReset()
if (tieMessage) println("It's a tie! Let's cut to determine the winner.")
var currentStep = 0
var remaining = CardManager.cardContainer.size - (players.length - 1)
val cut: mutable.HashMap[Player, Card] = mutable.HashMap()
for (player <- players) {
var selCard: Card = null
while (selCard == null) {
println(s"${player.name} enter a number between 1 and $remaining.")
try {
val selected = readLine().toInt - 1
if (selected >= 0 && selected < remaining) {
selCard = CardManager.cardContainer(currentStep + selected)
cut.put(player, selCard)
currentStep += selected + 1
remaining -= selected
} else {
println("Please enter a valid number.")
}
} catch {
case e: NumberFormatException =>
println("Please enter a valid number.")
}
}
}
println("The cards are:")
val a: Array[String] = Array("", "", "", "", "", "", "", "")
for ((player, card) <- cut) {
val playerNameLength = player.name.length
a(0) += " " + player.name + ":" + (" " * (playerNameLength - 1))
val rendered = card.renderAsString()
a(1) += " " + rendered(0)
a(2) += " " + rendered(1)
a(3) += " " + rendered(2)
a(4) += " " + rendered(3)
a(5) += " " + rendered(4)
a(6) += " " + rendered(5)
a(7) += " " + rendered(6)
}
a.foreach(println)
var currentHighest: Card = null
val winner: ListBuffer[Player] = ListBuffer()
for ((player, card) <- cut) {
breakable {
if (currentHighest == null) {
currentHighest = card
winner += player
break
}
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) {
println(s"${winner.head.name} wins the cut!")
return winner.head
}
println("It's a tie again! Let's cut again.")
determineWinnerTieText(winner.toList, false)
}
override def pickNextTrumpsuit(player: Player): Suit = {
println("Which suit do you want to pick as the next trump suit?")
println("1: Hearts")
println("2: Diamonds")
println("3: Clubs")
println("4: Spades")
println()
player.currentHand().get.renderAsString().foreach(println)
try {
val suit = readLine().toInt
suit match {
case 1 => Suit.Hearts
case 2 => Suit.Diamonds
case 3 => Suit.Clubs
case 4 => Suit.Spades
case _ =>
println("Please enter a valid number.")
pickNextTrumpsuit(player)
}
} catch {
case e: NumberFormatException =>
println("Please enter a valid number.")
pickNextTrumpsuit(player)
}
}
override def showCards(player: Player): Boolean = {
val hand = player.currentHand()
if (hand.isEmpty) {
println("You don't have any cards.")
return false
}
println("Your cards:")
var rendered = hand.get.renderAsString()
rendered ::= {
var s = ""
for (i <- hand.get.cards.indices) {
s += s" ${i+1} " + " "
}
s
}
rendered.foreach(println)
true
}
override def showWon(player: Player, round: Round): String = {
s"$player won this round."
}
}
@@ -0,0 +1,58 @@
package de.knockoutwhist.events
import de.knockoutwhist.player.Player
import de.knockoutwhist.rounds.{Match, Round}
import de.knockoutwhist.utils.events.SimpleEvent
enum GLOBAL_STATUS {
case SHOW_TIE
case SHOW_TIE_WINNER
case SHOW_TIE_TIE
case SHOW_START_MATCH
case SHOW_TYPE_PLAYERS
case SHOW_FINISHED_MATCH
}
enum PLAYER_STATUS {
case SHOW_TURN
case SHOW_PLAY_CARD
case SHOW_DOG_PLAY_CARD
case SHOW_TIE_NUMBERS
case SHOW_TRUMPSUIT_OPTIONS
case SHOW_NOT_PLAYED
case SHOW_WON_PLAYER_TRICK
}
enum ROUND_STATUS {
case SHOW_START_ROUND
case WON_ROUND
case PLAYERS_OUT
}
enum ERROR_STATUS {
case INVALID_NUMBER
case NOT_A_NUMBER
case INVALID_INPUT
case INVALID_NUMBER_OF_PLAYERS
case IDENTICAL_NAMES
case INVALID_NAME_FORMAT
case WRONG_CARD
}
abstract class ShowStatusEvent extends SimpleEvent
case class ShowGlobalStatus(status: GLOBAL_STATUS, objects: Any*) extends ShowStatusEvent {
override def id: String = "ShowGlobalStatus"
}
case class ShowPlayerStatus(status: PLAYER_STATUS, player: Player, objects: Any*) extends ShowStatusEvent {
override def id: String = "ShowPlayerStatus"
}
case class ShowErrorStatus(status: ERROR_STATUS, objects: Any*) extends ShowStatusEvent {
override def id: String = "ShowErrorStatus"
}
case class ShowRoundStatus(status: ROUND_STATUS, currentRound: Round, objects: Any*) extends ShowStatusEvent {
override def id: String = "ShowRoundStatus"
}
@@ -0,0 +1,8 @@
package de.knockoutwhist.events.cards
import de.knockoutwhist.cards.Hand
import de.knockoutwhist.utils.events.SimpleEvent
final case class RenderHandEvent(hand: Hand, showNumbers: Boolean) extends SimpleEvent {
override def id: String = "RenderHandEvent"
}
@@ -0,0 +1,9 @@
package de.knockoutwhist.events.cards
import de.knockoutwhist.cards.Card
import de.knockoutwhist.player.Player
import de.knockoutwhist.utils.events.SimpleEvent
case class ShowTieCardsEvent(card: List[(Player, Card)]) extends SimpleEvent {
override def id: String = "ShowTieCardsEvent"
}
@@ -0,0 +1,10 @@
package de.knockoutwhist.events.directional
import de.knockoutwhist.cards.{Card, Hand}
import de.knockoutwhist.utils.events.ReturnableEvent
import scala.util.Try
case class RequestCardEvent(hand: Hand) extends ReturnableEvent[Try[Card]] {
override def id: String = "RequestCardEvent"
}
@@ -0,0 +1,10 @@
package de.knockoutwhist.events.directional
import de.knockoutwhist.cards.{Card, Hand}
import de.knockoutwhist.utils.events.ReturnableEvent
import scala.util.Try
case class RequestDogPlayCardEvent(hand: Hand, needs_to_play: Boolean) extends ReturnableEvent[Try[Option[Card]]] {
override def id: String = "RequestDogPlayCardEvent"
}
@@ -0,0 +1,10 @@
package de.knockoutwhist.events.directional
import de.knockoutwhist.utils.events.{SimpleEvent, ReturnableEvent}
import scala.io.StdIn
import scala.util.Try
case class RequestNumberEvent(min: Int, max: Int) extends ReturnableEvent[Try[Int]] {
override def id: String = "RequestNumberEvent"
}
@@ -0,0 +1,10 @@
package de.knockoutwhist.events.directional
import de.knockoutwhist.cards.Suit
import de.knockoutwhist.utils.events.ReturnableEvent
import scala.util.Try
case class RequestPickTrumpsuitEvent() extends ReturnableEvent[Try[Suit]] {
override def id: String = "RequestPickTrumpsuitEvent"
}
@@ -0,0 +1,8 @@
package de.knockoutwhist.events.round
import de.knockoutwhist.rounds.{Round, Trick}
import de.knockoutwhist.utils.events.SimpleEvent
case class ShowCurrentTrickEvent(round: Round, trick: Trick) extends SimpleEvent {
override def id: String = "ShowCurrentTrickEvent"
}
@@ -0,0 +1,7 @@
package de.knockoutwhist.events.util
import de.knockoutwhist.utils.events.SimpleEvent
case class DelayEvent (delay: Long) extends SimpleEvent {
override def id: String = "DelayEvent"
}
@@ -14,9 +14,7 @@ case class Player(name: String) {
this.hand = Some(hand) this.hand = Some(hand)
true true
} }
def pickTrumpsuit(): Suit = {
KnockOutWhist.matchControl.playerControl.pickNextTrumpsuit(this)
}
def removeCard(card: Card): Int = { def removeCard(card: Card): Int = {
hand = Some(hand.get.removeCard(card)) hand = Some(hand.get.removeCard(card))
hand.get.cards.size hand.get.cards.size
@@ -7,56 +7,11 @@ import de.knockoutwhist.player.Player
import scala.collection.mutable.ListBuffer import scala.collection.mutable.ListBuffer
import de.knockoutwhist.utils.Implicits._ import de.knockoutwhist.utils.Implicits._
case class Match(totalplayers: List[Player], private[rounds] var number_of_cards: Int = 7) { case class Match(totalplayers: List[Player], var number_of_cards: Int = 7) {
private[rounds] val roundlist: ListBuffer[Round] = ListBuffer[Round]() val roundlist: ListBuffer[Round] = ListBuffer[Round]()
private var current_round: Option[Round] = None var current_round: Option[Round] = None
private[rounds] var dogLife = false var dogLife = false
def create_round(): Round = {
val remainingPlayer = roundlist.isEmpty ? totalplayers |: roundlist.last.remainingPlayers()
provideCards(remainingPlayer)
if (roundlist.isEmpty) {
val random_trumpsuit = CardManager.nextCard().suit
current_round = Some(new Round(random_trumpsuit, this, remainingPlayer, true))
} else {
val winner = roundlist.last.winner
val trumpsuit = winner.pickTrumpsuit()
current_round = Some(new Round(trumpsuit, this, remainingPlayer, false))
}
number_of_cards -= 1
current_round.get
}
def isOver: Boolean = {
if(roundlist.isEmpty) {
false
} else {
roundlist.last.remainingPlayers().size == 1
}
}
private def provideCards(players: List[Player]): Int = {
if(!KnockOutWhist.DEBUG_MODE) CardManager.shuffleAndReset()
var hands = 0
for (player <- players) {
if(!player.doglife) {
player.provideHand(CardManager.createHand(number_of_cards))
} else {
player.provideHand(CardManager.createHand(1))
}
hands += 1
}
hands
}
def finalizeMatch(): Player = {
if(!isOver) {
throw new IllegalStateException("Match is not over yet.")
}
roundlist.last.remainingPlayers().head
}
override def toString: String = { override def toString: String = {
s"${totalplayers}, ${number_of_cards}" s"${totalplayers}, ${number_of_cards}"
@@ -2,77 +2,26 @@ package de.knockoutwhist.rounds
import de.knockoutwhist.KnockOutWhist import de.knockoutwhist.KnockOutWhist
import de.knockoutwhist.cards.{CardManager, Suit} import de.knockoutwhist.cards.{CardManager, Suit}
import de.knockoutwhist.player.Player import de.knockoutwhist.player.Player
import de.knockoutwhist.utils.Implicits._ import de.knockoutwhist.utils.Implicits.*
import scala.collection.mutable import scala.collection.mutable
import scala.collection.mutable.ListBuffer import scala.collection.mutable.ListBuffer
case class Round private[rounds](trumpSuit: Suit, matchImpl: Match, private[rounds] val tricklist: ListBuffer[Trick], players_in: List[Player], players_out: List[Player] = null, winner: Player = null, firstRound: Boolean) { case class Round (trumpSuit: Suit, matchImpl: Match, tricklist: ListBuffer[Trick], players_in: List[Player], players_out: List[Player] = null, winner: Player = null, firstRound: Boolean) {
def this(trumpSuit: Suit, matchImpl: Match, players_in: List[Player], firstRound: Boolean) = { def this(trumpSuit: Suit, matchImpl: Match, players_in: List[Player], firstRound: Boolean) = {
this(trumpSuit, matchImpl, ListBuffer[Trick](), players_in, firstRound = firstRound) this(trumpSuit, matchImpl, ListBuffer[Trick](), players_in, firstRound = firstRound)
} }
private var current_trick: Option[Trick] = None private var current_trick: Option[Trick] = None
def get_current_trick(): Trick = { def get_current_trick(): Option[Trick] = {
current_trick.getOrElse(create_trick()) current_trick
} }
def get_tricks(): List[Trick] = tricklist.toList
def create_trick(): Trick = { def set_current_trick(trick: Trick): Unit = {
val trick = new Trick(this)
current_trick = Some(trick) current_trick = Some(trick)
trick
}
def isOver: Boolean = {
players_in.map(_.currentHand()).count(_.get.cards.isEmpty) == players_in.size
}
def dogNeedsToPlay: Boolean = {
players_in.filter(!_.doglife).map(_.currentHand()).exists(_.get.cards.isEmpty)
}
def finalizeRound(force: Boolean = false): (Player, Round) = {
if(!force && tricklist.isEmpty)
throw new IllegalStateException("No tricks played in this round")
if(!force && !isOver)
throw new IllegalStateException("Not all tricks were played in this round")
val tricksMapped = 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 = firstRound
? List()
|: players_in.filter(!tricksMapped.contains(_))
if(playersOut.nonEmpty && !matchImpl.dogLife) {
matchImpl.dogLife = true
playersOut.foreach(p => p.doglife = true)
playersOut = List()
}
tricksMapped.keys.foreach(p => {p.doglife = false})
val winner = (winners.size == 1)
? winners.head
|: KnockOutWhist.matchControl.playerControl.determineWinnerTie(winners.toList)
val finalRound = Round(trumpSuit, matchImpl, tricklist, players_in, playersOut, winner, firstRound)
matchImpl.roundlist += finalRound
(winner, finalRound)
}
def remainingPlayers(): List[Player] = {
if (players_out == null) {
return players_in
}
players_in.filter(!players_out.contains(_))
} }
override def toString: String = { override def toString: String = {
s"$trumpSuit, $tricklist, $players_in, $players_out, $winner, $firstRound" s"$trumpSuit, $tricklist, $players_in, $players_out, $winner, $firstRound"
} }
@@ -7,54 +7,21 @@ import de.knockoutwhist.player.Player
import scala.collection.mutable import scala.collection.mutable
case class Trick private(round: Round, cards: mutable.HashMap[Card, Player], winner: Player = null, finished: Boolean = false) { case class Trick (round: Round, cards: mutable.HashMap[Card, Player], winner: Player = null, finished: Boolean = false) {
def this(round: Round) = { def this(round: Round) = {
this(round, mutable.HashMap[Card, Player]()) this(round, mutable.HashMap[Card, Player]())
} }
private var first_card: Option[Card] = None // statt als Parameter im Konstruktor private var first_card: Option[Card] = None // statt als Parameter im Konstruktor
def get_first_card(): Option[Card] = first_card def set_first_card(card: Card): Option[Card] = {
if(first_card.isDefined) {
/**
* Play a card in the trick
* @param card The card to play
* @return True if the card has a chance of winning the trick, false otherwise
*/
def playCard(card: Card, player: Player): Boolean = {
if (finished) {
throw new IllegalStateException("This trick is already finished") throw new IllegalStateException("This trick is already finished")
} else {
if (first_card.isEmpty) {
first_card = Some(card)
cards += (card -> player)
true
} else if (card.suit == first_card.getOrElse(card).suit) { // Wert aus Option extrahieren
cards += (card -> player)
true
} else if (card.suit == round.trumpSuit) {
cards += (card -> player)
true
} else {
cards += (card -> player)
false
}
} }
first_card = Some(card)
first_card
} }
def get_first_card(): Option[Card] = first_card
def wonTrick(): (Player, Trick) = {
val winningCard = {
if (cards.keys.exists(_.suit == round.trumpSuit)) {
cards.keys.filter(_.suit == round.trumpSuit).maxBy(_.cardValue.ordinal) //stream
} else {
cards.keys.filter(_.suit == first_card.get.suit).maxBy(_.cardValue.ordinal) //stream
}
}
val winningPlayer = cards(winningCard)
val finalTrick = Trick(round, cards, winningPlayer, true)
round.tricklist += finalTrick
(winningPlayer, finalTrick)
}
override def toString: String = { override def toString: String = {
s"$cards, $winner, $finished" s"$cards, $winner, $finished"
@@ -0,0 +1,7 @@
package de.knockoutwhist.ui
trait UI {
def initial: Boolean
}
@@ -0,0 +1,319 @@
package de.knockoutwhist.ui.tui
import de.knockoutwhist.cards.{Card, CardValue, Hand, Suit}
import de.knockoutwhist.control.{ControlHandler, MatchControl}
import de.knockoutwhist.events.*
import de.knockoutwhist.events.ERROR_STATUS.*
import de.knockoutwhist.events.GLOBAL_STATUS.*
import de.knockoutwhist.events.PLAYER_STATUS.*
import de.knockoutwhist.events.ROUND_STATUS.{PLAYERS_OUT, SHOW_START_ROUND, WON_ROUND}
import de.knockoutwhist.events.cards.{RenderHandEvent, ShowTieCardsEvent}
import de.knockoutwhist.events.directional.{RequestCardEvent, RequestDogPlayCardEvent, RequestNumberEvent, RequestPickTrumpsuitEvent}
import de.knockoutwhist.events.round.ShowCurrentTrickEvent
import de.knockoutwhist.player.Player
import de.knockoutwhist.ui.UI
import de.knockoutwhist.utils.events.{EventListener, ReturnableEvent}
import scala.annotation.tailrec
import scala.io.StdIn.readLine
import scala.util.{Failure, Success, Try}
object TUIMain extends EventListener with UI {
var init = false
override def listen[R](event: ReturnableEvent[R]): Option[R] = {
event match {
case event: RenderHandEvent =>
TUICards.renderHandEvent(event.hand, event.showNumbers).foreach(println)
Some(true)
case event: ShowTieCardsEvent =>
val a: Array[String] = Array("", "", "", "", "", "", "", "")
for ((player,card) <- event.card) {
val playerNameLength = player.name.length
a(0) += " " + player.name + ":" + (" " * (playerNameLength - 1))
val rendered = TUICards.renderCardAsString(card)
a(1) += " " + rendered(0)
a(2) += " " + rendered(1)
a(3) += " " + rendered(2)
a(4) += " " + rendered(3)
a(5) += " " + rendered(4)
a(6) += " " + rendered(5)
a(7) += " " + rendered(6)
}
a.foreach(println)
Some(true)
case event: ShowGlobalStatus =>
event.status match {
case SHOW_TIE =>
println("It's a tie! Let's cut to determine the winner.")
Some(true)
case SHOW_TIE_WINNER =>
if(event.objects.length != 1 || !event.objects.head.isInstanceOf[Player]) {
None
} else {
println(s"${event.objects.head.asInstanceOf[Player].name} wins the cut!")
Some(true)
}
case SHOW_TIE_TIE =>
println("It's a tie again! Let's cut again.")
Some(true)
case SHOW_START_MATCH =>
TUIUtil.clearConsole()
println("Starting a new match...")
TUIUtil.clearConsole(2)
Some(true)
case SHOW_TYPE_PLAYERS =>
println("Please enter the names of the players, separated by a comma.")
Some(true)
case SHOW_FINISHED_MATCH =>
if (event.objects.length != 1 || !event.objects.head.isInstanceOf[Player]) {
None
} else {
TUIUtil.clearConsole()
println(s"The match is over. The winner is ${event.objects.head.asInstanceOf[Player]}")
Some(true)
}
}
case event: ShowPlayerStatus =>
val player = event.player
event.status match {
case SHOW_TURN =>
println("It's your turn, " + player.name + ".")
Some(true)
case SHOW_PLAY_CARD =>
println("Which card do you want to play?")
Some(true)
case SHOW_DOG_PLAY_CARD =>
if(event.objects.length != 1 || !event.objects.head.isInstanceOf[Boolean]) {
None
} else {
println("You are using your dog life. Do you want to play your final card now?")
if (event.objects.head.asInstanceOf[Boolean]) {
println("You have to play your final card this round!")
println("Please enter y to play your final card.")
Some(true)
} else {
println("Please enter y/n to play your final card.")
Some(true)
}
}
case SHOW_TIE_NUMBERS =>
if(event.objects.length != 1 || !event.objects.head.isInstanceOf[Int]) {
None
} else {
println(s"${player.name} enter a number between 1 and ${event.objects.head.asInstanceOf[Int]}.")
Some(true)
}
case SHOW_TRUMPSUIT_OPTIONS =>
println("Which suit do you want to pick as the next trump suit?")
println("1: Hearts")
println("2: Diamonds")
println("3: Clubs")
println("4: Spades")
println()
Some(true)
case SHOW_NOT_PLAYED =>
println(s"Player ${event.player} decided to not play his card")
Some(true)
case SHOW_WON_PLAYER_TRICK =>
println(s"${event.player.name} won the trick.")
TUIUtil.clearConsole(2)
Some(true)
}
case event: ShowRoundStatus =>
event.status match {
case SHOW_START_ROUND =>
TUIUtil.clearConsole()
println(s"Starting a new round. The trump suit is ${event.currentRound.trumpSuit}.")
TUIUtil.clearConsole(2)
Some(true)
case WON_ROUND =>
if (event.objects.length != 1 || !event.objects.head.isInstanceOf[Player]) {
None
} else {
println(s"${event.objects.head.asInstanceOf[Player].name} won the round.")
Some(true)
}
case PLAYERS_OUT =>
println("The following players are out of the game:")
event.currentRound.players_out.foreach(p => {
println(p.name)
})
Some(true)
}
case event: ShowErrorStatus =>
event.status match {
case INVALID_NUMBER =>
println("Please enter a valid number.")
Some(true)
case NOT_A_NUMBER =>
println("Please enter a valid number.")
Some(true)
case INVALID_INPUT =>
println("Please enter a valid input")
Some(true)
case INVALID_NUMBER_OF_PLAYERS =>
println("Please enter at least two names.")
Some(true)
case IDENTICAL_NAMES =>
println("Please enter unique names.")
Some(true)
case INVALID_NAME_FORMAT =>
println("Please enter valid names. Those can not be empty, shorter than 2 or longer then 10 characters.")
Some(true)
case WRONG_CARD =>
if(event.objects.length != 1 || !event.objects.head.isInstanceOf[Card]) {
None
} else {
println(f"You have to play a card of suit: ${event.objects.head.asInstanceOf[Card].suit}\n")
Some(true)
}
}
case event: RequestNumberEvent =>
Some(Try {
val input = readLine()
val number = input.toInt
if (number < event.min || number > event.max) {
throw new IllegalArgumentException(s"Number must be between ${event.min} and ${event.max}")
}
number
})
case event: RequestCardEvent =>
Some(Try {
val card = readLine().toInt - 1
if (card < 0 || card >= event.hand.cards.length) {
throw new IllegalArgumentException(s"Number has to be between 1 and ${event.hand.cards.length}")
} else {
event.hand.cards(card)
}
})
case event: RequestDogPlayCardEvent =>
Some(Try {
val card = readLine()
if (card.equalsIgnoreCase("y")) {
Some(event.hand.cards.head)
} else if (card.equalsIgnoreCase("n") && !event.needs_to_play) {
None
} else {
throw new IllegalArgumentException("Didn't want to play card but had to")
}
}
)
case event: RequestPickTrumpsuitEvent =>
Some(Try {
val suit = readLine().toInt
suit match {
case 1 => Suit.Hearts
case 2 => Suit.Diamonds
case 3 => Suit.Clubs
case 4 => Suit.Spades
case _ => throw IllegalArgumentException("Didn't enter a number between 1 and 4")
}
})
case event: ShowCurrentTrickEvent =>
TUIUtil.clearConsole()
val sb = new StringBuilder()
sb.append("Current Trick:\n")
sb.append("Trump-Suit: " + event.round.trumpSuit + "\n")
if (event.trick.get_first_card().isDefined) {
sb.append(s"Suit to play: ${event.trick.get_first_card().get.suit}\n")
}
for ((card, player) <- event.trick.cards) {
sb.append(s"${player.name} played ${card.toString}\n")
}
println(sb.toString())
Some(true)
case _ => None
}
}
object TUICards {
def renderCardAsString(card: Card): Vector[String] = {
if (card.cardValue == CardValue.Ten) {
return Vector(
s"┌─────────┐",
s"│${cardColour(card.suit)}${Console.BOLD}${card.cardValue.cardType()}${Console.RESET} │",
"│ │",
s"│ ${cardColour(card.suit)}${Console.BOLD}${card.suit.cardType()}${Console.RESET} │",
"│ │",
s"│ ${cardColour(card.suit)}${Console.BOLD}${card.cardValue.cardType()}${Console.RESET}│",
s"└─────────┘"
)
}
Vector(
s"┌─────────┐",
s"│${cardColour(card.suit)}${Console.BOLD}${card.cardValue.cardType()}${Console.RESET} │",
"│ │",
s"│ ${cardColour(card.suit)}${Console.BOLD}${card.suit.cardType()}${Console.RESET} │",
"│ │",
s"│ ${cardColour(card.suit)}${Console.BOLD}${card.cardValue.cardType()}${Console.RESET}│",
s"└─────────┘"
)
}
private[ui] def cardColour(suit: Suit): String = suit match {
case Suit.Hearts | Suit.Diamonds => Console.RED
case Suit.Clubs | Suit.Spades => Console.BLACK
}
def renderHandEvent(hand: Hand, showNumbers: Boolean): Vector[String] = {
val cardStrings = hand.cards.map(TUICards.renderCardAsString)
var zipped = cardStrings.transpose
if (showNumbers) zipped = {
List.tabulate(hand.cards.length) { i =>
s" ${i + 1} "
}
} :: zipped
zipped.map(_.mkString(" ")).toVector
}
}
private object TUIUtil {
def clearConsole(lines: Int = 32): Int = {
var l = 0
for (_ <- 0 until lines) {
println()
l += 1
}
l
}
}
@tailrec
override def initial: Boolean = {
if (init) {
return false
}
init = true
TUIUtil.clearConsole()
println("Welcome to Knockout Whist!")
println()
println("Please select an option:")
println("1. Start a new match")
println("2. Exit")
Try{
readLine().toInt
} match {
case Success(value) =>
value match {
case 1 =>
MatchControl.startMatch()
init = false
initial
case 2 =>
println("Exiting the game.")
true
case _ =>
ControlHandler.invoke(ShowErrorStatus(INVALID_NUMBER))
init = false
initial
}
case Failure(_) =>
ControlHandler.invoke(ShowErrorStatus(NOT_A_NUMBER))
init = false
initial
}
}
}
@@ -1,9 +1,10 @@
package de.knockoutwhist.utils package de.knockoutwhist.utils
class CustomPlayerQueue[A] (protected var players: Array[A], val start: Int = 0) extends Iterable[A] { class CustomPlayerQueue[A] (protected var players: Array[A], val start: Int = 0) extends Iterable[A] {
private var current = start private var current = start
def nextPlayer(): A = { def nextPlayer(): A = {
val player = players(current) val player = players(current)
current = (current + 1) % players.length current = (current + 1) % players.length
@@ -0,0 +1,18 @@
package de.knockoutwhist.utils
import de.knockoutwhist.events.util.DelayEvent
import de.knockoutwhist.utils.events.{EventListener, ReturnableEvent}
object DelayHandler extends EventListener {
private[knockoutwhist] var activateDelay: Boolean = true
override def listen[R](event: ReturnableEvent[R]): Option[R] = {
event match {
case event: DelayEvent =>
if(activateDelay) Thread.sleep(event.delay)
Some(true)
case _ => None
}
}
}
@@ -0,0 +1,30 @@
package de.knockoutwhist.utils.events
import scala.collection.mutable
abstract class EventHandler {
private[events] var listeners: mutable.ListBuffer[EventListener] = mutable.ListBuffer[EventListener]()
def addListener(listener: EventListener): Int = {
listeners = (listeners += listener).sorted
listeners.size
}
def removeListener(listener: EventListener): Int = {
listeners = (listeners -= listener).sorted
listeners.size
}
def invoke[R](event: ReturnableEvent[R]): R = {
event match {
case simpleEvent: SimpleEvent =>
val result = listeners.map(_.listen(simpleEvent)).filter(_.isDefined).map(_.get).toList
if(result.isEmpty) return false
result.reduce((a,b) => a && b)
case returnableEvent: ReturnableEvent[R] =>
val result = listeners.view.map(_.listen(returnableEvent)).find(_.isDefined).flatten
result.getOrElse(throw new IllegalStateException("No listener returned a result"))
}
}
}
@@ -0,0 +1,21 @@
package de.knockoutwhist.utils.events
enum Priority extends Ordered[Priority] :
case High, Normal, Low
override def compare(that: Priority): Int = {
if this == that then 0
else if this == High then 1
else if that == High then -1
else if this == Low then -1
else 1
}
end Priority
trait EventListener extends Ordered[EventListener] {
def priority: Priority = Priority.Normal
def listen[R](event: ReturnableEvent[R]): Option[R]
override def compare(that: EventListener): Int = that.priority.compare(priority)
}
@@ -0,0 +1,7 @@
package de.knockoutwhist.utils.events
trait ReturnableEvent[R] {
def id: String
}
@@ -0,0 +1,3 @@
package de.knockoutwhist.utils.events
trait SimpleEvent extends ReturnableEvent[Boolean]
@@ -0,0 +1,37 @@
package de.knockoutwhist
import de.knockoutwhist.events.util.DelayEvent
import de.knockoutwhist.utils.DelayHandler
import de.knockoutwhist.utils.events.EventHandler
import org.scalatest.concurrent.*
import org.scalatest.matchers.should.Matchers
import org.scalatest.time.SpanSugar.*
import org.scalatest.wordspec.AnyWordSpec
import scala.language.postfixOps
class DelayHandlerTests extends AnyWordSpec with TimeLimits with Matchers {
private val eventHandler = new EventHandler() {}
eventHandler.addListener(DelayHandler)
"A delay event" should {
val delayEvent = DelayEvent(100)
"be able to be created" in {
delayEvent should not be null
}
"have the correct id" in {
delayEvent.id should be ("DelayEvent")
}
"have the correct delay" in {
delayEvent.delay should be (100)
}
"not be longer than 120ms if it's set to 100ms" in {
DelayHandler.activateDelay = true
failAfter(200 millis) {
eventHandler.invoke(delayEvent)
}
}
}
}
@@ -1,17 +1,21 @@
package de.knockoutwhist package de.knockoutwhist
import de.knockoutwhist.testutils.TestUtil import de.knockoutwhist.testutils.TestUtil
import de.knockoutwhist.ui.tui.TUIMain
import org.scalatest.funsuite.AnyFunSuite import org.scalatest.funsuite.AnyFunSuite
class MainTests extends AnyFunSuite { class MainTests extends AnyFunSuite {
test("Main should be able to go to the main menu") { test("Main should be able to go to the main menu") {
TestUtil.disableDelay()
TUIMain.init = false
TestUtil.simulateInput("2\n") { TestUtil.simulateInput("2\n") {
KnockOutWhist.main(Array()) KnockOutWhist.main(Array())
} }
} }
test("Main should be able to be executed twice") { test("Main should be able to be executed twice") {
TestUtil.disableDelay()
TestUtil.simulateInput("2\n") { TestUtil.simulateInput("2\n") {
assertThrows[IllegalStateException] { assertThrows[IllegalStateException] {
KnockOutWhist.main(Array()) KnockOutWhist.main(Array())
@@ -1,9 +1,11 @@
package de.knockoutwhist package de.knockoutwhist
import de.knockoutwhist.cards.{CardTests, DeckTests, HandTests} import de.knockoutwhist.cards.{CardTests, DeckTests, HandTests}
import de.knockoutwhist.control.text.{TextMatchControllerTests, TextPlayerControllerTests} import de.knockoutwhist.control.{MatchControllerTests, PlayerControllerTests}
import de.knockoutwhist.events.TestAllEvent
import de.knockoutwhist.player.PlayerTests import de.knockoutwhist.player.PlayerTests
import de.knockoutwhist.rounds.{GameplayTests, MatchTests, TrickTests} import de.knockoutwhist.rounds.{GameplayTests, MatchTests, TrickTests}
import de.knockoutwhist.utils.events.EventTests
import de.knockoutwhist.utils.{ImplicitTests, QueueTests} import de.knockoutwhist.utils.{ImplicitTests, QueueTests}
import org.scalatest.Sequential import org.scalatest.Sequential
@@ -15,10 +17,12 @@ class TestSequence extends Sequential(
new QueueTests(), new QueueTests(),
new ImplicitTests(), new ImplicitTests(),
new PlayerTests(), new PlayerTests(),
new TextPlayerControllerTests(), new PlayerControllerTests(),
new TextMatchControllerTests(), new MatchControllerTests(),
new CardTests(), new CardTests(),
new DeckTests(), new DeckTests(),
new HandTests(), new HandTests(),
new EventTests(),
) {} new TestAllEvent(),
new DelayHandlerTests()
)
@@ -1,11 +1,16 @@
package de.knockoutwhist.cards package de.knockoutwhist.cards
import de.knockoutwhist.utils.events.EventHandler
import de.knockoutwhist.cards.CardValue.{Ace, Ten}
import de.knockoutwhist.events.cards.RenderHandEvent
import de.knockoutwhist.testutils.TestUtil
import de.knockoutwhist.ui.tui.TUIMain.TUICards.renderCardAsString
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec import org.scalatest.wordspec.AnyWordSpec
class CardTests extends AnyWordSpec with Matchers{ class CardTests extends AnyWordSpec with Matchers{
"A card" should { "A card" should {
TestUtil.disableDelay()
"be displayed with correct value and Suit" in { "be displayed with correct value and Suit" in {
val card = Card(CardValue.Ace, Suit.Spades) val card = Card(CardValue.Ace, Suit.Spades)
val e = "Ace of Spades" val e = "Ace of Spades"
@@ -22,7 +27,7 @@ class CardTests extends AnyWordSpec with Matchers{
s"│ ${Console.BLACK}${Console.BOLD}A${Console.RESET}│", s"│ ${Console.BLACK}${Console.BOLD}A${Console.RESET}│",
"└─────────┘" "└─────────┘"
) )
card.renderAsString() shouldBe expectedResult renderCardAsString(Card(CardValue.Ace, Suit.Spades)) shouldBe expectedResult
} }
"can be rendered for CardValue Ten" in { "can be rendered for CardValue Ten" in {
val card = Card(CardValue.Ten, Suit.Spades) val card = Card(CardValue.Ten, Suit.Spades)
@@ -35,7 +40,7 @@ class CardTests extends AnyWordSpec with Matchers{
s"│ ${Console.BLACK}${Console.BOLD}10${Console.RESET}│", s"│ ${Console.BLACK}${Console.BOLD}10${Console.RESET}│",
"└─────────┘" "└─────────┘"
) )
card.renderAsString() shouldBe expectedResult renderCardAsString(Card(CardValue.Ten, Suit.Spades)) shouldBe expectedResult
} }
"be able to reset the order" in { "be able to reset the order" in {
CardManager.shuffleAndReset() CardManager.shuffleAndReset()
@@ -1,5 +1,6 @@
package de.knockoutwhist.cards package de.knockoutwhist.cards
import de.knockoutwhist.testutils.TestUtil
import org.scalatest.matchers.must.Matchers import org.scalatest.matchers.must.Matchers
import org.scalatest.matchers.should.Matchers.{should, shouldBe} import org.scalatest.matchers.should.Matchers.{should, shouldBe}
import org.scalatest.wordspec.AnyWordSpec import org.scalatest.wordspec.AnyWordSpec
@@ -7,6 +8,7 @@ import org.scalatest.wordspec.AnyWordSpec
class DeckTests extends AnyWordSpec with Matchers{ class DeckTests extends AnyWordSpec with Matchers{
"A deck" should { "A deck" should {
TestUtil.disableDelay()
"not be empty" in { "not be empty" in {
CardManager.cardContainer must not be empty CardManager.cardContainer must not be empty
} }
@@ -1,6 +1,8 @@
package de.knockoutwhist.cards package de.knockoutwhist.cards
import de.knockoutwhist.cards.{Card, CardValue, Hand, Suit} import de.knockoutwhist.cards.{Card, CardValue, Hand, Suit}
import de.knockoutwhist.testutils.TestUtil
import de.knockoutwhist.ui.tui.TUIMain.TUICards.renderHandEvent
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec import org.scalatest.wordspec.AnyWordSpec
@@ -9,6 +11,7 @@ import scala.collection.mutable.ListBuffer
class HandTests extends AnyWordSpec with Matchers { class HandTests extends AnyWordSpec with Matchers {
"The hand" should { "The hand" should {
TestUtil.disableDelay()
"be able to remove cards from its hand" in { "be able to remove cards from its hand" in {
val handholder = ListBuffer[Card]() val handholder = ListBuffer[Card]()
handholder.addOne(Card(CardValue.Ace, Suit.Spades)) handholder.addOne(Card(CardValue.Ace, Suit.Spades))
@@ -48,7 +51,7 @@ class HandTests extends AnyWordSpec with Matchers {
s"│ ${Console.BLACK}${Console.BOLD}A${Console.RESET}│ │ ${Console.RED}${Console.BOLD}Q${Console.RESET}│", s"│ ${Console.BLACK}${Console.BOLD}A${Console.RESET}│ │ ${Console.RED}${Console.BOLD}Q${Console.RESET}│",
"└─────────┘ └─────────┘" "└─────────┘ └─────────┘"
) )
hand.renderAsString() shouldBe expectedResult renderHandEvent(Hand(List(Card(CardValue.Ace, Suit.Spades), Card(CardValue.Queen, Suit.Diamonds))), false) shouldBe expectedResult
} }
} }
@@ -1,68 +1,63 @@
package de.knockoutwhist.control.text package de.knockoutwhist.control
import de.knockoutwhist.cards.CardManager import de.knockoutwhist.cards.CardManager
import de.knockoutwhist.cards.CardValue.Ace import de.knockoutwhist.cards.CardValue.Ace
import de.knockoutwhist.cards.Suit.Hearts import de.knockoutwhist.cards.Suit.Hearts
import de.knockoutwhist.cards.{Card, CardValue, Hand, Suit} import de.knockoutwhist.cards.{Card, CardValue, Hand, Suit}
import de.knockoutwhist.control.{MatchControl, RoundControl, TrickControl}
import de.knockoutwhist.player.Player import de.knockoutwhist.player.Player
import de.knockoutwhist.rounds.{Match, Round, Trick} import de.knockoutwhist.rounds.{Match, Round, Trick}
import de.knockoutwhist.testutils.TestUtil import de.knockoutwhist.testutils.TestUtil
import de.knockoutwhist.ui.tui.TUIMain
import de.knockoutwhist.utils.CustomPlayerQueue import de.knockoutwhist.utils.CustomPlayerQueue
import org.scalatest.matchers.must.Matchers import org.scalatest.matchers.must.Matchers
import org.scalatest.matchers.should.Matchers.{should, shouldBe} import org.scalatest.matchers.should.Matchers.{should, shouldBe}
import org.scalatest.wordspec.AnyWordSpec import org.scalatest.wordspec.AnyWordSpec
class TextMatchControllerTests extends AnyWordSpec with Matchers { class MatchControllerTests extends AnyWordSpec with Matchers {
"The start function" should {
"throw no exception" in {
TestUtil.cancelOut() {
TestUtil.simulateInput("a\n2\n") {
TextMatchControl.start()
}
}
}
}
"The enter players function" should { "The enter players function" should {
TestUtil.disableDelay()
"throw no exception" in { "throw no exception" in {
TestUtil.cancelOut() { TestUtil.cancelOut() {
TestUtil.simulateInput("foo,bar\n") { TestUtil.simulateInput("foo,bar\n") {
TextMatchControl.enterPlayers() should be (List(Player("foo"), Player("bar"))) MatchControl.enterPlayers() should be (List(Player("foo"), Player("bar")))
} }
} }
} }
"not accept less than 2 players" in { "not accept less than 2 players" in {
TestUtil.cancelOut() { TestUtil.cancelOut() {
TestUtil.simulateInput("foo\nbar,foo2\n") { TestUtil.simulateInput("foo\nbar,foo2\n") {
TextMatchControl.enterPlayers() should be (List(Player("bar"), Player("foo2"))) MatchControl.enterPlayers() should be (List(Player("bar"), Player("foo2")))
} }
} }
} }
"not accept players with the same name" in { "not accept players with the same name" in {
TestUtil.cancelOut() { TestUtil.cancelOut() {
TestUtil.simulateInput("foo,foo\nbar,foo\n") { TestUtil.simulateInput("foo,foo\nbar,foo\n") {
TextMatchControl.enterPlayers() should be (List(Player("bar"), Player("foo"))) MatchControl.enterPlayers() should be (List(Player("bar"), Player("foo")))
} }
} }
} }
"not accept player names less than 2 or greater than 10 characters" in { "not accept player names less than 2 or greater than 10 characters" in {
TestUtil.cancelOut() { TestUtil.cancelOut() {
TestUtil.simulateInput("f,b\nbarrrrrrrrrrrrrrrrr,foooooooooooooooooooo\nbar,foo\n") { TestUtil.simulateInput("f,b\nbarrrrrrrrrrrrrrrrr,foooooooooooooooooooo\nbar,foo\n") {
TextMatchControl.enterPlayers() should be (List(Player("bar"), Player("foo"))) MatchControl.enterPlayers() should be (List(Player("bar"), Player("foo")))
} }
} }
} }
} }
"The control round function" should { "The control round function" should {
TestUtil.disableDelay()
"throw no exception and return a winner" in { "throw no exception and return a winner" in {
val players = List(Player("foo"), Player("bar")) val players = List(Player("foo"), Player("bar"))
val matchImpl = Match(players, 1) val matchImpl = Match(players, 1)
TestUtil.disableDebugMode() TestUtil.disableDebugMode()
TextMatchControl.playerQueue = CustomPlayerQueue[Player](players.toArray[Player], 0) MatchControl.playerQueue = CustomPlayerQueue[Player](players.toArray[Player], 0)
TestUtil.cancelOut() { TestUtil.cancelOut() {
TestUtil.simulateInput("1\n1\n1\n") { TestUtil.simulateInput("1\n1\n1\n") {
TextMatchControl.controlRound(matchImpl).winner should be (players.head).or(be (players(1))) RoundControl.controlRound(matchImpl).winner should be (players.head).or(be (players(1)))
} }
} }
} }
@@ -73,45 +68,48 @@ class TextMatchControllerTests extends AnyWordSpec with Matchers {
CardManager.shuffleAndReset() CardManager.shuffleAndReset()
CardManager.resetOrder() CardManager.resetOrder()
TextMatchControl.playerQueue = CustomPlayerQueue[Player](players.toArray[Player], 0) MatchControl.playerQueue = CustomPlayerQueue[Player](players.toArray[Player], 0)
TestUtil.cancelOut() { TestUtil.cancelOut() {
TestUtil.simulateInput("1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n") { TestUtil.simulateInput("1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n") {
TextMatchControl.controlRound(matchImpl).winner should be(players.head).or(be(players(1))) RoundControl.controlRound(matchImpl).winner should be(players.head).or(be(players(1)))
} }
} }
} }
} }
"The next round function" should { "The next round function" should {
TestUtil.disableDelay()
"return null if the match is over" in { "return null if the match is over" in {
val players = List(Player("foo")) val players = List(Player("foo"))
val matchImpl = Match(players, 2) val matchImpl = Match(players, 2)
TestUtil.enableDebugMode() TestUtil.enableDebugMode()
TextMatchControl.playerQueue = CustomPlayerQueue[Player](players.toArray[Player], 0) MatchControl.playerQueue = CustomPlayerQueue[Player](players.toArray[Player], 0)
TestUtil.cancelOut() { TestUtil.cancelOut() {
TestUtil.simulateInput("1\n1\n1\n") { TestUtil.simulateInput("1\n1\n1\n") {
TextMatchControl.controlRound(matchImpl) RoundControl.controlRound(matchImpl)
TextMatchControl.nextRound(matchImpl) should be (null) RoundControl.nextRound(matchImpl) should be (null)
} }
} }
} }
} }
"The next trick function" should { "The next trick function" should {
TestUtil.disableDelay()
"return null if the round is over" in { "return null if the round is over" in {
val players = List(Player("foo")) val players = List(Player("foo"))
val matchImpl = Match(players, 2) val matchImpl = Match(players, 2)
TestUtil.enableDebugMode() TestUtil.enableDebugMode()
TextMatchControl.playerQueue = CustomPlayerQueue[Player](players.toArray[Player], 0) MatchControl.playerQueue = CustomPlayerQueue[Player](players.toArray[Player], 0)
TestUtil.cancelOut() { TestUtil.cancelOut() {
TestUtil.simulateInput("1\n1\n1\n") { TestUtil.simulateInput("1\n1\n1\n") {
val round = TextMatchControl.controlRound(matchImpl) val round = RoundControl.controlRound(matchImpl)
TextMatchControl.nextTrick(round) should be (null) TrickControl.nextTrick(round) should be (null)
} }
} }
} }
} }
"The controlSuit function" should { "The controlSuit function" should {
TestUtil.disableDelay()
"check if a player can play from the correct suit but doesnt" in { "check if a player can play from the correct suit but doesnt" in {
val player1 = Player("Gunter") val player1 = Player("Gunter")
val player2 = Player("Peter") val player2 = Player("Peter")
@@ -121,18 +119,19 @@ class TextMatchControllerTests extends AnyWordSpec with Matchers {
val matchImpl = Match(players, 2) val matchImpl = Match(players, 2)
val round = new Round(Suit.Clubs, matchImpl, players, false) val round = new Round(Suit.Clubs, matchImpl, players, false)
val trick = new Trick(round) val trick = new Trick(round)
trick.playCard(Card(Ace, Suit.Hearts), player2) TrickControl.playCard(trick, round, Card(Ace, Suit.Hearts), player2)
TestUtil.enableDebugMode() TestUtil.enableDebugMode()
TextMatchControl.playerQueue = CustomPlayerQueue[Player](players.toArray[Player], 0) MatchControl.playerQueue = CustomPlayerQueue[Player](players.toArray[Player], 0)
TestUtil.cancelOut() { TestUtil.cancelOut() {
TestUtil.simulateInput("1\n2\n") { TestUtil.simulateInput("1\n2\n") {
val card = TextMatchControl.controlSuitplayed(trick, player1) val card = TrickControl.controlSuitplayed(trick, player1)
} }
} }
} }
} }
"The control Trick function" should { "The control Trick function" should {
TestUtil.disableDelay()
"return the other player if the dog decides not to play" in { "return the other player if the dog decides not to play" in {
val foo = Player("foo") val foo = Player("foo")
foo.doglife = true foo.doglife = true
@@ -142,11 +141,11 @@ class TextMatchControllerTests extends AnyWordSpec with Matchers {
val players = List(foo, bar) val players = List(foo, bar)
val matchImpl = Match(players, 2) val matchImpl = Match(players, 2)
TestUtil.enableDebugMode() TestUtil.enableDebugMode()
TextMatchControl.playerQueue = CustomPlayerQueue[Player](players.toArray[Player], 0) MatchControl.playerQueue = CustomPlayerQueue[Player](players.toArray[Player], 0)
val round = new Round(Hearts,matchImpl,players,false) val round = new Round(Hearts,matchImpl,players,false)
TestUtil.cancelOut() { TestUtil.cancelOut() {
TestUtil.simulateInput("n\n1\n") { TestUtil.simulateInput("n\n1\n") {
val finalTrick = TextMatchControl.controlTrick(round) val finalTrick = TrickControl.controlTrick(round)
finalTrick.winner should be(bar) finalTrick.winner should be(bar)
} }
} }
@@ -164,11 +163,11 @@ class TextMatchControllerTests extends AnyWordSpec with Matchers {
bar.provideHand(CardManager.createHand(3)) bar.provideHand(CardManager.createHand(3))
val players = List(foo, bar) val players = List(foo, bar)
val matchImpl = Match(players, 2) val matchImpl = Match(players, 2)
TextMatchControl.playerQueue = CustomPlayerQueue[Player](players.toArray[Player], 0) MatchControl.playerQueue = CustomPlayerQueue[Player](players.toArray[Player], 0)
val round = new Round(foo.currentHand().get.cards.head.suit, matchImpl, players, false) val round = new Round(foo.currentHand().get.cards.head.suit, matchImpl, players, false)
TestUtil.cancelOut() { TestUtil.cancelOut() {
TestUtil.simulateInput("y\n1\n") { TestUtil.simulateInput("y\n1\n") {
val finalTrick = TextMatchControl.controlTrick(round) val finalTrick = TrickControl.controlTrick(round)
finalTrick.winner should be(bar) finalTrick.winner should be(bar)
} }
} }
@@ -1,22 +1,24 @@
package de.knockoutwhist.control.text package de.knockoutwhist.control
import de.knockoutwhist.cards.{CardManager, Hand} import de.knockoutwhist.cards.{CardManager, Hand}
import de.knockoutwhist.cards.Suit._ import de.knockoutwhist.cards.Suit.*
import de.knockoutwhist.control.PlayerControl
import de.knockoutwhist.player.Player import de.knockoutwhist.player.Player
import de.knockoutwhist.rounds.Round import de.knockoutwhist.rounds.Round
import de.knockoutwhist.testutils.TestUtil import de.knockoutwhist.testutils.TestUtil
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec import org.scalatest.wordspec.AnyWordSpec
class TextPlayerControllerTests extends AnyWordSpec with Matchers { class PlayerControllerTests extends AnyWordSpec with Matchers {
"The text player controller play function" should { "The text player controller play function" should {
TestUtil.disableDelay()
CardManager.shuffleAndReset() CardManager.shuffleAndReset()
TestUtil.cancelOut() { TestUtil.cancelOut() {
"throw an exception of the player has no hand" in { "throw an exception of the player has no hand" in {
assertThrows[IllegalStateException] { assertThrows[NoSuchElementException] {
TestUtil.simulateInput("1\n") { TestUtil.simulateInput("1\n") {
TextPlayerControl.playCard(Player("Foo")) PlayerControl.playCard(Player("Foo"))
} }
} }
} }
@@ -26,7 +28,7 @@ class TextPlayerControllerTests extends AnyWordSpec with Matchers {
val hand = CardManager.createHand(1) val hand = CardManager.createHand(1)
player.provideHand(hand) player.provideHand(hand)
val card = TestUtil.simulateInput("0\na\n1\n") { val card = TestUtil.simulateInput("0\na\n1\n") {
TextPlayerControl.playCard(player) PlayerControl.playCard(player)
} }
card should be(hand.cards.head) card should be(hand.cards.head)
} }
@@ -36,7 +38,7 @@ class TextPlayerControllerTests extends AnyWordSpec with Matchers {
val hand = CardManager.createHand(1) val hand = CardManager.createHand(1)
player.provideHand(hand) player.provideHand(hand)
val card = TestUtil.simulateInput("1\n") { val card = TestUtil.simulateInput("1\n") {
TextPlayerControl.playCard(player) PlayerControl.playCard(player)
} }
card should be(hand.cards.head) card should be(hand.cards.head)
} }
@@ -51,10 +53,10 @@ class TextPlayerControllerTests extends AnyWordSpec with Matchers {
val round = new Round(Spades, null, List(player), false) val round = new Round(Spades, null, List(player), false)
TestUtil.cancelOut() { TestUtil.cancelOut() {
"throw an exception of the player has no hand" in { "throw an exception of the player has no hand" in {
assertThrows[IllegalStateException] { assertThrows[NoSuchElementException] {
TestUtil.simulateInput("y\n") { TestUtil.simulateInput("y\n") {
TestUtil.disableDebugMode() TestUtil.disableDebugMode()
TextPlayerControl.dogplayCard(Player("Foo"), round) PlayerControl.dogplayCard(Player("Foo"), round)
} }
} }
} }
@@ -65,7 +67,7 @@ class TextPlayerControllerTests extends AnyWordSpec with Matchers {
val hand = CardManager.createHand(1) val hand = CardManager.createHand(1)
player.provideHand(hand) player.provideHand(hand)
val card = TestUtil.simulateInput("a\ny\n") { val card = TestUtil.simulateInput("a\ny\n") {
TextPlayerControl.dogplayCard(player, round) PlayerControl.dogplayCard(player, round)
} }
card should be(Some(hand.cards.head)) card should be(Some(hand.cards.head))
} }
@@ -76,7 +78,7 @@ class TextPlayerControllerTests extends AnyWordSpec with Matchers {
val hand = CardManager.createHand(1) val hand = CardManager.createHand(1)
player.provideHand(hand) player.provideHand(hand)
val card = TestUtil.simulateInput("y\n") { val card = TestUtil.simulateInput("y\n") {
TextPlayerControl.dogplayCard(player, round) PlayerControl.dogplayCard(player, round)
} }
card should be(Some(hand.cards.head)) card should be(Some(hand.cards.head))
} }
@@ -87,7 +89,7 @@ class TextPlayerControllerTests extends AnyWordSpec with Matchers {
val hand = CardManager.createHand(1) val hand = CardManager.createHand(1)
player.provideHand(hand) player.provideHand(hand)
val card = TestUtil.simulateInput("n\n") { val card = TestUtil.simulateInput("n\n") {
TextPlayerControl.dogplayCard(player, round) PlayerControl.dogplayCard(player, round)
} }
card should be(None) card should be(None)
} }
@@ -101,7 +103,7 @@ class TextPlayerControllerTests extends AnyWordSpec with Matchers {
player2.provideHand(Hand(List())) player2.provideHand(Hand(List()))
val round2 = new Round(Spades, null, List(player1,player2), true) val round2 = new Round(Spades, null, List(player1,player2), true)
val card = TestUtil.simulateInput("n\ny\n") { val card = TestUtil.simulateInput("n\ny\n") {
TextPlayerControl.dogplayCard(player1, round2) PlayerControl.dogplayCard(player1, round2)
} }
card should be(Some(player1.currentHand().get.cards.head)) card should be(Some(player1.currentHand().get.cards.head))
} }
@@ -116,7 +118,7 @@ class TextPlayerControllerTests extends AnyWordSpec with Matchers {
val player2 = Player("Bar") val player2 = Player("Bar")
val players = List(player1, player2) val players = List(player1, player2)
val winner = TestUtil.simulateInput("1\n2\n") { val winner = TestUtil.simulateInput("1\n2\n") {
TextPlayerControl.determineWinnerTie(players) PlayerControl.determineWinnerTie(players)
} }
winner should be(player2).or(be(player1)) winner should be(player2).or(be(player1))
} }
@@ -127,7 +129,7 @@ class TextPlayerControllerTests extends AnyWordSpec with Matchers {
val player2 = Player("Bar") val player2 = Player("Bar")
val players = List(player1, player2) val players = List(player1, player2)
val winner = TestUtil.simulateInput("1\n13\n5\n1\n") { val winner = TestUtil.simulateInput("1\n13\n5\n1\n") {
TextPlayerControl.determineWinnerTie(players) PlayerControl.determineWinnerTie(players)
} }
winner should be(player2) winner should be(player2)
} }
@@ -138,7 +140,7 @@ class TextPlayerControllerTests extends AnyWordSpec with Matchers {
val player2 = Player("Bar") val player2 = Player("Bar")
val players = List(player1, player2) val players = List(player1, player2)
val winner = TestUtil.simulateInput("13\n1\n") { val winner = TestUtil.simulateInput("13\n1\n") {
TextPlayerControl.determineWinnerTie(players) PlayerControl.determineWinnerTie(players)
} }
winner should be(player1) winner should be(player1)
} }
@@ -149,7 +151,7 @@ class TextPlayerControllerTests extends AnyWordSpec with Matchers {
val player2 = Player("Bar") val player2 = Player("Bar")
val players = List(player1, player2) val players = List(player1, player2)
val winner = TestUtil.simulateInput("a\n200\n1\n2\n") { val winner = TestUtil.simulateInput("a\n200\n1\n2\n") {
TextPlayerControl.determineWinnerTie(players) PlayerControl.determineWinnerTie(players)
} }
winner should be(player2) winner should be(player2)
} }
@@ -163,7 +165,7 @@ class TextPlayerControllerTests extends AnyWordSpec with Matchers {
val player = Player("Foo") val player = Player("Foo")
player.provideHand(CardManager.createHand(4)) player.provideHand(CardManager.createHand(4))
val suit = TestUtil.simulateInput("4\n") { val suit = TestUtil.simulateInput("4\n") {
TextPlayerControl.pickNextTrumpsuit(player) PlayerControl.pickNextTrumpsuit(player)
} }
suit should be(Spades) suit should be(Spades)
} }
@@ -172,7 +174,7 @@ class TextPlayerControllerTests extends AnyWordSpec with Matchers {
val player = Player("Foo") val player = Player("Foo")
player.provideHand(CardManager.createHand(4)) player.provideHand(CardManager.createHand(4))
val suit = TestUtil.simulateInput("1\n") { val suit = TestUtil.simulateInput("1\n") {
TextPlayerControl.pickNextTrumpsuit(player) PlayerControl.pickNextTrumpsuit(player)
} }
suit should be(Hearts) suit should be(Hearts)
} }
@@ -181,7 +183,7 @@ class TextPlayerControllerTests extends AnyWordSpec with Matchers {
val player = Player("Foo") val player = Player("Foo")
player.provideHand(CardManager.createHand(4)) player.provideHand(CardManager.createHand(4))
val suit = TestUtil.simulateInput("2\n") { val suit = TestUtil.simulateInput("2\n") {
TextPlayerControl.pickNextTrumpsuit(player) PlayerControl.pickNextTrumpsuit(player)
} }
suit should be(Diamonds) suit should be(Diamonds)
} }
@@ -190,7 +192,7 @@ class TextPlayerControllerTests extends AnyWordSpec with Matchers {
val player = Player("Foo") val player = Player("Foo")
player.provideHand(CardManager.createHand(4)) player.provideHand(CardManager.createHand(4))
val suit = TestUtil.simulateInput("3\n") { val suit = TestUtil.simulateInput("3\n") {
TextPlayerControl.pickNextTrumpsuit(player) PlayerControl.pickNextTrumpsuit(player)
} }
suit should be(Clubs) suit should be(Clubs)
} }
@@ -199,37 +201,37 @@ class TextPlayerControllerTests extends AnyWordSpec with Matchers {
val player = Player("Foo") val player = Player("Foo")
player.provideHand(CardManager.createHand(4)) player.provideHand(CardManager.createHand(4))
val suit = TestUtil.simulateInput("a\n10\n1\n") { val suit = TestUtil.simulateInput("a\n10\n1\n") {
TextPlayerControl.pickNextTrumpsuit(player) PlayerControl.pickNextTrumpsuit(player)
} }
suit should be(Hearts) suit should be(Hearts)
} }
} }
} }
"The text player controller showCards function" should { // "The text player controller showCards function" should {
TestUtil.cancelOut() { // TestUtil.cancelOut() {
"return true if the player wants to show their cards" in { // "return true if the player wants to show their cards" in {
TestUtil.disableDebugMode() // TestUtil.disableDebugMode()
val player = Player("Foo") // val player = Player("Foo")
player.provideHand(CardManager.createHand(4)) // player.provideHand(CardManager.createHand(4))
TextPlayerControl.showCards(player) should be(true) // TextPlayerControl.showCards(player) should be(true)
} // }
"return false if the player does not want to show their cards" in { // "return false if the player does not want to show their cards" in {
TestUtil.disableDebugMode() // TestUtil.disableDebugMode()
val player = Player("Foo") // val player = Player("Foo")
TextPlayerControl.showCards(player) should be(false) // TextPlayerControl.showCards(player) should be(false)
} // }
} // }
} // }
//
"The text player controller showWon function" should { // "The text player controller showWon function" should {
TestUtil.cancelOut() { // TestUtil.cancelOut() {
"print the winner of the match" in { // "print the winner of the match" in {
TestUtil.disableDebugMode() // TestUtil.disableDebugMode()
val player = Player("Foo") // val player = Player("Foo")
TextPlayerControl.showWon(player, null) should be("Foo won this round.") // TextPlayerControl.showWon(player, null) should be("Foo won this round.")
} // }
} // }
} // }
} }
@@ -0,0 +1,497 @@
package de.knockoutwhist.events
import de.knockoutwhist.KnockOutWhist
import de.knockoutwhist.cards.CardValue.{Queen, Two}
import de.knockoutwhist.cards.Suit.Hearts
import de.knockoutwhist.cards.{Card, CardManager, CardValue, Hand, Suit}
import de.knockoutwhist.control.{RoundControl, TrickControl}
import de.knockoutwhist.events.GLOBAL_STATUS.*
import de.knockoutwhist.events.PLAYER_STATUS.*
import de.knockoutwhist.events.ERROR_STATUS.*
import de.knockoutwhist.events.ROUND_STATUS.*
import de.knockoutwhist.events.cards.{RenderHandEvent, ShowTieCardsEvent}
import de.knockoutwhist.events.directional.{RequestCardEvent, RequestDogPlayCardEvent, RequestNumberEvent, RequestPickTrumpsuitEvent}
import de.knockoutwhist.events.round.ShowCurrentTrickEvent
import de.knockoutwhist.player.Player
import de.knockoutwhist.rounds.{Match, Round}
import de.knockoutwhist.testutils.{TestUtil, TestUtil as shouldBe}
import de.knockoutwhist.ui.tui.TUIMain
import de.knockoutwhist.utils.events.{EventHandler, SimpleEvent}
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
import scala.collection.mutable.ListBuffer
import scala.compiletime.uninitialized
import scala.util.{Failure, Success}
class TestAllEvent extends AnyWordSpec with Matchers {
private val eventHandler = new EventHandler() {}
eventHandler.addListener(TUIMain)
"The render hand event" should {
TestUtil.disableDelay()
CardManager.resetOrder()
val event1 = RenderHandEvent(CardManager.createHand(1), true)
val event2 = RenderHandEvent(CardManager.createHand(1), false)
"be able to be created" in {
event1 should not be null
}
"have the correct id" in {
event1.id should be ("RenderHandEvent")
}
"have the correct parameter (showing numbers)" in {
event1.showNumbers should be (true)
}
"have the correct parameter (not showing numbers)" in {
event2.showNumbers should be (false)
}
"be able to be handled" in {
TestUtil.cancelOut() {
eventHandler.invoke(event1) should be(true)
eventHandler.invoke(event2) should be(true)
}
}
}
"The show tie cards event" should {
TestUtil.disableDelay()
val event = ShowTieCardsEvent(List((Player("Foo"), Card(Two,Suit.Hearts))))
"be able to be created" in {
event should not be null
}
"have the correct id" in {
event.id should be ("ShowTieCardsEvent")
}
"have the correct player" in {
event.card.head._1 should be (Player("Foo"))
}
"have the correct card" in {
event.card.head._2.cardValue should be (Two)
event.card.head._2.suit should be (Suit.Hearts)
}
"be able to be handled" in {
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
}
"The request card event" should {
TestUtil.disableDelay()
val hand = CardManager.createHand(1)
val event = RequestCardEvent(hand)
"be able to be created" in {
event should not be null
}
"have the correct id" in {
event.id should be("RequestCardEvent")
}
"have the correct hand" in {
event.hand should be(hand)
}
"be able to be handled" in {
TestUtil.cancelOut() {
TestUtil.simulateInput("0\n") {
eventHandler.invoke(event)
} shouldBe a [Failure[?]]
TestUtil.simulateInput("5\n") {
eventHandler.invoke(event)
} shouldBe a[Failure[?]]
val result = TestUtil.simulateInput("1\n") {
eventHandler.invoke(event)
}
result should be (Success(hand.cards.head))
}
}
}
"The request number event" should {
TestUtil.disableDelay()
val event = RequestNumberEvent(6, 12)
"be able to be created" in {
event should not be null
}
"have the correct id" in {
event.id should be("RequestNumberEvent")
}
"have the correct min & max" in {
event.min should be(6)
event.max should be(12)
}
"be able to be handled" in {
TestUtil.cancelOut() {
TestUtil.simulateInput("0\n") {
eventHandler.invoke(event)
} shouldBe a [Failure[?]]
TestUtil.simulateInput("28\n") {
eventHandler.invoke(event)
} shouldBe a[Failure[?]]
val result = TestUtil.simulateInput("8\n") {
eventHandler.invoke(event)
}
result should be (Success(8))
}
}
}
"The RequestDogPlayCardEvent" should {
TestUtil.disableDelay()
val hand = Hand(List(Card(CardValue.Ten, Suit.Spades)))
val event = RequestDogPlayCardEvent(hand, true)
"be able to get created" in {
event should not be null
}
"have the correct ID" in {
event.id should be("RequestDogPlayCardEvent")
}
"return the card played if it was played" in {
TestUtil.simulateInput("y\n") {
eventHandler.invoke(event)
} shouldBe a[Success[?]]
}
"return an exception if player doesn't want to play but has to" in {
TestUtil.simulateInput("n\n") {
eventHandler.invoke(event)
} shouldBe a [Failure[?]]
}
"return None when a player doesn't play a card and is able to do so" in {
val hand2 = Hand(List(Card(CardValue.Ten, Suit.Spades), Card(CardValue.Nine, Suit.Spades)))
val event2 = RequestDogPlayCardEvent(hand2, false)
TestUtil.simulateInput("n\n") {
eventHandler.invoke(event2)
} shouldBe a [Success[?]]
}
}
"The RequestPickTrumpsuitEvent" should {
TestUtil.disableDelay()
val event = RequestPickTrumpsuitEvent()
"be able to get created" in {
event should not be null
}
"have an ID" in {
event.id should be("RequestPickTrumpsuitEvent")
}
"be a Success if the player picks a trumpsuit" in {
TestUtil.simulateInput("1\n") {
eventHandler.invoke(event)
} shouldBe a [Success[?]]
TestUtil.simulateInput("2\n") {
eventHandler.invoke(event)
} shouldBe a[Success[?]]
TestUtil.simulateInput("3\n") {
eventHandler.invoke(event)
} shouldBe a[Success[?]]
TestUtil.simulateInput("4\n") {
eventHandler.invoke(event)
} shouldBe a[Success[?]]
}
"fail if the player doesn't pick a trumpsuit" in {
TestUtil.simulateInput("5\n") {
eventHandler.invoke(event)
} shouldBe a [Failure[?]]
}
}
"The ShowCurrentTrickEvent" should {
TestUtil.disableDelay()
val player1 = Player("Gunter")
val player2 = Player("Peter")
val listplayers = List(player1, player2)
val match1 = Match(listplayers)
val round = RoundControl.create_round(match1)
val trick = TrickControl.create_trick(round)
TrickControl.playCard(trick, round, Card(CardValue.Ten, Suit.Spades), player1)
val event = ShowCurrentTrickEvent(round, trick)
"be able to get created" in {
event should not be null
}
"have an ID" in {
event.id should be("ShowCurrentTrickEvent")
}
"should be true if a trick can be displayed" in {
eventHandler.invoke(event) shouldBe true
}
}
"The show global status event" should {
TestUtil.disableDelay()
var event: ShowGlobalStatus = null
"be able to be created" in {
event = ShowGlobalStatus(SHOW_TIE)
event should not be null
}
"have the correct id" in {
event = ShowGlobalStatus(SHOW_TIE)
event.id should be ("ShowGlobalStatus")
}
"have the correct status" in {
event = ShowGlobalStatus(SHOW_TIE)
event.status should be (SHOW_TIE)
}
"be able to be handled with status SHOW_TIE" in {
event = ShowGlobalStatus(SHOW_TIE)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to return false with status SHOW_TIE_WINNER if arguments mismatch" in {
event = ShowGlobalStatus(SHOW_TIE_WINNER)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(false)
}
}
"be able to return true with status SHOW_TIE_WINNER if arguments match" in {
event = ShowGlobalStatus(SHOW_TIE_WINNER, Player("Foo"))
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to be handled with status SHOW_TIE_TIE" in {
event = ShowGlobalStatus(SHOW_TIE_TIE)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to be handled with status SHOW_START_MATCH" in {
event = ShowGlobalStatus(SHOW_START_MATCH)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to be handled with status SHOW_TYPE_PLAYERS" in {
event = ShowGlobalStatus(SHOW_TYPE_PLAYERS)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to return false with status SHOW_FINISHED_MATCH if arguments mismatch" in {
event = ShowGlobalStatus(SHOW_FINISHED_MATCH)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(false)
}
}
"be able to return true with status SHOW_FINISHED_MATCH if arguments match" in {
event = ShowGlobalStatus(SHOW_FINISHED_MATCH, Player("Foo"))
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
}
"The show player status event" should {
TestUtil.disableDelay()
val player = Player("Foo")
var event: ShowPlayerStatus = null
"be able to be created" in {
event = ShowPlayerStatus(SHOW_TURN, player)
event should not be null
}
"have the correct id" in {
event = ShowPlayerStatus(SHOW_TURN, player)
event.id should be ("ShowPlayerStatus")
}
"have the correct status" in {
event = ShowPlayerStatus(SHOW_TURN, player)
event.status should be (SHOW_TURN)
}
"have the correct player" in {
event = ShowPlayerStatus(SHOW_TURN, player)
event.player should be (player)
}
"be able to be handled with status SHOW_TURN" in {
event = ShowPlayerStatus(SHOW_TURN, player)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to be handled with status SHOW_PLAY_CARD" in {
event = ShowPlayerStatus(SHOW_PLAY_CARD, player)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to return false with status SHOW_DOG_PLAY_CARD if arguments mismatch" in {
event = ShowPlayerStatus(SHOW_DOG_PLAY_CARD, player)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(false)
}
}
"be able to return true with status SHOW_DOG_PLAY_CARD if arguments match" in {
event = ShowPlayerStatus(SHOW_DOG_PLAY_CARD, player, true)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to return false with status SHOW_TIE_NUMBERS if arguments mismatch" in {
event = ShowPlayerStatus(SHOW_TIE_NUMBERS, player)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(false)
}
}
"be able to return true with status SHOW_TIE_NUMBERS if arguments match" in {
event = ShowPlayerStatus(SHOW_TIE_NUMBERS, player, 5)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to be handled with status SHOW_TRUMPSUIT_OPTIONS" in {
event = ShowPlayerStatus(SHOW_TRUMPSUIT_OPTIONS, player)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to be handled with status SHOW_NOT_PLAYED" in {
event = ShowPlayerStatus(SHOW_NOT_PLAYED, player)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to be handled with status SHOW_WON_PLAYER_TRICK" in {
event = ShowPlayerStatus(SHOW_WON_PLAYER_TRICK, player)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
}
"The show round status event" should {
TestUtil.disableDelay()
val round = Round(trumpSuit = Hearts, matchImpl = null, tricklist = ListBuffer(), players_in = null, firstRound = false, players_out = List(Player("Foo")))
var event: ShowRoundStatus = null
"be able to be created" in {
event = ShowRoundStatus(SHOW_START_ROUND, round)
event should not be null
}
"have the correct id" in {
event = ShowRoundStatus(SHOW_START_ROUND, round)
event.id should be ("ShowRoundStatus")
}
"have the correct status" in {
event = ShowRoundStatus(SHOW_START_ROUND, round)
event.status should be (SHOW_START_ROUND)
}
"have the correct round" in {
event = ShowRoundStatus(SHOW_START_ROUND, round)
event.currentRound should be (round)
}
"be able to be handled with status SHOW_START_ROUND" in {
event = ShowRoundStatus(SHOW_START_ROUND, round)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to return false with status WON_ROUND if arguments mismatch" in {
event = ShowRoundStatus(WON_ROUND, round)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(false)
}
}
"be able to return true with status WON_ROUND if arguments match" in {
event = ShowRoundStatus(WON_ROUND, round, Player("Foo"))
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to be handled with status PLAYERS_OUT" in {
event = ShowRoundStatus(PLAYERS_OUT, round)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
}
"The error status event" should {
TestUtil.disableDelay()
var event: ShowErrorStatus = null
"be able to be created" in {
event = ShowErrorStatus(INVALID_NUMBER)
event should not be null
}
"have the correct id" in {
event = ShowErrorStatus(INVALID_NUMBER)
event.id should be ("ShowErrorStatus")
}
"have the correct status" in {
event = ShowErrorStatus(INVALID_NUMBER)
event.status should be (INVALID_NUMBER)
}
"be able to be handled with status INVALID_NUMBER" in {
event = ShowErrorStatus(INVALID_NUMBER)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to be handled with status NOT_A_NUMBER" in {
event = ShowErrorStatus(NOT_A_NUMBER)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to be handled with status INVALID_INPUT" in {
event = ShowErrorStatus(INVALID_INPUT)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to be handled with status INVALID_NUMBER_OF_PLAYERS" in {
event = ShowErrorStatus(INVALID_NUMBER_OF_PLAYERS)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to be handled with status IDENTICAL_NAMES" in {
event = ShowErrorStatus(IDENTICAL_NAMES)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to be handled with status INVALID_NAME_FORMAT" in {
event = ShowErrorStatus(INVALID_NAME_FORMAT)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
"be able to return false with status WRONG_CARD if arguments mismatch" in {
event = ShowErrorStatus(WRONG_CARD)
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(false)
}
}
"be able to return true with status WRONG_CARD if arguments match" in {
event = ShowErrorStatus(WRONG_CARD, Card(CardValue.Queen, Suit.Hearts))
TestUtil.cancelOut() {
eventHandler.invoke(event) should be(true)
}
}
}
"An Event" should {
TestUtil.disableDelay()
"return None if it doesn't exist" in {
val event = new SimpleEvent {
override def id: String = "ShowEnte"
}
eventHandler.invoke(event) shouldBe false
}
}
/*"The pick next trumpsuit event" should {
val event = PickNextTrumpsuitEvent(Player("Foo"))
"be able to be created" in {
event should not be null
}
"have the correct id" in {
event.id should be ("PickNextTrumpsuitEvent")
}
"have the correct player" in {
event.player should be (Player("Foo"))
}
"be able to be handled" in {
TestUtil.enableDebugMode()
CardManager.resetOrder()
TestUtil.simulateInput("1\n") {
eventHandler.invoke(event)
} should be (Suit.Hearts)
}
}*/
}
@@ -1,12 +1,14 @@
package de.knockoutwhist.player package de.knockoutwhist.player
import de.knockoutwhist.cards.{Card, CardValue, Hand, Suit} import de.knockoutwhist.cards.{Card, CardValue, Hand, Suit}
import de.knockoutwhist.testutils.TestUtil
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec import org.scalatest.wordspec.AnyWordSpec
class PlayerTests extends AnyWordSpec with Matchers { class PlayerTests extends AnyWordSpec with Matchers {
"A player" should { "A player" should {
TestUtil.disableDelay()
"be able to have a hand" in { "be able to have a hand" in {
val card = Card(CardValue.Ace, Suit.Spades) val card = Card(CardValue.Ace, Suit.Spades)
val card2 = Card(CardValue.Ten, Suit.Spades) val card2 = Card(CardValue.Ten, Suit.Spades)
@@ -1,8 +1,8 @@
package de.knockoutwhist.rounds package de.knockoutwhist.rounds
import de.knockoutwhist.cards.CardManager import de.knockoutwhist.cards.CardManager
import de.knockoutwhist.control.text.TextMatchControl
import de.knockoutwhist.testutils.TestUtil import de.knockoutwhist.testutils.TestUtil
import de.knockoutwhist.ui.tui.TUIMain
import org.scalatest.matchers.must.Matchers import org.scalatest.matchers.must.Matchers
import org.scalatest.matchers.should.Matchers.{should, shouldBe} import org.scalatest.matchers.should.Matchers.{should, shouldBe}
import org.scalatest.wordspec.AnyWordSpec import org.scalatest.wordspec.AnyWordSpec
@@ -14,11 +14,12 @@ class GameplayTests extends AnyWordSpec with Matchers {
"The Match Control" must { "The Match Control" must {
"not throw an exception" in { "not throw an exception" in {
TestUtil.enableDebugMode() TestUtil.enableDebugMode()
TestUtil.disableDelay()
CardManager.shuffleAndReset() CardManager.shuffleAndReset()
CardManager.resetOrder() CardManager.resetOrder()
TestUtil.cancelOut() { TestUtil.cancelOut() {
TestUtil.simulateInput("1\nLeon,Janis\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\ny\n1\n1\n1\n2\n") { TestUtil.simulateInput("a\n5\n1\nLeon,Janis\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\n1\ny\n1\n1\n1\n2\n") {
TextMatchControl.start() TUIMain.initial
} }
} }
} }
@@ -2,6 +2,7 @@ package de.knockoutwhist.rounds
import de.knockoutwhist.KnockOutWhist import de.knockoutwhist.KnockOutWhist
import de.knockoutwhist.cards.{Card, CardManager, CardValue, Suit} import de.knockoutwhist.cards.{Card, CardManager, CardValue, Suit}
import de.knockoutwhist.control.{MatchControl, PlayerControl, RoundControl, TrickControl}
import de.knockoutwhist.player.Player import de.knockoutwhist.player.Player
import de.knockoutwhist.testutils.TestUtil import de.knockoutwhist.testutils.TestUtil
import org.scalatest.matchers.must.Matchers import org.scalatest.matchers.must.Matchers
@@ -15,82 +16,74 @@ import scala.collection.mutable.ListBuffer
class MatchTests extends AnyWordSpec with Matchers{ class MatchTests extends AnyWordSpec with Matchers{
"A Match" should { "A Match" should {
TestUtil.disableDelay()
TestUtil.cancelOut() { TestUtil.cancelOut() {
TestUtil.disableDebugMode() TestUtil.disableDebugMode()
val player1 = Player("Gunter") val player1 = Player("Gunter")
val player2 = Player("Peter") val player2 = Player("Peter")
val player_list = List(player1, player2) val player_list = List(player1, player2)
val match1 = Match(player_list) val match1 = Match(player_list)
val round1 = match1.create_round() val round1 = RoundControl.create_round(match1)
val trumpsuit = round1.trumpSuit val trumpsuit = round1.trumpSuit
val trick1 = round1.create_trick() val trick1 = TrickControl.create_trick(round1)
val playedcard1 = TestUtil.simulateInput("1\n") { val playedcard1 = TestUtil.simulateInput("1\n") {
KnockOutWhist.matchControl.playerControl.playCard(player1) PlayerControl.playCard(player1)
} }
trick1.playCard(playedcard1, player1) TrickControl.playCard(trick1, round1, playedcard1, player1)
val playedcard2 = TestUtil.simulateInput("1\n") { val playedcard2 = TestUtil.simulateInput("1\n") {
KnockOutWhist.matchControl.playerControl.playCard(player2) PlayerControl.playCard(player2)
} }
trick1.playCard(playedcard2, player2) TrickControl.playCard(trick1, round1, playedcard2, player2)
"return the players ingame in players_remaining" in { "return the players ingame in players_remaining" in {
round1.remainingPlayers() should be(player_list) RoundControl.remainingPlayers(round1) should be(player_list)
} }
val rtrick1 = trick1.wonTrick() val rtrick1 = TrickControl.wonTrick(trick1, round1)
round1.finalizeRound(true) RoundControl.finalizeRound(round1, match1, true)
val round2 = TestUtil.simulateInput("1\n") { val round2 = TestUtil.simulateInput("1\n") {
match1.create_round() RoundControl.create_round(match1)
} }
TestUtil.enableDebugMode() TestUtil.enableDebugMode()
var trick2: Trick = null val trick2: Trick = TrickControl.create_trick(round2)
"should be able to create a trick with requesting the current trick" in {
trick2 = round2.get_current_trick()
}
if (trick2 == null) {
trick2 = round2.get_current_trick()
}
val playedcard3 = TestUtil.simulateInput("1\n") { val playedcard3 = TestUtil.simulateInput("1\n") {
KnockOutWhist.matchControl.playerControl.playCard(player1) PlayerControl.playCard(player1)
} }
trick1.playCard(playedcard3, player1) TrickControl.playCard(trick1, round2, playedcard3, player1)
val playedcard4 = TestUtil.simulateInput("1\n") { val playedcard4 = TestUtil.simulateInput("1\n") {
KnockOutWhist.matchControl.playerControl.playCard(player2) PlayerControl.playCard(player2)
} }
trick2.playCard(playedcard4, player2) TrickControl.playCard(trick2, round2, playedcard4, player2)
"be able to return the current trick of the round" in { "be able to return the current trick of the round" in {
round2.get_current_trick() should be(trick2) round2.get_current_trick().get should be(trick2)
} }
val rtrick2 = trick2.wonTrick() val rtrick2 = TrickControl.wonTrick(trick2, round2)
"return false if the round isn't over" in { "return false if the round isn't over" in {
round2.isOver shouldBe false RoundControl.isOver(round2) shouldBe false
}
"be able to return all the tricks of the round" in {
round1.get_tricks() should be(List(rtrick1._2))
} }
"be able to tell if a dog needs to play" in { "be able to tell if a dog needs to play" in {
round2.dogNeedsToPlay shouldBe false RoundControl.dogNeedsToPlay(round2) shouldBe false
} }
"error out if a round is finalized without all tricks played" in { "error out if a round is finalized without all tricks played" in {
assertThrows[IllegalStateException] { assertThrows[IllegalStateException] {
round2.finalizeRound() RoundControl.finalizeRound(round2, match1)
} }
} }
round2.finalizeRound(true) RoundControl.finalizeRound(round2, match1, true)
val round3 = TestUtil.simulateInput("1\n") { val round3 = TestUtil.simulateInput("1\n") {
match1.create_round() RoundControl.create_round(match1)
} }
val trick3 = round3.create_trick() val trick3 = TrickControl.create_trick(round3)
val playedcard5 = TestUtil.simulateInput("1\n") { val playedcard5 = TestUtil.simulateInput("1\n") {
KnockOutWhist.matchControl.playerControl.playCard(player1) PlayerControl.playCard(player1)
} }
trick1.playCard(playedcard5, player1) TrickControl.playCard(trick1, round3, playedcard5, player1) //stand trick1
val playedcard6 = TestUtil.simulateInput("1\n") { val playedcard6 = TestUtil.simulateInput("1\n") {
KnockOutWhist.matchControl.playerControl.playCard(player2) PlayerControl.playCard(player2)
} }
trick3.playCard(playedcard6, player2) TrickControl.playCard(trick3, round3, playedcard6, player2)
trick3.wonTrick() TrickControl.wonTrick(trick3, round3)
"throw an exception if a match gets finalized before it is finished" in { "throw an exception if a match gets finalized before it is finished" in {
assertThrows[IllegalStateException] { //If exception is thrown, assertThrows returns succeeded assertThrows[IllegalStateException] { //If exception is thrown, assertThrows returns succeeded
match1.finalizeMatch() MatchControl.finalizeMatch(match1)
} }
} }
"be able to create a random trumpsuit for first round" in { "be able to create a random trumpsuit for first round" in {
@@ -99,38 +92,38 @@ class MatchTests extends AnyWordSpec with Matchers{
"return false when no round has been completed" in { "return false when no round has been completed" in {
CardManager.shuffleAndReset() CardManager.shuffleAndReset()
val match3 = Match(List(Player("Gunter"))) val match3 = Match(List(Player("Gunter")))
match3.create_round() RoundControl.create_round(match3)
match3.isOver shouldBe false MatchControl.isOver(match3) shouldBe false
} }
"return true if one player is remaining after a round has been played" in { "return true if one player is remaining after a round has been played" in {
round3.finalizeRound(true) RoundControl.finalizeRound(round3, match1, true)
match1.isOver shouldBe true MatchControl.isOver(match1) shouldBe true
} }
val round4 = TestUtil.simulateInput("1\n") { val round4 = TestUtil.simulateInput("1\n") {
match1.create_round() RoundControl.create_round(match1)
} }
val trick4 = round4.create_trick() val trick4 = TrickControl.create_trick(round4)
val playedcard7 = Card(CardValue.Ace, Suit.Hearts) val playedcard7 = Card(CardValue.Ace, Suit.Hearts)
val playedcard8 = Card(CardValue.Two, Suit.Hearts) val playedcard8 = Card(CardValue.Two, Suit.Hearts)
trick4.playCard(playedcard7, player1) TrickControl.playCard(trick4, round4, playedcard7, player1)
trick4.playCard(playedcard8, player2) TrickControl.playCard(trick4, round4, playedcard8, player2)
trick4.wonTrick() TrickControl.wonTrick(trick4, round4)
val trick5 = round4.create_trick() val trick5 = TrickControl.create_trick(round4)
trick5.playCard(playedcard8, player1) TrickControl.playCard(trick5, round4, playedcard8, player1)
trick5.playCard(playedcard7, player2) TrickControl.playCard(trick5, round4, playedcard7, player2)
trick5.wonTrick() TrickControl.wonTrick(trick5, round4)
CardManager.shuffleAndReset() CardManager.shuffleAndReset()
val roundResult = TestUtil.simulateInput("1\n13\n") { val roundResult = TestUtil.simulateInput("1\n13\n") {
round4.finalizeRound(true) RoundControl.finalizeRound(round4, match1, true)
} }
val round5 = TestUtil.simulateInput("1\n") { val round5 = TestUtil.simulateInput("1\n") {
match1.create_round() RoundControl.create_round(match1)
} }
"error out if a round is finalized without any tricks played" in { "error out if a round is finalized without any tricks played" in {
assertThrows[IllegalStateException] { assertThrows[IllegalStateException] {
round5.finalizeRound() RoundControl.finalizeRound(round5, match1)
} }
} }
"be able to finalize a round" in { "be able to finalize a round" in {
@@ -141,7 +134,7 @@ class MatchTests extends AnyWordSpec with Matchers{
round5.toString should be(s"${Suit.Hearts}, ${ListBuffer()}, $player_list, null, null, false") round5.toString should be(s"${Suit.Hearts}, ${ListBuffer()}, $player_list, null, null, false")
} }
"show the winner of the match when it has ended" in { "show the winner of the match when it has ended" in {
match1.finalizeMatch() shouldBe Player("Peter") MatchControl.finalizeMatch(match1) shouldBe Player("Peter")
} }
"have a working toString Method" in { "have a working toString Method" in {
match1.toString shouldBe "List(Gunter, Peter), 2" match1.toString shouldBe "List(Gunter, Peter), 2"
@@ -1,13 +1,16 @@
package de.knockoutwhist.rounds package de.knockoutwhist.rounds
import de.knockoutwhist.cards.{Card, CardValue, Suit} import de.knockoutwhist.cards.{Card, CardValue, Suit}
import de.knockoutwhist.control.TrickControl
import de.knockoutwhist.player.Player import de.knockoutwhist.player.Player
import de.knockoutwhist.testutils.TestUtil
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec import org.scalatest.wordspec.AnyWordSpec
class TrickTests extends AnyWordSpec with Matchers { class TrickTests extends AnyWordSpec with Matchers {
"A trick" should { "A trick" should {
TestUtil.disableDelay()
"be able to return the first card after it was played" in { "be able to return the first card after it was played" in {
val player = Player("Gunter") val player = Player("Gunter")
val player2 = Player("Peter") val player2 = Player("Peter")
@@ -16,7 +19,7 @@ class TrickTests extends AnyWordSpec with Matchers {
val card = Card(CardValue.Ace, Suit.Spades) val card = Card(CardValue.Ace, Suit.Spades)
val card2 = Card(CardValue.Two, Suit.Clubs) val card2 = Card(CardValue.Two, Suit.Clubs)
val trick = new Trick(round) val trick = new Trick(round)
trick.playCard(card, player) TrickControl.playCard(trick, round, card, player)
trick.get_first_card().isEmpty shouldBe false trick.get_first_card().isEmpty shouldBe false
trick.get_first_card().get shouldBe card trick.get_first_card().get shouldBe card
} }
@@ -35,9 +38,9 @@ class TrickTests extends AnyWordSpec with Matchers {
val card = Card(CardValue.Ace, Suit.Spades) val card = Card(CardValue.Ace, Suit.Spades)
val trick = new Trick(round) val trick = new Trick(round)
trick.playCard(card, player) TrickControl.playCard(trick, round, card, player)
val won = trick.wonTrick() val won = TrickControl.wonTrick(trick, round)
won(0) shouldBe player won(0) shouldBe player
won(1).cards should equal(trick.cards) won(1).cards should equal(trick.cards)
@@ -50,11 +53,11 @@ class TrickTests extends AnyWordSpec with Matchers {
val round = new Round(Suit.Spades, Match(playerlist), playerlist, false) val round = new Round(Suit.Spades, Match(playerlist), playerlist, false)
val card = Card(CardValue.Ace, Suit.Spades) val card = Card(CardValue.Ace, Suit.Spades)
val trick = new Trick(round) val trick = new Trick(round)
trick.playCard(card, player) TrickControl.playCard(trick, round, card, player)
val won = trick.wonTrick() val won = TrickControl.wonTrick(trick, round)
assertThrows[IllegalStateException] { //If exception is thrown, assertThrows returns succeeded assertThrows[IllegalStateException] { //If exception is thrown, assertThrows returns succeeded
won(1).playCard(card, player) TrickControl.playCard(won(1), round, card, player)
} }
} }
"filter the cards by suit correctly if no trump was played" in { "filter the cards by suit correctly if no trump was played" in {
@@ -65,9 +68,9 @@ class TrickTests extends AnyWordSpec with Matchers {
val card = Card(CardValue.Ace, Suit.Spades) val card = Card(CardValue.Ace, Suit.Spades)
val card2 = Card(CardValue.Ten, Suit.Spades) val card2 = Card(CardValue.Ten, Suit.Spades)
val trick = new Trick(round) val trick = new Trick(round)
trick.playCard(card, player) TrickControl.playCard(trick, round, card, player)
trick.playCard(card2, player2) TrickControl.playCard(trick, round, card2, player2)
val won = trick.wonTrick() val won = TrickControl.wonTrick(trick, round)
won(0) shouldBe player won(0) shouldBe player
} }
@@ -77,7 +80,7 @@ class TrickTests extends AnyWordSpec with Matchers {
val round = new Round(Suit.Spades, Match(playerlist), playerlist, false) val round = new Round(Suit.Spades, Match(playerlist), playerlist, false)
val card = Card(CardValue.Ace, Suit.Spades) val card = Card(CardValue.Ace, Suit.Spades)
val trick = new Trick(round) val trick = new Trick(round)
trick.playCard(card, player) shouldBe true TrickControl.playCard(trick, round, card, player) shouldBe true
} }
"return true if the suit matches the first card played" in { "return true if the suit matches the first card played" in {
val player = Player("Gunter") val player = Player("Gunter")
@@ -86,9 +89,9 @@ class TrickTests extends AnyWordSpec with Matchers {
val round = new Round(Suit.Diamonds, Match(playerlist), playerlist, false) val round = new Round(Suit.Diamonds, Match(playerlist), playerlist, false)
val card = Card(CardValue.Two, Suit.Spades) val card = Card(CardValue.Two, Suit.Spades)
val card2 = Card(CardValue.Ace, Suit.Spades) val card2 = Card(CardValue.Ace, Suit.Spades)
val trick = round.create_trick() val trick = TrickControl.create_trick(round)
trick.playCard(card, player) TrickControl.playCard(trick, round, card, player)
trick.playCard(card2, player2) shouldBe true TrickControl.playCard(trick, round, card2, player2) shouldBe true
} }
"return true if the card matches the trump-card" in { "return true if the card matches the trump-card" in {
val player = Player("Gunter") val player = Player("Gunter")
@@ -98,8 +101,8 @@ class TrickTests extends AnyWordSpec with Matchers {
val card = Card(CardValue.Ace, Suit.Spades) val card = Card(CardValue.Ace, Suit.Spades)
val card2 = Card(CardValue.Two, Suit.Diamonds) val card2 = Card(CardValue.Two, Suit.Diamonds)
val trick = new Trick(round) val trick = new Trick(round)
trick.playCard(card, player) TrickControl.playCard(trick, round, card, player)
trick.playCard(card2, player2) shouldBe true TrickControl.playCard(trick, round, card2, player2) shouldBe true
} }
"return false if the card doesn't match the suit of the trump-card + first-card" in { "return false if the card doesn't match the suit of the trump-card + first-card" in {
val player = Player("Gunter") val player = Player("Gunter")
@@ -109,8 +112,8 @@ class TrickTests extends AnyWordSpec with Matchers {
val card = Card(CardValue.Ace, Suit.Spades) val card = Card(CardValue.Ace, Suit.Spades)
val card2 = Card(CardValue.Two, Suit.Clubs) val card2 = Card(CardValue.Two, Suit.Clubs)
val trick = new Trick(round) val trick = new Trick(round)
trick.playCard(card, player) TrickControl.playCard(trick, round, card, player)
trick.playCard(card2, player2) shouldBe false TrickControl.playCard(trick, round, card2, player2) shouldBe false
} }
"have a working to string" in { "have a working to string" in {
val player = Player("Gunter") val player = Player("Gunter")
@@ -120,10 +123,23 @@ class TrickTests extends AnyWordSpec with Matchers {
val card = Card(CardValue.Ace, Suit.Spades) val card = Card(CardValue.Ace, Suit.Spades)
val card2 = Card(CardValue.Two, Suit.Clubs) val card2 = Card(CardValue.Two, Suit.Clubs)
val trick = new Trick(round) val trick = new Trick(round)
trick.playCard(card, player) TrickControl.playCard(trick, round, card, player)
trick.playCard(card2, player2) TrickControl.playCard(trick, round, card2, player2)
trick.toString() shouldBe s"${trick.cards}, ${null}, ${false}" trick.toString() shouldBe s"${trick.cards}, ${null}, ${false}"
} }
"can't set a first card twice" in {
val player = Player("Gunter")
val player2 = Player("Peter")
val playerlist = List(player, player2)
val round = new Round(Suit.Diamonds, Match(playerlist), playerlist, false)
val card = Card(CardValue.Ace, Suit.Spades)
val card2 = Card(CardValue.Two, Suit.Clubs)
val trick = new Trick(round)
trick.set_first_card(card)
assertThrows[IllegalStateException] {
trick.set_first_card(card2)
}
}
} }
} }
@@ -1,6 +1,7 @@
package de.knockoutwhist.testutils package de.knockoutwhist.testutils
import de.knockoutwhist.KnockOutWhist import de.knockoutwhist.KnockOutWhist
import de.knockoutwhist.utils.DelayHandler
import java.io.{ByteArrayInputStream, OutputStream} import java.io.{ByteArrayInputStream, OutputStream}
@@ -26,5 +27,9 @@ object TestUtil {
def disableDebugMode(): Unit = { def disableDebugMode(): Unit = {
KnockOutWhist.DEBUG_MODE_VAR = false KnockOutWhist.DEBUG_MODE_VAR = false
} }
def disableDelay(): Unit = {
DelayHandler.activateDelay = false
}
} }
@@ -0,0 +1,51 @@
package de.knockoutwhist.utils.events
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
class EventTests extends AnyWordSpec with Matchers {
"An EventListener Priority" should {
"have a working compare too" in {
val event1 = Priority.High
val event2 = Priority.Normal
val event3 = Priority.Low
event1 should be > event2
event2 should be < event1
event2 should be > event3
event3 should be < event2
event1 should be > event3
event3 should be < event1
}
}
"The event handler" should {
"add a listener" in {
val eventHandler = new EventHandler() {}
val listener = new EventListener {
override def listen[R](event: ReturnableEvent[R]): Option[R] = None
}
eventHandler.addListener(listener)
eventHandler.listeners should contain(listener)
}
"remove a listener" in {
val eventHandler = new EventHandler() {}
val listener = new EventListener {
override def listen[R](event: ReturnableEvent[R]): Option[R] = None
}
eventHandler.addListener(listener)
eventHandler.removeListener(listener)
eventHandler.listeners should not contain listener
}
"throw an exception if an event is sent without anyone to listen" in {
val eventHandler = new EventHandler() {}
assertThrows[IllegalStateException] {
eventHandler.invoke(new ReturnableEvent[String] {
override def id: String = "testEvent"
})
}
}
}
}