Compare commits
162 Commits
archive/te
...
archive/se
| Author | SHA1 | Date | |
|---|---|---|---|
| 48cd4d3956 | |||
| d54f814192 | |||
|
05b6ebb2a8
|
|||
| 079e66a5cf | |||
|
220c3d04ea
|
|||
| a6e514880c | |||
|
ec329df768
|
|||
|
a856843fc3
|
|||
|
|
f57537b3eb | ||
|
6c825845ea
|
|||
|
5b37ebb01d
|
|||
| 2b63fbe88d | |||
|
|
0e31134dfb | ||
|
|
d0ab531e16 | ||
|
b6812d1223
|
|||
|
|
169537c479 | ||
| aad61fd10a | |||
| d9b1bb2186 | |||
| d48e3b1daa | |||
|
fa8cab1d6f
|
|||
|
a53f4dda46
|
|||
|
483991bdbb
|
|||
|
bd73997959
|
|||
|
3a41a4bb4d
|
|||
|
474de82cde
|
|||
|
d39e92cfa8
|
|||
| e1d7405f51 | |||
|
a33e404378
|
|||
|
|
2e0d24adbd | ||
| 13199ddd74 | |||
|
3396355193
|
|||
|
|
111242a6aa | ||
|
|
681fdd4253 | ||
|
|
3fe3abe7fc | ||
|
e0b7a68207
|
|||
|
|
67a590e544 | ||
|
|
8742c5dc7a | ||
|
|
7a46bed011 | ||
|
|
9ecf91282e | ||
|
|
0526f99464 | ||
|
95228cbd51
|
|||
|
|
49e378b51a | ||
|
|
3b6ec1cd1b | ||
|
d21540bbe7
|
|||
|
|
05afe8b392 | ||
|
92e9149d55
|
|||
|
|
5324a16cbe | ||
|
|
ecff0a318d | ||
|
|
b8c5b7a389 | ||
|
3c83fec6b5
|
|||
|
a5f7f14d06
|
|||
|
4aafd01f92
|
|||
|
|
2fe0ddcf45 | ||
|
|
e1c2d660f1 | ||
|
648a1b2ce7
|
|||
|
56cc2f1182
|
|||
|
|
f7b09a0bd9 | ||
|
|
ed8356577c | ||
|
085002a35a
|
|||
|
c3dcca6bde
|
|||
|
4e43e25437
|
|||
|
9d5194bf1d
|
|||
|
|
f5250f401f | ||
|
30e395d649
|
|||
|
597c0f196a
|
|||
|
33c14d3731
|
|||
|
961f91eef8
|
|||
|
|
bb51dcb69b | ||
|
82c6398dab
|
|||
|
|
b0e44b6690 | ||
|
0aae73f530
|
|||
|
de63e94342
|
|||
|
b45efc91cb
|
|||
|
fbd2f7a4dd
|
|||
|
7f4a08c673
|
|||
|
|
34d9986979 | ||
|
21e810b0da
|
|||
|
|
452810f9fa | ||
|
|
bfcd207c38 | ||
|
3f958cf20a
|
|||
|
faecdf00be
|
|||
|
de9aa383ea
|
|||
|
2fad96bb2c
|
|||
|
|
aec723d243 | ||
|
28c8f5850e
|
|||
|
5e065c04c7
|
|||
|
4ef7333ef5
|
|||
|
dddbd97362
|
|||
|
|
46075fa356 | ||
|
8553bf7941
|
|||
|
1311cb20b8
|
|||
|
|
87e9d66f46 | ||
|
9edea7082d
|
|||
|
0a4d725669
|
|||
| 9e9941cd70 | |||
| 0ce277fe33 | |||
|
|
4573f4c2a6 | ||
|
|
9c5947a18f | ||
|
337d27abbf
|
|||
|
4e8a8a958d
|
|||
|
a9871e6038
|
|||
| 0a37a583f7 | |||
| 9da9477763 | |||
|
36286be458
|
|||
|
ca3ffa4ba7
|
|||
|
|
82d7ab94b4 | ||
|
7487a0edf6
|
|||
|
6b3b702ffe
|
|||
|
|
1cc776bfaa | ||
|
|
1e50a40868 | ||
|
|
fe4f6a7f46 | ||
|
|
7f69e6f444 | ||
|
|
e589f36c22 | ||
|
|
33e15bca27 | ||
|
8d7420358d
|
|||
|
|
522a1cfa23 | ||
|
|
39336120e1 | ||
|
|
777e4c2d6b | ||
|
|
d75c2cf62a | ||
|
|
9191012aaf | ||
|
|
1a7e48864b | ||
|
|
6ef9e95cb8 | ||
|
c94e37f63e
|
|||
|
|
be4e00af44 | ||
|
|
ebea37aece | ||
| b9e896fc0b | |||
|
a0e2eadc93
|
|||
|
|
47169bdb8b | ||
| 3a6473379d | |||
| 11dab537ba | |||
| 3a246b4419 | |||
| d940620049 | |||
| 4d86baa6d9 | |||
| 14ef284229 | |||
| d73c4c0577 | |||
| 0b5bfc69e5 | |||
|
ad0494f0ba
|
|||
| 355addf8a5 | |||
|
efd5e0fae6
|
|||
|
8cb5012c8f
|
|||
|
|
6304ce44b9 | ||
|
ee44f6940b
|
|||
|
|
0c6de4b86f | ||
|
094afc5631
|
|||
| fabb2f3580 | |||
| bb0ad578be | |||
|
ce5efd7a05
|
|||
|
174fe3c993
|
|||
|
03ab3f3650
|
|||
|
|
a7f839d01c | ||
|
a7e84b9815
|
|||
|
4c7fb1c8bc
|
|||
|
ef1eb29fe3
|
|||
|
|
619cbcfaac | ||
|
fe46690fb4
|
|||
|
24bfd7925c
|
|||
|
11c0b61933
|
|||
|
0cc7756556
|
|||
|
153d028959
|
|||
|
|
177ec3772e | ||
|
c397d079bf
|
|||
|
a99182beaf
|
6
.idea/copilot.data.migration.agent.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AgentMigrationStateService">
|
||||
<option name="migrationStatus" value="COMPLETED" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/copilot.data.migration.ask.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AskMigrationStateService">
|
||||
<option name="migrationStatus" value="COMPLETED" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/copilot.data.migration.ask2agent.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Ask2AgentMigrationStateService">
|
||||
<option name="migrationStatus" value="COMPLETED" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/copilot.data.migration.edit.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="EditMigrationStateService">
|
||||
<option name="migrationStatus" value="COMPLETED" />
|
||||
</component>
|
||||
</project>
|
||||
1
DOCKER-COMMANDS
Normal file
@@ -0,0 +1 @@
|
||||
docker run -it --rm -e DISPLAY=host.docker.internal:0 -v /tmp/.X11-unix:/tmp/.X11-unix knockout:v1
|
||||
35
Dockerfile
Normal file
@@ -0,0 +1,35 @@
|
||||
FROM sbtscala/scala-sbt:eclipse-temurin-jammy-22_36_1.10.0_3.4.2
|
||||
|
||||
WORKDIR /knockout
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninterface
|
||||
|
||||
RUN apt-get update && apt-get install -y curl gnupg2 x11-apps
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libxext6 \
|
||||
libxrender1 \
|
||||
libxtst6 \
|
||||
libxi6 \
|
||||
libxrandr2 \
|
||||
libgtk-3-0 \
|
||||
xorg
|
||||
|
||||
RUN curl -sL https://dlcdn.apache.org/sbt/debian/sbt-1.9.4.deb -o sbt.deb
|
||||
|
||||
RUN dpkg -i sbt.deb || apt-get install -f -y
|
||||
|
||||
ENV DEBIAN_FRONTEND=dialog
|
||||
|
||||
RUN sbt update
|
||||
|
||||
ENV DISPLAY=:99
|
||||
ENV SBT_OPTS="-Xms512M -Xmx1536M -Xss2M -XX:MaxMetaspaceSize=512M"
|
||||
ENV _JAVA_OPTIONS="-Djava.security.policy=applet.policy -Dprism.order=sw"
|
||||
|
||||
VOLUME /tmp/.X11-unix
|
||||
|
||||
COPY . /knockout
|
||||
|
||||
RUN sbt compile
|
||||
|
||||
CMD ["sh", "-c", "sbt run"]
|
||||
@@ -1,4 +1,4 @@
|
||||
# Knock-out Whist
|
||||
# Knock-out Whist [](https://coveralls.io/github/16Janis12/KnockOutWhist?branch=master) [,branch:master/statusIcon.png)](https://teamcity.janis-eccarius.de/viewType.html?buildTypeId=KnockOutWhist_BuildAndTest&branch=master) [](https://sonarcloud.io/summary/new_code?id=16Janis12_KnockOutWhist)
|
||||
## About the Game
|
||||
Knock-out Whist is a trick-taking card game designed for 2 to 7 players. It uses a standard 52-card deck.
|
||||
The objective is to win tricks. Players who fail to win at least one trick in a round are eliminated.
|
||||
@@ -29,14 +29,17 @@ If they win a trick, they re-enter the game in the following round. When this ha
|
||||
## About the project
|
||||
This project is a Scala implementation of the Knock-out Whist card game. It is maintained by 2 students of the HTWG Konstanz.
|
||||
|
||||
Last stable build: [,branch:master/statusIcon.png)](https://teamcity.janis-eccarius.de/viewType.html?buildTypeId=KnockOutWhist_BuildAndTest&branch=master)
|
||||
Last stable build: [](https://teamcity.janis-eccarius.de/viewType.html?buildTypeId=KnockOutWhist_BuildAndTest&branch=master)
|
||||
|
||||
Last development build: [,branch:development/statusIcon.png)](https://teamcity.janis-eccarius.de/viewType.html?buildTypeId=KnockOutWhist_BuildAndTest&branch=development)
|
||||
Last development build: [](https://teamcity.janis-eccarius.de/viewType.html?buildTypeId=KnockOutWhist_BuildAndTest&branch=development)
|
||||
|
||||
Gitea: [https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist](https://git.janis-eccarius.de/KnockOutWhist/KnockOutWhist)
|
||||
|
||||
GitHub (Mirror): [https://github.com/16Janis12/KnockOutWhist](https://github.com/16Janis12/KnockOutWhist)
|
||||
|
||||
SonarQube: [https://sonarcloud.io/project/overview?id=16Janis12_KnockOutWhist](https://sonarcloud.io/project/overview?id=16Janis12_KnockOutWhist)
|
||||
|
||||
Coveralls: [https://coveralls.io/github/16Janis12/KnockOutWhist](https://coveralls.io/github/16Janis12/KnockOutWhist)
|
||||
|
||||
## Licence
|
||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||
34
build.sbt
@@ -1,5 +1,5 @@
|
||||
ThisBuild / version := "0.1.0-SNAPSHOT"
|
||||
ThisBuild / scalaVersion := "3.5.1"
|
||||
|
||||
|
||||
|
||||
Compile/mainClass := Some("de.knockoutwhist.KnockOutWhist")
|
||||
|
||||
@@ -10,17 +10,43 @@ version := {
|
||||
val buildNR = sys.env.getOrElse("BUI_COUNTER", "1")
|
||||
s"$major.$minor.$buildNR"
|
||||
}
|
||||
organization := "de.knockoutwhist"
|
||||
|
||||
ThisBuild / organization := "de.knockoutwhist"
|
||||
ThisBuild / version := version.value
|
||||
ThisBuild / scalaVersion := "3.5.1"
|
||||
|
||||
lazy val root = (project in file("."))
|
||||
.settings(
|
||||
name := "Projekt-zu-SE"
|
||||
name := "Projekt-zu-SE",
|
||||
fork in run := true,
|
||||
javaOptions in run += "-Xmx2G",
|
||||
assembly / mainClass := Some("de.knockoutwhist.KnockOutWhist"),
|
||||
assembly / assemblyJarName := s"KnockOutWhist-${version.value}.jar",
|
||||
)
|
||||
|
||||
|
||||
|
||||
libraryDependencies += "org.scalactic" %% "scalactic" % "3.2.18"
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.18" % "test"
|
||||
libraryDependencies +="io.github.mkpaz" % "atlantafx-base" % "2.0.1"
|
||||
libraryDependencies += "org.scalafx" %% "scalafx" % "22.0.0-R33"
|
||||
|
||||
libraryDependencies += "org.scala-lang.modules" %% "scala-xml" % "2.3.0"
|
||||
libraryDependencies += "org.playframework" %% "play-json" % "3.1.0-M1"
|
||||
|
||||
libraryDependencies ++= {
|
||||
// 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!")
|
||||
}
|
||||
Seq("base", "controls", "fxml", "graphics", "media", "swing", "web")
|
||||
.map(m => "org.openjfx" % s"javafx-$m" % "21" classifier osName)
|
||||
}
|
||||
|
||||
libraryDependencies += "net.codingwell" %% "scala-guice" % "7.0.0"
|
||||
|
||||
Test / testOptions += Tests.Filter(_.equals("de.knockoutwhist.TestSequence"))
|
||||
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.2.1")
|
||||
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.2.1")
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.0")
|
||||
addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.3.1")
|
||||
|
||||
BIN
src/main/resources/KnockOutLogo.png
Normal file
|
After Width: | Height: | Size: 683 KiB |
BIN
src/main/resources/background.png
Normal file
|
After Width: | Height: | Size: 610 KiB |
BIN
src/main/resources/cards/1B.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
src/main/resources/cards/1J.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/main/resources/cards/2B.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
src/main/resources/cards/2C.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
src/main/resources/cards/2D.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
src/main/resources/cards/2H.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
src/main/resources/cards/2J.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/main/resources/cards/2S.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
src/main/resources/cards/3C.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
src/main/resources/cards/3D.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
src/main/resources/cards/3H.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
src/main/resources/cards/3S.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
src/main/resources/cards/4C.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
src/main/resources/cards/4D.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
src/main/resources/cards/4H.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
src/main/resources/cards/4S.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
src/main/resources/cards/5C.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/main/resources/cards/5D.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
src/main/resources/cards/5H.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
src/main/resources/cards/5S.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
src/main/resources/cards/6C.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
src/main/resources/cards/6D.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
src/main/resources/cards/6H.png
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
src/main/resources/cards/6S.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
src/main/resources/cards/7C.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
src/main/resources/cards/7D.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
src/main/resources/cards/7H.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
src/main/resources/cards/7S.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
src/main/resources/cards/8C.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
src/main/resources/cards/8D.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/main/resources/cards/8H.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/main/resources/cards/8S.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/main/resources/cards/9C.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/main/resources/cards/9D.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/main/resources/cards/9H.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
src/main/resources/cards/9S.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
src/main/resources/cards/AC.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
src/main/resources/cards/ACB.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
src/main/resources/cards/AD.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
src/main/resources/cards/ADB.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
src/main/resources/cards/AH.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
src/main/resources/cards/AHB.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
src/main/resources/cards/AS.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
src/main/resources/cards/ASB.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/main/resources/cards/JC.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
src/main/resources/cards/JD.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
src/main/resources/cards/JH.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
src/main/resources/cards/JS.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
src/main/resources/cards/KC.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
src/main/resources/cards/KD.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
src/main/resources/cards/KH.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
src/main/resources/cards/KS.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
src/main/resources/cards/QC.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
src/main/resources/cards/QD.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
src/main/resources/cards/QH.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
src/main/resources/cards/QS.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
src/main/resources/cards/TC.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/main/resources/cards/TD.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/main/resources/cards/TH.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/main/resources/cards/TS.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/main/resources/checkmark.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
src/main/resources/poker.zip
Normal file
BIN
src/main/resources/return-icon.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
@@ -1,5 +0,0 @@
|
||||
import de.knockoutwhist.cards.CardManager
|
||||
|
||||
val originalDeck = List(CardManager.cardContainer)
|
||||
CardManager.shuffleAndReset()
|
||||
val shuffledDeck = CardManager.cardContainer
|
||||
@@ -1,24 +1,36 @@
|
||||
package de.knockoutwhist
|
||||
|
||||
|
||||
import de.knockoutwhist.control.MatchControl
|
||||
import de.knockoutwhist.control.text.TextMatchControl
|
||||
import com.google.inject.{Guice, Inject, Injector}
|
||||
import de.knockoutwhist.components.{Configuration, DefaultConfiguration}
|
||||
import de.knockoutwhist.control.{ControlHandler, ControlThread}
|
||||
import de.knockoutwhist.di.KnockOutConfigurationModule
|
||||
import de.knockoutwhist.events.ui.GameState.MAIN_MENU
|
||||
import de.knockoutwhist.events.ui.GameStateUpdateEvent
|
||||
import de.knockoutwhist.ui.gui.GUIMain
|
||||
import de.knockoutwhist.ui.tui.TUIMain
|
||||
|
||||
|
||||
object KnockOutWhist {
|
||||
|
||||
val matchControl: MatchControl = TextMatchControl
|
||||
|
||||
/*
|
||||
Debug mode:
|
||||
|
||||
Debug mode
|
||||
- Disables the random shuffle of the cards
|
||||
*/
|
||||
private[knockoutwhist] var DEBUG_MODE_VAR: Boolean = true
|
||||
private val injector: Injector = Guice.createInjector(KnockOutConfigurationModule())
|
||||
val config: Configuration = injector.getInstance(classOf[Configuration])
|
||||
private[knockoutwhist] var DEBUG_MODE_VAR: Boolean = false
|
||||
|
||||
def DEBUG_MODE = DEBUG_MODE_VAR
|
||||
def debugmode: Boolean = DEBUG_MODE_VAR
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
if(!matchControl.initial()) throw new IllegalStateException("Game could not be started.")
|
||||
ControlThread.start()
|
||||
config.persistenceManager.loadManager()
|
||||
if(!TUIMain.initial) throw new IllegalStateException("TUI could not be started.")
|
||||
if(!GUIMain.initial) throw new IllegalStateException("GUI could not be started.")
|
||||
ControlThread.runLater {
|
||||
ControlHandler.invoke(GameStateUpdateEvent(MAIN_MENU))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
package de.knockoutwhist.cards
|
||||
|
||||
import de.knockoutwhist.cards.CardValue.Ten
|
||||
import de.knockoutwhist.cards.Suit
|
||||
|
||||
enum Suit(identifier: String):
|
||||
|
||||
def cardType(): String = identifier
|
||||
@@ -34,34 +31,6 @@ enum CardValue(identifier: String):
|
||||
end CardValue
|
||||
|
||||
case class Card(cardValue: CardValue, suit: Suit) {
|
||||
|
||||
def cardColour(suit: Suit): String = suit match {
|
||||
case Suit.Hearts | Suit.Diamonds => Console.RED
|
||||
case Suit.Clubs | Suit.Spades => Console.BLACK
|
||||
}
|
||||
|
||||
def renderAsString(): Vector[String] = {
|
||||
if (cardValue == Ten) {
|
||||
return Vector(
|
||||
s"┌─────────┐",
|
||||
s"│${cardColour(suit)}${Console.BOLD}${cardValue.cardType()}${Console.RESET} │",
|
||||
"│ │",
|
||||
s"│ ${cardColour(suit)}${Console.BOLD}${suit.cardType()}${Console.RESET} │",
|
||||
"│ │",
|
||||
s"│ ${cardColour(suit)}${Console.BOLD}${cardValue.cardType()}${Console.RESET}│",
|
||||
s"└─────────┘"
|
||||
)
|
||||
}
|
||||
Vector(
|
||||
s"┌─────────┐",
|
||||
s"│${cardColour(suit)}${Console.BOLD}${cardValue.cardType()}${Console.RESET} │",
|
||||
"│ │",
|
||||
s"│ ${cardColour(suit)}${Console.BOLD}${suit.cardType()}${Console.RESET} │",
|
||||
"│ │",
|
||||
s"│ ${cardColour(suit)}${Console.BOLD}${cardValue.cardType()}${Console.RESET}│",
|
||||
s"└─────────┘"
|
||||
)
|
||||
}
|
||||
override def toString: String = s"$cardValue of $suit"
|
||||
//Combined String
|
||||
|
||||
override def toString: String = s"$cardValue of $suit"
|
||||
}
|
||||
|
||||
@@ -1,50 +1,21 @@
|
||||
package de.knockoutwhist.cards
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
trait CardManager {
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.util.Random
|
||||
def cardContainer: List[Card]
|
||||
|
||||
object CardManager {
|
||||
def shuffleAndReset(): Unit
|
||||
|
||||
def resetOrder(): Unit
|
||||
|
||||
var cardContainer: List[Card] = {
|
||||
val cc = ListBuffer[Card]()
|
||||
for (suit <- Suit.values) {
|
||||
for (cardValue <- CardValue.values) {
|
||||
cc += Card(cardValue, suit)
|
||||
}
|
||||
}
|
||||
cc.toList
|
||||
}
|
||||
private var currentIdx = 0
|
||||
def nextCard(): Card
|
||||
|
||||
def shuffleAndReset(): Unit = {
|
||||
cardContainer = Random.shuffle(cardContainer)
|
||||
currentIdx = 0
|
||||
}
|
||||
def createHand(amount: Int = 7): Hand
|
||||
|
||||
def resetOrder(): Unit = {
|
||||
cardContainer = cardContainer.sortBy(c => (c.suit.ordinal, c.cardValue.ordinal))
|
||||
currentIdx = 0
|
||||
}
|
||||
|
||||
def nextCard(): Card = {
|
||||
val card = cardContainer(currentIdx)
|
||||
if (currentIdx + 1 > 51) {
|
||||
throw new IndexOutOfBoundsException("Trying to access card 53(out of bounds)")
|
||||
} else {
|
||||
currentIdx += 1
|
||||
card
|
||||
}
|
||||
}
|
||||
def grabSpecificCard(card: Card): Card
|
||||
|
||||
def createHand(amount: Int = 7): Hand = {
|
||||
val hand = ListBuffer[Card]()
|
||||
for (_ <- 1 to amount) {
|
||||
hand += nextCard()
|
||||
}
|
||||
Hand(hand.toList)
|
||||
}
|
||||
def currentIndx: Int
|
||||
|
||||
def setState(cc: List[Card], currentIndex: Int): Unit
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package de.knockoutwhist.cards
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
case class Hand(cards: List[Card]) {
|
||||
|
||||
def removeCard(card: Card): Hand = {
|
||||
@@ -19,11 +17,5 @@ case class Hand(cards: List[Card]) {
|
||||
def hasTrumpSuit(trumpSuit: Suit): Boolean = {
|
||||
cards.exists(_.suit == trumpSuit)
|
||||
}
|
||||
|
||||
def renderAsString() : List[String] = {
|
||||
val cardStrings = cards.map(_.renderAsString())
|
||||
val zipped = cardStrings.transpose
|
||||
zipped.map(_.mkString(" "))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package de.knockoutwhist.cards.base
|
||||
|
||||
import de.knockoutwhist.cards.*
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.util.Random
|
||||
|
||||
class CardBaseManager extends CardManager {
|
||||
|
||||
override def setState(cc: List[Card], currentIndex: Int): Unit = {
|
||||
this.cc = cc
|
||||
this.currentIdx = currentIndex
|
||||
}
|
||||
|
||||
override def cardContainer: List[Card] = cc
|
||||
private var cc: List[Card] = {
|
||||
val cc = ListBuffer[Card]()
|
||||
for (suit <- Suit.values) {
|
||||
for (cardValue <- CardValue.values) {
|
||||
cc += Card(cardValue, suit)
|
||||
}
|
||||
}
|
||||
cc.toList
|
||||
}
|
||||
private var currentIdx = 0
|
||||
|
||||
override def currentIndx: Int = currentIdx
|
||||
|
||||
override def shuffleAndReset(): Unit = {
|
||||
cc = Random.shuffle(cc)
|
||||
currentIdx = 0
|
||||
}
|
||||
|
||||
override def resetOrder(): Unit = {
|
||||
cc = cc.sortBy(c => (c.suit.ordinal, c.cardValue.ordinal))
|
||||
currentIdx = 0
|
||||
}
|
||||
|
||||
override def nextCard(): Card = {
|
||||
val card = cc(currentIdx)
|
||||
if (currentIdx + 1 > 51) {
|
||||
throw new IndexOutOfBoundsException("Trying to access card 53(out of bounds)")
|
||||
} else {
|
||||
currentIdx += 1
|
||||
card
|
||||
}
|
||||
}
|
||||
|
||||
override def createHand(amount: Int = 7): Hand = {
|
||||
val hand = ListBuffer[Card]()
|
||||
for (_ <- 1 to amount) {
|
||||
hand += nextCard()
|
||||
}
|
||||
Hand(hand.toList)
|
||||
}
|
||||
|
||||
override def grabSpecificCard(card: Card): Card = {
|
||||
cc.filter(c => c.suit == card.suit && c.cardValue == card.cardValue).head
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package de.knockoutwhist.cards.stub
|
||||
|
||||
import de.knockoutwhist.cards.*
|
||||
|
||||
object StubCardManager extends CardManager {
|
||||
override def cardContainer: List[Card] = List()
|
||||
|
||||
override def shuffleAndReset(): Unit = {}
|
||||
|
||||
override def resetOrder(): Unit = {}
|
||||
|
||||
override def nextCard(): Card = Card(CardValue.Ace, Suit.Clubs)
|
||||
|
||||
override def createHand(amount: Int): Hand = Hand(List(Card(CardValue.Ace, Suit.Clubs)))
|
||||
|
||||
override def grabSpecificCard(card: Card): Card = card
|
||||
|
||||
override def currentIndx: Int = -1
|
||||
|
||||
override def setState(cc: List[Card], currentIndex: Int): Unit = {}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package de.knockoutwhist.components
|
||||
|
||||
import de.knockoutwhist.cards.CardManager
|
||||
import de.knockoutwhist.control.*
|
||||
import de.knockoutwhist.persistence.PersistenceManager
|
||||
import de.knockoutwhist.persistence.formats.FileFormatter
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.utils.CustomPlayerQueue
|
||||
|
||||
trait Configuration {
|
||||
def maincomponent: Maincomponent
|
||||
def matchcomponent: Matchcomponent
|
||||
def playeractrcomponent: Playeractrcomponent
|
||||
def playerlogcomponent: Playerlogcomponent
|
||||
def roundlogcomponent: Roundlogcomponent
|
||||
def trickcomponent: Tricklogcomponent
|
||||
def cardManager: CardManager
|
||||
def persistenceManager: PersistenceManager
|
||||
def fileFormatter: FileFormatter
|
||||
|
||||
def createRightQueue(players: Array[AbstractPlayer], start: Int = 0): CustomPlayerQueue[AbstractPlayer]
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package de.knockoutwhist.components
|
||||
|
||||
import com.google.inject.Guice
|
||||
import de.knockoutwhist.cards.CardManager
|
||||
import de.knockoutwhist.cards.base.CardBaseManager
|
||||
import de.knockoutwhist.control.*
|
||||
import de.knockoutwhist.control.controllerBaseImpl.*
|
||||
import de.knockoutwhist.di.KnockOutLogicModule
|
||||
import de.knockoutwhist.persistence.PersistenceManager
|
||||
import de.knockoutwhist.persistence.formats.FileFormatter
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.utils.CustomPlayerQueue
|
||||
import de.knockoutwhist.utils.baseQueue.{CustomPlayerBaseQueue, CustomPlayerQueueBuilder, QueueBuilder}
|
||||
|
||||
object DefaultConfiguration extends Configuration {
|
||||
|
||||
private val injector = Guice.createInjector(KnockOutLogicModule())
|
||||
|
||||
def maincomponent: Maincomponent = injector.getInstance(classOf[Maincomponent])
|
||||
def matchcomponent: Matchcomponent = injector.getInstance(classOf[Matchcomponent])
|
||||
def playeractrcomponent: Playeractrcomponent = injector.getInstance(classOf[Playeractrcomponent])
|
||||
def playerlogcomponent: Playerlogcomponent = injector.getInstance(classOf[Playerlogcomponent])
|
||||
def roundlogcomponent: Roundlogcomponent = injector.getInstance(classOf[Roundlogcomponent])
|
||||
def trickcomponent: Tricklogcomponent = injector.getInstance(classOf[Tricklogcomponent])
|
||||
def cardManager: CardManager = injector.getInstance(classOf[CardManager])
|
||||
def persistenceManager: PersistenceManager = injector.getInstance(classOf[PersistenceManager])
|
||||
def fileFormatter: FileFormatter = injector.getInstance(classOf[FileFormatter])
|
||||
|
||||
override def createRightQueue(players: Array[AbstractPlayer], start: Int): CustomPlayerQueue[AbstractPlayer] = {
|
||||
val builder = injector.getInstance(classOf[QueueBuilder])
|
||||
builder.setStart(start)
|
||||
builder.setPlayer(players)
|
||||
builder.build()
|
||||
}
|
||||
}
|
||||
88
src/main/scala/de/knockoutwhist/control/AILogic.scala
Normal file
@@ -0,0 +1,88 @@
|
||||
package de.knockoutwhist.control
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.cards.base.CardBaseManager
|
||||
import de.knockoutwhist.cards.{Card, Hand, Suit}
|
||||
import de.knockoutwhist.control.ControlHandler
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Round, Trick}
|
||||
|
||||
import scala.util.Random
|
||||
|
||||
case object AILogic {
|
||||
|
||||
def decideCard(ai: AbstractPlayer, round: Round, trick: Trick): Card = {
|
||||
if(trick.firstCard.isEmpty) return ai.currentHand().get.cards.maxBy(_.cardValue.ordinal)
|
||||
val firstCardSuit = trick.firstCard.get.suit
|
||||
val hand = ai.currentHand().get
|
||||
val cardsOfSuit = hand.cards.filter(_.suit == firstCardSuit)
|
||||
val trumpsInGame = trick.cards.keys.filter(_.suit == round.trumpSuit)
|
||||
if (cardsOfSuit.isEmpty) {
|
||||
val trumpCards = hand.cards.filter(_.suit == round.trumpSuit)
|
||||
if (trumpCards.isEmpty) hand.cards.minBy(_.cardValue.ordinal)
|
||||
else {
|
||||
val bestOption = decideWhichTrumpCard(hand, round, trick, trumpsInGame.toList)
|
||||
grabBestResult(bestOption, hand, round, trick)
|
||||
}
|
||||
} else {
|
||||
if(trumpsInGame.nonEmpty) cardsOfSuit.minBy(_.cardValue.ordinal)
|
||||
else cardsOfSuit.maxBy(_.cardValue.ordinal)
|
||||
}
|
||||
KnockOutWhist.config.cardManager.nextCard()
|
||||
}
|
||||
|
||||
|
||||
private def grabBestResult(bestOption: Option[Card], hand: Hand, round: Round, trick: Trick): Card = {
|
||||
bestOption match {
|
||||
case Some(card) => card
|
||||
case None =>
|
||||
val card = hand.cards.filter(_.suit != round.trumpSuit)
|
||||
if (card.isEmpty) hand.cards.minBy(_.cardValue.ordinal)
|
||||
else card.minBy(_.cardValue.ordinal)
|
||||
}
|
||||
}
|
||||
|
||||
private def decideWhichTrumpCard(hand: Hand, round: Round, trick: Trick, activeTrumps: List[Card]): Option[Card] = {
|
||||
val trumpCards = hand.cards.filter(_.suit == round.trumpSuit)
|
||||
if (round.playerQueue.size - trick.cards.size == 1 && activeTrumps.isEmpty) return Some(trumpCards.minBy(_.cardValue.ordinal))
|
||||
val highestTrump = trumpCards.maxBy(_.cardValue.ordinal)
|
||||
val activeTrump = activeTrumps.maxBy(_.cardValue.ordinal)
|
||||
if (highestTrump.cardValue.ordinal < activeTrump.cardValue.ordinal) None
|
||||
else {
|
||||
val higherTrumps = trumpCards.filter(_.cardValue.ordinal > activeTrump.cardValue.ordinal)
|
||||
if(round.playerQueue.size - trick.cards.size <= round.playersin.size * 0.5) Some(higherTrumps.minBy(_.cardValue.ordinal))
|
||||
else Some(highestTrump)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def decideTrumpSuit(ai: AbstractPlayer): Suit = {
|
||||
val hand = ai.currentHand().get
|
||||
hand.cards.groupBy(_.suit).maxBy(_._2.size)._1
|
||||
}
|
||||
|
||||
def decideTie(min: Int, max: Int): Int = {
|
||||
Random.between(min, max+1)
|
||||
}
|
||||
def decideDogCard(ai: AbstractPlayer, round: Round, trick: Trick, needstoplay: Boolean): Option[Card] = {
|
||||
val firstCardSuit = trick.firstCard.get.suit
|
||||
val hand = ai.currentHand().get
|
||||
val trumpsuit = round.trumpSuit
|
||||
val trumpsuitPlayed = trick.cards.keys.exists(_.suit == trumpsuit)
|
||||
if(needstoplay) {
|
||||
Some(hand.cards.head)
|
||||
} else if(trumpsuitPlayed) {
|
||||
sortbestcard(trick, trumpsuit, hand)
|
||||
} else {
|
||||
sortbestcard(trick, firstCardSuit, hand)
|
||||
}
|
||||
}
|
||||
|
||||
private def sortbestcard(trick: Trick, suit: Suit, hand: Hand): Option[Card] = {
|
||||
val highestCard = trick.cards.keys.filter(_.suit == suit).maxBy(_.cardValue.ordinal)
|
||||
if (hand.cards.head.suit == suit && hand.cards.head.cardValue.ordinal > highestCard.cardValue.ordinal) {
|
||||
return Some(hand.cards.head)
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
23
src/main/scala/de/knockoutwhist/control/ControlHandler.scala
Normal file
@@ -0,0 +1,23 @@
|
||||
package de.knockoutwhist.control
|
||||
|
||||
import de.knockoutwhist.ui.gui.GUIMain
|
||||
import de.knockoutwhist.ui.tui.TUIMain
|
||||
import de.knockoutwhist.utils.events.EventHandler
|
||||
import de.knockoutwhist.utils.{CustomThread, DelayHandler}
|
||||
|
||||
object ControlHandler extends EventHandler {
|
||||
|
||||
addListener(GUIMain)
|
||||
addListener(TUIMain)
|
||||
addListener(DelayHandler)
|
||||
|
||||
}
|
||||
|
||||
object ControlThread extends CustomThread {
|
||||
|
||||
setName("ControlThread")
|
||||
|
||||
def isControlThread: Boolean = Thread.currentThread().equals(ControlThread)
|
||||
|
||||
override def instance: CustomThread = ControlThread
|
||||
}
|
||||
25
src/main/scala/de/knockoutwhist/control/Maincomponent.scala
Normal file
@@ -0,0 +1,25 @@
|
||||
package de.knockoutwhist.control
|
||||
|
||||
import de.knockoutwhist.cards.Card
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||
|
||||
trait Maincomponent {
|
||||
|
||||
def startMatch(): Unit
|
||||
|
||||
def enteredPlayers(players: List[AbstractPlayer]): Unit
|
||||
|
||||
def controlMatch(matchImpl: Match): Unit
|
||||
|
||||
def controlRound(matchImpl: Match, round: Round): Unit
|
||||
|
||||
def endRound(matchImpl: Match, round: Round, winner: AbstractPlayer, playersOut: List[AbstractPlayer]): Unit
|
||||
|
||||
def controlTrick(matchImpl: Match, round: Round, trick: Trick, currentIndex: Int = 0): Unit
|
||||
|
||||
def controlPlayer(matchImpl: Match, round: Round, trick: Trick, player: AbstractPlayer, currentIndex: Int): Unit
|
||||
|
||||
def playCard(trick: Trick, card: Card, player: AbstractPlayer): Trick
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
package de.knockoutwhist.control
|
||||
|
||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||
|
||||
trait MatchControl {
|
||||
|
||||
def initial(): Boolean
|
||||
def start(): Unit
|
||||
def playerControl: PlayerControl
|
||||
|
||||
/**
|
||||
* Start the next round
|
||||
* @return the next round or null if the match is over
|
||||
*/
|
||||
def nextRound(matchImpl: Match): Round
|
||||
|
||||
/**
|
||||
* Start the next trick
|
||||
* @return the last trick or null if the round is over
|
||||
*/
|
||||
def nextTrick(roundImpl: Round): Trick
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package de.knockoutwhist.control
|
||||
|
||||
import de.knockoutwhist.rounds.Match
|
||||
|
||||
trait Matchcomponent {
|
||||
|
||||
def isOver(matchImpl: Match): Boolean
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package de.knockoutwhist.control
|
||||
|
||||
import de.knockoutwhist.cards.{Card, Suit}
|
||||
import de.knockoutwhist.player.Player
|
||||
import de.knockoutwhist.rounds.Round
|
||||
|
||||
trait PlayerControl {
|
||||
|
||||
def playCard(player: Player): Card
|
||||
def dogplayCard(player: Player, round: Round): Option[Card]
|
||||
def determineWinnerTie(players: List[Player]): Player
|
||||
def pickNextTrumpsuit(player: Player): Suit
|
||||
def showCards(player: Player): Boolean
|
||||
def showWon(player: Player, round: Round): String
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package de.knockoutwhist.control
|
||||
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||
|
||||
trait Playeractrcomponent {
|
||||
|
||||
def playCard(matchImpl: Match, player: AbstractPlayer, round: Round, trick: Trick, currentIndex: Int): Unit
|
||||
|
||||
|
||||
def dogplayCard(matchImpl: Match, player: AbstractPlayer, round: Round, trick: Trick, currentIndex: Int): Unit
|
||||
|
||||
|
||||
def pickNextTrumpsuit(matchImpl: Match, remaining_players: List[AbstractPlayer], firstRound: Boolean, player: AbstractPlayer): Unit
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package de.knockoutwhist.control
|
||||
|
||||
import de.knockoutwhist.cards.{Card, Suit}
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round}
|
||||
|
||||
import scala.collection.immutable
|
||||
import scala.util.Try
|
||||
|
||||
trait Playerlogcomponent {
|
||||
|
||||
def trumpsuitStep(matchImpl: Match, remaining_players: List[AbstractPlayer]): Unit
|
||||
|
||||
def trumpSuitSelected(matchImpl: Match, suit: Try[Suit], remaining_players: List[AbstractPlayer], firstRound: Boolean, decided: AbstractPlayer): Unit
|
||||
|
||||
def preSelect(winners: List[AbstractPlayer], matchImpl: Match, round: Round, playersout: List[AbstractPlayer]): Unit
|
||||
|
||||
def selectTie(winners: List[AbstractPlayer], matchImpl: Match, round: Round, playersout: List[AbstractPlayer], cut: immutable.HashMap[AbstractPlayer, Card], currentStep: Int, remaining: Int, currentIndex: Int = 0): Unit
|
||||
|
||||
def selectedTie(winner: List[AbstractPlayer],matchImpl: Match, round: Round, playersout: List[AbstractPlayer], cut: immutable.HashMap[AbstractPlayer, Card], value: Try[Int], currentStep: Int, remaining: Int, currentIndex: Int = 0): Unit
|
||||
|
||||
def evaluateTieWinner(matchImpl: Match, round: Round, playersout: List[AbstractPlayer], cut: immutable.HashMap[AbstractPlayer, Card]): Unit
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package de.knockoutwhist.control
|
||||
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round}
|
||||
|
||||
trait Roundlogcomponent {
|
||||
|
||||
def isOver(round: Round): Boolean
|
||||
|
||||
def dogNeedsToPlay(round: Round): Boolean
|
||||
|
||||
def finalizeRound(round: Round, matchImpl: Match, force: Boolean = false): (Match, Round, List[AbstractPlayer], List[AbstractPlayer])
|
||||
|
||||
def remainingPlayers(round: Round): List[AbstractPlayer]
|
||||
|
||||
def provideCards(matchImpl: Match, players: List[AbstractPlayer]): (Match,List[AbstractPlayer])
|
||||
|
||||
def smashResults(round: Round): Round
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package de.knockoutwhist.control
|
||||
|
||||
import de.knockoutwhist.cards.Card
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||
|
||||
import scala.util.Try
|
||||
|
||||
trait Tricklogcomponent {
|
||||
|
||||
def controlSuitplayed(card: Try[Card], matchImpl: Match, round: Round, trick: Trick, currentIndex: Int, player: AbstractPlayer): Unit
|
||||
|
||||
def controlDogPlayed(card: Try[Option[Card]], matchImpl: Match, round: Round, trick: Trick, currentIndex: Int, player: AbstractPlayer): Unit
|
||||
|
||||
def alternativeCards(card: Card, round: Round, trick: Trick, player: AbstractPlayer): List[Card]
|
||||
|
||||
def wonTrick(trick: Trick, round: Round): (AbstractPlayer, Trick)
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.cards.Card
|
||||
import de.knockoutwhist.control.*
|
||||
import de.knockoutwhist.events.GLOBAL_STATUS.SHOW_FINISHED_MATCH
|
||||
import de.knockoutwhist.events.PLAYER_STATUS.SHOW_WON_PLAYER_TRICK
|
||||
import de.knockoutwhist.events.ROUND_STATUS.{PLAYERS_OUT, WON_ROUND}
|
||||
import de.knockoutwhist.events.round.ShowCurrentTrickEvent
|
||||
import de.knockoutwhist.events.ui.GameState.{MAIN_MENU, PLAYERS, TIE}
|
||||
import de.knockoutwhist.events.ui.GameStateUpdateEvent
|
||||
import de.knockoutwhist.events.util.DelayEvent
|
||||
import de.knockoutwhist.events.{ShowGlobalStatus, ShowPlayerStatus, ShowRoundStatus}
|
||||
import de.knockoutwhist.persistence.MethodEntryPoint.{ControlMatch, ControlRound, ControlTrick}
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||
import de.knockoutwhist.undo.UndoManager
|
||||
import de.knockoutwhist.undo.commands.EnterPlayersCommand
|
||||
import de.knockoutwhist.utils.Implicits.*
|
||||
|
||||
object MainLogic extends Maincomponent {
|
||||
|
||||
def startMatch(): Unit = {
|
||||
ControlHandler.invoke(GameStateUpdateEvent(PLAYERS))
|
||||
}
|
||||
|
||||
def enteredPlayers(players: List[AbstractPlayer]): Unit = {
|
||||
UndoManager.doStep(EnterPlayersCommand(players))
|
||||
}
|
||||
|
||||
def controlMatch(matchImpl: Match): Unit = {
|
||||
KnockOutWhist.config.persistenceManager.updateMatch(matchImpl)
|
||||
KnockOutWhist.config.persistenceManager.updateMethodEntryPoint(ControlMatch)
|
||||
if(KnockOutWhist.config.matchcomponent.isOver(matchImpl)) {
|
||||
ControlHandler.invoke(ShowGlobalStatus(SHOW_FINISHED_MATCH, KnockOutWhist.config.roundlogcomponent.remainingPlayers(matchImpl.roundlist.last).head))
|
||||
//ControlHandler.invoke(GameStateUpdateEvent(MAIN_MENU))
|
||||
} else {
|
||||
val remainingPlayer = matchImpl.roundlist.isEmpty ? matchImpl.totalplayers |: KnockOutWhist.config.roundlogcomponent.remainingPlayers(matchImpl.roundlist.last)
|
||||
val newMatch = KnockOutWhist.config.roundlogcomponent.provideCards(matchImpl, remainingPlayer)
|
||||
KnockOutWhist.config.playerlogcomponent.trumpsuitStep(newMatch._1, newMatch._2)
|
||||
}
|
||||
}
|
||||
|
||||
def controlRound(matchImpl: Match, round: Round): Unit = {
|
||||
KnockOutWhist.config.persistenceManager.updateMatch(matchImpl)
|
||||
KnockOutWhist.config.persistenceManager.updateRound(round)
|
||||
KnockOutWhist.config.persistenceManager.updateMethodEntryPoint(ControlRound)
|
||||
if(!KnockOutWhist.config.roundlogcomponent.isOver(round)) {
|
||||
val trick = Trick()
|
||||
controlTrick(matchImpl, round, trick)
|
||||
return
|
||||
}
|
||||
val result = KnockOutWhist.config.roundlogcomponent.finalizeRound(KnockOutWhist.config.roundlogcomponent.smashResults(round), matchImpl)
|
||||
if(result._3.size == 1) {
|
||||
endRound(result._1, result._2, result._3.head, result._4)
|
||||
} else {
|
||||
KnockOutWhist.config.playerlogcomponent.preSelect(result._3, result._1, result._2, result._4)
|
||||
}
|
||||
}
|
||||
|
||||
def endRound(matchImpl: Match, round: Round, winner: AbstractPlayer, playersOut: List[AbstractPlayer]): Unit = {
|
||||
val finalRound = Round(round.trumpSuit, round.tricklist, round.playersin, playersOut, round.startingPlayer, winner, firstRound = round.firstRound)
|
||||
val newMatch = matchImpl.addRound(finalRound)
|
||||
ControlHandler.invoke(ShowRoundStatus(WON_ROUND, finalRound, winner))
|
||||
ControlHandler.invoke(DelayEvent(2000L))
|
||||
if (finalRound.playersout.nonEmpty) {
|
||||
ControlHandler.invoke(ShowRoundStatus(PLAYERS_OUT, finalRound))
|
||||
}
|
||||
controlMatch(newMatch)
|
||||
}
|
||||
|
||||
def controlTrick(matchImpl: Match, round: Round, trick: Trick, currentIndex: Int = 0): Unit = {
|
||||
KnockOutWhist.config.persistenceManager.updateMatch(matchImpl)
|
||||
KnockOutWhist.config.persistenceManager.updateRound(round)
|
||||
KnockOutWhist.config.persistenceManager.updateTrick(trick)
|
||||
KnockOutWhist.config.persistenceManager.updateCurrentIndex(currentIndex)
|
||||
KnockOutWhist.config.persistenceManager.updateMethodEntryPoint(ControlTrick)
|
||||
if(currentIndex < round.playersin.size) {
|
||||
val player = round.playerQueue.nextPlayer()
|
||||
controlPlayer(matchImpl, round, trick, player, currentIndex)
|
||||
}else {
|
||||
val result = KnockOutWhist.config.trickcomponent.wonTrick(trick, round)
|
||||
val newRound = round.addTrick(result._2)
|
||||
ControlHandler.invoke(ShowPlayerStatus(SHOW_WON_PLAYER_TRICK, result._1, result._2))
|
||||
newRound.playerQueue.resetAndSetStart(result._1)
|
||||
ControlHandler.invoke(DelayEvent(1000L))
|
||||
controlRound(matchImpl, newRound)
|
||||
}
|
||||
}
|
||||
|
||||
def controlPlayer(matchImpl: Match, round: Round, trick: Trick, player: AbstractPlayer, currentIndex: Int): Unit = {
|
||||
ControlHandler.invoke(ShowCurrentTrickEvent(round, trick))
|
||||
if (!player.doglife) {
|
||||
KnockOutWhist.config.playeractrcomponent.playCard(matchImpl, player, round, trick, currentIndex)
|
||||
} else if (player.currentHand().exists(_.cards.nonEmpty)) {
|
||||
KnockOutWhist.config.playeractrcomponent.dogplayCard(matchImpl, player, round, trick, currentIndex)
|
||||
}else {
|
||||
controlTrick(matchImpl, round, trick, currentIndex+1)
|
||||
}
|
||||
}
|
||||
|
||||
def playCard(trick: Trick, card: Card, player: AbstractPlayer): Trick = {
|
||||
if (trick.firstCard.isEmpty) {
|
||||
trick.setfirstcard(card).addCard(card, player)
|
||||
} else {
|
||||
trick.addCard(card, player)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.control.{ControlHandler, Matchcomponent}
|
||||
import de.knockoutwhist.rounds.Match
|
||||
|
||||
object MatchLogic extends Matchcomponent {
|
||||
def isOver(matchImpl: Match): Boolean = {
|
||||
if (matchImpl.roundlist.isEmpty) {
|
||||
false
|
||||
} else {
|
||||
KnockOutWhist.config.roundlogcomponent.remainingPlayers(matchImpl.roundlist.last).size == 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.control.{ControlHandler, Playeractrcomponent}
|
||||
import de.knockoutwhist.events.PLAYER_STATUS.*
|
||||
import de.knockoutwhist.events.ShowPlayerStatus
|
||||
import de.knockoutwhist.events.cards.RenderHandEvent
|
||||
import de.knockoutwhist.events.util.DelayEvent
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||
|
||||
object PlayerControl extends Playeractrcomponent {
|
||||
|
||||
|
||||
def playCard(matchImpl: Match, player: AbstractPlayer, round: Round, trick: Trick, currentIndex: Int): Unit = {
|
||||
ControlHandler.invoke(ShowPlayerStatus(SHOW_TURN, player))
|
||||
ControlHandler.invoke(DelayEvent(500))
|
||||
ControlHandler.invoke(ShowPlayerStatus(SHOW_PLAY_CARD, player))
|
||||
ControlHandler.invoke(RenderHandEvent(player.currentHand().get, true))
|
||||
player.handlePlayCard(player.currentHand().get, matchImpl, round, trick, currentIndex)
|
||||
}
|
||||
|
||||
def dogplayCard(matchImpl: Match, player: AbstractPlayer, round: Round, trick: Trick, currentIndex: Int): Unit = {
|
||||
ControlHandler.invoke(ShowPlayerStatus(SHOW_TURN, player))
|
||||
ControlHandler.invoke(DelayEvent(500))
|
||||
ControlHandler.invoke(ShowPlayerStatus(SHOW_DOG_PLAY_CARD, player, KnockOutWhist.config.roundlogcomponent.dogNeedsToPlay(round)))
|
||||
ControlHandler.invoke(RenderHandEvent(player.currentHand().get, false))
|
||||
player.handleDogPlayCard(player.currentHand().get, matchImpl, round, trick, currentIndex, KnockOutWhist.config.roundlogcomponent.dogNeedsToPlay(round))
|
||||
}
|
||||
|
||||
def pickNextTrumpsuit(matchImpl: Match, remaining_players: List[AbstractPlayer], firstRound: Boolean, player: AbstractPlayer): Unit = {
|
||||
ControlHandler.invoke(ShowPlayerStatus(SHOW_TRUMPSUIT_OPTIONS, player))
|
||||
ControlHandler.invoke(RenderHandEvent(player.currentHand().get, false))
|
||||
player.handlePickTrumpsuit(matchImpl, remaining_players, firstRound)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.cards.{Card, Suit}
|
||||
import de.knockoutwhist.control.{ControlHandler, Playerlogcomponent}
|
||||
import de.knockoutwhist.events.ERROR_STATUS.{INVALID_NUMBER, NOT_A_NUMBER}
|
||||
import de.knockoutwhist.events.GLOBAL_STATUS.{SHOW_TIE, SHOW_TIE_TIE, SHOW_TIE_WINNER}
|
||||
import de.knockoutwhist.events.PLAYER_STATUS.SHOW_TIE_NUMBERS
|
||||
import de.knockoutwhist.events.cards.ShowTieCardsEvent
|
||||
import de.knockoutwhist.events.ui.GameState.{INGAME, TIE}
|
||||
import de.knockoutwhist.events.ui.GameStateUpdateEvent
|
||||
import de.knockoutwhist.events.util.DelayEvent
|
||||
import de.knockoutwhist.events.{ShowErrorStatus, ShowGlobalStatus, ShowPlayerStatus}
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round}
|
||||
import de.knockoutwhist.ui.gui.TieMenu
|
||||
import de.knockoutwhist.undo.UndoManager
|
||||
import de.knockoutwhist.undo.commands.{SelectTieCommand, TrumpSuitSelectedCommand}
|
||||
|
||||
import scala.collection.immutable
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.util.Try
|
||||
|
||||
object PlayerLogic extends Playerlogcomponent {
|
||||
|
||||
def trumpsuitStep(matchImpl: Match, remaining_players: List[AbstractPlayer]): Unit = {
|
||||
if (matchImpl.roundlist.isEmpty) {
|
||||
val randomTrumpsuit = matchImpl.cardManager.nextCard().suit
|
||||
val newMatchImpl = matchImpl.setNumberOfCards(matchImpl.numberofcards - 1)
|
||||
val round = new Round(randomTrumpsuit, remaining_players, true)
|
||||
KnockOutWhist.config.maincomponent.controlRound(newMatchImpl, round)
|
||||
} else {
|
||||
val winner = matchImpl.totalplayers.filter(matchImpl.roundlist.last.winner.name == _.name).head
|
||||
KnockOutWhist.config.playeractrcomponent.pickNextTrumpsuit(matchImpl, remaining_players, false, winner)
|
||||
}
|
||||
}
|
||||
|
||||
def trumpSuitSelected(matchImpl: Match, suit: Try[Suit], remaining_players: List[AbstractPlayer], firstRound: Boolean, decided: AbstractPlayer): Unit = {
|
||||
if (suit.isFailure) {
|
||||
ControlHandler.invoke(ShowErrorStatus(INVALID_NUMBER))
|
||||
KnockOutWhist.config.playeractrcomponent.pickNextTrumpsuit(matchImpl, remaining_players, firstRound, decided)
|
||||
return
|
||||
}
|
||||
ControlHandler.invoke(GameStateUpdateEvent(INGAME))
|
||||
UndoManager.doStep(TrumpSuitSelectedCommand(matchImpl, suit.get, remaining_players, false, decided))
|
||||
}
|
||||
|
||||
def preSelect(winners: List[AbstractPlayer], matchImpl: Match, round: Round, playersout: List[AbstractPlayer]): Unit = {
|
||||
if (!KnockOutWhist.debugmode) matchImpl.cardManager.shuffleAndReset()
|
||||
ControlHandler.invoke(ShowGlobalStatus(SHOW_TIE))
|
||||
ControlHandler.invoke(GameStateUpdateEvent(TIE))
|
||||
selectTie(winners, matchImpl, round, playersout, immutable.HashMap(), 0, matchImpl.cardManager.cardContainer.size - (winners.length - 1))
|
||||
}
|
||||
|
||||
def selectTie(winners: List[AbstractPlayer], matchImpl: Match, round: Round, playersout: List[AbstractPlayer], cut: immutable.HashMap[AbstractPlayer, Card], currentStep: Int, remaining: Int, currentIndex: Int = 0): Unit = {
|
||||
ControlHandler.invoke(GameStateUpdateEvent(TIE))
|
||||
if(currentIndex == winners.size) {
|
||||
evaluateTieWinner(matchImpl, round, playersout, cut)
|
||||
} else {
|
||||
val player = winners(currentIndex)
|
||||
ControlHandler.invoke(ShowPlayerStatus(SHOW_TIE_NUMBERS, player, remaining))
|
||||
|
||||
player.handlePickTieCard(winners, matchImpl, round, playersout, cut, currentStep, remaining, currentIndex)
|
||||
}
|
||||
}
|
||||
|
||||
def selectedTie(winner: List[AbstractPlayer],matchImpl: Match, round: Round, playersout: List[AbstractPlayer], cut: immutable.HashMap[AbstractPlayer, Card], value: Try[Int], currentStep: Int, remaining: Int, currentIndex: Int = 0): Unit = {
|
||||
if (value.isFailure) {
|
||||
ControlHandler.invoke(ShowErrorStatus(NOT_A_NUMBER))
|
||||
selectTie(winner, matchImpl, round, playersout, cut, currentStep, remaining, currentIndex)
|
||||
return
|
||||
}
|
||||
val selCard = matchImpl.cardManager.cardContainer(currentStep + (value.get - 1))
|
||||
UndoManager.doStep(SelectTieCommand(winner, matchImpl, round, playersout, cut, value.get, selCard, currentStep, remaining, currentIndex))
|
||||
}
|
||||
|
||||
def evaluateTieWinner(matchImpl: Match, round: Round, playersout: List[AbstractPlayer], cut: immutable.HashMap[AbstractPlayer, Card]): Unit = {
|
||||
ControlHandler.invoke(ShowTieCardsEvent(cut.toList))
|
||||
val winner: ListBuffer[AbstractPlayer] = ListBuffer()
|
||||
var currentHighest: Card = null
|
||||
for ((player, card) <- cut) {
|
||||
if (currentHighest == null) {
|
||||
currentHighest = card
|
||||
winner += player
|
||||
} else {
|
||||
val compared = card.cardValue.ordinal.compareTo(currentHighest.cardValue.ordinal)
|
||||
if (compared > 0) {
|
||||
currentHighest = card
|
||||
winner.clear()
|
||||
winner += player
|
||||
} else if (compared == 0) {
|
||||
winner += player
|
||||
}
|
||||
}
|
||||
}
|
||||
if (winner.size == 1) {
|
||||
ControlHandler.invoke(ShowGlobalStatus(SHOW_TIE_WINNER, winner.head))
|
||||
KnockOutWhist.config.maincomponent.endRound(matchImpl, round, winner.head, playersout)
|
||||
return
|
||||
}
|
||||
ControlHandler.invoke(ShowGlobalStatus(SHOW_TIE_TIE))
|
||||
ControlHandler.invoke(DelayEvent(2000))
|
||||
preSelect(winner.toList, matchImpl, round, playersout)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.control.{ControlHandler, Roundlogcomponent}
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||
import de.knockoutwhist.utils.Implicits.*
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
object RoundLogic extends Roundlogcomponent{
|
||||
|
||||
def isOver(round: Round): Boolean = {
|
||||
round.playersin.map(_.currentHand()).count(_.get.cards.isEmpty) == round.playersin.size
|
||||
}
|
||||
|
||||
def dogNeedsToPlay(round: Round): Boolean = {
|
||||
round.playersin.filter(!_.doglife).map(_.currentHand()).exists(_.get.cards.isEmpty)
|
||||
}
|
||||
|
||||
def finalizeRound(round: Round, matchImpl: Match, force: Boolean = false): (Match, Round, List[AbstractPlayer], List[AbstractPlayer]) = {
|
||||
if (!force && round.tricklist.isEmpty)
|
||||
throw new IllegalStateException("No tricks played in this round")
|
||||
if (!force && !isOver(round))
|
||||
throw new IllegalStateException("Not all tricks were played in this round")
|
||||
val tricksMapped = round.tricklist
|
||||
.map(t => t.winner)
|
||||
.groupBy(identity).map((p, l) => (p, l.size)) //l.size = Anzahl gewonnener Tricks
|
||||
val winners = tricksMapped
|
||||
.filter((p, i) => i == tricksMapped.values.max)
|
||||
.keys
|
||||
var playersOut = round.firstRound
|
||||
? List()
|
||||
|: round.playersin.filter(!tricksMapped.contains(_))
|
||||
|
||||
var newMatch = matchImpl
|
||||
var newRound = round
|
||||
if (playersOut.nonEmpty && !matchImpl.dogLife) {
|
||||
newMatch = matchImpl.setDogLife()
|
||||
|
||||
val playersUpdated = ListBuffer[AbstractPlayer]()
|
||||
playersUpdated ++= tricksMapped.keys
|
||||
playersOut.foreach(p => {
|
||||
playersUpdated += p.setDogLife()
|
||||
})
|
||||
newMatch = newMatch.updatePlayers(playersUpdated.toList)
|
||||
newRound = newRound.updatePlayersIn(playersUpdated.toList)
|
||||
playersOut = List()
|
||||
}
|
||||
(newMatch, newRound, winners.toList, playersOut)
|
||||
}
|
||||
|
||||
def remainingPlayers(round: Round): List[AbstractPlayer] = {
|
||||
if (round.playersout == null) {
|
||||
return round.playersin
|
||||
}
|
||||
round.playersin.filter(!round.playersout.contains(_))
|
||||
}
|
||||
|
||||
def provideCards(matchImpl: Match, players: List[AbstractPlayer]): (Match,List[AbstractPlayer]) = {
|
||||
if (!KnockOutWhist.debugmode) matchImpl.cardManager.shuffleAndReset()
|
||||
val listbuff = new ListBuffer[AbstractPlayer]()
|
||||
for (player <- players) {
|
||||
if (!player.doglife) {
|
||||
val newPlayer = player.provideHand(matchImpl.cardManager.createHand(matchImpl.numberofcards))
|
||||
listbuff.addOne(newPlayer)
|
||||
} else {
|
||||
val newPlayer = player.provideHand(matchImpl.cardManager.createHand(1))
|
||||
listbuff.addOne(newPlayer)
|
||||
}
|
||||
}
|
||||
val matchResult = matchImpl.totalplayers.appendedAll(listbuff.toList).filter(!players.contains(_))
|
||||
(matchImpl.updatePlayers(matchResult), listbuff.toList)
|
||||
}
|
||||
|
||||
def smashResults(round: Round): Round = {
|
||||
val correctPlayers = round.playersin.groupMapReduce(_.id)(identity)((a, *) => a)
|
||||
val newTricks = round.tricklist.map(t => Trick(t.cards, correctPlayers.getOrElse(t.winner.id, t.winner), t.finished, t.firstCard))
|
||||
Round(round.trumpSuit, newTricks, round.playersin, round.playersout, round.startingPlayer, round.winner, round.firstRound)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package de.knockoutwhist.control.controllerBaseImpl
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.cards.{Card, Hand}
|
||||
import de.knockoutwhist.control.controllerBaseImpl.PlayerControl
|
||||
import de.knockoutwhist.control.{ControlHandler, Tricklogcomponent}
|
||||
import de.knockoutwhist.events.ERROR_STATUS.{INVALID_INPUT, INVALID_NUMBER, WRONG_CARD}
|
||||
import de.knockoutwhist.events.ShowErrorStatus
|
||||
import de.knockoutwhist.player.AbstractPlayer
|
||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||
import de.knockoutwhist.undo.UndoManager
|
||||
import de.knockoutwhist.undo.commands.{PlayerPlayCommand, PlayerPlayDogCommand}
|
||||
|
||||
import scala.util.Try
|
||||
|
||||
object TrickLogic extends Tricklogcomponent {
|
||||
|
||||
def controlSuitplayed(card: Try[Card], matchImpl: Match, round: Round, trick: Trick, currentIndex: Int, player: AbstractPlayer): Unit = {
|
||||
if (card.isFailure) {
|
||||
ControlHandler.invoke(ShowErrorStatus(INVALID_NUMBER))
|
||||
KnockOutWhist.config.playeractrcomponent.playCard(matchImpl, player, round, trick, currentIndex)
|
||||
return
|
||||
}
|
||||
val realCard = card.get
|
||||
if (trick.firstCard.isDefined) {
|
||||
val firstCard = trick.firstCard.get
|
||||
if (firstCard.suit != realCard.suit) {
|
||||
var hasSuit = false
|
||||
for (cardInHand <- player.currentHand().get.cards) {
|
||||
if (cardInHand.suit == firstCard.suit) {
|
||||
hasSuit = true
|
||||
}
|
||||
}
|
||||
if (hasSuit) {
|
||||
ControlHandler.invoke(ShowErrorStatus(WRONG_CARD, firstCard))
|
||||
KnockOutWhist.config.playeractrcomponent.playCard(matchImpl, player, round, trick, currentIndex)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
UndoManager.doStep(PlayerPlayCommand(matchImpl, round, trick, player, realCard, currentIndex))
|
||||
}
|
||||
|
||||
def controlDogPlayed(card: Try[Option[Card]], matchImpl: Match, round: Round, trick: Trick, currentIndex: Int, player: AbstractPlayer): Unit = {
|
||||
if (card.isFailure) {
|
||||
ControlHandler.invoke(ShowErrorStatus(INVALID_INPUT))
|
||||
KnockOutWhist.config.playeractrcomponent.dogplayCard(matchImpl, player, round, trick, currentIndex)
|
||||
return
|
||||
}
|
||||
UndoManager.doStep(PlayerPlayDogCommand(matchImpl, round, trick, player, card.get, currentIndex))
|
||||
}
|
||||
|
||||
def alternativeCards(card: Card, round: Round, trick: Trick, player: AbstractPlayer): List[Card] = {
|
||||
if (trick.firstCard.isDefined) {
|
||||
val firstCard = trick.firstCard.get
|
||||
if (firstCard.suit != card.suit) {
|
||||
val alternatives: List[Card] = for cardInHand <- player.currentHand().get.cards
|
||||
if cardInHand.suit == firstCard.suit
|
||||
yield cardInHand
|
||||
if(round.trumpSuit == card.suit && alternatives.isEmpty) {
|
||||
return Nil
|
||||
}
|
||||
if (alternatives.nonEmpty) {
|
||||
return alternatives
|
||||
}
|
||||
}
|
||||
}
|
||||
Nil
|
||||
}
|
||||
|
||||
def wonTrick(trick: Trick, round: Round): (AbstractPlayer, Trick) = {
|
||||
val winningCard = {
|
||||
if (trick.cards.keys.exists(_.suit == round.trumpSuit)) {
|
||||
trick.cards.keys.filter(_.suit == round.trumpSuit).maxBy(_.cardValue.ordinal) //stream
|
||||
} else {
|
||||
trick.cards.keys.filter(_.suit == trick.firstCard.get.suit).maxBy(_.cardValue.ordinal) //stream
|
||||
}
|
||||
}
|
||||
val winningPlayer = trick.cards(winningCard)
|
||||
val finalTrick = Trick(trick.cards, winningPlayer, true)
|
||||
(winningPlayer, finalTrick)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
package de.knockoutwhist.control.text
|
||||
|
||||
import de.knockoutwhist.KnockOutWhist
|
||||
import de.knockoutwhist.cards.Card
|
||||
import de.knockoutwhist.control.{MatchControl, PlayerControl}
|
||||
import de.knockoutwhist.player.Player
|
||||
import de.knockoutwhist.rounds.{Match, Round, Trick}
|
||||
import de.knockoutwhist.utils.CustomPlayerQueue
|
||||
|
||||
import scala.compiletime.uninitialized
|
||||
import scala.io.StdIn
|
||||
import scala.util.Random
|
||||
|
||||
object TextMatchControl extends MatchControl {
|
||||
|
||||
private[control] var playerQueue: CustomPlayerQueue[Player] = uninitialized
|
||||
private var init = false
|
||||
|
||||
override def initial(): Boolean = {
|
||||
if(init) {
|
||||
println("The game is already running.")
|
||||
return false
|
||||
}
|
||||
init = true
|
||||
println("Welcome to Knockout Whist!")
|
||||
start()
|
||||
true
|
||||
}
|
||||
|
||||
override def start(): Unit = {
|
||||
while(true) { //Main Gameplay Loop
|
||||
val input = printMenu()
|
||||
input match {
|
||||
case "1" =>
|
||||
startMatch()
|
||||
case "2" =>
|
||||
println("Exiting the game.")
|
||||
return
|
||||
case _ =>
|
||||
println("Invalid input. Please try again.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private[control] def startMatch(): Player = {
|
||||
clearConsole()
|
||||
println("Starting a new match.")
|
||||
val players = enterPlayers()
|
||||
playerQueue = CustomPlayerQueue[Player](players, Random.nextInt(players.length))
|
||||
clearConsole()
|
||||
controlMatch()
|
||||
}
|
||||
|
||||
private[control] def enterPlayers(): Array[Player] = {
|
||||
println("Please enter the names of the players, separated by a comma.")
|
||||
val names = StdIn.readLine().split(",")
|
||||
if(names.length < 2) {
|
||||
println("Please enter at least two names.")
|
||||
return enterPlayers()
|
||||
}
|
||||
if(names.distinct.length != names.length) {
|
||||
println("Please enter unique names.")
|
||||
return enterPlayers()
|
||||
}
|
||||
if(names.count(_.trim.isBlank) > 0 || names.count(_.trim.length <= 2) > 0 || names.count(_.trim.length > 10) > 0) {
|
||||
println("Please enter valid names. Those can not be empty, shorter than 2 or longer then 10 characters.")
|
||||
return enterPlayers()
|
||||
}
|
||||
names.map(s => Player(s))
|
||||
}
|
||||
|
||||
private[control] def controlMatch(): Player = {
|
||||
val matchImpl = Match(playerQueue.toList)
|
||||
while (!matchImpl.isOver) {
|
||||
val roundImpl = controlRound(matchImpl)
|
||||
}
|
||||
clearConsole()
|
||||
println(s"The match is over. The winner is ${matchImpl.finalizeMatch().name}.")
|
||||
matchImpl.finalizeMatch()
|
||||
}
|
||||
|
||||
private[control] def controlRound(matchImpl: Match): Round = {
|
||||
val roundImpl = nextRound(matchImpl)
|
||||
clearConsole(10)
|
||||
println(s"Starting a new round. The trump suit is ${roundImpl.trumpSuit}.")
|
||||
clearConsole(2)
|
||||
while (!roundImpl.isOver) {
|
||||
controlTrick(roundImpl)
|
||||
}
|
||||
val (roundWinner, finalRound) = roundImpl.finalizeRound()
|
||||
println(s"${roundWinner.name} won the round.")
|
||||
if(!KnockOutWhist.DEBUG_MODE) Thread.sleep(5000L)
|
||||
if(finalRound.players_out.nonEmpty) {
|
||||
println("The following players are out of the game:")
|
||||
finalRound.players_out.foreach(p => {
|
||||
println(p.name)
|
||||
playerQueue.remove(p)
|
||||
})
|
||||
}
|
||||
playerQueue.resetAndSetStart(roundWinner)
|
||||
finalRound
|
||||
}
|
||||
|
||||
private[control] def controlTrick(round: Round): Trick = {
|
||||
val trick = nextTrick(round)
|
||||
for (player <- playerQueue) {
|
||||
clearConsole()
|
||||
println(printTrick(round))
|
||||
if (!player.doglife) {
|
||||
val rightCard = controlSuitplayed(trick, player)
|
||||
player.removeCard(rightCard)
|
||||
trick.playCard(rightCard, player)
|
||||
} else if (player.currentHand().exists(_.cards.nonEmpty)) {
|
||||
val card = playerControl.dogplayCard(player, round)
|
||||
if (card.isEmpty) {
|
||||
println(f"Player $player decided to not play his card")
|
||||
} else {
|
||||
player.removeCard(card.get)
|
||||
trick.playCard(card.get, player)
|
||||
}
|
||||
}
|
||||
}
|
||||
val (winner, finalTrick) = trick.wonTrick()
|
||||
clearConsole()
|
||||
println(printTrick(round))
|
||||
println(s"${winner.name} won the trick.")
|
||||
clearConsole(2)
|
||||
playerQueue.resetAndSetStart(winner)
|
||||
if(!KnockOutWhist.DEBUG_MODE) Thread.sleep(3000L)
|
||||
finalTrick
|
||||
}
|
||||
private[control] def controlSuitplayed(trick: Trick, player: Player): Card = {
|
||||
var card = playerControl.playCard(player)
|
||||
if (trick.get_first_card().isDefined) {
|
||||
while (!(trick.get_first_card().get.suit == card.suit)) {
|
||||
var hasSuit = false
|
||||
for (cardInHand <- player.currentHand().get.cards) {
|
||||
if (cardInHand.suit == trick.get_first_card().get.suit) {
|
||||
hasSuit = true
|
||||
}
|
||||
}
|
||||
if(!hasSuit) {
|
||||
return card
|
||||
}else {
|
||||
println(f"You have to play a card of suit: ${trick.get_first_card().get.suit}\n")
|
||||
card = playerControl.playCard(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
card
|
||||
}
|
||||
|
||||
private[control] def printMenu(): String = {
|
||||
println("Please select an option:")
|
||||
println("1. Start a new match")
|
||||
println("2. Exit")
|
||||
StdIn.readLine()
|
||||
}
|
||||
|
||||
private[control] def printTrick(round: Round): String = {
|
||||
val sb = new StringBuilder()
|
||||
sb.append("Current Trick:\n")
|
||||
sb.append("Trump-Suit: " + round.trumpSuit + "\n")
|
||||
if(round.get_current_trick().get_first_card().isDefined) {
|
||||
sb.append(s"Suit to play: ${round.get_current_trick().get_first_card().get.suit}\n")
|
||||
}
|
||||
for((card, player) <- round.get_current_trick().cards) {
|
||||
sb.append(s"${player.name} played ${card.toString}\n")
|
||||
}
|
||||
sb.toString()
|
||||
}
|
||||
|
||||
private def clearConsole(lines: Int = 32): Int = {
|
||||
var l = 0
|
||||
for(_ <- 0 until lines) {
|
||||
println()
|
||||
l += 1
|
||||
}
|
||||
l
|
||||
}
|
||||
|
||||
override def playerControl: PlayerControl = {
|
||||
TextPlayerControl
|
||||
}
|
||||
|
||||
override def nextRound(matchImpl: Match): Round = {
|
||||
if(matchImpl.isOver) {
|
||||
println(s"The match is over. The winner is ${matchImpl.finalizeMatch().name}.")
|
||||
return null
|
||||
}
|
||||
matchImpl.create_round()
|
||||
}
|
||||
|
||||
override def nextTrick(roundImpl: Round): Trick = {
|
||||
if(roundImpl.isOver) {
|
||||
println("The round is over.")
|
||||
return null
|
||||
}
|
||||
roundImpl.create_trick()
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||