From 0ffd4c042e48af50fb5b1ffe64ae2a747d149181 Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Wed, 21 Mar 2018 14:18:55 +0100 Subject: [PATCH 01/21] Did some refactoring --- .../de/htwg/se/learn_duel/LearnDuel.scala | 7 +-- .../de/htwg/se/learn_duel/Observer.scala | 4 +- .../controller/impl/Controller.scala | 30 ++++++------ .../de/htwg/se/learn_duel/model/Answer.scala | 10 ++-- .../model/command/impl/CommandInvoker.scala | 4 +- .../command/impl/PlayerRemoveCommand.scala | 1 - .../htwg/se/learn_duel/model/impl/Game.scala | 2 - .../se/learn_duel/model/impl/Player.scala | 2 - .../scala/de/htwg/se/learn_duel/view/UI.scala | 1 - .../de/htwg/se/learn_duel/view/impl/TUI.scala | 40 +++++++-------- .../se/learn_duel/view/impl/gui/GUI.scala | 49 +++++++++---------- .../learn_duel/view/impl/gui/GameStage.scala | 14 +++--- .../learn_duel/view/impl/gui/InfoPopup.scala | 3 +- .../learn_duel/view/impl/gui/MenuStage.scala | 20 ++++---- .../view/impl/gui/ResultStage.scala | 23 +++++---- 15 files changed, 90 insertions(+), 120 deletions(-) diff --git a/src/main/scala/de/htwg/se/learn_duel/LearnDuel.scala b/src/main/scala/de/htwg/se/learn_duel/LearnDuel.scala index 17ffe1e..8452daf 100644 --- a/src/main/scala/de/htwg/se/learn_duel/LearnDuel.scala +++ b/src/main/scala/de/htwg/se/learn_duel/LearnDuel.scala @@ -1,15 +1,10 @@ package de.htwg.se.learn_duel import de.htwg.se.learn_duel.controller.Controller -import de.htwg.se.learn_duel.model.impl.Game import java.io.BufferedReader import com.google.inject.Guice -import de.htwg.se.learn_duel.model.Question import de.htwg.se.learn_duel.view.{GUI, TUI} -import play.api.libs.json.Json - -import scala.io.Source object LearnDuel { def main(args: Array[String]): Unit = { @@ -19,7 +14,7 @@ object LearnDuel { val tui = TUI.create(controller) GUI.create(controller) - controller.requestUpdate + controller.requestUpdate() tui.processInput(new BufferedReader(Console.in)) } } diff --git a/src/main/scala/de/htwg/se/learn_duel/Observer.scala b/src/main/scala/de/htwg/se/learn_duel/Observer.scala index 72aabbb..f90fe68 100644 --- a/src/main/scala/de/htwg/se/learn_duel/Observer.scala +++ b/src/main/scala/de/htwg/se/learn_duel/Observer.scala @@ -9,8 +9,8 @@ import UpdateAction._ import de.htwg.se.learn_duel.model.Game class UpdateData(updateAction: UpdateAction, gameState: Game) { - def getAction(): UpdateAction = updateAction - def getState(): Game = gameState + def getAction: UpdateAction = updateAction + def getState: Game = gameState } trait Observer { diff --git a/src/main/scala/de/htwg/se/learn_duel/controller/impl/Controller.scala b/src/main/scala/de/htwg/se/learn_duel/controller/impl/Controller.scala index 0299b75..db1db7d 100644 --- a/src/main/scala/de/htwg/se/learn_duel/controller/impl/Controller.scala +++ b/src/main/scala/de/htwg/se/learn_duel/controller/impl/Controller.scala @@ -28,7 +28,7 @@ class Controller @Inject() (gameState: Game) extends ControllerTrait { override def getPlayerNames: List[String] = { gameState.players.map(p => p.name) } - + override def onAddPlayer(name: Option[String]) : Unit = { invoker.execute(PlayerAddCommand(name, addPlayer, removePlayer)) } @@ -77,8 +77,8 @@ class Controller @Inject() (gameState: Game) extends ControllerTrait { val currentQuestion = gameState.currentQuestion.get val correctAnswer = currentQuestion.correctAnswer var (player: Option[Player], userInput: Int) = input match { - case x if (0 until 5 contains x) => (Some(gameState.players.head), input) - case x if (6 until 10 contains x) && (gameState.players.length > 1 )=> (Some(gameState.players(1)), input-5) // FIXME magic number -> local mp will be removed anyway + case x if 0 until 5 contains x => (Some(gameState.players.head), input) + case x if (6 until 10 contains x) && gameState.players.lengthCompare(1) > 0=> (Some(gameState.players(1)), input-5) // FIXME magic number -> local mp will be removed anyway case _ => (None, input) } @@ -158,23 +158,21 @@ class Controller @Inject() (gameState: Game) extends ControllerTrait { localTimer.scheduleAtFixedRate(new TimerTask { override def run(): Unit = { val newTime = gameState.currentQuestionTime match { - case Some(time) => { - val newTime = time - 1 - if (newTime == 0) { - nextQuestion() - None - } else { - Some(newTime) - } - } + case Some(time) => + val newTime = time - 1 + if (newTime == 0) { + nextQuestion() + None + } else { + Some(newTime) + } case None => None } newTime match { - case Some(time) => { - gameState.currentQuestionTime = newTime - notifyObserversAndSaveUpdate(new UpdateData(UpdateAction.TIMER_UPDATE, gameState)) - } + case Some(time) => + gameState.currentQuestionTime = newTime + notifyObserversAndSaveUpdate(new UpdateData(UpdateAction.TIMER_UPDATE, gameState)) case _ => } } diff --git a/src/main/scala/de/htwg/se/learn_duel/model/Answer.scala b/src/main/scala/de/htwg/se/learn_duel/model/Answer.scala index d37be9b..1a617d0 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/Answer.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/Answer.scala @@ -10,12 +10,10 @@ trait Answer { } object Answer { - implicit val answerWrites: Writes[Answer] = new Writes[Answer] { - def writes(answer: Answer): JsObject = Json.obj( - "id" -> answer.id, - "text" -> answer.text, - ) - } + implicit val answerWrites: Writes[Answer] = (answer: Answer) => Json.obj( + "id" -> answer.id, + "text" -> answer.text, + ) implicit val answerReads: Reads[Answer] = ( (JsPath \ "id").read[Int] and diff --git a/src/main/scala/de/htwg/se/learn_duel/model/command/impl/CommandInvoker.scala b/src/main/scala/de/htwg/se/learn_duel/model/command/impl/CommandInvoker.scala index 1882b28..15306a0 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/command/impl/CommandInvoker.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/command/impl/CommandInvoker.scala @@ -12,7 +12,7 @@ case class CommandInvoker() extends CommandInvokerTrait { override def undo(): Unit = { if (undoCommands.nonEmpty) { val lastCommand = undoCommands.last - lastCommand.undo + lastCommand.undo() redoCommands = redoCommands :+ lastCommand undoCommands = undoCommands diff List(lastCommand) } @@ -20,7 +20,7 @@ case class CommandInvoker() extends CommandInvokerTrait { override def redo(): Unit = { if (redoCommands.nonEmpty) { - redoCommands.head.redo + redoCommands.head.redo() undoCommands = undoCommands :+ redoCommands.head redoCommands = redoCommands diff List(redoCommands.head) } diff --git a/src/main/scala/de/htwg/se/learn_duel/model/command/impl/PlayerRemoveCommand.scala b/src/main/scala/de/htwg/se/learn_duel/model/command/impl/PlayerRemoveCommand.scala index 2b0bda2..3f6d83f 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/command/impl/PlayerRemoveCommand.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/command/impl/PlayerRemoveCommand.scala @@ -1,6 +1,5 @@ package de.htwg.se.learn_duel.model.command.impl -import de.htwg.se.learn_duel.controller.Controller import de.htwg.se.learn_duel.model.command.Command case class PlayerRemoveCommand( diff --git a/src/main/scala/de/htwg/se/learn_duel/model/impl/Game.scala b/src/main/scala/de/htwg/se/learn_duel/model/impl/Game.scala index ec33dd7..56ea236 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/impl/Game.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/impl/Game.scala @@ -1,7 +1,5 @@ package de.htwg.se.learn_duel.model.impl -import java.security.InvalidParameterException - import de.htwg.se.learn_duel.model.{Game => GameTrait, Player => PlayerTrait, Question => QuestionTrait} // FIXME find better way for resetting or do not let all props be specified in constructor (currently needed for JSON reader) diff --git a/src/main/scala/de/htwg/se/learn_duel/model/impl/Player.scala b/src/main/scala/de/htwg/se/learn_duel/model/impl/Player.scala index c2079f3..237d992 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/impl/Player.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/impl/Player.scala @@ -18,5 +18,3 @@ case class Player( override def toString: String = name } - - diff --git a/src/main/scala/de/htwg/se/learn_duel/view/UI.scala b/src/main/scala/de/htwg/se/learn_duel/view/UI.scala index dfbd13f..b4d8d3c 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/UI.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/UI.scala @@ -2,7 +2,6 @@ package de.htwg.se.learn_duel.view import java.util.concurrent.CountDownLatch -import com.google.inject.Inject import de.htwg.se.learn_duel.Observer import de.htwg.se.learn_duel.controller.Controller import de.htwg.se.learn_duel.model.{Player, Question} diff --git a/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala b/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala index f65212d..57ff0cb 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala @@ -1,7 +1,6 @@ package de.htwg.se.learn_duel.view.impl import java.io.BufferedReader -import java.util.{Timer, TimerTask} import com.typesafe.scalalogging.LazyLogging import de.htwg.se.learn_duel.{Observer, UpdateAction, UpdateData} @@ -69,31 +68,27 @@ class TUI (controller: Controller) extends UI with Observer with LazyLogging { // scalastyle:off override def update(updateParam: UpdateData): Unit = { updateParam.getAction() match { - case UpdateAction.BEGIN => displayMenu + case UpdateAction.BEGIN => displayMenu() case UpdateAction.CLOSE_APPLICATION => stopProcessingInput = true - case UpdateAction.SHOW_HELP => { + case UpdateAction.SHOW_HELP => logger.info(updateParam.getState().helpText.mkString("\n\n")) - } - case UpdateAction.PLAYER_UPDATE => displayPlayers - case UpdateAction.SHOW_GAME => { + case UpdateAction.PLAYER_UPDATE => displayPlayers() + case UpdateAction.SHOW_GAME => displayGamePretty( updateParam.getState().currentQuestion.get, - updateParam.getState().players.length > 1, + updateParam.getState().players.lengthCompare(1) > 0, updateParam.getState().currentQuestionTime.get ) inMenu = false; inGame = true; - } - case UpdateAction.TIMER_UPDATE => { + case UpdateAction.TIMER_UPDATE => displayGamePretty( updateParam.getState().currentQuestion.get, - updateParam.getState().players.length > 1, + updateParam.getState().players.lengthCompare(1) > 0, updateParam.getState().currentQuestionTime.get ) - } - case UpdateAction.SHOW_RESULT => { + case UpdateAction.SHOW_RESULT => displayResult(updateParam.getState().players) - } case _ => } } @@ -118,19 +113,18 @@ class TUI (controller: Controller) extends UI with Observer with LazyLogging { val playerPattern = """(?:a|r)(?:\s+(.*))?""".r try { line match { - case "q" => controller.onClose - case "n" => controller.onStartGame + case "q" => controller.onClose() + case "n" => controller.onStartGame() case playerPattern(name) if line.startsWith("a") => controller.onAddPlayer(Option(name)) case playerPattern(name) if line.startsWith("r") => if (name != null) { controller.onRemovePlayer(name) } - case "h" => controller.onHelp - case "u" => controller.onPlayerActionUndo - case "U" => controller.onPlayerActionRedo - case _ => { + case "h" => controller.onHelp() + case "u" => controller.onPlayerActionUndo() + case "U" => controller.onPlayerActionRedo() + case _ => logger.info("Unknown command") - displayMenu - } + displayMenu() } } catch { case e: ControllerException => logger.error(e.getMessage) @@ -139,7 +133,7 @@ class TUI (controller: Controller) extends UI with Observer with LazyLogging { protected def processGameInput(line: String): Unit = { line match { - case "q" => controller.onClose + case "q" => controller.onClose() case "1" => controller.onAnswerChosen(1) case "2" => controller.onAnswerChosen(2) case "3" => controller.onAnswerChosen(3) @@ -154,7 +148,7 @@ class TUI (controller: Controller) extends UI with Observer with LazyLogging { protected def processResultInput(line: String): Unit = { line match { - case "q" => controller.onClose + case "q" => controller.onClose() case _ => logger.info("Unknown command") } } diff --git a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GUI.scala b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GUI.scala index 1c1d168..ed53c5e 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GUI.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GUI.scala @@ -18,17 +18,15 @@ class GUI (controller: Controller, latch: CountDownLatch) extends JFXApp with UI // handle self defined exception in a 'global' exception handler Thread.currentThread().setUncaughtExceptionHandler((t: Thread, e: Throwable) => { e.getCause match { - case cause: ControllerException => { + case cause: ControllerException => val infoPopup = new InfoPopup("Error", cause.message) - infoPopup.getDialogPane().getStylesheets().add("styles.css") - infoPopup.show - } - case _ => { + infoPopup.getDialogPane.getStylesheets.add("styles.css") + infoPopup.show() + case _ => val sw = new StringWriter() e.printStackTrace(new PrintWriter(sw)) logger.error(Console.RED + sw.toString + Console.RESET) - controller.onClose - } + controller.onClose() } }) @@ -40,36 +38,33 @@ class GUI (controller: Controller, latch: CountDownLatch) extends JFXApp with UI // every update needs to be run on the JavaFX Application thread Platform.runLater { () => updateParam.getAction() match { - case UpdateAction.BEGIN => { + case UpdateAction.BEGIN => displayMenu() this.stage.onCloseRequest = { (_) => - controller.onClose + controller.onClose() } - } case UpdateAction.CLOSE_APPLICATION => this.stage.close() - case UpdateAction.SHOW_HELP => { + case UpdateAction.SHOW_HELP => val helpText = updateParam.getState().helpText val dlg = new InfoPopup("Learn Duel Help", helpText.mkString("\n\n")) - dlg.getDialogPane().getStylesheets().add("styles.css") - dlg.show - } - case UpdateAction.PLAYER_UPDATE => displayMenu - case UpdateAction.SHOW_GAME => { + dlg.getDialogPane.getStylesheets.add("styles.css") + dlg.show() + case UpdateAction.PLAYER_UPDATE => displayMenu() + case UpdateAction.SHOW_GAME => displayGame( updateParam.getState().currentQuestion.get, - updateParam.getState().players.length > 1 + updateParam.getState().players.lengthCompare(1) > 0 ) - } - case UpdateAction.TIMER_UPDATE => { + case UpdateAction.TIMER_UPDATE => updateParam.getState().currentQuestionTime match { - case Some(time) => { - if (this.stage.isInstanceOf[GameStage]) { - this.stage.asInstanceOf[GameStage].updateTime(time) + case Some(time) => + this.stage match { + case stage1: GameStage => + stage1.updateTime(time) + case _ => } - } case _ => } - } case UpdateAction.SHOW_RESULT => { displayResult(updateParam.getState().players) } @@ -81,9 +76,9 @@ class GUI (controller: Controller, latch: CountDownLatch) extends JFXApp with UI override def displayMenu(): Unit = { this.stage = new MenuStage( - _ => controller.onStartGame, - _ => controller.onHelp, - (controller.getPlayerNames, controller.nextPlayerName), + _ => controller.onStartGame(), + _ => controller.onHelp(), + (controller.getPlayerNames, controller.nextPlayerName()), (name) => controller.onAddPlayer(Some(name)), controller.onRemovePlayer ) diff --git a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GameStage.scala b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GameStage.scala index 34f0a69..e5b2c4a 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GameStage.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GameStage.scala @@ -10,7 +10,7 @@ import scalafx.Includes._ import scalafx.application.JFXApp.PrimaryStage import scalafx.scene.Scene import scalafx.scene.control.Button -import scalafx.scene.layout.{TilePane, VBox} +import scalafx.scene.layout.VBox import scalafx.scene.paint.Color import scalafx.scene.paint.Color._ import scalafx.scene.text.Text @@ -20,8 +20,8 @@ class GameStage( allowMouseInput: Boolean, onInput: Function[Int, Unit] ) extends PrimaryStage { - var timeRemaining = question.time - var timerText = new SimpleStringProperty { + var timeRemaining: Int = question.time + var timerText: SimpleStringProperty = new SimpleStringProperty { "Time remaining: " + timeRemaining + "s" } var timer: Option[Timer] = None @@ -40,13 +40,13 @@ class GameStage( root = new VBox { styleClass += "game" - val questionProp = new Text { + val questionProp: Text = new Text { text = question.text styleClass += "headline" } children += questionProp - val answerBox = new VBox { + val answerBox: VBox = new VBox { styleClass += "answer-container" question.answers.zipWithIndex.foreach { case (ans, i) => @@ -64,7 +64,7 @@ class GameStage( } children += answerBox - val timer = new Text { + val timer: Text = new Text { styleClass += "remaining-time" } timer.text.bind(timerText) @@ -81,7 +81,7 @@ class GameStage( } onKeyReleased = { e => { - e.getCode() match { + e.getCode match { case KeyCode.DIGIT1 => onInput(1) case KeyCode.DIGIT2 => onInput(2) case KeyCode.DIGIT3 => onInput(3) diff --git a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/InfoPopup.scala b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/InfoPopup.scala index 50cc19e..2b9efda 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/InfoPopup.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/InfoPopup.scala @@ -1,7 +1,6 @@ package de.htwg.se.learn_duel.view.impl.gui -import scalafx.geometry.Insets -import scalafx.scene.control.{ButtonType, Dialog, DialogPane} +import scalafx.scene.control.{ButtonType, Dialog} import scalafx.stage.Modality class InfoPopup(titleText: String, text: String) extends Dialog[Unit] { diff --git a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/MenuStage.scala b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/MenuStage.scala index 299e2b5..6792e31 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/MenuStage.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/MenuStage.scala @@ -4,13 +4,11 @@ import javafx.event.{ActionEvent, EventHandler} import scalafx.Includes._ import scalafx.application.JFXApp.PrimaryStage -import scalafx.geometry.{Insets, Pos} import scalafx.scene.Scene import scalafx.scene.control.{Button, TextField} import scalafx.scene.layout._ -import scalafx.scene.paint.Color import scalafx.scene.paint.Color._ -import scalafx.scene.text.{Text, TextAlignment} +import scalafx.scene.text.Text class MenuStage( newGameAction: EventHandler[ActionEvent], @@ -31,27 +29,27 @@ class MenuStage( root = new VBox { styleClass += "menu" - val headLine = new Text { + val headLine: Text = new Text { text = "Learn Duel" styleClass += "headline" } children += headLine - val newGameButton = new Button { + val newGameButton: Button = new Button { text = "New Game" onAction = newGameAction styleClass += "play-button" } children += newGameButton - val playerContainer = new VBox { + val playerContainer: VBox = new VBox { styleClass += "player-rows-container" for (playerName <- playerInfo._1) { val hBox = new HBox { styleClass += "player-container" - val txtFld = new TextField { + val txtFld: TextField = new TextField { text = playerName editable = false styleClass += "player-textfield" @@ -59,7 +57,7 @@ class MenuStage( } children += txtFld - val removeBtn = new Button { + val removeBtn: Button = new Button { text = "Remove" onAction = _ => playerRemoveAction(playerName) styleClass += "add-remove-buttons" @@ -74,13 +72,13 @@ class MenuStage( val hBox = new HBox { styleClass += "player-container" - val txtField = new TextField { + val txtField: TextField = new TextField { promptText = nextPlayername styleClass += "player-textfield" } children += txtField - val addBtn = new Button { + val addBtn: Button = new Button { text = "Add" onAction = _ => playerAddAction( if (txtField.getText.isEmpty) { @@ -100,7 +98,7 @@ class MenuStage( } children += playerContainer - val helpButton = new Button { + val helpButton: Button = new Button { text = "Help" onAction = helpAction styleClass += "help-button" diff --git a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/ResultStage.scala b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/ResultStage.scala index 7e2c81c..2fd2e18 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/ResultStage.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/ResultStage.scala @@ -7,7 +7,6 @@ import scalafx.application.JFXApp.PrimaryStage import scalafx.scene.control.Button import scalafx.scene.Scene import scalafx.scene.layout.VBox -import scalafx.scene.paint.Color import scalafx.scene.paint.Color._ import scalafx.scene.text.Text @@ -29,35 +28,35 @@ class ResultStage( root = new VBox { styleClass += "result" - val headline = new Text { + val headline: Text = new Text { text = "Result" styleClass += "headline" } children += headline - val playerContainer = new VBox { + val playerContainer: VBox = new VBox { styleClass += "player-results" players.foreach { p => { val singlePlayerContainer = new VBox { styleClass += "player-result" - val player = new Text { + val player: Text = new Text { text = p.name styleClass += "player-name" } children += player - val points = new Text { + val points: Text = new Text { text = "Points: " + p.points styleClass += "player-points" } children += points - val correctContainer = new VBox { + val correctContainer: VBox = new VBox { styleClass += "correct-container" - if (p.correctAnswers.length > 0) { + if (p.correctAnswers.nonEmpty) { val correctText = new Text { text = "Correct answers" styleClass += "correct-text" @@ -75,10 +74,10 @@ class ResultStage( } children += correctContainer - val wrongContainer = new VBox { + val wrongContainer: VBox = new VBox { styleClass += "wrong-container" - if (p.wrongAnswers.length > 0) { + if (p.wrongAnswers.nonEmpty) { val wrongText = new Text { text = "Wrong answers" styleClass += "wrong-text" @@ -102,17 +101,17 @@ class ResultStage( } children += playerContainer - val player = players.max[Player]{ case (p1: Player, p2: Player) => { + val player: Player = players.max[Player]{ case (p1: Player, p2: Player) => { p1.points.compareTo(p2.points) }} - val winner = new Text { + val winner: Text = new Text { text = "'" + player.name + "' won the game!" styleClass += "winner" } children += winner - val backButton = new Button { + val backButton: Button = new Button { text = "Back" onAction = backAction styleClass += "back-button" From 80b33dcebb9ed27dcf7b8a5b568598695a0aa045 Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Wed, 21 Mar 2018 14:26:34 +0100 Subject: [PATCH 02/21] Did some further refactoring --- .../de/htwg/se/learn_duel/view/impl/TUI.scala | 28 +++++++++---------- .../se/learn_duel/view/impl/gui/GUI.scala | 15 +++++----- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala b/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala index 57ff0cb..e8a6255 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala @@ -32,9 +32,9 @@ class TUI (controller: Controller) extends UI with Observer with LazyLogging { override def displayGame(question: Question, multiplayer: Boolean): Unit = { logger.info(question.text) - question.answers.zipWithIndex.foreach {case (ans, i) => { + question.answers.zipWithIndex.foreach {case (ans, i) => logger.info((i + 1) + "/" + (i + 5) + ": " + ans.text) - }} + } } override def displayResult(players: List[Player]): Unit = { @@ -56,9 +56,9 @@ class TUI (controller: Controller) extends UI with Observer with LazyLogging { }) }) - val player = players.max[Player]{ case (p1: Player, p2: Player) => { + val player = players.max[Player]{ case (p1: Player, p2: Player) => p1.points.compareTo(p2.points) - }} + } logger.info("") logger.info("'" + player.name + "' won the game!") @@ -67,28 +67,28 @@ class TUI (controller: Controller) extends UI with Observer with LazyLogging { // scalastyle:off override def update(updateParam: UpdateData): Unit = { - updateParam.getAction() match { + updateParam.getAction match { case UpdateAction.BEGIN => displayMenu() case UpdateAction.CLOSE_APPLICATION => stopProcessingInput = true case UpdateAction.SHOW_HELP => - logger.info(updateParam.getState().helpText.mkString("\n\n")) + logger.info(updateParam.getState.helpText.mkString("\n\n")) case UpdateAction.PLAYER_UPDATE => displayPlayers() case UpdateAction.SHOW_GAME => displayGamePretty( - updateParam.getState().currentQuestion.get, - updateParam.getState().players.lengthCompare(1) > 0, - updateParam.getState().currentQuestionTime.get + updateParam.getState.currentQuestion.get, + updateParam.getState.players.lengthCompare(1) > 0, + updateParam.getState.currentQuestionTime.get ) - inMenu = false; + inMenu = false inGame = true; case UpdateAction.TIMER_UPDATE => displayGamePretty( - updateParam.getState().currentQuestion.get, - updateParam.getState().players.lengthCompare(1) > 0, - updateParam.getState().currentQuestionTime.get + updateParam.getState.currentQuestion.get, + updateParam.getState.players.lengthCompare(1) > 0, + updateParam.getState.currentQuestionTime.get ) case UpdateAction.SHOW_RESULT => - displayResult(updateParam.getState().players) + displayResult(updateParam.getState.players) case _ => } } diff --git a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GUI.scala b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GUI.scala index ed53c5e..f00e357 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GUI.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GUI.scala @@ -37,7 +37,7 @@ class GUI (controller: Controller, latch: CountDownLatch) extends JFXApp with UI override def update(updateParam: UpdateData): Unit = { // every update needs to be run on the JavaFX Application thread Platform.runLater { () => - updateParam.getAction() match { + updateParam.getAction match { case UpdateAction.BEGIN => displayMenu() this.stage.onCloseRequest = { (_) => @@ -45,18 +45,18 @@ class GUI (controller: Controller, latch: CountDownLatch) extends JFXApp with UI } case UpdateAction.CLOSE_APPLICATION => this.stage.close() case UpdateAction.SHOW_HELP => - val helpText = updateParam.getState().helpText + val helpText = updateParam.getState.helpText val dlg = new InfoPopup("Learn Duel Help", helpText.mkString("\n\n")) dlg.getDialogPane.getStylesheets.add("styles.css") dlg.show() case UpdateAction.PLAYER_UPDATE => displayMenu() case UpdateAction.SHOW_GAME => displayGame( - updateParam.getState().currentQuestion.get, - updateParam.getState().players.lengthCompare(1) > 0 + updateParam.getState.currentQuestion.get, + updateParam.getState.players.lengthCompare(1) > 0 ) case UpdateAction.TIMER_UPDATE => - updateParam.getState().currentQuestionTime match { + updateParam.getState.currentQuestionTime match { case Some(time) => this.stage match { case stage1: GameStage => @@ -65,9 +65,8 @@ class GUI (controller: Controller, latch: CountDownLatch) extends JFXApp with UI } case _ => } - case UpdateAction.SHOW_RESULT => { - displayResult(updateParam.getState().players) - } + case UpdateAction.SHOW_RESULT => + displayResult(updateParam.getState.players) case _ => } } From 6bd653158e568b901aeea676eb43f96b911924a7 Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Wed, 21 Mar 2018 14:32:15 +0100 Subject: [PATCH 03/21] Fixed tests due to refactoring before --- .../controller/ControllerSpec.scala | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/test/scala/de/htwg/se/learn_duel/controller/ControllerSpec.scala b/src/test/scala/de/htwg/se/learn_duel/controller/ControllerSpec.scala index 8f3918c..fc613e9 100644 --- a/src/test/scala/de/htwg/se/learn_duel/controller/ControllerSpec.scala +++ b/src/test/scala/de/htwg/se/learn_duel/controller/ControllerSpec.scala @@ -36,8 +36,8 @@ class ControllerSpec extends WordSpec with Matchers { dummyObserver.updateData should be(None) controller.requestUpdate() dummyObserver.updateData.isDefined should be(true) - dummyObserver.updateData.get.getAction() should be(UpdateAction.BEGIN) - dummyObserver.updateData.get.getState() should be(gameState) + dummyObserver.updateData.get.getAction should be(UpdateAction.BEGIN) + dummyObserver.updateData.get.getState should be(gameState) } "be able to generate a list of player names" in { controller.getPlayerNames should be(gameState.players.map(p => p.name)) @@ -80,30 +80,30 @@ class ControllerSpec extends WordSpec with Matchers { } "indicate to observers that they should show the help" in { controller.onHelp() - dummyObserver.updateData.get.getAction() should be(UpdateAction.SHOW_HELP) + dummyObserver.updateData.get.getAction should be(UpdateAction.SHOW_HELP) } "indicate to observers that they should start the game" in { controller.onStartGame() - dummyObserver.updateData.get.getAction() should be(UpdateAction.SHOW_GAME) + dummyObserver.updateData.get.getAction should be(UpdateAction.SHOW_GAME) gameState.currentQuestion should not be(None) gameState.currentQuestionTime should not be(None) } "indicate to observers that they should close" in { controller.onClose() - dummyObserver.updateData.get.getAction() should be(UpdateAction.CLOSE_APPLICATION) + dummyObserver.updateData.get.getAction should be(UpdateAction.CLOSE_APPLICATION) } "be able to reset game state" in { controller.onPlayerActionRedo() gameState.playerCount() should be(2) gameState.currentQuestion should not be(None) gameState.currentQuestionTime should not be(None) - dummyObserver.updateData.get.getAction() should not be(UpdateAction.BEGIN) + dummyObserver.updateData.get.getAction should not be(UpdateAction.BEGIN) controller.reset() gameState.playerCount() should be(1) gameState.currentQuestion should be(None) gameState.currentQuestionTime should be(None) - dummyObserver.updateData.get.getAction() should be(UpdateAction.BEGIN) + dummyObserver.updateData.get.getAction should be(UpdateAction.BEGIN) } "add correctly and wrongly answered questions to player and move on to next question" in { gameState.players.head.correctAnswers.length should be(0) @@ -119,7 +119,7 @@ class ControllerSpec extends WordSpec with Matchers { controller.onAnswerChosen(1) gameState.players.head.wrongAnswers.length should be(1) - dummyObserver.updateData.get.getAction() should be(UpdateAction.SHOW_RESULT) + dummyObserver.updateData.get.getAction should be(UpdateAction.SHOW_RESULT) } "update question timer" in { controller.reset() @@ -131,12 +131,12 @@ class ControllerSpec extends WordSpec with Matchers { "remove observers correctly" in { controller.reset() controller.onHelp() - val currentAction = dummyObserver.updateData.get.getAction() + val currentAction = dummyObserver.updateData.get.getAction currentAction should not be(UpdateAction.SHOW_GAME) controller.removeObserver(dummyObserver) controller.onStartGame() - dummyObserver.updateData.get.getAction() should not be(UpdateAction.SHOW_GAME) - dummyObserver.updateData.get.getAction() should be(currentAction) + dummyObserver.updateData.get.getAction should not be(UpdateAction.SHOW_GAME) + dummyObserver.updateData.get.getAction should be(currentAction) } "throw when adding or removing too many players" in { controller.reset() @@ -182,7 +182,7 @@ class ControllerSpec extends WordSpec with Matchers { tempController.onStartGame() Thread.sleep(3000) - tempObserver.updateData.get.getAction() should be(UpdateAction.SHOW_RESULT) + tempObserver.updateData.get.getAction should be(UpdateAction.SHOW_RESULT) tempGame.players.foreach(p => p.wrongAnswers.length should be(2)) } } From f1930884c9c847f1c5fd025afae534f25cd4006f Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Wed, 21 Mar 2018 14:37:28 +0100 Subject: [PATCH 04/21] Refactored some tests --- .../learn_duel/controller/ControllerSpec.scala | 16 ++++++++-------- .../de/htwg/se/learn_duel/model/GameSpec.scala | 2 -- .../model/command/PlayerAddCommandSpec.scala | 2 -- .../model/command/PlayerRemoveCommandSpec.scala | 2 -- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/test/scala/de/htwg/se/learn_duel/controller/ControllerSpec.scala b/src/test/scala/de/htwg/se/learn_duel/controller/ControllerSpec.scala index fc613e9..4424443 100644 --- a/src/test/scala/de/htwg/se/learn_duel/controller/ControllerSpec.scala +++ b/src/test/scala/de/htwg/se/learn_duel/controller/ControllerSpec.scala @@ -85,8 +85,8 @@ class ControllerSpec extends WordSpec with Matchers { "indicate to observers that they should start the game" in { controller.onStartGame() dummyObserver.updateData.get.getAction should be(UpdateAction.SHOW_GAME) - gameState.currentQuestion should not be(None) - gameState.currentQuestionTime should not be(None) + gameState.currentQuestion should not be None + gameState.currentQuestionTime should not be None } "indicate to observers that they should close" in { controller.onClose() @@ -95,9 +95,9 @@ class ControllerSpec extends WordSpec with Matchers { "be able to reset game state" in { controller.onPlayerActionRedo() gameState.playerCount() should be(2) - gameState.currentQuestion should not be(None) - gameState.currentQuestionTime should not be(None) - dummyObserver.updateData.get.getAction should not be(UpdateAction.BEGIN) + gameState.currentQuestion should not be None + gameState.currentQuestionTime should not be None + dummyObserver.updateData.get.getAction should not be UpdateAction.BEGIN controller.reset() gameState.playerCount() should be(1) @@ -126,16 +126,16 @@ class ControllerSpec extends WordSpec with Matchers { controller.onStartGame() val time = gameState.currentQuestionTime Thread.sleep(2000) - gameState.currentQuestionTime should not be(time) + gameState.currentQuestionTime should not be time } "remove observers correctly" in { controller.reset() controller.onHelp() val currentAction = dummyObserver.updateData.get.getAction - currentAction should not be(UpdateAction.SHOW_GAME) + currentAction should not be UpdateAction.SHOW_GAME controller.removeObserver(dummyObserver) controller.onStartGame() - dummyObserver.updateData.get.getAction should not be(UpdateAction.SHOW_GAME) + dummyObserver.updateData.get.getAction should not be UpdateAction.SHOW_GAME dummyObserver.updateData.get.getAction should be(currentAction) } "throw when adding or removing too many players" in { diff --git a/src/test/scala/de/htwg/se/learn_duel/model/GameSpec.scala b/src/test/scala/de/htwg/se/learn_duel/model/GameSpec.scala index 05e253c..03f6770 100644 --- a/src/test/scala/de/htwg/se/learn_duel/model/GameSpec.scala +++ b/src/test/scala/de/htwg/se/learn_duel/model/GameSpec.scala @@ -1,7 +1,5 @@ package de.htwg.se.learn_duel.model -import java.security.InvalidParameterException - import de.htwg.se.learn_duel.model.impl.{Answer => AnswerImpl, Game => GameImpl, Player => PlayerImpl, Question => QuestionImpl} import org.junit.runner.RunWith import org.scalatest._ diff --git a/src/test/scala/de/htwg/se/learn_duel/model/command/PlayerAddCommandSpec.scala b/src/test/scala/de/htwg/se/learn_duel/model/command/PlayerAddCommandSpec.scala index bc407f3..a992299 100644 --- a/src/test/scala/de/htwg/se/learn_duel/model/command/PlayerAddCommandSpec.scala +++ b/src/test/scala/de/htwg/se/learn_duel/model/command/PlayerAddCommandSpec.scala @@ -1,7 +1,5 @@ package de.htwg.se.learn_duel.model.command -import java.security.InvalidParameterException - import de.htwg.se.learn_duel.model.command.impl.PlayerAddCommand import org.junit.runner.RunWith import org.scalatest._ diff --git a/src/test/scala/de/htwg/se/learn_duel/model/command/PlayerRemoveCommandSpec.scala b/src/test/scala/de/htwg/se/learn_duel/model/command/PlayerRemoveCommandSpec.scala index f73e135..e061632 100644 --- a/src/test/scala/de/htwg/se/learn_duel/model/command/PlayerRemoveCommandSpec.scala +++ b/src/test/scala/de/htwg/se/learn_duel/model/command/PlayerRemoveCommandSpec.scala @@ -1,7 +1,5 @@ package de.htwg.se.learn_duel.model.command -import java.security.InvalidParameterException - import de.htwg.se.learn_duel.model.command.impl.PlayerRemoveCommand import org.junit.runner.RunWith import org.scalatest._ From 15c3587ffa6097a6d2f99f47d40c1e31ef0d6c3a Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Thu, 22 Mar 2018 10:31:16 +0100 Subject: [PATCH 05/21] Refactored the enhancements mentioned * by @Spurnut and @DonatJR --- .../htwg/se/learn_duel/controller/Controller.scala | 4 ++-- .../se/learn_duel/controller/impl/Controller.scala | 14 +++++++------- .../learn_duel/model/command/CommandInvoker.scala | 2 +- .../de/htwg/se/learn_duel/view/impl/TUI.scala | 4 ++-- .../de/htwg/se/learn_duel/view/impl/gui/GUI.scala | 2 +- .../se/learn_duel/controller/ControllerSpec.scala | 6 +++--- .../model/command/CommandInvokerSpec.scala | 4 ++-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/scala/de/htwg/se/learn_duel/controller/Controller.scala b/src/main/scala/de/htwg/se/learn_duel/controller/Controller.scala index 06a42b6..38a57cd 100644 --- a/src/main/scala/de/htwg/se/learn_duel/controller/Controller.scala +++ b/src/main/scala/de/htwg/se/learn_duel/controller/Controller.scala @@ -5,9 +5,9 @@ import de.htwg.se.learn_duel.controller.impl.{Controller => ControllerImpl} import de.htwg.se.learn_duel.model.{Game, Resettable} trait Controller extends Observable with Resettable { - def nextPlayerName(): Option[String] + def nextPlayerName: Option[String] def getPlayerNames: List[String] - def maxPlayerCount(): Int + def maxPlayerCount: Int def requestUpdate(): Unit def reset(): Unit def onAddPlayer(name: Option[String]): Unit diff --git a/src/main/scala/de/htwg/se/learn_duel/controller/impl/Controller.scala b/src/main/scala/de/htwg/se/learn_duel/controller/impl/Controller.scala index db1db7d..f5cd431 100644 --- a/src/main/scala/de/htwg/se/learn_duel/controller/impl/Controller.scala +++ b/src/main/scala/de/htwg/se/learn_duel/controller/impl/Controller.scala @@ -14,7 +14,7 @@ class Controller @Inject() (gameState: Game) extends ControllerTrait { protected var questionIter: Iterator[Question] = Iterator.empty protected var timer: Option[Timer] = None protected var lastUpdate: UpdateData = new UpdateData(UpdateAction.BEGIN, gameState) - protected val invoker: CommandInvoker = CommandInvoker.create() + protected val invoker: CommandInvoker = CommandInvoker.create override def requestUpdate(): Unit = { notifyObserversAndSaveUpdate(lastUpdate) @@ -45,14 +45,14 @@ class Controller @Inject() (gameState: Game) extends ControllerTrait { invoker.redo() } - override def nextPlayerName(): Option[String] = { + override def nextPlayerName: Option[String] = { gameState.playerCount() match { case c if c < maxPlayerCount => Some(Player.baseName + (gameState.playerCount + 1).toString) case _ => None } } - override def maxPlayerCount(): Int = Game.maxPlayerCount + override def maxPlayerCount: Int = Game.maxPlayerCount override def onHelp(): Unit = { notifyObserversAndSaveUpdate(new UpdateData(UpdateAction.SHOW_HELP, gameState)) @@ -78,7 +78,7 @@ class Controller @Inject() (gameState: Game) extends ControllerTrait { val correctAnswer = currentQuestion.correctAnswer var (player: Option[Player], userInput: Int) = input match { case x if 0 until 5 contains x => (Some(gameState.players.head), input) - case x if (6 until 10 contains x) && gameState.players.lengthCompare(1) > 0=> (Some(gameState.players(1)), input-5) // FIXME magic number -> local mp will be removed anyway + case x if (6 until 10 contains x) && gameState.players.lengthCompare(1) > 0 => (Some(gameState.players(1)), input-5) // FIXME magic number -> local mp will be removed anyway case _ => (None, input) } @@ -107,7 +107,7 @@ class Controller @Inject() (gameState: Game) extends ControllerTrait { protected def addPlayer(name: Option[String]): String = { var playerName = name match { case Some(n) => n - case None => nextPlayerName().getOrElse("") // will not be used if None + case None => nextPlayerName.getOrElse("") // will not be used if None } if (gameState.playerCount == Game.maxPlayerCount) { @@ -171,8 +171,8 @@ class Controller @Inject() (gameState: Game) extends ControllerTrait { newTime match { case Some(time) => - gameState.currentQuestionTime = newTime - notifyObserversAndSaveUpdate(new UpdateData(UpdateAction.TIMER_UPDATE, gameState)) + gameState.currentQuestionTime = newTime + notifyObserversAndSaveUpdate(new UpdateData(UpdateAction.TIMER_UPDATE, gameState)) case _ => } } diff --git a/src/main/scala/de/htwg/se/learn_duel/model/command/CommandInvoker.scala b/src/main/scala/de/htwg/se/learn_duel/model/command/CommandInvoker.scala index 75c004f..89bc826 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/command/CommandInvoker.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/command/CommandInvoker.scala @@ -11,7 +11,7 @@ trait CommandInvoker { } object CommandInvoker { - def create(): CommandInvokerImpl = { + def create: CommandInvokerImpl = { CommandInvokerImpl() } } diff --git a/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala b/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala index e8a6255..599816b 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala @@ -80,7 +80,7 @@ class TUI (controller: Controller) extends UI with Observer with LazyLogging { updateParam.getState.currentQuestionTime.get ) inMenu = false - inGame = true; + inGame = true case UpdateAction.TIMER_UPDATE => displayGamePretty( updateParam.getState.currentQuestion.get, @@ -124,7 +124,7 @@ class TUI (controller: Controller) extends UI with Observer with LazyLogging { case "U" => controller.onPlayerActionRedo() case _ => logger.info("Unknown command") - displayMenu() + displayMenu } } catch { case e: ControllerException => logger.error(e.getMessage) diff --git a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GUI.scala b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GUI.scala index f00e357..00ac1f5 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GUI.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GUI.scala @@ -77,7 +77,7 @@ class GUI (controller: Controller, latch: CountDownLatch) extends JFXApp with UI this.stage = new MenuStage( _ => controller.onStartGame(), _ => controller.onHelp(), - (controller.getPlayerNames, controller.nextPlayerName()), + (controller.getPlayerNames, controller.nextPlayerName), (name) => controller.onAddPlayer(Some(name)), controller.onRemovePlayer ) diff --git a/src/test/scala/de/htwg/se/learn_duel/controller/ControllerSpec.scala b/src/test/scala/de/htwg/se/learn_duel/controller/ControllerSpec.scala index 4424443..25aba7a 100644 --- a/src/test/scala/de/htwg/se/learn_duel/controller/ControllerSpec.scala +++ b/src/test/scala/de/htwg/se/learn_duel/controller/ControllerSpec.scala @@ -43,7 +43,7 @@ class ControllerSpec extends WordSpec with Matchers { controller.getPlayerNames should be(gameState.players.map(p => p.name)) } "have a max player count" in { - controller.maxPlayerCount() should be (Game.maxPlayerCount) + controller.maxPlayerCount should be (Game.maxPlayerCount) } "be able to add a player" in { gameState.playerCount() should be(1) @@ -69,13 +69,13 @@ class ControllerSpec extends WordSpec with Matchers { } "not supply a next player name if max player count is reached" in { gameState.playerCount() should be(2) - val nextName = controller.nextPlayerName() + val nextName = controller.nextPlayerName nextName should be(None) } "supply a next player name if max player count was not reached" in { controller.onPlayerActionUndo() gameState.playerCount() should be(1) - val nextName = controller.nextPlayerName() + val nextName = controller.nextPlayerName nextName should be(Some("Player2")) } "indicate to observers that they should show the help" in { diff --git a/src/test/scala/de/htwg/se/learn_duel/model/command/CommandInvokerSpec.scala b/src/test/scala/de/htwg/se/learn_duel/model/command/CommandInvokerSpec.scala index 013d76f..1bb05ed 100644 --- a/src/test/scala/de/htwg/se/learn_duel/model/command/CommandInvokerSpec.scala +++ b/src/test/scala/de/htwg/se/learn_duel/model/command/CommandInvokerSpec.scala @@ -1,6 +1,6 @@ package de.htwg.se.learn_duel.model.command -import de.htwg.se.learn_duel.model.command.impl.{ CommandInvoker => CommandInvokerImpl, PlayerAddCommand } +import de.htwg.se.learn_duel.model.command.impl.{PlayerAddCommand, CommandInvoker => CommandInvokerImpl} import org.junit.runner.RunWith import org.scalatest._ import org.scalatest.junit.JUnitRunner @@ -73,7 +73,7 @@ class CommandInvokerSpec extends WordSpec with Matchers { } "constructed with factory method" should { - val commandInvoker = CommandInvoker.create() + val commandInvoker = CommandInvoker.create "have no saved commands" in { commandInvoker.undoCommands.length should be(0) commandInvoker.redoCommands.length should be(0) From 58c5dc8d54815dc5dca3918086c0c84acf6695fe Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Mon, 26 Mar 2018 11:57:31 +0200 Subject: [PATCH 06/21] Used scalafmt to reformat complete src base --- build.sbt | 8 +- .../de/htwg/se/learn_duel/GuiceModule.scala | 16 +- .../de/htwg/se/learn_duel/LearnDuel.scala | 16 +- .../de/htwg/se/learn_duel/Observable.scala | 10 +- .../de/htwg/se/learn_duel/Observer.scala | 12 +- .../se/learn_duel/controller/Controller.scala | 32 +- .../controller/ControllerException.scala | 4 +- .../controller/impl/Controller.scala | 412 +++++++++--------- .../ControllerProcedureFailed.scala | 6 +- .../NotEnoughPlayersException.scala | 6 +- .../exceptions/PlayerExistsException.scala | 6 +- .../PlayerNotExistingException.scala | 6 +- .../exceptions/TooManyPlayersException.scala | 6 +- .../de/htwg/se/learn_duel/model/Answer.scala | 21 +- .../de/htwg/se/learn_duel/model/Game.scala | 56 +-- .../de/htwg/se/learn_duel/model/Player.scala | 46 +- .../htwg/se/learn_duel/model/Question.scala | 48 +- .../htwg/se/learn_duel/model/Resettable.scala | 2 +- .../se/learn_duel/model/command/Command.scala | 6 +- .../model/command/CommandInvoker.scala | 20 +- .../model/command/impl/CommandInvoker.scala | 41 +- .../model/command/impl/PlayerAddCommand.scala | 20 +- .../command/impl/PlayerRemoveCommand.scala | 18 +- .../htwg/se/learn_duel/model/impl/Game.scala | 82 ++-- .../se/learn_duel/model/impl/Player.scala | 18 +- .../se/learn_duel/model/impl/Question.scala | 17 +- .../scala/de/htwg/se/learn_duel/view/UI.scala | 32 +- .../de/htwg/se/learn_duel/view/impl/TUI.scala | 290 ++++++------ .../se/learn_duel/view/impl/gui/GUI.scala | 152 +++---- .../learn_duel/view/impl/gui/GameStage.scala | 187 ++++---- .../learn_duel/view/impl/gui/InfoPopup.scala | 8 +- .../learn_duel/view/impl/gui/MenuStage.scala | 175 ++++---- .../view/impl/gui/ResultStage.scala | 205 ++++----- .../controller/ControllerSpec.scala | 369 ++++++++-------- .../htwg/se/learn_duel/model/AnswerSpec.scala | 35 +- .../htwg/se/learn_duel/model/GameSpec.scala | 143 +++--- .../htwg/se/learn_duel/model/PlayerSpec.scala | 98 ++--- .../se/learn_duel/model/QuestionSpec.scala | 69 +-- .../model/command/CommandInvokerSpec.scala | 126 +++--- .../model/command/PlayerAddCommandSpec.scala | 51 ++- .../command/PlayerRemoveCommandSpec.scala | 47 +- 41 files changed, 1503 insertions(+), 1419 deletions(-) diff --git a/build.sbt b/build.sbt index 44f7367..ee0a4a0 100644 --- a/build.sbt +++ b/build.sbt @@ -1,7 +1,7 @@ -name := "LearnDuel" -organization := "de.htwg.se" -version := "0.0.1" -scalaVersion := "2.12.4" +name := "LearnDuel" +organization := "de.htwg.se" +version := "0.0.1" +scalaVersion := "2.12.4" libraryDependencies += "org.scalactic" %% "scalactic" % "3.0.4" libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.4" % "test" diff --git a/src/main/scala/de/htwg/se/learn_duel/GuiceModule.scala b/src/main/scala/de/htwg/se/learn_duel/GuiceModule.scala index 5bc5aa6..1f76ff8 100644 --- a/src/main/scala/de/htwg/se/learn_duel/GuiceModule.scala +++ b/src/main/scala/de/htwg/se/learn_duel/GuiceModule.scala @@ -10,12 +10,14 @@ import play.api.libs.json.{JsValue, Json} import scala.io.Source class GuiceModule extends AbstractModule { - val jsonString: String = Source.fromResource("questions.json").getLines.mkString("\n") - val json: JsValue = Json.parse(jsonString) - val questions: List[Question] = Json.fromJson[List[Question]](json).getOrElse(List()) + val jsonString: String = + Source.fromResource("questions.json").getLines.mkString("\n") + val json: JsValue = Json.parse(jsonString) + val questions: List[Question] = + Json.fromJson[List[Question]](json).getOrElse(List()) - override def configure(): Unit = { - bind(classOf[Game]).toInstance(GameImpl(questions = questions)) - bind(classOf[Controller]).to(classOf[ControllerImpl]) - } + override def configure(): Unit = { + bind(classOf[Game]).toInstance(GameImpl(questions = questions)) + bind(classOf[Controller]).to(classOf[ControllerImpl]) + } } diff --git a/src/main/scala/de/htwg/se/learn_duel/LearnDuel.scala b/src/main/scala/de/htwg/se/learn_duel/LearnDuel.scala index 8452daf..393815b 100644 --- a/src/main/scala/de/htwg/se/learn_duel/LearnDuel.scala +++ b/src/main/scala/de/htwg/se/learn_duel/LearnDuel.scala @@ -7,14 +7,14 @@ import com.google.inject.Guice import de.htwg.se.learn_duel.view.{GUI, TUI} object LearnDuel { - def main(args: Array[String]): Unit = { - val injector = Guice.createInjector(new GuiceModule()) - val controller = injector.getInstance(classOf[Controller]) + def main(args: Array[String]): Unit = { + val injector = Guice.createInjector(new GuiceModule()) + val controller = injector.getInstance(classOf[Controller]) - val tui = TUI.create(controller) - GUI.create(controller) + val tui = TUI.create(controller) + GUI.create(controller) - controller.requestUpdate() - tui.processInput(new BufferedReader(Console.in)) - } + controller.requestUpdate() + tui.processInput(new BufferedReader(Console.in)) + } } diff --git a/src/main/scala/de/htwg/se/learn_duel/Observable.scala b/src/main/scala/de/htwg/se/learn_duel/Observable.scala index 4f42607..633e9f5 100644 --- a/src/main/scala/de/htwg/se/learn_duel/Observable.scala +++ b/src/main/scala/de/htwg/se/learn_duel/Observable.scala @@ -1,8 +1,10 @@ package de.htwg.se.learn_duel trait Observable { - private var observers: List[Observer] = List() - def addObserver(observer: Observer): Unit = observers = observer :: observers - def removeObserver(observer: Observer): Unit = observers = observers.filter(_ != observer) - protected def notifyObservers(updateParam: UpdateData): Unit = observers.foreach(o => o.update(updateParam)) + private var observers: List[Observer] = List() + def addObserver(observer: Observer): Unit = observers = observer :: observers + def removeObserver(observer: Observer): Unit = + observers = observers.filter(_ != observer) + protected def notifyObservers(updateParam: UpdateData): Unit = + observers.foreach(o => o.update(updateParam)) } diff --git a/src/main/scala/de/htwg/se/learn_duel/Observer.scala b/src/main/scala/de/htwg/se/learn_duel/Observer.scala index f90fe68..70dd911 100644 --- a/src/main/scala/de/htwg/se/learn_duel/Observer.scala +++ b/src/main/scala/de/htwg/se/learn_duel/Observer.scala @@ -1,18 +1,18 @@ package de.htwg.se.learn_duel object UpdateAction extends Enumeration { - type UpdateAction = Value - val BEGIN, CLOSE_APPLICATION, PLAYER_UPDATE, SHOW_HELP, - SHOW_GAME, SHOW_RESULT, TIMER_UPDATE = Value + type UpdateAction = Value + val BEGIN, CLOSE_APPLICATION, PLAYER_UPDATE, SHOW_HELP, SHOW_GAME, + SHOW_RESULT, TIMER_UPDATE = Value } import UpdateAction._ import de.htwg.se.learn_duel.model.Game class UpdateData(updateAction: UpdateAction, gameState: Game) { - def getAction: UpdateAction = updateAction - def getState: Game = gameState + def getAction: UpdateAction = updateAction + def getState: Game = gameState } trait Observer { - def update(updateData: UpdateData): Unit + def update(updateData: UpdateData): Unit } diff --git a/src/main/scala/de/htwg/se/learn_duel/controller/Controller.scala b/src/main/scala/de/htwg/se/learn_duel/controller/Controller.scala index 38a57cd..a7dbffe 100644 --- a/src/main/scala/de/htwg/se/learn_duel/controller/Controller.scala +++ b/src/main/scala/de/htwg/se/learn_duel/controller/Controller.scala @@ -5,23 +5,23 @@ import de.htwg.se.learn_duel.controller.impl.{Controller => ControllerImpl} import de.htwg.se.learn_duel.model.{Game, Resettable} trait Controller extends Observable with Resettable { - def nextPlayerName: Option[String] - def getPlayerNames: List[String] - def maxPlayerCount: Int - def requestUpdate(): Unit - def reset(): Unit - def onAddPlayer(name: Option[String]): Unit - def onRemovePlayer(name: String): Unit - def onPlayerActionUndo(): Unit - def onPlayerActionRedo(): Unit - def onHelp(): Unit - def onStartGame(): Unit - def onClose(): Unit - def onAnswerChosen(input: Int) + def nextPlayerName: Option[String] + def getPlayerNames: List[String] + def maxPlayerCount: Int + def requestUpdate(): Unit + def reset(): Unit + def onAddPlayer(name: Option[String]): Unit + def onRemovePlayer(name: String): Unit + def onPlayerActionUndo(): Unit + def onPlayerActionRedo(): Unit + def onHelp(): Unit + def onStartGame(): Unit + def onClose(): Unit + def onAnswerChosen(input: Int) } object Controller { - def create(gameState: Game): ControllerImpl = { - new ControllerImpl(gameState) - } + def create(gameState: Game): ControllerImpl = { + new ControllerImpl(gameState) + } } diff --git a/src/main/scala/de/htwg/se/learn_duel/controller/ControllerException.scala b/src/main/scala/de/htwg/se/learn_duel/controller/ControllerException.scala index 5426008..78ea525 100644 --- a/src/main/scala/de/htwg/se/learn_duel/controller/ControllerException.scala +++ b/src/main/scala/de/htwg/se/learn_duel/controller/ControllerException.scala @@ -1,6 +1,6 @@ package de.htwg.se.learn_duel.controller trait ControllerException { - self: Throwable => - val message: String + self: Throwable => + val message: String } diff --git a/src/main/scala/de/htwg/se/learn_duel/controller/impl/Controller.scala b/src/main/scala/de/htwg/se/learn_duel/controller/impl/Controller.scala index f5cd431..d2caa79 100644 --- a/src/main/scala/de/htwg/se/learn_duel/controller/impl/Controller.scala +++ b/src/main/scala/de/htwg/se/learn_duel/controller/impl/Controller.scala @@ -6,216 +6,240 @@ import com.google.inject.Inject import de.htwg.se.learn_duel.controller.impl.exceptions._ import de.htwg.se.learn_duel.controller.{Controller => ControllerTrait} import de.htwg.se.learn_duel.model.command.CommandInvoker -import de.htwg.se.learn_duel.model.command.impl.{PlayerAddCommand, PlayerRemoveCommand} +import de.htwg.se.learn_duel.model.command.impl.{ + PlayerAddCommand, + PlayerRemoveCommand +} import de.htwg.se.learn_duel.model.{Game, Player, Question} import de.htwg.se.learn_duel.{UpdateAction, UpdateData} -class Controller @Inject() (gameState: Game) extends ControllerTrait { - protected var questionIter: Iterator[Question] = Iterator.empty - protected var timer: Option[Timer] = None - protected var lastUpdate: UpdateData = new UpdateData(UpdateAction.BEGIN, gameState) - protected val invoker: CommandInvoker = CommandInvoker.create +class Controller @Inject()(gameState: Game) extends ControllerTrait { + protected var questionIter: Iterator[Question] = Iterator.empty + protected var timer: Option[Timer] = None + protected var lastUpdate: UpdateData = + new UpdateData(UpdateAction.BEGIN, gameState) + protected val invoker: CommandInvoker = CommandInvoker.create + + override def requestUpdate(): Unit = { + notifyObserversAndSaveUpdate(lastUpdate) + } + + override def reset(): Unit = { + gameState.reset() + notifyObserversAndSaveUpdate(new UpdateData(UpdateAction.BEGIN, gameState)) + } - override def requestUpdate(): Unit = { - notifyObserversAndSaveUpdate(lastUpdate) - } + override def getPlayerNames: List[String] = { + gameState.players.map(p => p.name) + } + + override def onAddPlayer(name: Option[String]): Unit = { + invoker.execute(PlayerAddCommand(name, addPlayer, removePlayer)) + } - override def reset(): Unit = { - gameState.reset() - notifyObserversAndSaveUpdate(new UpdateData(UpdateAction.BEGIN, gameState)) - } + override def onRemovePlayer(name: String): Unit = { + invoker.execute(PlayerRemoveCommand(name, removePlayer, addPlayer)) + } - override def getPlayerNames: List[String] = { - gameState.players.map(p => p.name) - } + override def onPlayerActionUndo(): Unit = { + invoker.undo() + } - override def onAddPlayer(name: Option[String]) : Unit = { - invoker.execute(PlayerAddCommand(name, addPlayer, removePlayer)) - } + override def onPlayerActionRedo(): Unit = { + invoker.redo() + } - override def onRemovePlayer(name: String): Unit = { - invoker.execute(PlayerRemoveCommand(name, removePlayer, addPlayer)) + override def nextPlayerName: Option[String] = { + gameState.playerCount() match { + case c if c < maxPlayerCount => + Some(Player.baseName + (gameState.playerCount + 1).toString) + case _ => None } + } - override def onPlayerActionUndo(): Unit = { - invoker.undo() - } + override def maxPlayerCount: Int = Game.maxPlayerCount - override def onPlayerActionRedo(): Unit = { - invoker.redo() - } + override def onHelp(): Unit = { + notifyObserversAndSaveUpdate( + new UpdateData(UpdateAction.SHOW_HELP, gameState)) + } - override def nextPlayerName: Option[String] = { - gameState.playerCount() match { - case c if c < maxPlayerCount => Some(Player.baseName + (gameState.playerCount + 1).toString) - case _ => None - } - } - - override def maxPlayerCount: Int = Game.maxPlayerCount + override def onStartGame(): Unit = { + resetQuestionIterator() - override def onHelp(): Unit = { - notifyObserversAndSaveUpdate(new UpdateData(UpdateAction.SHOW_HELP, gameState)) + if (questionIter.isEmpty) { + throw new IllegalStateException("Can't start game without questions") } - override def onStartGame(): Unit = { - resetQuestionIterator() - - if (questionIter.isEmpty) { - throw new IllegalStateException("Can't start game without questions") - } + nextQuestion() + } - nextQuestion() - } - - override def onClose(): Unit = { - notifyObserversAndSaveUpdate(new UpdateData(UpdateAction.CLOSE_APPLICATION, gameState)) - } + override def onClose(): Unit = { + notifyObserversAndSaveUpdate( + new UpdateData(UpdateAction.CLOSE_APPLICATION, gameState)) + } - // scalastyle:off - override def onAnswerChosen(input: Int): Unit = { - val currentQuestion = gameState.currentQuestion.get - val correctAnswer = currentQuestion.correctAnswer - var (player: Option[Player], userInput: Int) = input match { - case x if 0 until 5 contains x => (Some(gameState.players.head), input) - case x if (6 until 10 contains x) && gameState.players.lengthCompare(1) > 0 => (Some(gameState.players(1)), input-5) // FIXME magic number -> local mp will be removed anyway - case _ => (None, input) + // scalastyle:off + override def onAnswerChosen(input: Int): Unit = { + val currentQuestion = gameState.currentQuestion.get + val correctAnswer = currentQuestion.correctAnswer + var (player: Option[Player], userInput: Int) = input match { + case x if 0 until 5 contains x => (Some(gameState.players.head), input) + case x + if (6 until 10 contains x) && gameState.players.lengthCompare(1) > 0 => + (Some(gameState.players(1)), input - 5) // FIXME magic number -> local mp will be removed anyway + case _ => (None, input) + } + + if (player.isDefined && !playerAnsweredQuestion(player.get, + currentQuestion.id)) { + if (userInput == correctAnswer) { + player.get.points += currentQuestion.points + player.get.correctAnswers = player.get.correctAnswers :+ currentQuestion + } else { + player.get.wrongAnswers = player.get.wrongAnswers :+ currentQuestion + } + } + + // check if question was answered by all players + val allAnswered = gameState.players.forall(p => { + (p.correctAnswers ::: p.wrongAnswers).exists(q => { + q.id == currentQuestion.id + }) + }) + + if (allAnswered) { + nextQuestion() + } + } + // scalastyle:on + + protected def addPlayer(name: Option[String]): String = { + var playerName = name match { + case Some(n) => n + case None => + nextPlayerName.getOrElse("") // will not be used if None + } + + if (gameState.playerCount == Game.maxPlayerCount) { + throw TooManyPlayersException( + "There are too many players to add another one") + } else if (gameState.players.exists(p => p.name == playerName)) { + throw PlayerExistsException(s"'$playerName' already exists") + } + + try { + gameState.addPlayer(Player.create(playerName)) + } catch { + case e: Throwable => + throw ControllerProcedureFailed("Adding player failed: " + e.getMessage) + } + + notifyObserversAndSaveUpdate( + new UpdateData(UpdateAction.PLAYER_UPDATE, gameState)) + + playerName + } + + protected def removePlayer(name: String): Unit = { + if (gameState.playerCount == 1) { + throw NotEnoughPlayersException( + "There are not enough players to remove one") + } + + gameState.players.find(p => p.name == name) match { + case Some(p) => + gameState.removePlayer(p) + notifyObserversAndSaveUpdate( + new UpdateData(UpdateAction.PLAYER_UPDATE, gameState)) + case None => + throw PlayerNotExistingException(s"Player '$name' does not exist") + } + } + + protected def resetQuestionIterator(): Unit = { + questionIter = gameState.questions.iterator + } + + protected def stopTimer(): Unit = { + if (timer.isDefined) { + timer.get.cancel() + } + } + + protected def setUpTimer(): Unit = { + val localTimer = new Timer(true) + timer = Some(localTimer) + + localTimer.scheduleAtFixedRate( + new TimerTask { + override def run(): Unit = { + val newTime = gameState.currentQuestionTime match { + case Some(time) => + val newTime = time - 1 + if (newTime == 0) { + nextQuestion() + None + } else { + Some(newTime) + } + case None => None + } + + newTime match { + case Some(time) => + gameState.currentQuestionTime = newTime + notifyObserversAndSaveUpdate( + new UpdateData(UpdateAction.TIMER_UPDATE, gameState)) + case _ => + } } - - if (player.isDefined && !playerAnsweredQuestion(player.get, currentQuestion.id)) { - if (userInput == correctAnswer) { - player.get.points += currentQuestion.points - player.get.correctAnswers = player.get.correctAnswers :+ currentQuestion - } else { - player.get.wrongAnswers = player.get.wrongAnswers :+ currentQuestion - } - } - - // check if question was answered by all players - val allAnswered = gameState.players.forall(p => { - (p.correctAnswers ::: p.wrongAnswers).exists(q => { - q.id == currentQuestion.id - }) - }) - - if (allAnswered) { - nextQuestion() - } - } - // scalastyle:on - - protected def addPlayer(name: Option[String]): String = { - var playerName = name match { - case Some(n) => n - case None => nextPlayerName.getOrElse("") // will not be used if None - } - - if (gameState.playerCount == Game.maxPlayerCount) { - throw TooManyPlayersException("There are too many players to add another one") - } - else if (gameState.players.exists(p => p.name == playerName)) { - throw PlayerExistsException(s"'$playerName' already exists") - } - - try { - gameState.addPlayer(Player.create(playerName)) - } catch { - case e: Throwable => throw ControllerProcedureFailed("Adding player failed: " + e.getMessage) - } - - notifyObserversAndSaveUpdate(new UpdateData(UpdateAction.PLAYER_UPDATE, gameState)) - - playerName - } - - protected def removePlayer(name: String): Unit = { - if (gameState.playerCount == 1) { - throw NotEnoughPlayersException("There are not enough players to remove one") - } - - gameState.players.find(p => p.name == name) match { - case Some(p) => - gameState.removePlayer(p) - notifyObserversAndSaveUpdate(new UpdateData(UpdateAction.PLAYER_UPDATE, gameState)) - case None => throw PlayerNotExistingException(s"Player '$name' does not exist") - } - } - - protected def resetQuestionIterator(): Unit = { - questionIter = gameState.questions.iterator - } - - protected def stopTimer(): Unit = { - if (timer.isDefined) { - timer.get.cancel() - } - } - - protected def setUpTimer(): Unit = { - val localTimer = new Timer(true) - timer = Some(localTimer) - - localTimer.scheduleAtFixedRate(new TimerTask { - override def run(): Unit = { - val newTime = gameState.currentQuestionTime match { - case Some(time) => - val newTime = time - 1 - if (newTime == 0) { - nextQuestion() - None - } else { - Some(newTime) - } - case None => None - } - - newTime match { - case Some(time) => - gameState.currentQuestionTime = newTime - notifyObserversAndSaveUpdate(new UpdateData(UpdateAction.TIMER_UPDATE, gameState)) - case _ => - } - } - }, 1000, 1000) - } - - protected def nextQuestion(): Unit = { - stopTimer() - - // implicitely add not given answer as wrong answer - if (gameState.currentQuestion.isDefined) { - val currentQuestion = gameState.currentQuestion.get - val noAnswerPlayers: List[Player] = gameState.players.filter(p => { - !playerAnsweredQuestion(p, currentQuestion.id) - }) - - noAnswerPlayers.foreach(p => p.wrongAnswers = p.wrongAnswers :+ currentQuestion) - } - - if (questionIter.hasNext) { - showGame() - } else { - notifyObserversAndSaveUpdate(new UpdateData(UpdateAction.SHOW_RESULT, gameState)) - } - } - - protected def showGame(): Unit = { - val nextQuestion = questionIter.next() - gameState.currentQuestion = Some(nextQuestion) - gameState.currentQuestionTime = Some(nextQuestion.time) - setUpTimer() - - notifyObserversAndSaveUpdate(new UpdateData(UpdateAction.SHOW_GAME, gameState)) - } - - protected def playerAnsweredQuestion(p: Player, questionId: Int): Boolean = { - (p.correctAnswers ::: p.wrongAnswers).exists(q => { - q.id == questionId - }) - } - - protected def notifyObserversAndSaveUpdate(data: UpdateData): Unit = { - lastUpdate = data - notifyObservers(data) - } + }, + 1000, + 1000 + ) + } + + protected def nextQuestion(): Unit = { + stopTimer() + + // implicitely add not given answer as wrong answer + if (gameState.currentQuestion.isDefined) { + val currentQuestion = gameState.currentQuestion.get + val noAnswerPlayers: List[Player] = gameState.players.filter(p => { + !playerAnsweredQuestion(p, currentQuestion.id) + }) + + noAnswerPlayers.foreach(p => + p.wrongAnswers = p.wrongAnswers :+ currentQuestion) + } + + if (questionIter.hasNext) { + showGame() + } else { + notifyObserversAndSaveUpdate( + new UpdateData(UpdateAction.SHOW_RESULT, gameState)) + } + } + + protected def showGame(): Unit = { + val nextQuestion = questionIter.next() + gameState.currentQuestion = Some(nextQuestion) + gameState.currentQuestionTime = Some(nextQuestion.time) + setUpTimer() + + notifyObserversAndSaveUpdate( + new UpdateData(UpdateAction.SHOW_GAME, gameState)) + } + + protected def playerAnsweredQuestion(p: Player, questionId: Int): Boolean = { + (p.correctAnswers ::: p.wrongAnswers).exists(q => { + q.id == questionId + }) + } + + protected def notifyObserversAndSaveUpdate(data: UpdateData): Unit = { + lastUpdate = data + notifyObservers(data) + } } diff --git a/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/ControllerProcedureFailed.scala b/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/ControllerProcedureFailed.scala index 79a0203..0e9367b 100644 --- a/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/ControllerProcedureFailed.scala +++ b/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/ControllerProcedureFailed.scala @@ -2,6 +2,6 @@ package de.htwg.se.learn_duel.controller.impl.exceptions import de.htwg.se.learn_duel.controller.ControllerException -case class ControllerProcedureFailed(message: String) extends Exception(message) with ControllerException { - -} +case class ControllerProcedureFailed(message: String) + extends Exception(message) + with ControllerException {} diff --git a/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/NotEnoughPlayersException.scala b/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/NotEnoughPlayersException.scala index ef19347..edcf238 100644 --- a/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/NotEnoughPlayersException.scala +++ b/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/NotEnoughPlayersException.scala @@ -2,6 +2,6 @@ package de.htwg.se.learn_duel.controller.impl.exceptions import de.htwg.se.learn_duel.controller.ControllerException -case class NotEnoughPlayersException(message: String) extends Exception(message) with ControllerException { - -} +case class NotEnoughPlayersException(message: String) + extends Exception(message) + with ControllerException {} diff --git a/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/PlayerExistsException.scala b/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/PlayerExistsException.scala index 26d5fdb..6ca4da6 100644 --- a/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/PlayerExistsException.scala +++ b/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/PlayerExistsException.scala @@ -2,6 +2,6 @@ package de.htwg.se.learn_duel.controller.impl.exceptions import de.htwg.se.learn_duel.controller.ControllerException -case class PlayerExistsException(message: String) extends Exception(message) with ControllerException { - -} +case class PlayerExistsException(message: String) + extends Exception(message) + with ControllerException {} diff --git a/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/PlayerNotExistingException.scala b/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/PlayerNotExistingException.scala index 7fd67fa..1c18e48 100644 --- a/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/PlayerNotExistingException.scala +++ b/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/PlayerNotExistingException.scala @@ -2,6 +2,6 @@ package de.htwg.se.learn_duel.controller.impl.exceptions import de.htwg.se.learn_duel.controller.ControllerException -case class PlayerNotExistingException(message: String) extends Exception(message) with ControllerException { - -} +case class PlayerNotExistingException(message: String) + extends Exception(message) + with ControllerException {} diff --git a/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/TooManyPlayersException.scala b/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/TooManyPlayersException.scala index ec28238..13317aa 100644 --- a/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/TooManyPlayersException.scala +++ b/src/main/scala/de/htwg/se/learn_duel/controller/impl/exceptions/TooManyPlayersException.scala @@ -2,6 +2,6 @@ package de.htwg.se.learn_duel.controller.impl.exceptions import de.htwg.se.learn_duel.controller.ControllerException -case class TooManyPlayersException(message: String) extends Exception(message) with ControllerException { - -} +case class TooManyPlayersException(message: String) + extends Exception(message) + with ControllerException {} diff --git a/src/main/scala/de/htwg/se/learn_duel/model/Answer.scala b/src/main/scala/de/htwg/se/learn_duel/model/Answer.scala index 1a617d0..964a09b 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/Answer.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/Answer.scala @@ -5,18 +5,19 @@ import play.api.libs.json._ import play.api.libs.functional.syntax._ trait Answer { - val id: Int - val text: String + val id: Int + val text: String } object Answer { - implicit val answerWrites: Writes[Answer] = (answer: Answer) => Json.obj( - "id" -> answer.id, - "text" -> answer.text, - ) + implicit val answerWrites: Writes[Answer] = (answer: Answer) => + Json.obj( + "id" -> answer.id, + "text" -> answer.text, + ) - implicit val answerReads: Reads[Answer] = ( - (JsPath \ "id").read[Int] and - (JsPath \ "text").read[String] - )(AnswerImpl.apply _) + implicit val answerReads: Reads[Answer] = ( + (JsPath \ "id").read[Int] and + (JsPath \ "text").read[String] + )(AnswerImpl.apply _) } diff --git a/src/main/scala/de/htwg/se/learn_duel/model/Game.scala b/src/main/scala/de/htwg/se/learn_duel/model/Game.scala index 9d4eee8..9f4102f 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/Game.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/Game.scala @@ -5,39 +5,39 @@ import play.api.libs.json._ import play.api.libs.functional.syntax._ trait Game extends Resettable { - var helpText: List[String] - var players: List[Player] - var questions: List[Question] - var currentQuestion: Option[Question] - var currentQuestionTime: Option[Int] + var helpText: List[String] + var players: List[Player] + var questions: List[Question] + var currentQuestion: Option[Question] + var currentQuestionTime: Option[Int] - def addPlayer(player: Player): Unit - def removePlayer(player: Player): Unit - def playerCount(): Int + def addPlayer(player: Player): Unit + def removePlayer(player: Player): Unit + def playerCount(): Int - def addQuestion(question: Question): Unit - def removeQuestion(question: Question): Unit - def questionCount(): Int + def addQuestion(question: Question): Unit + def removeQuestion(question: Question): Unit + def questionCount(): Int } object Game { - val maxPlayerCount = 2 + val maxPlayerCount = 2 - implicit val gameWrites: Writes[Game] = new Writes[Game] { - def writes(game: Game): JsObject = Json.obj( - "helpText" -> game.helpText, - "players" -> game.players, - "questions" -> game.questions, - "currentQuestion" -> game.currentQuestion, - "currentQuestionTime" -> game.currentQuestionTime - ) - } + implicit val gameWrites: Writes[Game] = new Writes[Game] { + def writes(game: Game): JsObject = Json.obj( + "helpText" -> game.helpText, + "players" -> game.players, + "questions" -> game.questions, + "currentQuestion" -> game.currentQuestion, + "currentQuestionTime" -> game.currentQuestionTime + ) + } - implicit val questionReads: Reads[Game] = ( - (JsPath \ "helpText").read[List[String]] and - (JsPath \ "players").read[List[Player]] and - (JsPath \ "questions").read[List[Question]] and - (JsPath \ "currentQuestion").readNullable[Question] and - (JsPath \ "currentQuestionTime").readNullable[Int] - )(GameImpl.apply _) + implicit val questionReads: Reads[Game] = ( + (JsPath \ "helpText").read[List[String]] and + (JsPath \ "players").read[List[Player]] and + (JsPath \ "questions").read[List[Question]] and + (JsPath \ "currentQuestion").readNullable[Question] and + (JsPath \ "currentQuestionTime").readNullable[Int] + )(GameImpl.apply _) } diff --git a/src/main/scala/de/htwg/se/learn_duel/model/Player.scala b/src/main/scala/de/htwg/se/learn_duel/model/Player.scala index 856c293..8cb3bdd 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/Player.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/Player.scala @@ -5,32 +5,32 @@ import play.api.libs.json._ import play.api.libs.functional.syntax._ trait Player { - val name: String - var points: Int - var correctAnswers: List[Question] - var wrongAnswers: List[Question] - def toString: String + val name: String + var points: Int + var correctAnswers: List[Question] + var wrongAnswers: List[Question] + def toString: String } object Player { - val baseName = "Player" - def create(name: String): PlayerImpl = { - PlayerImpl(name) - } + val baseName = "Player" + def create(name: String): PlayerImpl = { + PlayerImpl(name) + } - implicit val playerWrites: Writes[Player] = new Writes[Player] { - def writes(player: Player): JsObject = Json.obj( - "name" -> player.name, - "points" -> player.points, - "correctAnswers" -> player.correctAnswers, - "wrongAnswers" -> player.wrongAnswers - ) - } + implicit val playerWrites: Writes[Player] = new Writes[Player] { + def writes(player: Player): JsObject = Json.obj( + "name" -> player.name, + "points" -> player.points, + "correctAnswers" -> player.correctAnswers, + "wrongAnswers" -> player.wrongAnswers + ) + } - implicit val playerReads: Reads[Player] = ( - (JsPath \ "name").read[String] and - (JsPath \ "points").read[Int] and - (JsPath \ "correctAnswers").read[List[Question]] and - (JsPath \ "wrongAnswers").read[List[Question]] - )(PlayerImpl.apply _) + implicit val playerReads: Reads[Player] = ( + (JsPath \ "name").read[String] and + (JsPath \ "points").read[Int] and + (JsPath \ "correctAnswers").read[List[Question]] and + (JsPath \ "wrongAnswers").read[List[Question]] + )(PlayerImpl.apply _) } diff --git a/src/main/scala/de/htwg/se/learn_duel/model/Question.scala b/src/main/scala/de/htwg/se/learn_duel/model/Question.scala index 2d5dd35..50acbc2 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/Question.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/Question.scala @@ -5,32 +5,32 @@ import play.api.libs.json._ import play.api.libs.functional.syntax._ trait Question { - val id: Int - val text: String - val points: Int - val answers: List[Answer] - val correctAnswer: Int - val time: Int + val id: Int + val text: String + val points: Int + val answers: List[Answer] + val correctAnswer: Int + val time: Int } object Question { - implicit val questionWrites: Writes[Question] = new Writes[Question] { - def writes(question: Question): JsObject = Json.obj( - "id" -> question.id, - "text" -> question.text, - "points" -> question.points, - "answers" -> question.answers, - "correctAnswer" -> question.correctAnswer, - "time" -> question.time - ) - } + implicit val questionWrites: Writes[Question] = new Writes[Question] { + def writes(question: Question): JsObject = Json.obj( + "id" -> question.id, + "text" -> question.text, + "points" -> question.points, + "answers" -> question.answers, + "correctAnswer" -> question.correctAnswer, + "time" -> question.time + ) + } - implicit val questionReads: Reads[Question] = ( - (JsPath \ "id").read[Int] and - (JsPath \ "text").read[String] and - (JsPath \ "points").read[Int] and - (JsPath \ "answers").read[List[Answer]] and - (JsPath \ "correctAnswer").read[Int] and - (JsPath \ "time").read[Int] - )(QuestionImpl.apply _) + implicit val questionReads: Reads[Question] = ( + (JsPath \ "id").read[Int] and + (JsPath \ "text").read[String] and + (JsPath \ "points").read[Int] and + (JsPath \ "answers").read[List[Answer]] and + (JsPath \ "correctAnswer").read[Int] and + (JsPath \ "time").read[Int] + )(QuestionImpl.apply _) } diff --git a/src/main/scala/de/htwg/se/learn_duel/model/Resettable.scala b/src/main/scala/de/htwg/se/learn_duel/model/Resettable.scala index 9961ffa..9b0d471 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/Resettable.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/Resettable.scala @@ -1,5 +1,5 @@ package de.htwg.se.learn_duel.model trait Resettable { - def reset(): Unit + def reset(): Unit } diff --git a/src/main/scala/de/htwg/se/learn_duel/model/command/Command.scala b/src/main/scala/de/htwg/se/learn_duel/model/command/Command.scala index e1fdbe1..77660c7 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/command/Command.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/command/Command.scala @@ -1,7 +1,7 @@ package de.htwg.se.learn_duel.model.command trait Command { - def execute(): Unit - def undo(): Unit - def redo(): Unit + def execute(): Unit + def undo(): Unit + def redo(): Unit } diff --git a/src/main/scala/de/htwg/se/learn_duel/model/command/CommandInvoker.scala b/src/main/scala/de/htwg/se/learn_duel/model/command/CommandInvoker.scala index 89bc826..e5b9ea7 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/command/CommandInvoker.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/command/CommandInvoker.scala @@ -1,17 +1,19 @@ package de.htwg.se.learn_duel.model.command -import de.htwg.se.learn_duel.model.command.impl.{ CommandInvoker => CommandInvokerImpl } +import de.htwg.se.learn_duel.model.command.impl.{ + CommandInvoker => CommandInvokerImpl +} trait CommandInvoker { - var undoCommands: List[Command] = List() - var redoCommands: List[Command] = List() - def execute(cmd: Command): Unit - def undo(): Unit - def redo(): Unit + var undoCommands: List[Command] = List() + var redoCommands: List[Command] = List() + def execute(cmd: Command): Unit + def undo(): Unit + def redo(): Unit } object CommandInvoker { - def create: CommandInvokerImpl = { - CommandInvokerImpl() - } + def create: CommandInvokerImpl = { + CommandInvokerImpl() + } } diff --git a/src/main/scala/de/htwg/se/learn_duel/model/command/impl/CommandInvoker.scala b/src/main/scala/de/htwg/se/learn_duel/model/command/impl/CommandInvoker.scala index 15306a0..fdf1e0f 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/command/impl/CommandInvoker.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/command/impl/CommandInvoker.scala @@ -1,28 +1,31 @@ package de.htwg.se.learn_duel.model.command.impl -import de.htwg.se.learn_duel.model.command.{Command, CommandInvoker => CommandInvokerTrait} +import de.htwg.se.learn_duel.model.command.{ + Command, + CommandInvoker => CommandInvokerTrait +} case class CommandInvoker() extends CommandInvokerTrait { - override def execute(cmd: Command): Unit = { - cmd.execute() - undoCommands = undoCommands :+ cmd - redoCommands = List() - } + override def execute(cmd: Command): Unit = { + cmd.execute() + undoCommands = undoCommands :+ cmd + redoCommands = List() + } - override def undo(): Unit = { - if (undoCommands.nonEmpty) { - val lastCommand = undoCommands.last - lastCommand.undo() - redoCommands = redoCommands :+ lastCommand - undoCommands = undoCommands diff List(lastCommand) - } + override def undo(): Unit = { + if (undoCommands.nonEmpty) { + val lastCommand = undoCommands.last + lastCommand.undo() + redoCommands = redoCommands :+ lastCommand + undoCommands = undoCommands diff List(lastCommand) } + } - override def redo(): Unit = { - if (redoCommands.nonEmpty) { - redoCommands.head.redo() - undoCommands = undoCommands :+ redoCommands.head - redoCommands = redoCommands diff List(redoCommands.head) - } + override def redo(): Unit = { + if (redoCommands.nonEmpty) { + redoCommands.head.redo() + undoCommands = undoCommands :+ redoCommands.head + redoCommands = redoCommands diff List(redoCommands.head) } + } } diff --git a/src/main/scala/de/htwg/se/learn_duel/model/command/impl/PlayerAddCommand.scala b/src/main/scala/de/htwg/se/learn_duel/model/command/impl/PlayerAddCommand.scala index 65e3132..9b43182 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/command/impl/PlayerAddCommand.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/command/impl/PlayerAddCommand.scala @@ -7,17 +7,17 @@ case class PlayerAddCommand( addPlayer: Function[Option[String], String], removePlayer: Function[String, Unit] ) extends Command { - var actualName: String = "" + var actualName: String = "" - override def execute(): Unit = { - actualName = addPlayer(name) - } + override def execute(): Unit = { + actualName = addPlayer(name) + } - override def undo(): Unit = { - removePlayer(actualName) - } + override def undo(): Unit = { + removePlayer(actualName) + } - override def redo(): Unit = { - execute() - } + override def redo(): Unit = { + execute() + } } diff --git a/src/main/scala/de/htwg/se/learn_duel/model/command/impl/PlayerRemoveCommand.scala b/src/main/scala/de/htwg/se/learn_duel/model/command/impl/PlayerRemoveCommand.scala index 3f6d83f..61ccffc 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/command/impl/PlayerRemoveCommand.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/command/impl/PlayerRemoveCommand.scala @@ -7,15 +7,15 @@ case class PlayerRemoveCommand( removePlayer: Function[String, Unit], addPlayer: Function[Option[String], String] ) extends Command { - override def execute(): Unit = { - removePlayer(name) - } + override def execute(): Unit = { + removePlayer(name) + } - override def undo(): Unit = { - addPlayer(Some(name)) - } + override def undo(): Unit = { + addPlayer(Some(name)) + } - override def redo(): Unit = { - execute() - } + override def redo(): Unit = { + execute() + } } diff --git a/src/main/scala/de/htwg/se/learn_duel/model/impl/Game.scala b/src/main/scala/de/htwg/se/learn_duel/model/impl/Game.scala index 56ea236..4a76fa6 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/impl/Game.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/impl/Game.scala @@ -1,44 +1,50 @@ package de.htwg.se.learn_duel.model.impl -import de.htwg.se.learn_duel.model.{Game => GameTrait, Player => PlayerTrait, Question => QuestionTrait} +import de.htwg.se.learn_duel.model.{ + Game => GameTrait, + Player => PlayerTrait, + Question => QuestionTrait +} // FIXME find better way for resetting or do not let all props be specified in constructor (currently needed for JSON reader) -case class Game ( - var helpText: List[String] = List(), - var players: List[PlayerTrait] = List(), - var questions: List[QuestionTrait] = List(), - var currentQuestion: Option[QuestionTrait] = None, - var currentQuestionTime: Option[Int] = None +case class Game( + var helpText: List[String] = List(), + var players: List[PlayerTrait] = List(), + var questions: List[QuestionTrait] = List(), + var currentQuestion: Option[QuestionTrait] = None, + var currentQuestionTime: Option[Int] = None ) extends GameTrait { - protected val initialQuestions: List[QuestionTrait] = questions - reset() - - override def addPlayer(player: PlayerTrait): Unit = players = players :+ player - - override def removePlayer(player: PlayerTrait): Unit = players = players.filter(_ != player) - - override def playerCount(): Int = players.size - - override def addQuestion(question: QuestionTrait): Unit = questions = questions :+ question - - override def removeQuestion(question: QuestionTrait): Unit = questions = questions.filter(_ != question) - - override def questionCount(): Int = questions.size - - override def reset(): Unit = { - questions = initialQuestions - currentQuestion = None - currentQuestionTime = None - players = List() - addPlayer(Player("Player1")) - helpText = List( - "Learn Duel is based on QuizDuel and works in a similar fashion, but with a new twist:\nYou play with questions based on your school or study assignments.", - "For now, there is only local play, but online features will be added later.\nIf you are playing alone, the answers can be selected with the mouse or the keys 1-4.\nIn local multiplayer mode player 1 can specify his answer with the keys 1-4 and\nplayer 2 with the keys 6-9.", - "Future features:", - "* define your own questions", - "* play with up to 3 friends online and compete against each other" - ) - } + protected val initialQuestions: List[QuestionTrait] = questions + reset() + + override def addPlayer(player: PlayerTrait): Unit = + players = players :+ player + + override def removePlayer(player: PlayerTrait): Unit = + players = players.filter(_ != player) + + override def playerCount(): Int = players.size + + override def addQuestion(question: QuestionTrait): Unit = + questions = questions :+ question + + override def removeQuestion(question: QuestionTrait): Unit = + questions = questions.filter(_ != question) + + override def questionCount(): Int = questions.size + + override def reset(): Unit = { + questions = initialQuestions + currentQuestion = None + currentQuestionTime = None + players = List() + addPlayer(Player("Player1")) + helpText = List( + "Learn Duel is based on QuizDuel and works in a similar fashion, but with a new twist:\nYou play with questions based on your school or study assignments.", + "For now, there is only local play, but online features will be added later.\nIf you are playing alone, the answers can be selected with the mouse or the keys 1-4.\nIn local multiplayer mode player 1 can specify his answer with the keys 1-4 and\nplayer 2 with the keys 6-9.", + "Future features:", + "* define your own questions", + "* play with up to 3 friends online and compete against each other" + ) + } } - - diff --git a/src/main/scala/de/htwg/se/learn_duel/model/impl/Player.scala b/src/main/scala/de/htwg/se/learn_duel/model/impl/Player.scala index 237d992..b29c340 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/impl/Player.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/impl/Player.scala @@ -2,7 +2,10 @@ package de.htwg.se.learn_duel.model.impl import java.security.InvalidParameterException -import de.htwg.se.learn_duel.model.{Player => PlayerTrait, Question => QuestionTrait} +import de.htwg.se.learn_duel.model.{ + Player => PlayerTrait, + Question => QuestionTrait +} case class Player( name: String, @@ -10,11 +13,12 @@ case class Player( var correctAnswers: List[QuestionTrait] = List(), var wrongAnswers: List[QuestionTrait] = List() ) extends PlayerTrait { - if (name.isEmpty) { - throw new InvalidParameterException("Player name cannot be empty") - } else if (!name.matches("\\S+")) { - throw new InvalidParameterException("Player name may not contain whitespaces") - } + if (name.isEmpty) { + throw new InvalidParameterException("Player name cannot be empty") + } else if (!name.matches("\\S+")) { + throw new InvalidParameterException( + "Player name may not contain whitespaces") + } - override def toString: String = name + override def toString: String = name } diff --git a/src/main/scala/de/htwg/se/learn_duel/model/impl/Question.scala b/src/main/scala/de/htwg/se/learn_duel/model/impl/Question.scala index dc24711..b097cbd 100644 --- a/src/main/scala/de/htwg/se/learn_duel/model/impl/Question.scala +++ b/src/main/scala/de/htwg/se/learn_duel/model/impl/Question.scala @@ -1,13 +1,16 @@ package de.htwg.se.learn_duel.model.impl -import de.htwg.se.learn_duel.model.{Answer => AnswerTrait, Question => QuestionTrait} +import de.htwg.se.learn_duel.model.{ + Answer => AnswerTrait, + Question => QuestionTrait +} // needed for json (de)serialization case class Question( - id: Int, - text: String, - points: Int, - answers: List[AnswerTrait], - correctAnswer: Int, - time: Int + id: Int, + text: String, + points: Int, + answers: List[AnswerTrait], + correctAnswer: Int, + time: Int ) extends QuestionTrait {} diff --git a/src/main/scala/de/htwg/se/learn_duel/view/UI.scala b/src/main/scala/de/htwg/se/learn_duel/view/UI.scala index b4d8d3c..4fe2eee 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/UI.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/UI.scala @@ -12,28 +12,28 @@ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future trait UI extends Observer { - def displayMenu(): Unit - def displayGame(currentQuestion: Question, multiplayer: Boolean): Unit - def displayResult(players: List[Player]): Unit + def displayMenu(): Unit + def displayGame(currentQuestion: Question, multiplayer: Boolean): Unit + def displayResult(players: List[Player]): Unit } object TUI { - def create(controller: Controller): TUIImpl = { - new TUIImpl(controller) - } + def create(controller: Controller): TUIImpl = { + new TUIImpl(controller) + } } object GUI { - def create(controller: Controller): Unit = { - val latch = new CountDownLatch(1) - val gui = new GUIImpl(controller, latch) - - // run GUI on its own thread - Future { - gui.main(Array()) - } + def create(controller: Controller): Unit = { + val latch = new CountDownLatch(1) + val gui = new GUIImpl(controller, latch) - // wait for initialization of JFXApp to be done - latch.await() + // run GUI on its own thread + Future { + gui.main(Array()) } + + // wait for initialization of JFXApp to be done + latch.await() + } } diff --git a/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala b/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala index 599816b..a41aafe 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala @@ -8,157 +8,163 @@ import de.htwg.se.learn_duel.controller.{Controller, ControllerException} import de.htwg.se.learn_duel.model.{Player, Question} import de.htwg.se.learn_duel.view.UI -class TUI (controller: Controller) extends UI with Observer with LazyLogging { - controller.addObserver(this) - var stopProcessingInput = false - var inMenu = true - var inGame = false - - def displayPlayers(): Unit = { - logger.info("Current players: " + controller.getPlayerNames.mkString(", ")) +class TUI(controller: Controller) extends UI with Observer with LazyLogging { + controller.addObserver(this) + var stopProcessingInput = false + var inMenu = true + var inGame = false + + def displayPlayers(): Unit = { + logger.info("Current players: " + controller.getPlayerNames.mkString(", ")) + } + + override def displayMenu(): Unit = { + logger.info("") + logger.info("Welcome to Learn Duel") + displayPlayers() + logger.info("n => new game") + logger.info("a [name] => add player") + logger.info("r [name] => remove player") + logger.info("h => show help") + logger.info("q => exit") + logger.info("") + } + + override def displayGame(question: Question, multiplayer: Boolean): Unit = { + logger.info(question.text) + question.answers.zipWithIndex.foreach { + case (ans, i) => + logger.info((i + 1) + "/" + (i + 5) + ": " + ans.text) } - - override def displayMenu(): Unit = { - logger.info("") - logger.info("Welcome to Learn Duel") - displayPlayers() - logger.info("n => new game") - logger.info("a [name] => add player") - logger.info("r [name] => remove player") - logger.info("h => show help") - logger.info("q => exit") - logger.info("") - } - - override def displayGame(question: Question, multiplayer: Boolean): Unit = { - logger.info(question.text) - question.answers.zipWithIndex.foreach {case (ans, i) => - logger.info((i + 1) + "/" + (i + 5) + ": " + ans.text) - } - } - - override def displayResult(players: List[Player]): Unit = { - logger.info("") - logger.info("RESULT:") - players.foreach(p => { - logger.info("Player '" + p.name + "':") - logger.info("Points: " + p.points) - logger.info("Correct answers:") - p.correctAnswers.foreach(q => { - logger.info("\t" + q.text) - }) - - logger.info("Wrong answers:") - p.wrongAnswers.foreach(q => { - logger.info("\t" + q.text) - val correctAnswer = q.answers.find(a => a.id == q.correctAnswer).get - logger.info("\tcorrect answer is: " + correctAnswer.text) - }) - }) - - val player = players.max[Player]{ case (p1: Player, p2: Player) => - p1.points.compareTo(p2.points) - } - - logger.info("") - logger.info("'" + player.name + "' won the game!") - logger.info("") + } + + override def displayResult(players: List[Player]): Unit = { + logger.info("") + logger.info("RESULT:") + players.foreach(p => { + logger.info("Player '" + p.name + "':") + logger.info("Points: " + p.points) + logger.info("Correct answers:") + p.correctAnswers.foreach(q => { + logger.info("\t" + q.text) + }) + + logger.info("Wrong answers:") + p.wrongAnswers.foreach(q => { + logger.info("\t" + q.text) + val correctAnswer = q.answers.find(a => a.id == q.correctAnswer).get + logger.info("\tcorrect answer is: " + correctAnswer.text) + }) + }) + + val player = players.max[Player] { + case (p1: Player, p2: Player) => + p1.points.compareTo(p2.points) } - // scalastyle:off - override def update(updateParam: UpdateData): Unit = { - updateParam.getAction match { - case UpdateAction.BEGIN => displayMenu() - case UpdateAction.CLOSE_APPLICATION => stopProcessingInput = true - case UpdateAction.SHOW_HELP => - logger.info(updateParam.getState.helpText.mkString("\n\n")) - case UpdateAction.PLAYER_UPDATE => displayPlayers() - case UpdateAction.SHOW_GAME => - displayGamePretty( - updateParam.getState.currentQuestion.get, - updateParam.getState.players.lengthCompare(1) > 0, - updateParam.getState.currentQuestionTime.get - ) - inMenu = false - inGame = true - case UpdateAction.TIMER_UPDATE => - displayGamePretty( - updateParam.getState.currentQuestion.get, - updateParam.getState.players.lengthCompare(1) > 0, - updateParam.getState.currentQuestionTime.get - ) - case UpdateAction.SHOW_RESULT => - displayResult(updateParam.getState.players) - case _ => - } - } - - def processInput(input: BufferedReader): Unit = { - while (!stopProcessingInput) { - if (input.ready()) { - val line = input.readLine() - if (inMenu){ - processMenuInput(line) - } else if (inGame) { - processGameInput(line) - } else { - processResultInput(line) - } - } else { - Thread.sleep(200) // don't waste cpu cycles if no input is given - } - } + logger.info("") + logger.info("'" + player.name + "' won the game!") + logger.info("") + } + + // scalastyle:off + override def update(updateParam: UpdateData): Unit = { + updateParam.getAction match { + case UpdateAction.BEGIN => displayMenu() + case UpdateAction.CLOSE_APPLICATION => stopProcessingInput = true + case UpdateAction.SHOW_HELP => + logger.info(updateParam.getState.helpText.mkString("\n\n")) + case UpdateAction.PLAYER_UPDATE => displayPlayers() + case UpdateAction.SHOW_GAME => + displayGamePretty( + updateParam.getState.currentQuestion.get, + updateParam.getState.players.lengthCompare(1) > 0, + updateParam.getState.currentQuestionTime.get + ) + inMenu = false + inGame = true + case UpdateAction.TIMER_UPDATE => + displayGamePretty( + updateParam.getState.currentQuestion.get, + updateParam.getState.players.lengthCompare(1) > 0, + updateParam.getState.currentQuestionTime.get + ) + case UpdateAction.SHOW_RESULT => + displayResult(updateParam.getState.players) + case _ => } - protected def processMenuInput(line: String): Unit = { - val playerPattern = """(?:a|r)(?:\s+(.*))?""".r - try { - line match { - case "q" => controller.onClose() - case "n" => controller.onStartGame() - case playerPattern(name) if line.startsWith("a") => controller.onAddPlayer(Option(name)) - case playerPattern(name) if line.startsWith("r") => if (name != null) { - controller.onRemovePlayer(name) - } - case "h" => controller.onHelp() - case "u" => controller.onPlayerActionUndo() - case "U" => controller.onPlayerActionRedo() - case _ => - logger.info("Unknown command") - displayMenu - } - } catch { - case e: ControllerException => logger.error(e.getMessage) + } + + def processInput(input: BufferedReader): Unit = { + while (!stopProcessingInput) { + if (input.ready()) { + val line = input.readLine() + if (inMenu) { + processMenuInput(line) + } else if (inGame) { + processGameInput(line) + } else { + processResultInput(line) } + } else { + Thread.sleep(200) // don't waste cpu cycles if no input is given + } } - - protected def processGameInput(line: String): Unit = { - line match { - case "q" => controller.onClose() - case "1" => controller.onAnswerChosen(1) - case "2" => controller.onAnswerChosen(2) - case "3" => controller.onAnswerChosen(3) - case "4" => controller.onAnswerChosen(4) - case "6" => controller.onAnswerChosen(6) - case "7" => controller.onAnswerChosen(7) - case "8" => controller.onAnswerChosen(8) - case "9" => controller.onAnswerChosen(9) - case _ => logger.info("Unknown command") - } + } + protected def processMenuInput(line: String): Unit = { + val playerPattern = """(?:a|r)(?:\s+(.*))?""".r + try { + line match { + case "q" => controller.onClose() + case "n" => controller.onStartGame() + case playerPattern(name) if line.startsWith("a") => + controller.onAddPlayer(Option(name)) + case playerPattern(name) if line.startsWith("r") => + if (name != null) { + controller.onRemovePlayer(name) + } + case "h" => controller.onHelp() + case "u" => controller.onPlayerActionUndo() + case "U" => controller.onPlayerActionRedo() + case _ => + logger.info("Unknown command") + displayMenu + } + } catch { + case e: ControllerException => logger.error(e.getMessage) } - - protected def processResultInput(line: String): Unit = { - line match { - case "q" => controller.onClose() - case _ => logger.info("Unknown command") - } + } + + protected def processGameInput(line: String): Unit = { + line match { + case "q" => controller.onClose() + case "1" => controller.onAnswerChosen(1) + case "2" => controller.onAnswerChosen(2) + case "3" => controller.onAnswerChosen(3) + case "4" => controller.onAnswerChosen(4) + case "6" => controller.onAnswerChosen(6) + case "7" => controller.onAnswerChosen(7) + case "8" => controller.onAnswerChosen(8) + case "9" => controller.onAnswerChosen(9) + case _ => logger.info("Unknown command") } - // scalastyle:on + } - protected def displayGamePretty(question: Question, multiplayer: Boolean, timeRemaining: Int): Unit = { - logger.info("") - displayGame(question, multiplayer) - logger.info("Time remaining: " + timeRemaining + "s") - logger.info("") + protected def processResultInput(line: String): Unit = { + line match { + case "q" => controller.onClose() + case _ => logger.info("Unknown command") } + } + // scalastyle:on + + protected def displayGamePretty(question: Question, + multiplayer: Boolean, + timeRemaining: Int): Unit = { + logger.info("") + displayGame(question, multiplayer) + logger.info("Time remaining: " + timeRemaining + "s") + logger.info("") + } } diff --git a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GUI.scala b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GUI.scala index 00ac1f5..029098e 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GUI.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GUI.scala @@ -12,86 +12,92 @@ import de.htwg.se.learn_duel.{Observer, UpdateAction, UpdateData} import scalafx.application.JFXApp -class GUI (controller: Controller, latch: CountDownLatch) extends JFXApp with UI with Observer with LazyLogging { - controller.addObserver(this) +class GUI(controller: Controller, latch: CountDownLatch) + extends JFXApp + with UI + with Observer + with LazyLogging { + controller.addObserver(this) - // handle self defined exception in a 'global' exception handler - Thread.currentThread().setUncaughtExceptionHandler((t: Thread, e: Throwable) => { - e.getCause match { - case cause: ControllerException => - val infoPopup = new InfoPopup("Error", cause.message) - infoPopup.getDialogPane.getStylesheets.add("styles.css") - infoPopup.show() - case _ => - val sw = new StringWriter() - e.printStackTrace(new PrintWriter(sw)) - logger.error(Console.RED + sw.toString + Console.RESET) - controller.onClose() - } + // handle self defined exception in a 'global' exception handler + Thread + .currentThread() + .setUncaughtExceptionHandler((t: Thread, e: Throwable) => { + e.getCause match { + case cause: ControllerException => + val infoPopup = new InfoPopup("Error", cause.message) + infoPopup.getDialogPane.getStylesheets.add("styles.css") + infoPopup.show() + case _ => + val sw = new StringWriter() + e.printStackTrace(new PrintWriter(sw)) + logger.error(Console.RED + sw.toString + Console.RESET) + controller.onClose() + } }) - // signal initialization down - latch.countDown() + // signal initialization down + latch.countDown() - // scalastyle:off - override def update(updateParam: UpdateData): Unit = { - // every update needs to be run on the JavaFX Application thread - Platform.runLater { () => - updateParam.getAction match { - case UpdateAction.BEGIN => - displayMenu() - this.stage.onCloseRequest = { (_) => - controller.onClose() - } - case UpdateAction.CLOSE_APPLICATION => this.stage.close() - case UpdateAction.SHOW_HELP => - val helpText = updateParam.getState.helpText - val dlg = new InfoPopup("Learn Duel Help", helpText.mkString("\n\n")) - dlg.getDialogPane.getStylesheets.add("styles.css") - dlg.show() - case UpdateAction.PLAYER_UPDATE => displayMenu() - case UpdateAction.SHOW_GAME => - displayGame( - updateParam.getState.currentQuestion.get, - updateParam.getState.players.lengthCompare(1) > 0 - ) - case UpdateAction.TIMER_UPDATE => - updateParam.getState.currentQuestionTime match { - case Some(time) => - this.stage match { - case stage1: GameStage => - stage1.updateTime(time) - case _ => - } - case _ => - } - case UpdateAction.SHOW_RESULT => - displayResult(updateParam.getState.players) + // scalastyle:off + override def update(updateParam: UpdateData): Unit = { + // every update needs to be run on the JavaFX Application thread + Platform.runLater { () => + updateParam.getAction match { + case UpdateAction.BEGIN => + displayMenu() + this.stage.onCloseRequest = { (_) => + controller.onClose() + } + case UpdateAction.CLOSE_APPLICATION => this.stage.close() + case UpdateAction.SHOW_HELP => + val helpText = updateParam.getState.helpText + val dlg = new InfoPopup("Learn Duel Help", helpText.mkString("\n\n")) + dlg.getDialogPane.getStylesheets.add("styles.css") + dlg.show() + case UpdateAction.PLAYER_UPDATE => displayMenu() + case UpdateAction.SHOW_GAME => + displayGame( + updateParam.getState.currentQuestion.get, + updateParam.getState.players.lengthCompare(1) > 0 + ) + case UpdateAction.TIMER_UPDATE => + updateParam.getState.currentQuestionTime match { + case Some(time) => + this.stage match { + case stage1: GameStage => + stage1.updateTime(time) case _ => - } - } + } + case _ => + } + case UpdateAction.SHOW_RESULT => + displayResult(updateParam.getState.players) + case _ => + } } - // scalastyle:on + } + // scalastyle:on - override def displayMenu(): Unit = { - this.stage = new MenuStage( - _ => controller.onStartGame(), - _ => controller.onHelp(), - (controller.getPlayerNames, controller.nextPlayerName), - (name) => controller.onAddPlayer(Some(name)), - controller.onRemovePlayer - ) - } + override def displayMenu(): Unit = { + this.stage = new MenuStage( + _ => controller.onStartGame(), + _ => controller.onHelp(), + (controller.getPlayerNames, controller.nextPlayerName), + (name) => controller.onAddPlayer(Some(name)), + controller.onRemovePlayer + ) + } - override def displayGame(question: Question, multiplayer: Boolean): Unit = { - this.stage = new GameStage( - question, - !multiplayer, - controller.onAnswerChosen - ) - } + override def displayGame(question: Question, multiplayer: Boolean): Unit = { + this.stage = new GameStage( + question, + !multiplayer, + controller.onAnswerChosen + ) + } - override def displayResult(players: List[Player]): Unit = { - this.stage = new ResultStage(players, controller.reset) - } + override def displayResult(players: List[Player]): Unit = { + this.stage = new ResultStage(players, controller.reset) + } } diff --git a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GameStage.scala b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GameStage.scala index e5b2c4a..7f9db95 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GameStage.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/GameStage.scala @@ -20,104 +20,111 @@ class GameStage( allowMouseInput: Boolean, onInput: Function[Int, Unit] ) extends PrimaryStage { - var timeRemaining: Int = question.time - var timerText: SimpleStringProperty = new SimpleStringProperty { - "Time remaining: " + timeRemaining + "s" - } - var timer: Option[Timer] = None - - title.value = "Learn Duel Game" - width = 640 - height = allowMouseInput match { - case true => 480 - case false => 520 - } - - scene = new Scene { - fill = White - stylesheets += "styles.css" - - root = new VBox { - styleClass += "game" - - val questionProp: Text = new Text { - text = question.text - styleClass += "headline" - } - children += questionProp - - val answerBox: VBox = new VBox { - styleClass += "answer-container" - - question.answers.zipWithIndex.foreach { case (ans, i) => - val btn = new Button { - styleClass += "answer-button" - - text = "Answer " + (i + 1) + ": " + ans.text - if (allowMouseInput) { - onAction = _ => onInput(ans.id) - } - } - - children += btn - } - } - children += answerBox - - val timer: Text = new Text { - styleClass += "remaining-time" + var timeRemaining: Int = question.time + var timerText: SimpleStringProperty = new SimpleStringProperty { + "Time remaining: " + timeRemaining + "s" + } + var timer: Option[Timer] = None + + title.value = "Learn Duel Game" + width = 640 + height = allowMouseInput match { + case true => 480 + case false => 520 + } + + scene = new Scene { + fill = White + stylesheets += "styles.css" + + root = new VBox { + styleClass += "game" + + val questionProp: Text = new Text { + text = question.text + styleClass += "headline" + } + children += questionProp + + val answerBox: VBox = new VBox { + styleClass += "answer-container" + + question.answers.zipWithIndex.foreach { + case (ans, i) => + val btn = new Button { + styleClass += "answer-button" + + text = "Answer " + (i + 1) + ": " + ans.text + if (allowMouseInput) { + onAction = _ => onInput(ans.id) + } } - timer.text.bind(timerText) - children += timer - if (!allowMouseInput) { - val warning = new Text { - text = "Mouse input not allowed, use keyboard instead" - fill = Color.Red - } - - children += warning - } + children += btn + } + } + children += answerBox + + val timer: Text = new Text { + styleClass += "remaining-time" + } + timer.text.bind(timerText) + children += timer + + if (!allowMouseInput) { + val warning = new Text { + text = "Mouse input not allowed, use keyboard instead" + fill = Color.Red } - onKeyReleased = { e => { - e.getCode match { - case KeyCode.DIGIT1 => onInput(1) - case KeyCode.DIGIT2 => onInput(2) - case KeyCode.DIGIT3 => onInput(3) - case KeyCode.DIGIT4 => onInput(4) - case KeyCode.DIGIT6 => onInput(6) - case KeyCode.DIGIT7 => onInput(7) - case KeyCode.DIGIT8 => onInput(8) - case KeyCode.DIGIT9 => onInput(9) - case _ => - } - }} + children += warning + } } - setUpTimer() - - def updateTime(timeRemaining: Int): Unit = { - if (timer.isDefined) { - timer.get.cancel() + onKeyReleased = { e => + { + e.getCode match { + case KeyCode.DIGIT1 => onInput(1) + case KeyCode.DIGIT2 => onInput(2) + case KeyCode.DIGIT3 => onInput(3) + case KeyCode.DIGIT4 => onInput(4) + case KeyCode.DIGIT6 => onInput(6) + case KeyCode.DIGIT7 => onInput(7) + case KeyCode.DIGIT8 => onInput(8) + case KeyCode.DIGIT9 => onInput(9) + case _ => } - - this.timeRemaining = timeRemaining - setUpTimer() + } } + } - protected def setUpTimer(): Unit = { - timer = Some(new Timer(true)) - val localTimer = timer.get - - localTimer.scheduleAtFixedRate(new TimerTask{ - override def run(): Unit = { - timerText.set("Time remaining: " + timeRemaining + "s") - timeRemaining -= 1 - if (timeRemaining < 0) { - localTimer.cancel() - } - } - }, 0, 1000) + setUpTimer() + + def updateTime(timeRemaining: Int): Unit = { + if (timer.isDefined) { + timer.get.cancel() } + + this.timeRemaining = timeRemaining + setUpTimer() + } + + protected def setUpTimer(): Unit = { + timer = Some(new Timer(true)) + val localTimer = timer.get + + localTimer.scheduleAtFixedRate( + new TimerTask { + override def run(): Unit = { + timerText.set("Time remaining: " + timeRemaining + "s") + timeRemaining -= 1 + if (timeRemaining < 0) { + localTimer.cancel() + } + } + }, + 0, + 1000 + ) + } } diff --git a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/InfoPopup.scala b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/InfoPopup.scala index 2b9efda..cbcc013 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/InfoPopup.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/InfoPopup.scala @@ -4,9 +4,9 @@ import scalafx.scene.control.{ButtonType, Dialog} import scalafx.stage.Modality class InfoPopup(titleText: String, text: String) extends Dialog[Unit] { - title = titleText - contentText = text + title = titleText + contentText = text - dialogPane.value.getButtonTypes.add(ButtonType.OK) - initModality(Modality.None) + dialogPane.value.getButtonTypes.add(ButtonType.OK) + initModality(Modality.None) } diff --git a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/MenuStage.scala b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/MenuStage.scala index 6792e31..11d2ae7 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/MenuStage.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/MenuStage.scala @@ -11,100 +11,101 @@ import scalafx.scene.paint.Color._ import scalafx.scene.text.Text class MenuStage( - newGameAction: EventHandler[ActionEvent], - helpAction: EventHandler[ActionEvent], - playerInfo: (List[String], Option[String]), - playerAddAction: Function[String, Unit], - playerRemoveAction: Function[String, Unit] + newGameAction: EventHandler[ActionEvent], + helpAction: EventHandler[ActionEvent], + playerInfo: (List[String], Option[String]), + playerAddAction: Function[String, Unit], + playerRemoveAction: Function[String, Unit] ) extends PrimaryStage { - title.value = "Learn Duel Menu" - resizable = false - width = 480 - height = 540 - - scene = new Scene { - fill = White - stylesheets += "styles.css" - - root = new VBox { - styleClass += "menu" - - val headLine: Text = new Text { - text = "Learn Duel" - styleClass += "headline" + title.value = "Learn Duel Menu" + resizable = false + width = 480 + height = 540 + + scene = new Scene { + fill = White + stylesheets += "styles.css" + + root = new VBox { + styleClass += "menu" + + val headLine: Text = new Text { + text = "Learn Duel" + styleClass += "headline" + } + children += headLine + + val newGameButton: Button = new Button { + text = "New Game" + onAction = newGameAction + styleClass += "play-button" + } + children += newGameButton + + val playerContainer: VBox = new VBox { + styleClass += "player-rows-container" + + for (playerName <- playerInfo._1) { + val hBox = new HBox { + styleClass += "player-container" + + val txtFld: TextField = new TextField { + text = playerName + editable = false + styleClass += "player-textfield" + styleClass += "player-textfield-non-editable" } - children += headLine + children += txtFld - val newGameButton: Button = new Button { - text = "New Game" - onAction = newGameAction - styleClass += "play-button" + val removeBtn: Button = new Button { + text = "Remove" + onAction = _ => playerRemoveAction(playerName) + styleClass += "add-remove-buttons" } - children += newGameButton - - val playerContainer: VBox = new VBox { - styleClass += "player-rows-container" - - for (playerName <- playerInfo._1) { - val hBox = new HBox { - styleClass += "player-container" - - val txtFld: TextField = new TextField { - text = playerName - editable = false - styleClass += "player-textfield" - styleClass += "player-textfield-non-editable" - } - children += txtFld - - val removeBtn: Button = new Button { - text = "Remove" - onAction = _ => playerRemoveAction(playerName) - styleClass += "add-remove-buttons" - } - children += removeBtn - } - children += hBox - } - - playerInfo._2 match { - case Some(nextPlayername) => { - val hBox = new HBox { - styleClass += "player-container" - - val txtField: TextField = new TextField { - promptText = nextPlayername - styleClass += "player-textfield" - } - children += txtField - - val addBtn: Button = new Button { - text = "Add" - onAction = _ => playerAddAction( - if (txtField.getText.isEmpty) { - txtField.getPromptText - } else { - txtField.getText - } - ) - styleClass += "add-remove-buttons" - } - children += addBtn - } - children += hBox + children += removeBtn + } + children += hBox + } + + playerInfo._2 match { + case Some(nextPlayername) => { + val hBox = new HBox { + styleClass += "player-container" + + val txtField: TextField = new TextField { + promptText = nextPlayername + styleClass += "player-textfield" + } + children += txtField + + val addBtn: Button = new Button { + text = "Add" + onAction = _ => + playerAddAction( + if (txtField.getText.isEmpty) { + txtField.getPromptText + } else { + txtField.getText } - case _ => - } + ) + styleClass += "add-remove-buttons" + } + children += addBtn } - children += playerContainer + children += hBox + } + case _ => + } + } + children += playerContainer - val helpButton: Button = new Button { - text = "Help" - onAction = helpAction - styleClass += "help-button" - } + val helpButton: Button = new Button { + text = "Help" + onAction = helpAction + styleClass += "help-button" + } - children += helpButton - } + children += helpButton } + } } diff --git a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/ResultStage.scala b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/ResultStage.scala index 2fd2e18..0a1de0b 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/ResultStage.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/impl/gui/ResultStage.scala @@ -14,109 +14,114 @@ class ResultStage( players: List[Player], backAction: Function0[Unit] ) extends PrimaryStage { - title.value = "Learn Duel Result" - width = 480 - height = players.length match { - case 1 => 560 - case _ => 720 - } - - scene = new Scene { - fill = White - stylesheets += "styles.css" - - root = new VBox { - styleClass += "result" - - val headline: Text = new Text { - text = "Result" - styleClass += "headline" - } - children += headline - - val playerContainer: VBox = new VBox { - styleClass += "player-results" - - players.foreach { p => { - val singlePlayerContainer = new VBox { - styleClass += "player-result" - - val player: Text = new Text { - text = p.name - styleClass += "player-name" - } - children += player - - val points: Text = new Text { - text = "Points: " + p.points - styleClass += "player-points" - } - children += points - - val correctContainer: VBox = new VBox { - styleClass += "correct-container" - - if (p.correctAnswers.nonEmpty) { - val correctText = new Text { - text = "Correct answers" - styleClass += "correct-text" - } - children += correctText - - p.correctAnswers.foreach(q => { - val correctQuestion = new Text { - text = q.text - styleClass += "correct-answer" - } - children += correctQuestion - }) - } - } - children += correctContainer - - val wrongContainer: VBox = new VBox { - styleClass += "wrong-container" - - if (p.wrongAnswers.nonEmpty) { - val wrongText = new Text { - text = "Wrong answers" - styleClass += "wrong-text" - } - children += wrongText - - p.wrongAnswers.foreach(q => { - val correctAnswer = q.answers.find(a => a.id == q.correctAnswer).get - val answerText = new Text { - text = q.text + " (correct answer: " + correctAnswer.text + ")" - styleClass += "wrong-answer" - } - children += answerText - }) - } - } - children += wrongContainer + title.value = "Learn Duel Result" + width = 480 + height = players.length match { + case 1 => 560 + case _ => 720 + } + + scene = new Scene { + fill = White + stylesheets += "styles.css" + + root = new VBox { + styleClass += "result" + + val headline: Text = new Text { + text = "Result" + styleClass += "headline" + } + children += headline + + val playerContainer: VBox = new VBox { + styleClass += "player-results" + + players.foreach { p => + { + val singlePlayerContainer = new VBox { + styleClass += "player-result" + + val player: Text = new Text { + text = p.name + styleClass += "player-name" + } + children += player + + val points: Text = new Text { + text = "Points: " + p.points + styleClass += "player-points" + } + children += points + + val correctContainer: VBox = new VBox { + styleClass += "correct-container" + + if (p.correctAnswers.nonEmpty) { + val correctText = new Text { + text = "Correct answers" + styleClass += "correct-text" + } + children += correctText + + p.correctAnswers.foreach(q => { + val correctQuestion = new Text { + text = q.text + styleClass += "correct-answer" } - children += singlePlayerContainer - }} - } - children += playerContainer - - val player: Player = players.max[Player]{ case (p1: Player, p2: Player) => { - p1.points.compareTo(p2.points) - }} - - val winner: Text = new Text { - text = "'" + player.name + "' won the game!" - styleClass += "winner" + children += correctQuestion + }) + } + } + children += correctContainer + + val wrongContainer: VBox = new VBox { + styleClass += "wrong-container" + + if (p.wrongAnswers.nonEmpty) { + val wrongText = new Text { + text = "Wrong answers" + styleClass += "wrong-text" + } + children += wrongText + + p.wrongAnswers.foreach(q => { + val correctAnswer = + q.answers.find(a => a.id == q.correctAnswer).get + val answerText = new Text { + text = q.text + " (correct answer: " + correctAnswer.text + ")" + styleClass += "wrong-answer" + } + children += answerText + }) + } + } + children += wrongContainer } - children += winner + children += singlePlayerContainer + } + } + } + children += playerContainer - val backButton: Button = new Button { - text = "Back" - onAction = backAction - styleClass += "back-button" - } - children += backButton + val player: Player = players.max[Player] { + case (p1: Player, p2: Player) => { + p1.points.compareTo(p2.points) } + } + + val winner: Text = new Text { + text = "'" + player.name + "' won the game!" + styleClass += "winner" + } + children += winner + + val backButton: Button = new Button { + text = "Back" + onAction = backAction + styleClass += "back-button" + } + children += backButton } + } } diff --git a/src/test/scala/de/htwg/se/learn_duel/controller/ControllerSpec.scala b/src/test/scala/de/htwg/se/learn_duel/controller/ControllerSpec.scala index 25aba7a..346dfc6 100644 --- a/src/test/scala/de/htwg/se/learn_duel/controller/ControllerSpec.scala +++ b/src/test/scala/de/htwg/se/learn_duel/controller/ControllerSpec.scala @@ -13,196 +13,201 @@ import play.api.libs.json.{JsValue, Json} import scala.io.Source class DummyObserver extends Observer { - var updateData: Option[UpdateData] = None - override def update(updateData: UpdateData): Unit = { - this.updateData = Some(updateData) - } + var updateData: Option[UpdateData] = None + override def update(updateData: UpdateData): Unit = { + this.updateData = Some(updateData) + } } @RunWith(classOf[JUnitRunner]) class ControllerSpec extends WordSpec with Matchers { - "A Controller" when { - val jsonString: String = Source.fromResource("test_questions.json").getLines.mkString("\n") - val json: JsValue = Json.parse(jsonString) - val questionList: List[Question] = Json.fromJson[List[Question]](json).getOrElse(List()) - val gameState = GameImpl(questions = questionList) - val dummyObserver = new DummyObserver() - - "constructed" should { - val controller = new ControllerImpl(gameState) - - "update observers with the last update on request to do so" in { - controller.addObserver(dummyObserver) - dummyObserver.updateData should be(None) - controller.requestUpdate() - dummyObserver.updateData.isDefined should be(true) - dummyObserver.updateData.get.getAction should be(UpdateAction.BEGIN) - dummyObserver.updateData.get.getState should be(gameState) - } - "be able to generate a list of player names" in { - controller.getPlayerNames should be(gameState.players.map(p => p.name)) - } - "have a max player count" in { - controller.maxPlayerCount should be (Game.maxPlayerCount) - } - "be able to add a player" in { - gameState.playerCount() should be(1) - controller.onAddPlayer(Some("player")) - gameState.playerCount() should be(2) - } - "be able to remove a player" in { - gameState.playerCount() should be(2) - controller.onRemovePlayer("player") - gameState.playerCount() should be(1) - } - "be able to add a player with a default name" in { - gameState.playerCount() should be(1) - controller.onAddPlayer(None) - gameState.playerCount() should be(2) - } - "be able do undo and redo a player action" in { - gameState.playerCount() should be(2) - controller.onPlayerActionUndo() - gameState.playerCount() should be(1) - controller.onPlayerActionRedo() - gameState.playerCount() should be(2) - } - "not supply a next player name if max player count is reached" in { - gameState.playerCount() should be(2) - val nextName = controller.nextPlayerName - nextName should be(None) - } - "supply a next player name if max player count was not reached" in { - controller.onPlayerActionUndo() - gameState.playerCount() should be(1) - val nextName = controller.nextPlayerName - nextName should be(Some("Player2")) - } - "indicate to observers that they should show the help" in { - controller.onHelp() - dummyObserver.updateData.get.getAction should be(UpdateAction.SHOW_HELP) - } - "indicate to observers that they should start the game" in { - controller.onStartGame() - dummyObserver.updateData.get.getAction should be(UpdateAction.SHOW_GAME) - gameState.currentQuestion should not be None - gameState.currentQuestionTime should not be None - } - "indicate to observers that they should close" in { - controller.onClose() - dummyObserver.updateData.get.getAction should be(UpdateAction.CLOSE_APPLICATION) - } - "be able to reset game state" in { - controller.onPlayerActionRedo() - gameState.playerCount() should be(2) - gameState.currentQuestion should not be None - gameState.currentQuestionTime should not be None - dummyObserver.updateData.get.getAction should not be UpdateAction.BEGIN - - controller.reset() - gameState.playerCount() should be(1) - gameState.currentQuestion should be(None) - gameState.currentQuestionTime should be(None) - dummyObserver.updateData.get.getAction should be(UpdateAction.BEGIN) - } - "add correctly and wrongly answered questions to player and move on to next question" in { - gameState.players.head.correctAnswers.length should be(0) - gameState.players.head.wrongAnswers.length should be(0) - controller.onStartGame() - - val question1 = gameState.currentQuestion - - controller.onAnswerChosen(2) - - gameState.players.head.correctAnswers.length should be(1) - - controller.onAnswerChosen(1) - - gameState.players.head.wrongAnswers.length should be(1) - dummyObserver.updateData.get.getAction should be(UpdateAction.SHOW_RESULT) - } - "update question timer" in { - controller.reset() - controller.onStartGame() - val time = gameState.currentQuestionTime - Thread.sleep(2000) - gameState.currentQuestionTime should not be time - } - "remove observers correctly" in { - controller.reset() - controller.onHelp() - val currentAction = dummyObserver.updateData.get.getAction - currentAction should not be UpdateAction.SHOW_GAME - controller.removeObserver(dummyObserver) - controller.onStartGame() - dummyObserver.updateData.get.getAction should not be UpdateAction.SHOW_GAME - dummyObserver.updateData.get.getAction should be(currentAction) - } - "throw when adding or removing too many players" in { - controller.reset() - controller.onAddPlayer(None) - assertThrows[TooManyPlayersException] { - controller.onAddPlayer(Some("invalidthirdplayer")) - } - - controller.reset() - assertThrows[NotEnoughPlayersException] { - controller.onRemovePlayer("Player1") - } - } - "throw when adding a already existing or removing a non existing player" in { - assertThrows[PlayerExistsException] { - controller.onAddPlayer(Some("Player1")) - } - - controller.onAddPlayer(None) - assertThrows[PlayerNotExistingException] { - controller.onRemovePlayer("nonexistingplayer") - } - } - "throw when internal objects throw" in { - controller.reset() - assertThrows[ControllerProcedureFailed] { - controller.onAddPlayer(Some("Player With Space")) - } - } - } - "constructed with very small questions timers" should { - "show next question and results when timer runs out" in { - val tempJsonString = jsonString.replace("\"time\": 60", "\"time\": 1") - val tempJson = Json.parse(tempJsonString) - val tempList = Json.fromJson[List[Question]](tempJson).getOrElse(List()) - val tempGame = GameImpl(questions = tempList) - val tempController = Controller.create(tempGame) - val tempObserver = new DummyObserver() - - tempController.addObserver(tempObserver) - tempController.onAddPlayer(None) - - tempController.onStartGame() - Thread.sleep(3000) - - tempObserver.updateData.get.getAction should be(UpdateAction.SHOW_RESULT) - tempGame.players.foreach(p => p.wrongAnswers.length should be(2)) - } + "A Controller" when { + val jsonString: String = + Source.fromResource("test_questions.json").getLines.mkString("\n") + val json: JsValue = Json.parse(jsonString) + val questionList: List[Question] = + Json.fromJson[List[Question]](json).getOrElse(List()) + val gameState = GameImpl(questions = questionList) + val dummyObserver = new DummyObserver() + + "constructed" should { + val controller = new ControllerImpl(gameState) + + "update observers with the last update on request to do so" in { + controller.addObserver(dummyObserver) + dummyObserver.updateData should be(None) + controller.requestUpdate() + dummyObserver.updateData.isDefined should be(true) + dummyObserver.updateData.get.getAction should be(UpdateAction.BEGIN) + dummyObserver.updateData.get.getState should be(gameState) + } + "be able to generate a list of player names" in { + controller.getPlayerNames should be(gameState.players.map(p => p.name)) + } + "have a max player count" in { + controller.maxPlayerCount should be(Game.maxPlayerCount) + } + "be able to add a player" in { + gameState.playerCount() should be(1) + controller.onAddPlayer(Some("player")) + gameState.playerCount() should be(2) + } + "be able to remove a player" in { + gameState.playerCount() should be(2) + controller.onRemovePlayer("player") + gameState.playerCount() should be(1) + } + "be able to add a player with a default name" in { + gameState.playerCount() should be(1) + controller.onAddPlayer(None) + gameState.playerCount() should be(2) + } + "be able do undo and redo a player action" in { + gameState.playerCount() should be(2) + controller.onPlayerActionUndo() + gameState.playerCount() should be(1) + controller.onPlayerActionRedo() + gameState.playerCount() should be(2) + } + "not supply a next player name if max player count is reached" in { + gameState.playerCount() should be(2) + val nextName = controller.nextPlayerName + nextName should be(None) + } + "supply a next player name if max player count was not reached" in { + controller.onPlayerActionUndo() + gameState.playerCount() should be(1) + val nextName = controller.nextPlayerName + nextName should be(Some("Player2")) + } + "indicate to observers that they should show the help" in { + controller.onHelp() + dummyObserver.updateData.get.getAction should be(UpdateAction.SHOW_HELP) + } + "indicate to observers that they should start the game" in { + controller.onStartGame() + dummyObserver.updateData.get.getAction should be(UpdateAction.SHOW_GAME) + gameState.currentQuestion should not be None + gameState.currentQuestionTime should not be None + } + "indicate to observers that they should close" in { + controller.onClose() + dummyObserver.updateData.get.getAction should be( + UpdateAction.CLOSE_APPLICATION) + } + "be able to reset game state" in { + controller.onPlayerActionRedo() + gameState.playerCount() should be(2) + gameState.currentQuestion should not be None + gameState.currentQuestionTime should not be None + dummyObserver.updateData.get.getAction should not be UpdateAction.BEGIN + + controller.reset() + gameState.playerCount() should be(1) + gameState.currentQuestion should be(None) + gameState.currentQuestionTime should be(None) + dummyObserver.updateData.get.getAction should be(UpdateAction.BEGIN) + } + "add correctly and wrongly answered questions to player and move on to next question" in { + gameState.players.head.correctAnswers.length should be(0) + gameState.players.head.wrongAnswers.length should be(0) + controller.onStartGame() + + val question1 = gameState.currentQuestion + + controller.onAnswerChosen(2) + + gameState.players.head.correctAnswers.length should be(1) + + controller.onAnswerChosen(1) + + gameState.players.head.wrongAnswers.length should be(1) + dummyObserver.updateData.get.getAction should be( + UpdateAction.SHOW_RESULT) + } + "update question timer" in { + controller.reset() + controller.onStartGame() + val time = gameState.currentQuestionTime + Thread.sleep(2000) + gameState.currentQuestionTime should not be time + } + "remove observers correctly" in { + controller.reset() + controller.onHelp() + val currentAction = dummyObserver.updateData.get.getAction + currentAction should not be UpdateAction.SHOW_GAME + controller.removeObserver(dummyObserver) + controller.onStartGame() + dummyObserver.updateData.get.getAction should not be UpdateAction.SHOW_GAME + dummyObserver.updateData.get.getAction should be(currentAction) + } + "throw when adding or removing too many players" in { + controller.reset() + controller.onAddPlayer(None) + assertThrows[TooManyPlayersException] { + controller.onAddPlayer(Some("invalidthirdplayer")) } - "constructed with no questions" should { - val newState = GameImpl(questions = questionList) - newState.questions = List() - val controller = new ControllerImpl(newState) + controller.reset() + assertThrows[NotEnoughPlayersException] { + controller.onRemovePlayer("Player1") + } + } + "throw when adding a already existing or removing a non existing player" in { + assertThrows[PlayerExistsException] { + controller.onAddPlayer(Some("Player1")) + } - "throw on start game" in { - assertThrows[IllegalStateException] { - controller.onStartGame() - } - } + controller.onAddPlayer(None) + assertThrows[PlayerNotExistingException] { + controller.onRemovePlayer("nonexistingplayer") + } + } + "throw when internal objects throw" in { + controller.reset() + assertThrows[ControllerProcedureFailed] { + controller.onAddPlayer(Some("Player With Space")) } - "constructed with factory method" should { - val manualController = new ControllerImpl(gameState) - "be valid" in { - val controller = Controller.create(gameState) - } + } + } + "constructed with very small questions timers" should { + "show next question and results when timer runs out" in { + val tempJsonString = jsonString.replace("\"time\": 60", "\"time\": 1") + val tempJson = Json.parse(tempJsonString) + val tempList = Json.fromJson[List[Question]](tempJson).getOrElse(List()) + val tempGame = GameImpl(questions = tempList) + val tempController = Controller.create(tempGame) + val tempObserver = new DummyObserver() + + tempController.addObserver(tempObserver) + tempController.onAddPlayer(None) + + tempController.onStartGame() + Thread.sleep(3000) + + tempObserver.updateData.get.getAction should be( + UpdateAction.SHOW_RESULT) + tempGame.players.foreach(p => p.wrongAnswers.length should be(2)) + } + } + "constructed with no questions" should { + val newState = GameImpl(questions = questionList) + newState.questions = List() + + val controller = new ControllerImpl(newState) + + "throw on start game" in { + assertThrows[IllegalStateException] { + controller.onStartGame() } + } + } + "constructed with factory method" should { + val manualController = new ControllerImpl(gameState) + "be valid" in { + val controller = Controller.create(gameState) + } } + } } diff --git a/src/test/scala/de/htwg/se/learn_duel/model/AnswerSpec.scala b/src/test/scala/de/htwg/se/learn_duel/model/AnswerSpec.scala index 7bb7930..b1df916 100644 --- a/src/test/scala/de/htwg/se/learn_duel/model/AnswerSpec.scala +++ b/src/test/scala/de/htwg/se/learn_duel/model/AnswerSpec.scala @@ -8,25 +8,24 @@ import play.api.libs.json.Json @RunWith(classOf[JUnitRunner]) class AnswerSpec extends WordSpec with Matchers { - "A Answer" when { - "new" should { - val answer = AnswerImpl(0, "text") - "have an ID" in { - answer.id should be(0) - } - "have a text" in { - answer.text should be("text") - } - } - "serialized to JSON" should { - "be correct" in { - val answer = AnswerImpl(0, "text") + "A Answer" when { + "new" should { + val answer = AnswerImpl(0, "text") + "have an ID" in { + answer.id should be(0) + } + "have a text" in { + answer.text should be("text") + } + } + "serialized to JSON" should { + "be correct" in { + val answer = AnswerImpl(0, "text") - val jsonValue = Json.parse("{\"id\": 0, \"text\": \"text\"}") + val jsonValue = Json.parse("{\"id\": 0, \"text\": \"text\"}") - Json.toJson(answer) should be(jsonValue) - } - } + Json.toJson(answer) should be(jsonValue) + } } + } } - diff --git a/src/test/scala/de/htwg/se/learn_duel/model/GameSpec.scala b/src/test/scala/de/htwg/se/learn_duel/model/GameSpec.scala index 03f6770..70b3031 100644 --- a/src/test/scala/de/htwg/se/learn_duel/model/GameSpec.scala +++ b/src/test/scala/de/htwg/se/learn_duel/model/GameSpec.scala @@ -1,6 +1,11 @@ package de.htwg.se.learn_duel.model -import de.htwg.se.learn_duel.model.impl.{Answer => AnswerImpl, Game => GameImpl, Player => PlayerImpl, Question => QuestionImpl} +import de.htwg.se.learn_duel.model.impl.{ + Answer => AnswerImpl, + Game => GameImpl, + Player => PlayerImpl, + Question => QuestionImpl +} import org.junit.runner.RunWith import org.scalatest._ import org.scalatest.junit.JUnitRunner @@ -8,81 +13,81 @@ import play.api.libs.json.Json @RunWith(classOf[JUnitRunner]) class GameSpec extends WordSpec with Matchers { - "A Game" when { - "new" should { - val game = GameImpl() + "A Game" when { + "new" should { + val game = GameImpl() - "have no questions" in { - game.questions.length should be(0) - } - "have a standard player" in { - game.players.length should be(1) - } - "have no current question" in { - game.currentQuestion should be(None) - } - "have no current question time" in { - game.currentQuestionTime should be(None) - } - "should have a help text" in { - game.helpText should be(List( - "Learn Duel is based on QuizDuel and works in a similar fashion, but with a new twist:\nYou play with questions based on your school or study assignments.", - "For now, there is only local play, but online features will be added later.\nIf you are playing alone, the answers can be selected with the mouse or the keys 1-4.\nIn local multiplayer mode player 1 can specify his answer with the keys 1-4 and\nplayer 2 with the keys 6-9.", - "Future features:", - "* define your own questions", - "* play with up to 3 friends online and compete against each other" - )) - } - } - "new with supplied questions" should { - val answers = List(AnswerImpl(0, "text"), AnswerImpl(1, "text2")) - val question1 = QuestionImpl(0, "text1", 20, answers, 0, 20) - val question2 = QuestionImpl(1, "text2", 30, answers, 0, 30) - val questionList = List(question1, question2) - val game = GameImpl(questions = questionList) + "have no questions" in { + game.questions.length should be(0) + } + "have a standard player" in { + game.players.length should be(1) + } + "have no current question" in { + game.currentQuestion should be(None) + } + "have no current question time" in { + game.currentQuestionTime should be(None) + } + "should have a help text" in { + game.helpText should be(List( + "Learn Duel is based on QuizDuel and works in a similar fashion, but with a new twist:\nYou play with questions based on your school or study assignments.", + "For now, there is only local play, but online features will be added later.\nIf you are playing alone, the answers can be selected with the mouse or the keys 1-4.\nIn local multiplayer mode player 1 can specify his answer with the keys 1-4 and\nplayer 2 with the keys 6-9.", + "Future features:", + "* define your own questions", + "* play with up to 3 friends online and compete against each other" + )) + } + } + "new with supplied questions" should { + val answers = List(AnswerImpl(0, "text"), AnswerImpl(1, "text2")) + val question1 = QuestionImpl(0, "text1", 20, answers, 0, 20) + val question2 = QuestionImpl(1, "text2", 30, answers, 0, 30) + val questionList = List(question1, question2) + val game = GameImpl(questions = questionList) - "have these questions" in { - game.questions should be(questionList) - } - } - "serialized to JSON" should { - "be correct" in { - val game = GameImpl() + "have these questions" in { + game.questions should be(questionList) + } + } + "serialized to JSON" should { + "be correct" in { + val game = GameImpl() - val jsonValue = Json.parse("{\"helpText\":[\"Learn Duel is based on QuizDuel and works in a similar fashion, but with a new twist:\\nYou play with questions based on your school or study assignments.\",\"For now, there is only local play, but online features will be added later.\\nIf you are playing alone, the answers can be selected with the mouse or the keys 1-4.\\nIn local multiplayer mode player 1 can specify his answer with the keys 1-4 and\\nplayer 2 with the keys 6-9.\",\"Future features:\",\"* define your own questions\",\"* play with up to 3 friends online and compete against each other\"],\"players\":[{\"name\":\"Player1\",\"points\":0,\"correctAnswers\":[],\"wrongAnswers\":[]}],\"questions\":[],\"currentQuestion\":null,\"currentQuestionTime\":null}") + val jsonValue = Json.parse( + "{\"helpText\":[\"Learn Duel is based on QuizDuel and works in a similar fashion, but with a new twist:\\nYou play with questions based on your school or study assignments.\",\"For now, there is only local play, but online features will be added later.\\nIf you are playing alone, the answers can be selected with the mouse or the keys 1-4.\\nIn local multiplayer mode player 1 can specify his answer with the keys 1-4 and\\nplayer 2 with the keys 6-9.\",\"Future features:\",\"* define your own questions\",\"* play with up to 3 friends online and compete against each other\"],\"players\":[{\"name\":\"Player1\",\"points\":0,\"correctAnswers\":[],\"wrongAnswers\":[]}],\"questions\":[],\"currentQuestion\":null,\"currentQuestionTime\":null}") - Json.toJson(game) should be(jsonValue) - } - } - "constructed" should { - val player = PlayerImpl("player") - val question = QuestionImpl(0, "text1", 20, List(), 0, 20) - val game = GameImpl() + Json.toJson(game) should be(jsonValue) + } + } + "constructed" should { + val player = PlayerImpl("player") + val question = QuestionImpl(0, "text1", 20, List(), 0, 20) + val game = GameImpl() - "correctly add players" in { - game.playerCount() should be(1) - game.addPlayer(player) - game.playerCount() should be(2) - } + "correctly add players" in { + game.playerCount() should be(1) + game.addPlayer(player) + game.playerCount() should be(2) + } - "correctly remove players" in { - game.playerCount() should be(2) - game.removePlayer(player) - game.playerCount() should be(1) - } + "correctly remove players" in { + game.playerCount() should be(2) + game.removePlayer(player) + game.playerCount() should be(1) + } - "correclty add questions" in { - game.questionCount() should be(0) - game.addQuestion(question) - game.questionCount() should be(1) - } + "correclty add questions" in { + game.questionCount() should be(0) + game.addQuestion(question) + game.questionCount() should be(1) + } - "correctly remove questions" in { - game.questionCount() should be(1) - game.removeQuestion(question) - game.questionCount() should be(0) - } - } + "correctly remove questions" in { + game.questionCount() should be(1) + game.removeQuestion(question) + game.questionCount() should be(0) + } } + } } - diff --git a/src/test/scala/de/htwg/se/learn_duel/model/PlayerSpec.scala b/src/test/scala/de/htwg/se/learn_duel/model/PlayerSpec.scala index fb515d4..ea41e83 100644 --- a/src/test/scala/de/htwg/se/learn_duel/model/PlayerSpec.scala +++ b/src/test/scala/de/htwg/se/learn_duel/model/PlayerSpec.scala @@ -10,61 +10,61 @@ import play.api.libs.json.Json @RunWith(classOf[JUnitRunner]) class PlayerSpec extends WordSpec with Matchers { - "A Player" when { - "new" should { - val player = PlayerImpl("YourName") - "have a name" in { - player.name should be("YourName") - } - "have a nice String representation" in { - player.toString should be("YourName") - } - "have no points" in { - player.points should be(0) - } - "have no correct answers" in { - player.correctAnswers.length should be (0) - } - "have no wrong answers" in { - player.wrongAnswers.length should be (0) - } - } + "A Player" when { + "new" should { + val player = PlayerImpl("YourName") + "have a name" in { + player.name should be("YourName") + } + "have a nice String representation" in { + player.toString should be("YourName") + } + "have no points" in { + player.points should be(0) + } + "have no correct answers" in { + player.correctAnswers.length should be(0) + } + "have no wrong answers" in { + player.wrongAnswers.length should be(0) + } + } - "constructed by factory method" should { - "be the same as constructing it directly" in { - val player = Player.create("YourName") - player.name should be("YourName") - player.toString should be("YourName") - player.points should be(0) - player.correctAnswers.length should be (0) - player.wrongAnswers.length should be (0) - } - } + "constructed by factory method" should { + "be the same as constructing it directly" in { + val player = Player.create("YourName") + player.name should be("YourName") + player.toString should be("YourName") + player.points should be(0) + player.correctAnswers.length should be(0) + player.wrongAnswers.length should be(0) + } + } - "constructed without name" should { - "not accept emtpy names" in { - assertThrows[InvalidParameterException] { - val player = PlayerImpl("") - } - } + "constructed without name" should { + "not accept emtpy names" in { + assertThrows[InvalidParameterException] { + val player = PlayerImpl("") } + } + } - "constructed with a name with withspace" should { - "not accept names with whitespace" in { - assertThrows[InvalidParameterException] { - val player = PlayerImpl("Your Name") - } - } + "constructed with a name with withspace" should { + "not accept names with whitespace" in { + assertThrows[InvalidParameterException] { + val player = PlayerImpl("Your Name") } - "serialized to JSON" should { - "be correct" in { - val player = PlayerImpl("YourName") + } + } + "serialized to JSON" should { + "be correct" in { + val player = PlayerImpl("YourName") - val jsonValue = Json.parse("{ \"name\": \"YourName\", \"points\": 0, \"correctAnswers\": [], \"wrongAnswers\": [] }") + val jsonValue = Json.parse( + "{ \"name\": \"YourName\", \"points\": 0, \"correctAnswers\": [], \"wrongAnswers\": [] }") - Json.toJson(player) should be(jsonValue) - } - } + Json.toJson(player) should be(jsonValue) + } } + } } - diff --git a/src/test/scala/de/htwg/se/learn_duel/model/QuestionSpec.scala b/src/test/scala/de/htwg/se/learn_duel/model/QuestionSpec.scala index 68fb9dc..ffb6b09 100644 --- a/src/test/scala/de/htwg/se/learn_duel/model/QuestionSpec.scala +++ b/src/test/scala/de/htwg/se/learn_duel/model/QuestionSpec.scala @@ -1,6 +1,9 @@ package de.htwg.se.learn_duel.model -import de.htwg.se.learn_duel.model.impl.{Answer => AnswerImpl, Question => QuestionImpl} +import de.htwg.se.learn_duel.model.impl.{ + Answer => AnswerImpl, + Question => QuestionImpl +} import org.junit.runner.RunWith import org.scalatest._ import org.scalatest.junit.JUnitRunner @@ -8,39 +11,39 @@ import play.api.libs.json.Json @RunWith(classOf[JUnitRunner]) class QuestionSpec extends WordSpec with Matchers { - "A Question" when { - "new" should { - val answers = List(AnswerImpl(0, "text"), AnswerImpl(1, "text2")) - val question = QuestionImpl(0, "text", 20, answers, 0, 20) - "have a ID" in { - question.id should be(0) - } - "have a text" in { - question.text should be("text") - } - "have a point value" in { - question.points should be(20) - } - "have a list of answers" in { - question.answers should be (answers) - } - "have a correct answer" in { - question.correctAnswer should be(0) - } - "have a time" in { - question.time should be(20) - } - } - "serialized to JSON" should { - "be correct" in { - val answers = List(AnswerImpl(0, "text")) - val question = QuestionImpl(0, "text", 20, answers, 0, 20) + "A Question" when { + "new" should { + val answers = List(AnswerImpl(0, "text"), AnswerImpl(1, "text2")) + val question = QuestionImpl(0, "text", 20, answers, 0, 20) + "have a ID" in { + question.id should be(0) + } + "have a text" in { + question.text should be("text") + } + "have a point value" in { + question.points should be(20) + } + "have a list of answers" in { + question.answers should be(answers) + } + "have a correct answer" in { + question.correctAnswer should be(0) + } + "have a time" in { + question.time should be(20) + } + } + "serialized to JSON" should { + "be correct" in { + val answers = List(AnswerImpl(0, "text")) + val question = QuestionImpl(0, "text", 20, answers, 0, 20) - val jsonValue = Json.parse("{\"id\": 0, \"text\": \"text\", \"points\": 20, \"answers\": [{ \"id\": 0, \"text\": \"text\" }],\"correctAnswer\": 0, \"time\": 20}") + val jsonValue = Json.parse( + "{\"id\": 0, \"text\": \"text\", \"points\": 20, \"answers\": [{ \"id\": 0, \"text\": \"text\" }],\"correctAnswer\": 0, \"time\": 20}") - Json.toJson(question) should be(jsonValue) - } - } + Json.toJson(question) should be(jsonValue) + } } + } } - diff --git a/src/test/scala/de/htwg/se/learn_duel/model/command/CommandInvokerSpec.scala b/src/test/scala/de/htwg/se/learn_duel/model/command/CommandInvokerSpec.scala index 1bb05ed..162bb8e 100644 --- a/src/test/scala/de/htwg/se/learn_duel/model/command/CommandInvokerSpec.scala +++ b/src/test/scala/de/htwg/se/learn_duel/model/command/CommandInvokerSpec.scala @@ -1,84 +1,86 @@ package de.htwg.se.learn_duel.model.command -import de.htwg.se.learn_duel.model.command.impl.{PlayerAddCommand, CommandInvoker => CommandInvokerImpl} +import de.htwg.se.learn_duel.model.command.impl.{ + PlayerAddCommand, + CommandInvoker => CommandInvokerImpl +} import org.junit.runner.RunWith import org.scalatest._ import org.scalatest.junit.JUnitRunner @RunWith(classOf[JUnitRunner]) class CommandInvokerSpec extends WordSpec with Matchers { - "A CommandInvoker" when { - "constructed" should { - var addCalled = false - var removeCalled = false - val nameAfterAdd = "playerAdd" - val playerAddCommand = PlayerAddCommand( - Some("player"), - _ => { addCalled = true; nameAfterAdd }, - _ => { removeCalled = true; } - ) - val commandInvoker = CommandInvokerImpl() + "A CommandInvoker" when { + "constructed" should { + var addCalled = false + var removeCalled = false + val nameAfterAdd = "playerAdd" + val playerAddCommand = PlayerAddCommand( + Some("player"), + _ => { addCalled = true; nameAfterAdd }, + _ => { removeCalled = true; } + ) + val commandInvoker = CommandInvokerImpl() - "have no saved commands" in { - commandInvoker.undoCommands.length should be(0) - commandInvoker.redoCommands.length should be(0) - } - "execute given commands and save them" in { - addCalled should be(false) + "have no saved commands" in { + commandInvoker.undoCommands.length should be(0) + commandInvoker.redoCommands.length should be(0) + } + "execute given commands and save them" in { + addCalled should be(false) - commandInvoker.execute(playerAddCommand) + commandInvoker.execute(playerAddCommand) - addCalled should be(true) - commandInvoker.redoCommands.length should be(0) - commandInvoker.undoCommands.length should be(1) - } - "be able to undo command" in { - removeCalled should be(false) - commandInvoker.redoCommands.length should be(0) - commandInvoker.undoCommands.length should be(1) + addCalled should be(true) + commandInvoker.redoCommands.length should be(0) + commandInvoker.undoCommands.length should be(1) + } + "be able to undo command" in { + removeCalled should be(false) + commandInvoker.redoCommands.length should be(0) + commandInvoker.undoCommands.length should be(1) - commandInvoker.undo() + commandInvoker.undo() - removeCalled should be(true) - commandInvoker.redoCommands.length should be(1) - commandInvoker.undoCommands.length should be(0) - } - "not do anything on undo when there are no commands to undo" in { - commandInvoker.undoCommands.length should be(0) + removeCalled should be(true) + commandInvoker.redoCommands.length should be(1) + commandInvoker.undoCommands.length should be(0) + } + "not do anything on undo when there are no commands to undo" in { + commandInvoker.undoCommands.length should be(0) - removeCalled = false - commandInvoker.undo() + removeCalled = false + commandInvoker.undo() - removeCalled should be(false) - } - "be able to redo command" in { - addCalled = false - commandInvoker.redoCommands.length should be(1) - commandInvoker.undoCommands.length should be(0) + removeCalled should be(false) + } + "be able to redo command" in { + addCalled = false + commandInvoker.redoCommands.length should be(1) + commandInvoker.undoCommands.length should be(0) - commandInvoker.redo() + commandInvoker.redo() - addCalled should be(true) - commandInvoker.redoCommands.length should be(0) - commandInvoker.undoCommands.length should be(1) - } - "not do anything on redo when there are no commands to redo" in { - commandInvoker.redoCommands.length should be(0) + addCalled should be(true) + commandInvoker.redoCommands.length should be(0) + commandInvoker.undoCommands.length should be(1) + } + "not do anything on redo when there are no commands to redo" in { + commandInvoker.redoCommands.length should be(0) - addCalled = false - commandInvoker.redo() + addCalled = false + commandInvoker.redo() - addCalled should be(false) - } - } + addCalled should be(false) + } + } - "constructed with factory method" should { - val commandInvoker = CommandInvoker.create - "have no saved commands" in { - commandInvoker.undoCommands.length should be(0) - commandInvoker.redoCommands.length should be(0) - } - } + "constructed with factory method" should { + val commandInvoker = CommandInvoker.create + "have no saved commands" in { + commandInvoker.undoCommands.length should be(0) + commandInvoker.redoCommands.length should be(0) + } } + } } - diff --git a/src/test/scala/de/htwg/se/learn_duel/model/command/PlayerAddCommandSpec.scala b/src/test/scala/de/htwg/se/learn_duel/model/command/PlayerAddCommandSpec.scala index a992299..e20e05d 100644 --- a/src/test/scala/de/htwg/se/learn_duel/model/command/PlayerAddCommandSpec.scala +++ b/src/test/scala/de/htwg/se/learn_duel/model/command/PlayerAddCommandSpec.scala @@ -7,34 +7,33 @@ import org.scalatest.junit.JUnitRunner @RunWith(classOf[JUnitRunner]) class PlayerAddCommandSpec extends WordSpec with Matchers { - "A PlayerAddCommand" when { - "constructed" should { - var addCalled = false - var removeCalled = false - val nameAfterAdd = "playerAdd" - val playerAddCommand = PlayerAddCommand( - Some("player"), - _ => { addCalled = true; nameAfterAdd }, - _ => { removeCalled = true; } - ) + "A PlayerAddCommand" when { + "constructed" should { + var addCalled = false + var removeCalled = false + val nameAfterAdd = "playerAdd" + val playerAddCommand = PlayerAddCommand( + Some("player"), + _ => { addCalled = true; nameAfterAdd }, + _ => { removeCalled = true; } + ) - "call the add function on execute" in { - playerAddCommand.execute() - addCalled should be(true) - playerAddCommand.actualName should be(nameAfterAdd) - } + "call the add function on execute" in { + playerAddCommand.execute() + addCalled should be(true) + playerAddCommand.actualName should be(nameAfterAdd) + } - "call the remove function on undo" in { - playerAddCommand.undo() - removeCalled should be(true) - } + "call the remove function on undo" in { + playerAddCommand.undo() + removeCalled should be(true) + } - "call the add function on redo" in { - addCalled = false - playerAddCommand.redo() - addCalled should be(true) - } - } + "call the add function on redo" in { + addCalled = false + playerAddCommand.redo() + addCalled should be(true) + } } + } } - diff --git a/src/test/scala/de/htwg/se/learn_duel/model/command/PlayerRemoveCommandSpec.scala b/src/test/scala/de/htwg/se/learn_duel/model/command/PlayerRemoveCommandSpec.scala index e061632..c0d7dc1 100644 --- a/src/test/scala/de/htwg/se/learn_duel/model/command/PlayerRemoveCommandSpec.scala +++ b/src/test/scala/de/htwg/se/learn_duel/model/command/PlayerRemoveCommandSpec.scala @@ -7,32 +7,31 @@ import org.scalatest.junit.JUnitRunner @RunWith(classOf[JUnitRunner]) class PlayerRemoveCommandSpec extends WordSpec with Matchers { - "A PlayerRemoveCommand" when { - "constructed" should { - var addCalled = false - var removeCalled = false - val playerRemoveCommand = PlayerRemoveCommand( - "player", - _ => { removeCalled = true; }, - _ => { addCalled = true; "player" } - ) + "A PlayerRemoveCommand" when { + "constructed" should { + var addCalled = false + var removeCalled = false + val playerRemoveCommand = PlayerRemoveCommand( + "player", + _ => { removeCalled = true; }, + _ => { addCalled = true; "player" } + ) - "call the remove function on execute" in { - playerRemoveCommand.execute() - removeCalled should be(true) - } + "call the remove function on execute" in { + playerRemoveCommand.execute() + removeCalled should be(true) + } - "call the add function on undo" in { - playerRemoveCommand.undo() - addCalled should be(true) - } + "call the add function on undo" in { + playerRemoveCommand.undo() + addCalled should be(true) + } - "call the remove function on redo" in { - removeCalled = false - playerRemoveCommand.redo() - removeCalled should be(true) - } - } + "call the remove function on redo" in { + removeCalled = false + playerRemoveCommand.redo() + removeCalled should be(true) + } } + } } - From a17493c3082772ef4889f20acb1cecd279e00ab6 Mon Sep 17 00:00:00 2001 From: SpurNut Date: Wed, 28 Mar 2018 13:11:54 +0200 Subject: [PATCH 07/21] added ensime config- and cache-files to .gitignore --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index eac3d09..b68bb14 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,9 @@ project/.sbtserver # Application learn-duel.log + +#Ensime +.ensime +.ensime_cache/ +ensime-langserver.log +pc.stdout.log \ No newline at end of file From f281318411db5f0b560f7da3c3ff1312d3f3c3ea Mon Sep 17 00:00:00 2001 From: SpurNut Date: Wed, 28 Mar 2018 15:31:58 +0200 Subject: [PATCH 08/21] added tasks configuration for vscode --- .vscode/tasks.json | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .vscode/tasks.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..7cb2303 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,33 @@ +{ + "version": "2.0.0", + "command": "sbt", + "type": "shell", + "presentation": { + "reveal": "always" + }, + "tasks": [ + { + "label": "build", + "type": "shell", + "command": "sbt compile", + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "test", + "type": "shell", + "command": "sbt test", + "group": { + "kind": "test", + "isDefault": true + } + }, + { + "label": "run", + "type": "shell", + "command": "sbt 'run -Dhttps.port=9443 -Dhttp.port=disabled'" + } + ] +} From f212490f30aa80de1cd0503e64e9e29dc3061a59 Mon Sep 17 00:00:00 2001 From: SpurNut Date: Wed, 28 Mar 2018 15:42:24 +0200 Subject: [PATCH 09/21] fixed vscode shortcut for running the game --- .vscode/tasks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 7cb2303..c3186d8 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -27,7 +27,7 @@ { "label": "run", "type": "shell", - "command": "sbt 'run -Dhttps.port=9443 -Dhttp.port=disabled'" + "command": "sbt run" } ] } From f664a7966cac8e826eba760bc275f229f0a9baaf Mon Sep 17 00:00:00 2001 From: Jonas Reinwald Date: Wed, 4 Apr 2018 11:15:21 +0200 Subject: [PATCH 10/21] added sbt filter for source directories (needed for ensime to not generate these on each start) --- build.sbt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.sbt b/build.sbt index ee0a4a0..f4ab166 100644 --- a/build.sbt +++ b/build.sbt @@ -14,6 +14,9 @@ libraryDependencies += "com.typesafe.play" %% "play-json" % "2.6.6" libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.3" libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.7.2" +unmanagedSourceDirectories in Compile := (unmanagedSourceDirectories in Compile).value.filter{ _.exists } +unmanagedSourceDirectories in Test := (unmanagedSourceDirectories in Test).value.filter{ _.exists } + import org.scoverage.coveralls.Imports.CoverallsKeys._ coverageExcludedPackages := ".*view.*;.*GuiceModule.*;.*LearnDuel.*" coverageEnabled := true From b965bc80d51c64fde6ff10a40d2c884e39455696 Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Wed, 4 Apr 2018 14:20:50 +0200 Subject: [PATCH 11/21] Add .vscode/settings.json to .gitignore --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b68bb14..8b7e216 100644 --- a/.gitignore +++ b/.gitignore @@ -49,4 +49,8 @@ learn-duel.log .ensime .ensime_cache/ ensime-langserver.log -pc.stdout.log \ No newline at end of file +pc.stdout.log + +#VS Code settings file +.vscode/settings.json + From d4379a6d06017c578d071fc9bf25f0dcf76935e2 Mon Sep 17 00:00:00 2001 From: Julian Raufelder Date: Wed, 4 Apr 2018 14:22:39 +0200 Subject: [PATCH 12/21] Add error handling if Gui start fail Co-authored-by: DonatJR Co-authored-by: SpurNut --- src/main/scala/de/htwg/se/learn_duel/view/UI.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/scala/de/htwg/se/learn_duel/view/UI.scala b/src/main/scala/de/htwg/se/learn_duel/view/UI.scala index 4fe2eee..08c6560 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/UI.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/UI.scala @@ -31,6 +31,11 @@ object GUI { // run GUI on its own thread Future { gui.main(Array()) + } recover { + case _ => + if (latch.getCount() != 0) { + latch.countDown(); + } } // wait for initialization of JFXApp to be done From d86020f3a85ec5c7bea9022c4057ceda3904c976 Mon Sep 17 00:00:00 2001 From: Jonas Reinwald Date: Mon, 18 Jun 2018 17:52:32 +0200 Subject: [PATCH 13/21] fix incorrect input parsing in tui --- src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala b/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala index a41aafe..87b5390 100644 --- a/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala +++ b/src/main/scala/de/htwg/se/learn_duel/view/impl/TUI.scala @@ -70,7 +70,11 @@ class TUI(controller: Controller) extends UI with Observer with LazyLogging { // scalastyle:off override def update(updateParam: UpdateData): Unit = { updateParam.getAction match { - case UpdateAction.BEGIN => displayMenu() + case UpdateAction.BEGIN => { + displayMenu() + inMenu = true + inGame = false + } case UpdateAction.CLOSE_APPLICATION => stopProcessingInput = true case UpdateAction.SHOW_HELP => logger.info(updateParam.getState.helpText.mkString("\n\n")) @@ -91,6 +95,8 @@ class TUI(controller: Controller) extends UI with Observer with LazyLogging { ) case UpdateAction.SHOW_RESULT => displayResult(updateParam.getState.players) + inMenu = false + inGame = false case _ => } } From b78107963ebddc71557417e435ff6b551db20812 Mon Sep 17 00:00:00 2001 From: Jonas Reinwald Date: Wed, 8 Apr 2020 10:17:08 +0200 Subject: [PATCH 14/21] fix badges and update sbt --- README.md | 41 ++++++++++++++++++++++++---------------- project/build.properties | 2 +- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index eba1ed6..2986101 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,25 @@ -# Learn Duel [![Build Status](https://travis-ci.org/DonatJR/learn-duel.svg?branch=develop)](https://travis-ci.org/DonatJR/learn-duel) [![Coverage Status](https://coveralls.io/repos/github/DonatJR/learn-duel/badge.svg?branch=develop)](https://coveralls.io/github/DonatJR/learn-duel?branch=develop) - -## About -Learn Duel is based on QuizDuel and works in a similar fashion, but with a new twist: -You play with questions based on your school or study assignments. - -## Current features -For now, there is only local play, but online features will be added later. -If you are playing alone, the answers can be selected with the mouse or the keys 1-4. -In local multiplayer mode player 1 can specify his answer with the keys 1-4 and -player 2 with the keys 6-9. - - -## Future features: - - define your own questions - - play with up to 3 friends online and compete against each other +# Learn Duel + +## Build status +Travis: +* develop [![Build Status](https://travis-ci.org/bb30/learn-duel.svg?branch=develop)](https://travis-ci.org/bb30/learn-duel) +* master [![Build Status](https://travis-ci.org/bb30/learn-duel.svg?branch=master)](https://travis-ci.org/bb30/learn-duel) + +codecov: +* develop [![codecov](https://codecov.io/gh/bb30/learn-duel/branch/develop/graph/badge.svg)](https://codecov.io/gh/bb30/learn-duel) +* master [![codecov](https://codecov.io/gh/bb30/learn-duel/branch/master/graph/badge.svg)](https://codecov.io/gh/bb30/learn-duel) + +## About +Learn Duel is based on QuizDuel and works in a similar fashion, but with a new twist: +You play with questions based on your school or study assignments. + +## Current features +For now, there is only local play, but online features will be added later. +If you are playing alone, the answers can be selected with the mouse or the keys 1-4. +In local multiplayer mode player 1 can specify his answer with the keys 1-4 and +player 2 with the keys 6-9. + + +## Future features: + - define your own questions + - play with up to 3 friends online and compete against each other diff --git a/project/build.properties b/project/build.properties index 9abea12..e0441b7 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.0.3 +sbt.version=1.3.9 From 9cae353210009077cc8981db27d95f4ac55b4339 Mon Sep 17 00:00:00 2001 From: Jonas Reinwald Date: Wed, 8 Apr 2020 10:27:04 +0200 Subject: [PATCH 15/21] downgrade sbt version --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index e0441b7..42e2ce8 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.3.9 +sbt.version=1.3.8 From 8066e9b0d57f79ba7582c6445a6d5cbb7f9f4009 Mon Sep 17 00:00:00 2001 From: Jonas Reinwald Date: Wed, 8 Apr 2020 10:36:22 +0200 Subject: [PATCH 16/21] use explicit jdk version on travis --- .travis.yml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index cf8e207..a6fab70 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,12 @@ -language: scala - -scala: - - 2.12.4 - -script: - - sbt clean coverage test coverageReport -after_success: - - sbt coveralls +language: scala + +scala: + - 2.12.4 + +jdk: + - oraclejdk8 + +script: + - sbt clean coverage test coverageReport +after_success: + - sbt coveralls From efeea48adb44863cfa6f14e1b67e9b0980fa97fa Mon Sep 17 00:00:00 2001 From: Jonas Reinwald Date: Wed, 8 Apr 2020 10:39:45 +0200 Subject: [PATCH 17/21] use openjdk on travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a6fab70..b973649 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ scala: - 2.12.4 jdk: - - oraclejdk8 + - openjdk8 script: - sbt clean coverage test coverageReport From c77ecb22711fa3ce3bf468267cc9923fb1db0f37 Mon Sep 17 00:00:00 2001 From: Jonas Reinwald Date: Wed, 8 Apr 2020 10:53:09 +0200 Subject: [PATCH 18/21] upgrade scala version --- build.sbt | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/build.sbt b/build.sbt index f4ab166..b8b5e0e 100644 --- a/build.sbt +++ b/build.sbt @@ -1,23 +1,23 @@ -name := "LearnDuel" -organization := "de.htwg.se" -version := "0.0.1" -scalaVersion := "2.12.4" - -libraryDependencies += "org.scalactic" %% "scalactic" % "3.0.4" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.4" % "test" -libraryDependencies += "junit" % "junit" % "4.12" % "test" - -libraryDependencies += "org.scalafx" %% "scalafx" % "8.0.144-R12" -libraryDependencies += "com.google.inject" % "guice" % "4.1.0" -libraryDependencies += "net.codingwell" %% "scala-guice" % "4.1.0" -libraryDependencies += "com.typesafe.play" %% "play-json" % "2.6.6" -libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.3" -libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.7.2" - -unmanagedSourceDirectories in Compile := (unmanagedSourceDirectories in Compile).value.filter{ _.exists } -unmanagedSourceDirectories in Test := (unmanagedSourceDirectories in Test).value.filter{ _.exists } - -import org.scoverage.coveralls.Imports.CoverallsKeys._ -coverageExcludedPackages := ".*view.*;.*GuiceModule.*;.*LearnDuel.*" -coverageEnabled := true -coverageHighlighting := true +name := "LearnDuel" +organization := "de.htwg.se" +version := "0.0.1" +scalaVersion := "2.12.5" + +libraryDependencies += "org.scalactic" %% "scalactic" % "3.0.4" +libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.4" % "test" +libraryDependencies += "junit" % "junit" % "4.12" % "test" + +libraryDependencies += "org.scalafx" %% "scalafx" % "8.0.144-R12" +libraryDependencies += "com.google.inject" % "guice" % "4.1.0" +libraryDependencies += "net.codingwell" %% "scala-guice" % "4.1.0" +libraryDependencies += "com.typesafe.play" %% "play-json" % "2.6.6" +libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.3" +libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.7.2" + +unmanagedSourceDirectories in Compile := (unmanagedSourceDirectories in Compile).value.filter{ _.exists } +unmanagedSourceDirectories in Test := (unmanagedSourceDirectories in Test).value.filter{ _.exists } + +import org.scoverage.coveralls.Imports.CoverallsKeys._ +coverageExcludedPackages := ".*view.*;.*GuiceModule.*;.*LearnDuel.*" +coverageEnabled := true +coverageHighlighting := true From a009bc71049679db41d03fe4906698d1d7594774 Mon Sep 17 00:00:00 2001 From: Jonas Reinwald Date: Wed, 8 Apr 2020 11:13:51 +0200 Subject: [PATCH 19/21] upgrade ScalaFX version --- .travis.yml | 2 +- build.sbt | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b973649..8b902cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ scala: - 2.12.4 jdk: - - openjdk8 + - openjdk11 script: - sbt clean coverage test coverageReport diff --git a/build.sbt b/build.sbt index b8b5e0e..ac6f1f1 100644 --- a/build.sbt +++ b/build.sbt @@ -7,7 +7,23 @@ libraryDependencies += "org.scalactic" %% "scalactic" % "3.0.4" libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.4" % "test" libraryDependencies += "junit" % "junit" % "4.12" % "test" -libraryDependencies += "org.scalafx" %% "scalafx" % "8.0.144-R12" +// Add dependency on ScalaFX library +libraryDependencies += "org.scalafx" %% "scalafx" % "12.0.2-R18" + +// Determine OS version of JavaFX binaries +lazy val osName = System.getProperty("os.name") match { + case n if n.startsWith("Linux") => "linux" + case n if n.startsWith("Mac") => "mac" + case n if n.startsWith("Windows") => "win" + case _ => throw new Exception("Unknown platform!") +} + +// Add dependency on JavaFX libraries, OS dependent +lazy val javaFXModules = Seq("base", "controls", "fxml", "graphics", "media", "swing", "web") +libraryDependencies ++= javaFXModules.map( m => + "org.openjfx" % s"javafx-$m" % "12.0.2" classifier osName +) + libraryDependencies += "com.google.inject" % "guice" % "4.1.0" libraryDependencies += "net.codingwell" %% "scala-guice" % "4.1.0" libraryDependencies += "com.typesafe.play" %% "play-json" % "2.6.6" From fa4caa17cc66daeddbfe4003edd6dc8b2e268ea2 Mon Sep 17 00:00:00 2001 From: Jonas Reinwald Date: Wed, 8 Apr 2020 11:24:36 +0200 Subject: [PATCH 20/21] and another Scala upgrade --- .travis.yml | 2 +- build.sbt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8b902cb..b6ab996 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: scala scala: - - 2.12.4 + - 2.12.6 jdk: - openjdk11 diff --git a/build.sbt b/build.sbt index ac6f1f1..bdd44ad 100644 --- a/build.sbt +++ b/build.sbt @@ -1,7 +1,7 @@ name := "LearnDuel" organization := "de.htwg.se" version := "0.0.1" -scalaVersion := "2.12.5" +scalaVersion := "2.12.6" libraryDependencies += "org.scalactic" %% "scalactic" % "3.0.4" libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.4" % "test" From fab444c112a96a8a93f5f8c739127b510ce3fec0 Mon Sep 17 00:00:00 2001 From: Jonas Reinwald Date: Wed, 8 Apr 2020 11:33:31 +0200 Subject: [PATCH 21/21] change coverage report to codecov --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b6ab996..30b57dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,4 +9,4 @@ jdk: script: - sbt clean coverage test coverageReport after_success: - - sbt coveralls + - bash <(curl -s https://codecov.io/bash) -t 2289df37-0bc4-41a7-8803-45e20561b24f