diff --git a/knockoutwhistweb/app/components/WebApplicationConfiguration.scala b/knockoutwhistweb/app/components/WebApplicationConfiguration.scala index 3b174df..0747614 100644 --- a/knockoutwhistweb/app/components/WebApplicationConfiguration.scala +++ b/knockoutwhistweb/app/components/WebApplicationConfiguration.scala @@ -1,9 +1,13 @@ package components +import controllers.WebUIMain import de.knockoutwhist.components.DefaultConfiguration +import de.knockoutwhist.ui.UI +import de.knockoutwhist.utils.events.EventListener -//class WebApplicationConfiguration extends DefaultConfiguration { - - +class WebApplicationConfiguration extends DefaultConfiguration { -//} + override def uis: Set[UI] = super.uis + WebUIMain + override def listener: Set[EventListener] = super.listener + WebUIMain + +} diff --git a/knockoutwhistweb/app/controllers/GameManager.scala b/knockoutwhistweb/app/controllers/GameManager.scala new file mode 100644 index 0000000..904415b --- /dev/null +++ b/knockoutwhistweb/app/controllers/GameManager.scala @@ -0,0 +1,8 @@ +package controllers + +object GameManager { + + + + +} diff --git a/knockoutwhistweb/app/controllers/HomeController.scala b/knockoutwhistweb/app/controllers/HomeController.scala index 64be26e..e8a769c 100644 --- a/knockoutwhistweb/app/controllers/HomeController.scala +++ b/knockoutwhistweb/app/controllers/HomeController.scala @@ -1,11 +1,13 @@ package controllers -import javax.inject.* +import com.google.inject.{Guice, Injector} +import de.knockoutwhist.KnockOutWhist +import de.knockoutwhist.components.Configuration +import di.KnockOutWebConfigurationModule import play.api.* import play.api.mvc.* -import de.knockoutwhist.KnockOutWhist -import de.knockoutwhist.control.ControlHandler -import de.knockoutwhist.ui.tui.TUIMain + +import javax.inject.* /** * This controller creates an `Action` to handle HTTP requests to the @@ -15,6 +17,7 @@ import de.knockoutwhist.ui.tui.TUIMain class HomeController @Inject()(val controllerComponents: ControllerComponents) extends BaseController { private var initial = false + private val injector: Injector = Guice.createInjector(KnockOutWebConfigurationModule()) /** * Create an Action to render an HTML page. @@ -26,8 +29,7 @@ class HomeController @Inject()(val controllerComponents: ControllerComponents) e def index(): Action[AnyContent] = { if (!initial) { initial = true - ControlHandler.addListener(WebUI) - KnockOutWhist.main(new Array[String](_length = 0)) + KnockOutWhist.entry(injector.getInstance(classOf[Configuration])) } Action { implicit request => Ok(views.html.index.apply()) @@ -36,11 +38,8 @@ class HomeController @Inject()(val controllerComponents: ControllerComponents) e def ingame(): Action[AnyContent] = { Action { implicit request => - Ok(views.html.tui.apply(WebUI.latestOutput)) + Ok(views.html.tui.apply(WebUIMain.latestOutput)) } } - - def showTUI(): Action[AnyContent] = Action { implicit request => - Ok(views.html.tui.render(WebUI.latestOutput)) - } + } \ No newline at end of file diff --git a/knockoutwhistweb/app/controllers/WebUI.scala b/knockoutwhistweb/app/controllers/WebUI.scala deleted file mode 100644 index ddce42d..0000000 --- a/knockoutwhistweb/app/controllers/WebUI.scala +++ /dev/null @@ -1,521 +0,0 @@ -package controllers - -import de.knockoutwhist.events.directional.RequestPickTrumpsuitEvent -import de.knockoutwhist.events.round.ShowCurrentTrickEvent -import de.knockoutwhist.ui.UI -import de.knockoutwhist.ui.tui.TUIMain.{init, runLater, start} -import de.knockoutwhist.utils.events.{EventListener, SimpleEvent} -import de.knockoutwhist.KnockOutWhist -import de.knockoutwhist.cards.{Card, CardValue, Hand, Suit} -import de.knockoutwhist.control.controllerBaseImpl.{PlayerLogic, TrickLogic} -import de.knockoutwhist.control.{ControlHandler, ControlThread} -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.* -import de.knockoutwhist.events.round.ShowCurrentTrickEvent -import de.knockoutwhist.events.ui.GameState.MAIN_MENU -import de.knockoutwhist.events.ui.{GameState, GameStateUpdateEvent} -import de.knockoutwhist.events.util.DelayEvent -import de.knockoutwhist.player.Playertype.HUMAN -import de.knockoutwhist.player.{AbstractPlayer, PlayerFactory} -import de.knockoutwhist.ui.UI -import de.knockoutwhist.undo.{UndoManager, UndoneException} -import de.knockoutwhist.utils.CustomThread -import de.knockoutwhist.utils.events.{EventListener, SimpleEvent} - -import java.io.{BufferedReader, InputStreamReader} -import java.util.concurrent.atomic.AtomicBoolean -import scala.annotation.tailrec -import scala.util.{Failure, Success, Try} - -object WebUI extends CustomThread with EventListener with UI { - - override def initial: Boolean = { - if (init) { - return false - } - init = true - start() - true - } - setName("WebUI") - - override def instance: CustomThread = WebUI - - var init = false - override def runLater[R](op: => R): Unit = { - interrupted.set(true) - super.runLater(op) - } - var latestOutput: String = "" - private var internState: GameState = GameState.NO_SET - override def listen(event: SimpleEvent): Unit = { - runLater { - event match { - case event: RenderHandEvent => - renderhandmethod(event) - case event: ShowTieCardsEvent => - showtiecardseventmethod(event) - case event: ShowGlobalStatus => - showglobalstatusmethod(event) - case event: ShowPlayerStatus => - showplayerstatusmethod(event) - case event: ShowRoundStatus => - showroundstatusmethod(event) - case event: ShowErrorStatus => - showerrstatmet(event) - case event: RequestTieNumberEvent => - reqnumbereventmet(event) - case event: RequestCardEvent => - reqcardeventmet(event) - case event: RequestDogPlayCardEvent => - reqdogeventmet(event) - case event: RequestPickTrumpsuitEvent => - reqpicktevmet(event) - case event: ShowCurrentTrickEvent => - showcurtrevmet(event) - case event: GameStateUpdateEvent => - if (internState != event.gameState) { - internState = event.gameState - if (event.gameState == GameState.MAIN_MENU) { - mainMenu() - } else if (event.gameState == GameState.PLAYERS) { - reqplayersevent() - } - Some(true) - } - case _ => None - } - } - } - - - object WebUICards { - def renderCardAsString(card: Card): Vector[String] = { - val lines = "│ │" - if (card.cardValue == CardValue.Ten) { - latestOutput += Vector( - s"┌─────────┐", - s"│${cardColour(card.suit)}${Console.BOLD}${card.cardValue.cardType()}${Console.RESET} │", - lines, - s"│ ${cardColour(card.suit)}${Console.BOLD}${card.suit.cardType()}${Console.RESET} │", - lines, - s"│ ${cardColour(card.suit)}${Console.BOLD}${card.cardValue.cardType()}${Console.RESET}│", - s"└─────────┘" - ) - return Vector( - s"┌─────────┐", - s"│${cardColour(card.suit)}${Console.BOLD}${card.cardValue.cardType()}${Console.RESET} │", - lines, - s"│ ${cardColour(card.suit)}${Console.BOLD}${card.suit.cardType()}${Console.RESET} │", - lines, - s"│ ${cardColour(card.suit)}${Console.BOLD}${card.cardValue.cardType()}${Console.RESET}│", - s"└─────────┘" - ) - } - latestOutput += Vector( - s"┌─────────┐", - s"│${cardColour(card.suit)}${Console.BOLD}${card.cardValue.cardType()}${Console.RESET} │", - lines, - s"│ ${cardColour(card.suit)}${Console.BOLD}${card.suit.cardType()}${Console.RESET} │", - lines, - s"│ ${cardColour(card.suit)}${Console.BOLD}${card.cardValue.cardType()}${Console.RESET}│", - s"└─────────┘" - ) - return Vector( - s"┌─────────┐", - s"│${cardColour(card.suit)}${Console.BOLD}${card.cardValue.cardType()}${Console.RESET} │", - lines, - s"│ ${cardColour(card.suit)}${Console.BOLD}${card.suit.cardType()}${Console.RESET} │", - lines, - s"│ ${cardColour(card.suit)}${Console.BOLD}${card.cardValue.cardType()}${Console.RESET}│", - s"└─────────┘" - ) - } - - private 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(WebUICards.renderCardAsString) - var zipped = cardStrings.transpose - if (showNumbers) zipped = { - List.tabulate(hand.cards.length) { i => - s" ${i + 1} " - } - } :: zipped - latestOutput += zipped.map(_.mkString(" ")).toVector - zipped.map(_.mkString(" ")).toVector - } - } - - //override def initial: Boolean = { - //if (init) { - //return false - //} - //init = true - //start() - //true - //} - - @tailrec - private def mainMenu(): Unit = { - latestOutput = "" - latestOutput += "Welcome to Knockout Whist\n" - latestOutput += "Please select an option:\n" - latestOutput += "1. Start a new match\n" - latestOutput += "2. Exit\n" - Try { - input().toInt - } match { - case Success(value) => - value match { - case 1 => - ControlThread.runLater { - KnockOutWhist.config.maincomponent.startMatch() - } - case 2 => - println("Exiting the game.") - System.exit(0) - case _ => - showerrstatmet(ShowErrorStatus(INVALID_NUMBER)) - ControlThread.runLater { - ControlHandler.invoke(DelayEvent(500)) - ControlHandler.invoke(GameStateUpdateEvent(MAIN_MENU)) - } - mainMenu() - } - case Failure(exception) => - exception match { - case undo: UndoneException => - case _ => - showerrstatmet(ShowErrorStatus(NOT_A_NUMBER)) - ControlThread.runLater { - ControlHandler.invoke(DelayEvent(500)) - ControlHandler.invoke(GameStateUpdateEvent(MAIN_MENU)) - } - } - } - } - - private def renderhandmethod(event: RenderHandEvent): Option[Boolean] = { - WebUICards.renderHandEvent(event.hand, event.showNumbers).foreach(println) - Some(true) - } - - private def showtiecardseventmethod(event: ShowTieCardsEvent): Option[Boolean] = { - val a: Array[String] = Array("", "", "", "", "", "", "", "") - for ((player, card) <- event.card) { - val playerNameLength = player.name.length - a(0) += " " + player.name + ":" + (" " * (playerNameLength - 1)) - val rendered = WebUICards.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) - } - - private def showglobalstatusmethod(event: ShowGlobalStatus): Option[Boolean] = { - 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[AbstractPlayer]) { - None - } else { - println(s"${event.objects.head.asInstanceOf[AbstractPlayer].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 => - latestOutput = "" - println("Starting a new match...") - wait(1000) - latestOutput = "" - Some(true) - case SHOW_TYPE_PLAYERS => - latestOutput += "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[AbstractPlayer]) { - None - } else { - latestOutput = "" - latestOutput += s"The match is over. The winner is ${event.objects.head.asInstanceOf[AbstractPlayer]}" - Some(true) - } - } - } - - private def showplayerstatusmethod(event: ShowPlayerStatus): Option[Boolean] = { - 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.") - wait(2000) - latestOutput = "" - Some(true) - } - } - - private def showroundstatusmethod(event: ShowRoundStatus): Option[Boolean] = { - event.status match { - case SHOW_START_ROUND => - latestOutput = "" - println(s"Starting a new round. The trump suit is ${event.currentRound.trumpSuit}.") - wait(2000) - latestOutput = "" - Some(true) - case WON_ROUND => - if (event.objects.length != 1 || !event.objects.head.isInstanceOf[AbstractPlayer]) { - None - } else { - println(s"${event.objects.head.asInstanceOf[AbstractPlayer].name} won the round.") - Some(true) - } - case PLAYERS_OUT => - println("The following players are out of the game:") - event.currentRound.playersout.foreach(p => { - println(p.name) - }) - Some(true) - } - } - - private def showerrstatmet(event: ShowErrorStatus): Option[Boolean] = { - event.status match { - case INVALID_NUMBER => - println("Please enter a valid number.") - Some(true) - case NOT_A_NUMBER => - println("Please enter a number.") - Some(true) - case INVALID_INPUT => - latestOutput += "Please enter a valid input" - Some(true) - case INVALID_NUMBER_OF_PLAYERS => - latestOutput += "Please enter at least two names." - Some(true) - case IDENTICAL_NAMES => - latestOutput += "Please enter unique names." - Some(true) - case INVALID_NAME_FORMAT => - latestOutput += "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 { - latestOutput += f"You have to play a card of suit: ${event.objects.head.asInstanceOf[Card].suit}\n" - Some(true) - } - } - } - - private def reqnumbereventmet(event: RequestTieNumberEvent): Option[Boolean] = { - val tryTie = Try { - val number = input().toInt - if (number < 1 || number > event.remaining) { - throw new IllegalArgumentException(s"Number must be between 1 and ${event.remaining}") - } - number - } - if (tryTie.isFailure && tryTie.failed.get.isInstanceOf[UndoneException]) { - return Some(true) - } - ControlThread.runLater { - KnockOutWhist.config.playerlogcomponent.selectedTie(event.winner, event.matchImpl, event.round, event.playersout, event.cut, tryTie, event.currentStep, event.remaining, event.currentIndex) - } - Some(true) - } - - private def reqcardeventmet(event: RequestCardEvent): Option[Boolean] = { - val tryCard = Try { - val card = input().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) - } - } - if (tryCard.isFailure && tryCard.failed.get.isInstanceOf[UndoneException]) { - return Some(true) - } - ControlThread.runLater { - KnockOutWhist.config.trickcomponent.controlSuitplayed(tryCard, event.matchImpl, event.round, event.trick, event.currentIndex, event.player) - } - Some(true) - } - - private def reqdogeventmet(event: RequestDogPlayCardEvent): Option[Boolean] = { - val tryDogCard = Try { - val card = input() - if (card.equalsIgnoreCase("y")) { - Some(event.hand.cards.head) - } else if (card.equalsIgnoreCase("n") && !event.needstoplay) { - None - } else { - throw new IllegalArgumentException("Didn't want to play card but had to") - } - } - if (tryDogCard.isFailure && tryDogCard.failed.get.isInstanceOf[UndoneException]) { - return Some(true) - } - ControlThread.runLater { - KnockOutWhist.config.trickcomponent.controlDogPlayed(tryDogCard, event.matchImpl, event.round, event.trick, event.currentIndex, event.player) - } - Some(true) - } - - private def reqplayersevent(): Option[Boolean] = { - showglobalstatusmethod(ShowGlobalStatus(SHOW_TYPE_PLAYERS)) - val names = Try { - input().split(",") - } - if (names.isFailure && names.failed.get.isInstanceOf[UndoneException]) { - return Some(true) - } - if (names.get.length < 2) { - showerrstatmet(ShowErrorStatus(INVALID_NUMBER_OF_PLAYERS)) - return reqplayersevent() - } - if (names.get.distinct.length != names.get.length) { - showerrstatmet(ShowErrorStatus(IDENTICAL_NAMES)) - return reqplayersevent() - } - if (names.get.count(_.trim.isBlank) > 0 - || names.get.count(_.trim.length <= 2) > 0 - || names.get.count(_.trim.length > 10) > 0) { - showerrstatmet(ShowErrorStatus(INVALID_NAME_FORMAT)) - return reqplayersevent() - } - ControlThread.runLater { - KnockOutWhist.config - .maincomponent - .enteredPlayers(names.get - .map(s => PlayerFactory.createPlayer(s, playertype = HUMAN)) - .toList) - } - Some(true) - } - - private def reqpicktevmet(event: RequestPickTrumpsuitEvent): Option[Boolean] = { - val trySuit = Try { - val suit = input().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") - } - } - if (trySuit.isFailure && trySuit.failed.get.isInstanceOf[UndoneException]) { - return Some(true) - } - ControlThread.runLater { - KnockOutWhist.config.playerlogcomponent.trumpSuitSelected(event.matchImpl, trySuit, event.remaining_players, event.firstRound, event.player) - } - Some(true) - } - - private def showcurtrevmet(event: ShowCurrentTrickEvent): Option[Boolean] = { - latestOutput = "" - val sb = new StringBuilder() - sb.append("Current Trick:\n") - sb.append("Trump-Suit: " + event.round.trumpSuit + "\n") - if (event.trick.firstCard.isDefined) { - sb.append(s"Suit to play: ${event.trick.firstCard.get.suit}\n") - } - for ((card, player) <- event.trick.cards) { - sb.append(s"${player.name} played ${card.toString}\n") - } - latestOutput += sb.toString() - //println(sb.toString()) - Some(true) - } - - private val isInIO: AtomicBoolean = new AtomicBoolean(false) - private val interrupted: AtomicBoolean = new AtomicBoolean(false) - - private def input(): String = { - interrupted.set(false) - val reader = new BufferedReader(new InputStreamReader(System.in)) - - while (!interrupted.get()) { - if (reader.ready()) { - val in = reader.readLine() - if (in.equals("undo")) { - UndoManager.undoStep() - throw new UndoneException("Undo") - } else if (in.equals("redo")) { - UndoManager.redoStep() - throw new UndoneException("Redo") - } else if (in.equals("load") - && KnockOutWhist.config.persistenceManager.canLoadfile("currentSnapshot")) { - KnockOutWhist.config.persistenceManager.loadFile("currentSnapshot.json") - throw new UndoneException("Load") - } else if (in.equals("save")) { - KnockOutWhist.config.persistenceManager.saveFile("currentSnapshot.json") - } - return in - } - Thread.sleep(50) - } - throw new UndoneException("Skipped") - } -} - diff --git a/knockoutwhistweb/app/controllers/WebUIMain.scala b/knockoutwhistweb/app/controllers/WebUIMain.scala new file mode 100644 index 0000000..b1f211f --- /dev/null +++ b/knockoutwhistweb/app/controllers/WebUIMain.scala @@ -0,0 +1,304 @@ +package controllers + +import de.knockoutwhist.cards.{Card, CardValue, Hand, Suit} +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.round.ShowCurrentTrickEvent +import de.knockoutwhist.events.ui.{GameState, GameStateUpdateEvent} +import de.knockoutwhist.player.AbstractPlayer +import de.knockoutwhist.ui.UI +import de.knockoutwhist.utils.CustomThread +import de.knockoutwhist.utils.events.{EventListener, SimpleEvent} + +object WebUIMain extends CustomThread with EventListener with UI { + + setName("WebUI") + + var init = false + private var internState: GameState = GameState.NO_SET + + var latestOutput: String = "" + + override def instance: CustomThread = WebUIMain + + override def listen(event: SimpleEvent): Unit = { + runLater { + event match { + case event: RenderHandEvent => + renderhandmethod(event) + case event: ShowTieCardsEvent => + showtiecardseventmethod(event) + case event: ShowGlobalStatus => + showglobalstatusmethod(event) + case event: ShowPlayerStatus => + showplayerstatusmethod(event) + case event: ShowRoundStatus => + showroundstatusmethod(event) + case event: ShowErrorStatus => + showerrstatmet(event) + case event: ShowCurrentTrickEvent => + showcurtrevmet(event) + case event: GameStateUpdateEvent => + if (internState != event.gameState) { + internState = event.gameState + if (event.gameState == GameState.MAIN_MENU) { + mainMenu() + } + Some(true) + } + case _ => None + } + } + } + + + object TUICards { + def renderCardAsString(card: Card): Vector[String] = { + val lines = "│ │" + if (card.cardValue == CardValue.Ten) { + return Vector( + s"┌─────────┐", + s"│${cardColour(card.suit)}${Console.BOLD}${card.cardValue.cardType()}${Console.RESET} │", + lines, + s"│ ${cardColour(card.suit)}${Console.BOLD}${card.suit.cardType()}${Console.RESET} │", + lines, + 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} │", + lines, + s"│ ${cardColour(card.suit)}${Console.BOLD}${card.suit.cardType()}${Console.RESET} │", + lines, + s"│ ${cardColour(card.suit)}${Console.BOLD}${card.cardValue.cardType()}${Console.RESET}│", + s"└─────────┘" + ) + } + + private 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() = { + latestOutput = "" + } + } + + override def initial: Boolean = { + if (init) { + return false + } + init = true + start() + true + } + + private def mainMenu(): Unit = { + TUIUtil.clearConsole() + println("Welcome to Knockout Whist!") + println() + println("Please select an option:") + println("1. Start a new match") + println("2. Exit") + } + + private def renderhandmethod(event: RenderHandEvent): Option[Boolean] = { + TUICards.renderHandEvent(event.hand, event.showNumbers).foreach(println) + Some(true) + } + private def showtiecardseventmethod(event: ShowTieCardsEvent): Option[Boolean] = { + 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) + } + private def showglobalstatusmethod(event: ShowGlobalStatus): Option[Boolean] = { + 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[AbstractPlayer]) { + None + } else { + println(s"${event.objects.head.asInstanceOf[AbstractPlayer].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...") + latestOutput += "\n\n" + 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[AbstractPlayer]) { + None + } else { + TUIUtil.clearConsole() + println(s"The match is over. The winner is ${event.objects.head.asInstanceOf[AbstractPlayer]}") + Some(true) + } + } + } + private def showplayerstatusmethod(event: ShowPlayerStatus): Option[Boolean] = { + 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.") + latestOutput = "\n\n" + Some(true) + } + } + private def showroundstatusmethod(event: ShowRoundStatus): Option[Boolean] = { + event.status match { + case SHOW_START_ROUND => + TUIUtil.clearConsole() + println(s"Starting a new round. The trump suit is ${event.currentRound.trumpSuit}.") + latestOutput = "\n\n" + Some(true) + case WON_ROUND => + if (event.objects.length != 1 || !event.objects.head.isInstanceOf[AbstractPlayer]) { + None + } else { + println(s"${event.objects.head.asInstanceOf[AbstractPlayer].name} won the round.") + Some(true) + } + case PLAYERS_OUT => + println("The following players are out of the game:") + event.currentRound.playersout.foreach(p => { + println(p.name) + }) + Some(true) + } + } + private def showerrstatmet(event: ShowErrorStatus): Option[Boolean] = { + event.status match { + case INVALID_NUMBER => + println("Please enter a valid number.") + Some(true) + case NOT_A_NUMBER => + println("Please enter a 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) + } + } + } + + private def showcurtrevmet(event: ShowCurrentTrickEvent): Option[Boolean] = { + TUIUtil.clearConsole() + val sb = new StringBuilder() + sb.append("Current Trick:\n") + sb.append("Trump-Suit: " + event.round.trumpSuit + "\n") + if (event.trick.firstCard.isDefined) { + sb.append(s"Suit to play: ${event.trick.firstCard.get.suit}\n") + } + for ((card, player) <- event.trick.cards) { + sb.append(s"${player.name} played ${card.toString}\n") + } + println(sb.toString()) + Some(true) + } + + private def println(s: String): Unit = { + latestOutput += s + "\n" + System.out.println(s) + } + + private def println(): Unit = { + latestOutput += "\n" + System.out.println() + } + + + +} diff --git a/knockoutwhistweb/app/di/KnockOutWebConfigurationModule.scala b/knockoutwhistweb/app/di/KnockOutWebConfigurationModule.scala new file mode 100644 index 0000000..c7bde92 --- /dev/null +++ b/knockoutwhistweb/app/di/KnockOutWebConfigurationModule.scala @@ -0,0 +1,13 @@ +package di + +import com.google.inject.AbstractModule +import components.WebApplicationConfiguration +import de.knockoutwhist.components.Configuration + +class KnockOutWebConfigurationModule extends AbstractModule { + + override def configure(): Unit = { + bind(classOf[Configuration]).to(classOf[WebApplicationConfiguration]) + } + +} \ No newline at end of file diff --git a/knockoutwhistweb/app/views/main.scala.html b/knockoutwhistweb/app/views/main.scala.html index a4607a8..808a8b8 100644 --- a/knockoutwhistweb/app/views/main.scala.html +++ b/knockoutwhistweb/app/views/main.scala.html @@ -1,10 +1,25 @@ -@(title: String)(content: play.twirl.api.Html) +@* + * This template is called from the `index` template. This template + * handles the rendering of the page header and body tags. It takes + * two arguments, a `String` for the title of the page and an `Html` + * object to insert into the body of the page. + *@ +@(title: String)(content: Html) + - + + @* Here's where we render the page title `String`. *@ @title + + + + @* And here's where we render the `Html` object containing + * the page content. *@ @content + + - \ No newline at end of file + diff --git a/knockoutwhistweb/app/views/tui.scala.html b/knockoutwhistweb/app/views/tui.scala.html index 60af01d..67b715a 100644 --- a/knockoutwhistweb/app/views/tui.scala.html +++ b/knockoutwhistweb/app/views/tui.scala.html @@ -1,5 +1,10 @@ -@(renderTUI: String) +@(toRender: String) -@main("Welcome to Play") { -

@renderTUI

+@main("Tui") { +
+ @for(line <- toRender.split('\n')) { +

@line

+ } +
} + diff --git a/knockoutwhistweb/conf/routes b/knockoutwhistweb/conf/routes index 90c173b..d8d4816 100644 --- a/knockoutwhistweb/conf/routes +++ b/knockoutwhistweb/conf/routes @@ -6,6 +6,5 @@ # An example controller showing a sample home page GET / controllers.HomeController.index() GET /ingame controllers.HomeController.ingame() -GET /showTUI controllers.HomeController.showTUI() # Map static resources from the /public folder to the /assets URL path GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)