Compare commits
11 Commits
30fb50c3b8
...
webapplica
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9f7275854 | ||
| dad604186e | |||
| 8e415390e7 | |||
| fc751af1ef | |||
|
|
9aa447f2f6 | ||
| 2b89f3d161 | |||
| c77eeff123 | |||
| 7cbb6e6ab7 | |||
|
|
6c57abb1db | ||
|
|
854189967f | ||
|
|
9098b7c4e3 |
4
.gitmodules
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[submodule "knockoutwhist"]
|
||||||
|
path = knockoutwhist
|
||||||
|
branch = webapplication
|
||||||
|
url = https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist.git
|
||||||
1
knockoutwhist
Submodule
@@ -1,9 +1,13 @@
|
|||||||
package components
|
package components
|
||||||
|
|
||||||
import de.knockoutwhist.components.DefaultConfiguration
|
import de.knockoutwhist.components.DefaultConfiguration
|
||||||
|
import controllers.Gamesession
|
||||||
|
import de.knockoutwhist.ui.UI
|
||||||
|
import de.knockoutwhist.utils.events.EventListener
|
||||||
|
|
||||||
class WebApplicationConfiguration extends DefaultConfiguration {
|
class WebApplicationConfiguration extends DefaultConfiguration {
|
||||||
|
|
||||||
|
override def uis: Set[UI] = super.uis + Gamesession
|
||||||
|
override def listener: Set[EventListener] = super.listener + Gamesession
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
57
knockoutwhistweb/app/controllers/Gamesession.scala
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import controllers.sessions.{AdvancedSession, PlayerSession, SimpleSession}
|
||||||
|
import de.knockoutwhist.cards.{Card, CardValue, Hand, Suit}
|
||||||
|
import de.knockoutwhist.control.GameLogic
|
||||||
|
import de.knockoutwhist.control.controllerBaseImpl.BaseGameLogic
|
||||||
|
import de.knockoutwhist.events.*
|
||||||
|
import de.knockoutwhist.events.player.{PlayCardEvent, PlayerEvent, ReceivedHandEvent, RequestTieChoiceEvent, RequestTrumpSuitEvent}
|
||||||
|
import de.knockoutwhist.player.AbstractPlayer
|
||||||
|
import de.knockoutwhist.rounds.Match
|
||||||
|
import de.knockoutwhist.ui.UI
|
||||||
|
import de.knockoutwhist.utils.CustomThread
|
||||||
|
import de.knockoutwhist.utils.events.{EventListener, SimpleEvent}
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
import scala.collection.mutable
|
||||||
|
|
||||||
|
class Gamesession(id: UUID) extends CustomThread with EventListener with UI {
|
||||||
|
|
||||||
|
setName("Gamesession")
|
||||||
|
private val sessions: mutable.Map[UUID, PlayerSession] = mutable.Map()
|
||||||
|
var init = false
|
||||||
|
var logic: Option[GameLogic] = None
|
||||||
|
override def instance: CustomThread = this
|
||||||
|
|
||||||
|
override def listen(event: SimpleEvent): Unit = {
|
||||||
|
runLater {
|
||||||
|
event match {
|
||||||
|
case event: PlayCardEvent =>
|
||||||
|
PodGameManager.transmit(event.player.id, event)
|
||||||
|
case event: ReceivedHandEvent =>
|
||||||
|
PodGameManager.transmit(event.player.id, event)
|
||||||
|
case event: RequestTieChoiceEvent =>
|
||||||
|
PodGameManager.transmit(event.player.id, event)
|
||||||
|
case event: RequestTrumpSuitEvent =>
|
||||||
|
PodGameManager.transmit(event.player.id, event)
|
||||||
|
case _ =>
|
||||||
|
PodGameManager.transmitAll(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def initial(gameLogic: GameLogic): Boolean = {
|
||||||
|
if (init) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
this.logic = Some(gameLogic)
|
||||||
|
init = true
|
||||||
|
start()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
def addSession(session: PlayerSession): Unit = {
|
||||||
|
sessions.put(session.id, session)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,9 +1,19 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import javax.inject._
|
import controllers.sessions.SimpleSession
|
||||||
import play.api._
|
import controllers.sessions.AdvancedSession
|
||||||
import play.api.mvc._
|
import com.google.inject.{Guice, Injector}
|
||||||
import de.knockoutwhist.KnockOutWhist
|
import de.knockoutwhist.KnockOutWhist
|
||||||
|
import de.knockoutwhist.components.Configuration
|
||||||
|
import de.knockoutwhist.rounds.Match
|
||||||
|
import di.KnockOutWebConfigurationModule
|
||||||
|
import play.api.*
|
||||||
|
import play.api.mvc.*
|
||||||
|
import play.twirl.api.Html
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
import javax.inject.*
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This controller creates an `Action` to handle HTTP requests to the
|
* This controller creates an `Action` to handle HTTP requests to the
|
||||||
@@ -13,6 +23,7 @@ import de.knockoutwhist.KnockOutWhist
|
|||||||
class HomeController @Inject()(val controllerComponents: ControllerComponents) extends BaseController {
|
class HomeController @Inject()(val controllerComponents: ControllerComponents) extends BaseController {
|
||||||
|
|
||||||
private var initial = false
|
private var initial = false
|
||||||
|
private val injector: Injector = Guice.createInjector(KnockOutWebConfigurationModule())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an Action to render an HTML page.
|
* Create an Action to render an HTML page.
|
||||||
@@ -24,18 +35,33 @@ class HomeController @Inject()(val controllerComponents: ControllerComponents) e
|
|||||||
def index(): Action[AnyContent] = {
|
def index(): Action[AnyContent] = {
|
||||||
if (!initial) {
|
if (!initial) {
|
||||||
initial = true
|
initial = true
|
||||||
KnockOutWhist.main(new Array[String](_length = 0))
|
KnockOutWhist.entry(injector.getInstance(classOf[Configuration]))
|
||||||
}
|
}
|
||||||
Action { implicit request: Request[AnyContent] => {
|
Action { implicit request =>
|
||||||
Ok(views.html.main.apply("KnockoutWhist")(views.html.))
|
Redirect("/sessions")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def sessions(): Action[AnyContent] = {
|
||||||
|
Action { implicit request =>
|
||||||
|
Ok(views.html.sessions.apply(PodGameManager.listSessions().map(f => f.toString)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def ingame(id: String): Action[AnyContent] = {
|
||||||
|
val uuid: UUID = UUID.fromString(id)
|
||||||
|
if (PodGameManager.identify(uuid).isEmpty) {
|
||||||
|
Action { implicit request =>
|
||||||
|
NotFound(views.html.tui.apply(List(Html(s"<p>Session with id $id not found!</p>"))))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val gamesession = PodGameManager.identify(uuid).get
|
||||||
|
val player = session.asInstanceOf[AdvancedSession].player
|
||||||
|
val logic =
|
||||||
|
Action { implicit request =>
|
||||||
|
Ok(views.html.matchy.apply(player, ))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def ingame(): Action[AnyContent] = {
|
|
||||||
Action { implicit request: Request[AnyContent] => {
|
|
||||||
Ok(views.html.tui.apply())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
41
knockoutwhistweb/app/controllers/PodGameManager.scala
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import controllers.sessions.PlayerSession
|
||||||
|
import de.knockoutwhist.utils.events.SimpleEvent
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
import scala.collection.mutable
|
||||||
|
|
||||||
|
object PodGameManager {
|
||||||
|
|
||||||
|
|
||||||
|
private val gamesession: mutable.Map[UUID, Gamesession] = mutable.Map()
|
||||||
|
|
||||||
|
def addGame(session: PlayerSession, gamesession: Gamesession): Unit = {
|
||||||
|
gamesession.put(session.id, gamesession)
|
||||||
|
}
|
||||||
|
def createGame(player: String): Unit = {
|
||||||
|
val game = Gamesession(UUID.randomUUID())
|
||||||
|
|
||||||
|
}
|
||||||
|
def clearSessions(): Unit = {
|
||||||
|
gamesession.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
def identify(id: UUID): Option[PlayerSession] = {
|
||||||
|
gamesession.get(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
def transmit(id: UUID, event: SimpleEvent): Unit = {
|
||||||
|
identify(id).foreach(_.updatePlayer(event))
|
||||||
|
}
|
||||||
|
|
||||||
|
def transmitAll(event: SimpleEvent): Unit = {
|
||||||
|
gamesession.foreach(session => session._2.updatePlayer(event))
|
||||||
|
}
|
||||||
|
|
||||||
|
def listSessions(): List[UUID] = {
|
||||||
|
sessions.keys.toList
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package controllers
|
|
||||||
|
|
||||||
import de.knockoutwhist.ui.UI
|
|
||||||
|
|
||||||
object WebUI extends UI {
|
|
||||||
|
|
||||||
override def initial: Boolean = true
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package controllers.sessions
|
||||||
|
|
||||||
|
import de.knockoutwhist.rounds.Match
|
||||||
|
import de.knockoutwhist.rounds.Round
|
||||||
|
import de.knockoutwhist.utils.events.SimpleEvent
|
||||||
|
import de.knockoutwhist.player.AbstractPlayer
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
case class AdvancedSession(player: AbstractPlayer) extends PlayerSession {
|
||||||
|
override def id(): UUID = {
|
||||||
|
player.id
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package controllers.sessions
|
||||||
|
|
||||||
|
import de.knockoutwhist.utils.events.SimpleEvent
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
trait PlayerSession {
|
||||||
|
|
||||||
|
def id: UUID
|
||||||
|
def updatePlayer(event: SimpleEvent): Unit
|
||||||
|
|
||||||
|
}
|
||||||
260
knockoutwhistweb/app/controllers/sessions/SimpleSession.scala
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
package controllers.sessions
|
||||||
|
|
||||||
|
import de.knockoutwhist.cards.{Card, CardValue, Hand}
|
||||||
|
import de.knockoutwhist.events.ERROR_STATUS.*
|
||||||
|
import de.knockoutwhist.events.GLOBAL_STATUS.*
|
||||||
|
import de.knockoutwhist.events.PLAYER_STATUS.*
|
||||||
|
import de.knockoutwhist.events.ROUND_STATUS.*
|
||||||
|
import de.knockoutwhist.events.{ShowErrorStatus, ShowGlobalStatus, ShowPlayerStatus, ShowRoundStatus}
|
||||||
|
import de.knockoutwhist.events.cards.{RenderHandEvent, ShowTieCardsEvent}
|
||||||
|
import de.knockoutwhist.events.round.ShowCurrentTrickEvent
|
||||||
|
import de.knockoutwhist.player.AbstractPlayer
|
||||||
|
import de.knockoutwhist.utils.events.SimpleEvent
|
||||||
|
import play.twirl.api.Html
|
||||||
|
import scalafx.scene.image.Image
|
||||||
|
import util.WebUIUtils
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
case class SimpleSession(id: UUID, private var output: List[Html]) extends PlayerSession {
|
||||||
|
def get(): List[Html] = {
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
override def updatePlayer(event: SimpleEvent): Unit = {
|
||||||
|
event match {
|
||||||
|
case event: RenderHandEvent =>
|
||||||
|
renderHand(event)
|
||||||
|
case event: ShowTieCardsEvent =>
|
||||||
|
showtiecardseventmethod(event)
|
||||||
|
case event: ShowGlobalStatus =>
|
||||||
|
showglobalstatusmethod(event)
|
||||||
|
case event: ShowPlayerStatus =>
|
||||||
|
showplayerstatusmethod(event)
|
||||||
|
case event: ShowRoundStatus =>
|
||||||
|
showroundstatusmethod(event)
|
||||||
|
case event: ShowErrorStatus =>
|
||||||
|
showerrstatmet(event)
|
||||||
|
case event: ShowCurrentTrickEvent =>
|
||||||
|
showcurtrevmet(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def clear(): Unit = {
|
||||||
|
output = List()
|
||||||
|
}
|
||||||
|
|
||||||
|
private def renderHand(event: RenderHandEvent): Unit = {
|
||||||
|
output = output :++ WebUICards.renderHandEvent(event.hand)
|
||||||
|
output = output :+ Html("<br>")
|
||||||
|
}
|
||||||
|
|
||||||
|
private def showtiecardseventmethod(event: ShowTieCardsEvent): Option[Boolean] = {
|
||||||
|
var l = List[Html]()
|
||||||
|
for ((player, card) <- event.card) {
|
||||||
|
l = l :+ Html(s"<p>${player.name}:</p>")
|
||||||
|
l = l :+ WebUIUtils.cardtoImage(card)
|
||||||
|
l = l :+ Html("<br>")
|
||||||
|
}
|
||||||
|
output = output :++ l
|
||||||
|
output = output :+ Html("<br>")
|
||||||
|
Some(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def showglobalstatusmethod(event: ShowGlobalStatus): Option[Boolean] = {
|
||||||
|
event.status match {
|
||||||
|
case SHOW_TIE =>
|
||||||
|
println("It's a tie! Let's cut to determine the winner.")
|
||||||
|
Some(true)
|
||||||
|
case SHOW_TIE_WINNER =>
|
||||||
|
if (event.objects.length != 1 || !event.objects.head.isInstanceOf[AbstractPlayer]) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
println(s"${event.objects.head.asInstanceOf[AbstractPlayer].name} wins the cut!")
|
||||||
|
Some(true)
|
||||||
|
}
|
||||||
|
case SHOW_TIE_TIE =>
|
||||||
|
println("It's a tie again! Let's cut again.")
|
||||||
|
Some(true)
|
||||||
|
case SHOW_START_MATCH =>
|
||||||
|
clear()
|
||||||
|
println("Starting a new match...")
|
||||||
|
output = output :+ Html("<br><br>")
|
||||||
|
Some(true)
|
||||||
|
case SHOW_TYPE_PLAYERS =>
|
||||||
|
println("Please enter the names of the players, separated by a comma.")
|
||||||
|
Some(true)
|
||||||
|
case SHOW_FINISHED_MATCH =>
|
||||||
|
if (event.objects.length != 1 || !event.objects.head.isInstanceOf[AbstractPlayer]) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
clear()
|
||||||
|
println(s"The match is over. The winner is ${event.objects.head.asInstanceOf[AbstractPlayer]}")
|
||||||
|
Some(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def showplayerstatusmethod(event: ShowPlayerStatus): Option[Boolean] = {
|
||||||
|
val player = event.player
|
||||||
|
event.status match {
|
||||||
|
case SHOW_PLAY_CARD =>
|
||||||
|
println("Which card do you want to play?")
|
||||||
|
Some(true)
|
||||||
|
case SHOW_DOG_PLAY_CARD =>
|
||||||
|
if (event.objects.length != 1 || !event.objects.head.isInstanceOf[Boolean]) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
println("You are using your dog life. Do you want to play your final card now?")
|
||||||
|
if (event.objects.head.asInstanceOf[Boolean]) {
|
||||||
|
println("You have to play your final card this round!")
|
||||||
|
println("Please enter y to play your final card.")
|
||||||
|
Some(true)
|
||||||
|
} else {
|
||||||
|
println("Please enter y/n to play your final card.")
|
||||||
|
Some(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case SHOW_TIE_NUMBERS =>
|
||||||
|
if (event.objects.length != 1 || !event.objects.head.isInstanceOf[Int]) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
println(s"${player.name} enter a number between 1 and ${event.objects.head.asInstanceOf[Int]}.")
|
||||||
|
Some(true)
|
||||||
|
}
|
||||||
|
case SHOW_TRUMPSUIT_OPTIONS =>
|
||||||
|
println("Which suit do you want to pick as the next trump suit?")
|
||||||
|
println("1: Hearts")
|
||||||
|
println("2: Diamonds")
|
||||||
|
println("3: Clubs")
|
||||||
|
println("4: Spades")
|
||||||
|
println()
|
||||||
|
Some(true)
|
||||||
|
case SHOW_NOT_PLAYED =>
|
||||||
|
println(s"Player ${event.player} decided to not play his card")
|
||||||
|
Some(true)
|
||||||
|
case SHOW_WON_PLAYER_TRICK =>
|
||||||
|
println(s"${event.player.name} won the trick.")
|
||||||
|
output = output :+ Html("<br><br>")
|
||||||
|
Some(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def showroundstatusmethod(event: ShowRoundStatus): Option[Boolean] = {
|
||||||
|
event.status match {
|
||||||
|
case SHOW_TURN =>
|
||||||
|
if (event.objects.length != 1 || !event.objects.head.isInstanceOf[AbstractPlayer]) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
println(s"It's ${event.objects.head.asInstanceOf[AbstractPlayer].name} turn.")
|
||||||
|
Some(true)
|
||||||
|
}
|
||||||
|
case SHOW_START_ROUND =>
|
||||||
|
clear()
|
||||||
|
println(s"Starting a new round. The trump suit is ${event.currentRound.trumpSuit}.")
|
||||||
|
output = output :+ Html("<br><br>")
|
||||||
|
Some(true)
|
||||||
|
case WON_ROUND =>
|
||||||
|
if (event.objects.length != 1 || !event.objects.head.isInstanceOf[AbstractPlayer]) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
println(s"${event.objects.head.asInstanceOf[AbstractPlayer].name} won the round.")
|
||||||
|
Some(true)
|
||||||
|
}
|
||||||
|
case PLAYERS_OUT =>
|
||||||
|
println("The following players are out of the game:")
|
||||||
|
event.currentRound.playersout.foreach(p => {
|
||||||
|
println(p.name)
|
||||||
|
})
|
||||||
|
Some(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def showerrstatmet(event: ShowErrorStatus): Option[Boolean] = {
|
||||||
|
event.status match {
|
||||||
|
case INVALID_NUMBER =>
|
||||||
|
println("Please enter a valid number.")
|
||||||
|
Some(true)
|
||||||
|
case NOT_A_NUMBER =>
|
||||||
|
println("Please enter a number.")
|
||||||
|
Some(true)
|
||||||
|
case INVALID_INPUT =>
|
||||||
|
println("Please enter a valid input")
|
||||||
|
Some(true)
|
||||||
|
case INVALID_NUMBER_OF_PLAYERS =>
|
||||||
|
println("Please enter at least two names.")
|
||||||
|
Some(true)
|
||||||
|
case IDENTICAL_NAMES =>
|
||||||
|
println("Please enter unique names.")
|
||||||
|
Some(true)
|
||||||
|
case INVALID_NAME_FORMAT =>
|
||||||
|
println("Please enter valid names. Those can not be empty, shorter than 2 or longer then 10 characters.")
|
||||||
|
Some(true)
|
||||||
|
case WRONG_CARD =>
|
||||||
|
if (event.objects.length != 1 || !event.objects.head.isInstanceOf[Card]) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
println(f"You have to play a card of suit: ${event.objects.head.asInstanceOf[Card].suit}\n")
|
||||||
|
Some(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def showcurtrevmet(event: ShowCurrentTrickEvent): Option[Boolean] = {
|
||||||
|
clear()
|
||||||
|
val sb = new StringBuilder()
|
||||||
|
sb.append("Current Trick:\n")
|
||||||
|
sb.append("Trump-Suit: " + event.round.trumpSuit + "\n")
|
||||||
|
if (event.trick.firstCard.isDefined) {
|
||||||
|
sb.append(s"Suit to play: ${event.trick.firstCard.get.suit}\n")
|
||||||
|
}
|
||||||
|
for ((card, player) <- event.trick.cards) {
|
||||||
|
sb.append(s"${player.name} played ${card.toString}\n")
|
||||||
|
}
|
||||||
|
println(sb.toString())
|
||||||
|
Some(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def println(s: String): Unit = {
|
||||||
|
var html = List[Html]()
|
||||||
|
for (line <- s.split("\n")) {
|
||||||
|
html = html :+ Html(line)
|
||||||
|
html = html :+ Html("<br>")
|
||||||
|
}
|
||||||
|
output = output :++ html
|
||||||
|
}
|
||||||
|
|
||||||
|
private def println(): Unit = {
|
||||||
|
output = output :+ Html("<br>")
|
||||||
|
}
|
||||||
|
|
||||||
|
object WebUICards {
|
||||||
|
def renderCardAsString(card: Card): Vector[String] = {
|
||||||
|
val lines = "│ │"
|
||||||
|
if (card.cardValue == CardValue.Ten) {
|
||||||
|
return Vector(
|
||||||
|
s"┌─────────┐",
|
||||||
|
s"│${card.cardValue.cardType()} │",
|
||||||
|
lines,
|
||||||
|
s"│ ${card.suit.cardType()} │",
|
||||||
|
lines,
|
||||||
|
s"│ ${card.cardValue.cardType()}│",
|
||||||
|
s"└─────────┘"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Vector(
|
||||||
|
s"┌─────────┐",
|
||||||
|
s"│${card.cardValue.cardType()} │",
|
||||||
|
lines,
|
||||||
|
s"│ ${card.suit.cardType()} │",
|
||||||
|
lines,
|
||||||
|
s"│ ${card.cardValue.cardType()}│",
|
||||||
|
s"└─────────┘"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def renderHandEvent(hand: Hand): List[Html] = {
|
||||||
|
hand.cards.map(WebUIUtils.cardtoImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
knockoutwhistweb/app/di/KnockOutWebConfigurationModule.scala
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package di
|
||||||
|
|
||||||
|
import com.google.inject.AbstractModule
|
||||||
|
import components.WebApplicationConfiguration
|
||||||
|
import de.knockoutwhist.components.Configuration
|
||||||
|
|
||||||
|
class KnockOutWebConfigurationModule extends AbstractModule {
|
||||||
|
|
||||||
|
override def configure(): Unit = {
|
||||||
|
bind(classOf[Configuration]).to(classOf[WebApplicationConfiguration])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
34
knockoutwhistweb/app/util/WebUIUtils.scala
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import de.knockoutwhist.cards.Card
|
||||||
|
import de.knockoutwhist.cards.CardValue.{Ace, Eight, Five, Four, Jack, King, Nine, Queen, Seven, Six, Ten, Three, Two}
|
||||||
|
import de.knockoutwhist.cards.Suit.{Clubs, Diamonds, Hearts, Spades}
|
||||||
|
import play.twirl.api.Html
|
||||||
|
import scalafx.scene.image.Image
|
||||||
|
|
||||||
|
object WebUIUtils {
|
||||||
|
def cardtoImage(card: Card): Html = {
|
||||||
|
val s = card.suit match {
|
||||||
|
case Spades => "S"
|
||||||
|
case Hearts => "H"
|
||||||
|
case Clubs => "C"
|
||||||
|
case Diamonds => "D"
|
||||||
|
}
|
||||||
|
val cv = card.cardValue match {
|
||||||
|
case Ace => "A"
|
||||||
|
case King => "K"
|
||||||
|
case Queen => "Q"
|
||||||
|
case Jack => "J"
|
||||||
|
case Ten => "T"
|
||||||
|
case Nine => "9"
|
||||||
|
case Eight => "8"
|
||||||
|
case Seven => "7"
|
||||||
|
case Six => "6"
|
||||||
|
case Five => "5"
|
||||||
|
case Four => "4"
|
||||||
|
case Three => "3"
|
||||||
|
case Two => "2"
|
||||||
|
}
|
||||||
|
views.html.output.card.apply(f"images/cards/$cv$s.png")(card.toString)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
@(output: String)
|
|
||||||
|
|
||||||
@main("Welcome to Play") {
|
@main("Welcome to Play") {
|
||||||
<h1>Welcome to Play!</h1>
|
<h1>Welcome to Play!</h1>
|
||||||
}
|
}
|
||||||
|
|||||||
25
knockoutwhistweb/app/views/main.scala.html
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
@*
|
||||||
|
* This template is called from the `index` template. This template
|
||||||
|
* handles the rendering of the page header and body tags. It takes
|
||||||
|
* two arguments, a `String` for the title of the page and an `Html`
|
||||||
|
* object to insert into the body of the page.
|
||||||
|
*@
|
||||||
|
@(title: String)(content: Html)
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
@* Here's where we render the page title `String`. *@
|
||||||
|
<title>@title</title>
|
||||||
|
<link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/main.css")">
|
||||||
|
<link rel="shortcut icon" type="image/png" href="@routes.Assets.versioned("images/favicon.png")">
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
@* And here's where we render the `Html` object containing
|
||||||
|
* the page content. *@
|
||||||
|
@content
|
||||||
|
|
||||||
|
<script src="@routes.Assets.versioned("javascripts/main.js")" type="text/javascript"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
9
knockoutwhistweb/app/views/matchy.scala.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
@(toRender: List[Any])
|
||||||
|
|
||||||
|
@main("Match") {
|
||||||
|
<div id="match">
|
||||||
|
@for(line <- toRender) {
|
||||||
|
@line
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
2
knockoutwhistweb/app/views/output/card.scala.html
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
@(src: String)(alt: String)
|
||||||
|
<img src="@routes.Assets.versioned(src)" alt="@alt"/>
|
||||||
3
knockoutwhistweb/app/views/output/text.scala.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
@(text: String)
|
||||||
|
<p>@text</p>
|
||||||
|
|
||||||
10
knockoutwhistweb/app/views/sessions.scala.html
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
@(toRender: List[String])
|
||||||
|
|
||||||
|
@main("Sessions") {
|
||||||
|
<div id="sessions">
|
||||||
|
@for(line <- toRender) {
|
||||||
|
<a href="@routes.HomeController.ingame(line)">@line</a><br>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1 +1,10 @@
|
|||||||
@()
|
@(toRender: List[Html])
|
||||||
|
|
||||||
|
@main("Tui") {
|
||||||
|
<div id="tui">
|
||||||
|
@for(line <- toRender) {
|
||||||
|
@line
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
# ~~~~
|
# ~~~~
|
||||||
|
|
||||||
# An example controller showing a sample home page
|
# An example controller showing a sample home page
|
||||||
GET / controllers.HomeController.index()
|
|
||||||
GET /ingame controllers.HomeController.ingame()
|
|
||||||
|
|
||||||
|
GET / controllers.HomeController.index()
|
||||||
|
GET /sessions controllers.HomeController.sessions()
|
||||||
|
GET /ingame/:id controllers.HomeController.ingame(id: String)
|
||||||
# Map static resources from the /public folder to the /assets URL path
|
# Map static resources from the /public folder to the /assets URL path
|
||||||
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
|
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
|
||||||
|
|||||||
BIN
knockoutwhistweb/public/images/cards/1B.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
knockoutwhistweb/public/images/cards/1J.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
knockoutwhistweb/public/images/cards/2B.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
knockoutwhistweb/public/images/cards/2C.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
knockoutwhistweb/public/images/cards/2D.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
knockoutwhistweb/public/images/cards/2H.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
knockoutwhistweb/public/images/cards/2J.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
knockoutwhistweb/public/images/cards/2S.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
knockoutwhistweb/public/images/cards/3C.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
knockoutwhistweb/public/images/cards/3D.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
knockoutwhistweb/public/images/cards/3H.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
knockoutwhistweb/public/images/cards/3S.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
knockoutwhistweb/public/images/cards/4C.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
knockoutwhistweb/public/images/cards/4D.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
knockoutwhistweb/public/images/cards/4H.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
knockoutwhistweb/public/images/cards/4S.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
knockoutwhistweb/public/images/cards/5C.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
knockoutwhistweb/public/images/cards/5D.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
knockoutwhistweb/public/images/cards/5H.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
knockoutwhistweb/public/images/cards/5S.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
knockoutwhistweb/public/images/cards/6C.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
knockoutwhistweb/public/images/cards/6D.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
knockoutwhistweb/public/images/cards/6H.png
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
knockoutwhistweb/public/images/cards/6S.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
knockoutwhistweb/public/images/cards/7C.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
knockoutwhistweb/public/images/cards/7D.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
knockoutwhistweb/public/images/cards/7H.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
knockoutwhistweb/public/images/cards/7S.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
knockoutwhistweb/public/images/cards/8C.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
knockoutwhistweb/public/images/cards/8D.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
knockoutwhistweb/public/images/cards/8H.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
knockoutwhistweb/public/images/cards/8S.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
knockoutwhistweb/public/images/cards/9C.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
knockoutwhistweb/public/images/cards/9D.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
knockoutwhistweb/public/images/cards/9H.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
knockoutwhistweb/public/images/cards/9S.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
knockoutwhistweb/public/images/cards/AC.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
knockoutwhistweb/public/images/cards/ACB.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
knockoutwhistweb/public/images/cards/AD.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
knockoutwhistweb/public/images/cards/ADB.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
knockoutwhistweb/public/images/cards/AH.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
knockoutwhistweb/public/images/cards/AHB.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
knockoutwhistweb/public/images/cards/AS.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
knockoutwhistweb/public/images/cards/ASB.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
knockoutwhistweb/public/images/cards/JC.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
knockoutwhistweb/public/images/cards/JD.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
knockoutwhistweb/public/images/cards/JH.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
knockoutwhistweb/public/images/cards/JS.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
knockoutwhistweb/public/images/cards/KC.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
knockoutwhistweb/public/images/cards/KD.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
knockoutwhistweb/public/images/cards/KH.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
knockoutwhistweb/public/images/cards/KS.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
knockoutwhistweb/public/images/cards/QC.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
knockoutwhistweb/public/images/cards/QD.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
knockoutwhistweb/public/images/cards/QH.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
knockoutwhistweb/public/images/cards/QS.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
knockoutwhistweb/public/images/cards/TC.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
knockoutwhistweb/public/images/cards/TD.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
knockoutwhistweb/public/images/cards/TH.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
knockoutwhistweb/public/images/cards/TS.png
Normal file
|
After Width: | Height: | Size: 14 KiB |