mirror of
https://github.com/Vale54321/schafkopf-bot.git
synced 2025-12-16 19:59:33 +01:00
Compare commits
3 Commits
v0.0.1-alp
...
kotlin
| Author | SHA1 | Date | |
|---|---|---|---|
| 27f04ec085 | |||
| 05300c1153 | |||
| 4a167bf3b4 |
281
Backend/pom.xml
281
Backend/pom.xml
@@ -1,145 +1,142 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>de.heiserer</groupId>
|
||||||
|
<artifactId>schafkopf-2</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<name>schafkopf-2</name>
|
||||||
|
<description>schafkopf-2</description>
|
||||||
|
<modules>
|
||||||
|
<module>schafkopf-shared</module>
|
||||||
|
</modules>
|
||||||
|
<properties>
|
||||||
|
<ktor_version>2.3.12</ktor_version>
|
||||||
|
<kotlin.code.style>official</kotlin.code.style>
|
||||||
|
<kotlin_version>2.0.0</kotlin_version>
|
||||||
|
<logback_version>1.4.14</logback_version>
|
||||||
|
<slf4j_version>2.0.9</slf4j_version>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
|
||||||
|
<main.class>de.heiserer.ApplicationKt</main.class>
|
||||||
|
</properties>
|
||||||
|
<repositories>
|
||||||
|
</repositories>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.ktor</groupId>
|
||||||
|
<artifactId>ktor-server-core-jvm</artifactId>
|
||||||
|
<version>${ktor_version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.ktor</groupId>
|
||||||
|
<artifactId>ktor-server-netty-jvm</artifactId>
|
||||||
|
<version>${ktor_version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<version>${logback_version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>${slf4j_version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.ktor</groupId>
|
||||||
|
<artifactId>ktor-server-tests-jvm</artifactId>
|
||||||
|
<version>${ktor_version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-test-junit</artifactId>
|
||||||
|
<version>${kotlin_version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlinx</groupId>
|
||||||
|
<artifactId>kotlinx-coroutines-debug</artifactId>
|
||||||
|
<version>1.6.4</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
|
||||||
|
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>${project.basedir}/src/main/resources</directory>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
|
||||||
<artifactId>schafkopf-backend-java</artifactId>
|
<plugins>
|
||||||
<packaging>pom</packaging>
|
<plugin>
|
||||||
<modules>
|
<artifactId>kotlin-maven-plugin</artifactId>
|
||||||
<module>schafkopf-client</module>
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
<module>schafkopf-server</module>
|
<version>${kotlin_version}</version>
|
||||||
<module>schafkopf-shared</module>
|
<configuration>
|
||||||
</modules>
|
<jvmTarget>1.8</jvmTarget>
|
||||||
<groupId>org.schafkopf</groupId>
|
</configuration>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<executions>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<execution>
|
||||||
|
<id>compile</id>
|
||||||
<properties>
|
<phase>compile</phase>
|
||||||
<maven-compiler.version>3.5.1</maven-compiler.version>
|
<goals>
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
<goal>compile</goal>
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
</goals>
|
||||||
<pi4j.version>2.4.0</pi4j.version>
|
</execution>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<execution>
|
||||||
<slf4j.version>1.7.32</slf4j.version>
|
<id>test-compile</id>
|
||||||
</properties>
|
<phase>test-compile</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>test-compile</goal>
|
||||||
<build>
|
</goals>
|
||||||
<finalName>schafkopf-backend-build</finalName>
|
</execution>
|
||||||
<plugins>
|
</executions>
|
||||||
<!-- Maven Clean Plugin -->
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-clean-plugin</artifactId>
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
<version>3.1.0</version>
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
<executions>
|
<version>1.2.1</version>
|
||||||
<execution>
|
<executions>
|
||||||
<id>clean-initial</id>
|
<execution>
|
||||||
<phase>pre-clean</phase>
|
<goals>
|
||||||
<goals>
|
<goal>java</goal>
|
||||||
<goal>clean</goal>
|
</goals>
|
||||||
</goals>
|
</execution>
|
||||||
</execution>
|
</executions>
|
||||||
</executions>
|
<configuration>
|
||||||
</plugin>
|
<mainClass>${main.class}</mainClass>
|
||||||
<plugin>
|
</configuration>
|
||||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
</plugin>
|
||||||
<executions>
|
<plugin>
|
||||||
<execution>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<configuration>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
<configLocation>google_checks.xml</configLocation>
|
<version>2.6</version>
|
||||||
<consoleOutput>true</consoleOutput>
|
<configuration>
|
||||||
<failOnViolation>true</failOnViolation>
|
<descriptorRefs>
|
||||||
<violationSeverity>warning</violationSeverity>
|
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||||
</configuration>
|
</descriptorRefs>
|
||||||
<goals>
|
<archive>
|
||||||
<goal>check</goal>
|
<manifest>
|
||||||
</goals>
|
<addClasspath>true</addClasspath>
|
||||||
<id>validate</id>
|
<mainClass>${main.class}</mainClass>
|
||||||
<phase>validate</phase>
|
</manifest>
|
||||||
</execution>
|
</archive>
|
||||||
</executions>
|
</configuration>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<executions>
|
||||||
<version>3.3.1</version>
|
<execution>
|
||||||
</plugin>
|
<id>assemble-all</id>
|
||||||
<plugin>
|
<phase>package</phase>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<goals>
|
||||||
<configuration>
|
<goal>single</goal>
|
||||||
<archive>
|
</goals>
|
||||||
<manifest>
|
</execution>
|
||||||
<mainClass>org.schafkopf.DedicatedServer</mainClass>
|
</executions>
|
||||||
</manifest>
|
</plugin>
|
||||||
</archive>
|
</plugins>
|
||||||
</configuration>
|
</build>
|
||||||
<version>3.3.0</version>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<showDeprecation>true</showDeprecation>
|
|
||||||
<showWarnings>true</showWarnings>
|
|
||||||
<verbose>false</verbose>
|
|
||||||
</configuration>
|
|
||||||
<version>3.11.0</version>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
<!-- Maven Assembly Plugin -->
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.fazecast</groupId>
|
|
||||||
<artifactId>jSerialComm</artifactId>
|
|
||||||
<version>2.6.0</version> <!-- Check for the latest version on the official repository -->
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<artifactId>slf4j-api</artifactId>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<version>${slf4j.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<artifactId>slf4j-simple</artifactId>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<version>${slf4j.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<!-- To write basic websockets against -->
|
|
||||||
<dependency>
|
|
||||||
<artifactId>websocket-jetty-api</artifactId>
|
|
||||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
|
||||||
<version>11.0.15</version>
|
|
||||||
</dependency>
|
|
||||||
<!-- To run websockets in embedded server -->
|
|
||||||
<dependency>
|
|
||||||
<artifactId>websocket-jetty-server</artifactId>
|
|
||||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
|
||||||
<version>11.0.20</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<artifactId>jetty-servlets</artifactId>
|
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
|
||||||
<version>11.0.19</version>
|
|
||||||
</dependency>
|
|
||||||
<!-- To run websockets client -->
|
|
||||||
<dependency>
|
|
||||||
<artifactId>websocket-jetty-client</artifactId>
|
|
||||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
|
||||||
<version>11.0.15</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<artifactId>gson</artifactId>
|
|
||||||
<groupId>com.google.code.gson</groupId>
|
|
||||||
<version>2.10.1</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<artifactId>dotenv-java</artifactId>
|
|
||||||
<groupId>io.github.cdimascio</groupId>
|
|
||||||
<version>3.0.0</version>
|
|
||||||
</dependency>
|
|
||||||
<!-- Jackson for JSON serialization/deserialization -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.json</groupId>
|
|
||||||
<artifactId>json</artifactId>
|
|
||||||
<version>20240303</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</project>
|
</project>
|
||||||
38
Backend/schafkopf-client/.gitignore
vendored
Normal file
38
Backend/schafkopf-client/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
src/main/resources/web-content
|
||||||
@@ -1,60 +1,144 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<groupId>de.heiserer</groupId>
|
||||||
<parent>
|
<artifactId>schafkopf-client</artifactId>
|
||||||
<groupId>org.schafkopf</groupId>
|
<version>0.0.1</version>
|
||||||
<artifactId>schafkopf-backend-java</artifactId>
|
<name>schafkopf-client</name>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<description>schafkopf-client</description>
|
||||||
</parent>
|
<properties>
|
||||||
|
<ktor_version>2.3.12</ktor_version>
|
||||||
|
<kotlin.code.style>official</kotlin.code.style>
|
||||||
|
<kotlin_version>2.0.0</kotlin_version>
|
||||||
|
<logback_version>1.4.14</logback_version>
|
||||||
|
<slf4j_version>2.0.9</slf4j_version>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
|
||||||
|
<main.class>de.heiserer.SchafkopfClientKt</main.class>
|
||||||
|
</properties>
|
||||||
|
<repositories>
|
||||||
|
</repositories>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.ktor</groupId>
|
||||||
|
<artifactId>ktor-server-core-jvm</artifactId>
|
||||||
|
<version>${ktor_version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.ktor</groupId>
|
||||||
|
<artifactId>ktor-server-netty-jvm</artifactId>
|
||||||
|
<version>${ktor_version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<version>${logback_version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>${slf4j_version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.ktor</groupId>
|
||||||
|
<artifactId>ktor-server-tests-jvm</artifactId>
|
||||||
|
<version>${ktor_version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-test-junit</artifactId>
|
||||||
|
<version>${kotlin_version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlinx</groupId>
|
||||||
|
<artifactId>kotlinx-coroutines-debug</artifactId>
|
||||||
|
<version>1.6.4</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>de.heiserer</groupId>
|
||||||
|
<artifactId>schafkopf-shared</artifactId>
|
||||||
|
<version>0.0.1</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<sourceDirectory>src/main/kotlin</sourceDirectory>
|
||||||
|
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
|
||||||
<artifactId>schafkopf-client</artifactId>
|
<plugins>
|
||||||
|
<plugin>
|
||||||
<properties>
|
<artifactId>kotlin-maven-plugin</artifactId>
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
<version>${kotlin_version}</version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<configuration>
|
||||||
</properties>
|
<jvmTarget>1.8</jvmTarget>
|
||||||
<dependencies>
|
</configuration>
|
||||||
<dependency>
|
<executions>
|
||||||
<groupId>org.schafkopf</groupId>
|
<execution>
|
||||||
<artifactId>schafkopf-shared</artifactId>
|
<id>compile</id>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<phase>compile</phase>
|
||||||
<scope>compile</scope>
|
<goals>
|
||||||
</dependency>
|
<goal>compile</goal>
|
||||||
</dependencies>
|
</goals>
|
||||||
<build>
|
</execution>
|
||||||
<finalName>schafkopf-client-build</finalName>
|
<execution>
|
||||||
<resources>
|
<id>test-compile</id>
|
||||||
<resource>
|
<phase>test-compile</phase>
|
||||||
<directory>src/main/resources</directory>
|
<goals>
|
||||||
</resource>
|
<goal>test-compile</goal>
|
||||||
</resources>
|
</goals>
|
||||||
<plugins>
|
</execution>
|
||||||
<plugin>
|
</executions>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
</plugin>
|
||||||
<version>3.3.0</version>
|
<plugin>
|
||||||
<configuration>
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
<descriptorRefs>
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
<version>1.2.1</version>
|
||||||
</descriptorRefs>
|
<executions>
|
||||||
<archive>
|
<execution>
|
||||||
<manifest>
|
<goals>
|
||||||
<mainClass>org.schafkopf.SchafkopfClient</mainClass>
|
<goal>java</goal>
|
||||||
</manifest>
|
</goals>
|
||||||
</archive>
|
</execution>
|
||||||
</configuration>
|
</executions>
|
||||||
<executions>
|
<configuration>
|
||||||
<execution>
|
<mainClass>${main.class}</mainClass>
|
||||||
<id>make-assembly</id>
|
</configuration>
|
||||||
<phase>package</phase>
|
</plugin>
|
||||||
<goals>
|
<plugin>
|
||||||
<goal>single</goal>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
</goals>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
</execution>
|
<version>2.6</version>
|
||||||
</executions>
|
<configuration>
|
||||||
</plugin>
|
<descriptorRefs>
|
||||||
</plugins>
|
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||||
</build>
|
</descriptorRefs>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<addClasspath>true</addClasspath>
|
||||||
|
<mainClass>${main.class}</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>assemble-all</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
</project>
|
</project>
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
import jakarta.servlet.DispatcherType;
|
|
||||||
import java.awt.Desktop;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.List;
|
|
||||||
import org.eclipse.jetty.server.Server;
|
|
||||||
import org.eclipse.jetty.server.ServerConnector;
|
|
||||||
import org.eclipse.jetty.servlet.DefaultServlet;
|
|
||||||
import org.eclipse.jetty.servlet.FilterHolder;
|
|
||||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
|
||||||
import org.eclipse.jetty.servlets.CrossOriginFilter;
|
|
||||||
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main Class that represents the Backend Server.
|
|
||||||
*/
|
|
||||||
public class BackendServer implements MessageSender {
|
|
||||||
|
|
||||||
private final Server server;
|
|
||||||
|
|
||||||
private final List<FrontendEndpoint> frontendEndpoints = new ArrayList<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an Instance of the Backend Server.
|
|
||||||
*/
|
|
||||||
public BackendServer(String hostName, int port, boolean openFrontend,
|
|
||||||
MessageListener messageListener) throws Exception {
|
|
||||||
server = new Server();
|
|
||||||
|
|
||||||
ServerConnector connector = new ServerConnector(server);
|
|
||||||
connector.setHost(hostName);
|
|
||||||
connector.setPort(port);
|
|
||||||
server.addConnector(connector);
|
|
||||||
|
|
||||||
// Setup the basic application "context" for this application at "/"
|
|
||||||
// This is also known as the handler tree (in jetty speak)
|
|
||||||
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
|
|
||||||
context.setContextPath("/");
|
|
||||||
server.setHandler(context);
|
|
||||||
|
|
||||||
// Configure CORS settings
|
|
||||||
configureCors(context);
|
|
||||||
|
|
||||||
URL webContentUrl = getClass().getClassLoader().getResource("web-content");
|
|
||||||
if (webContentUrl == null) {
|
|
||||||
throw new RuntimeException("Unable to find 'web-content' directory");
|
|
||||||
}
|
|
||||||
String webContentPath = webContentUrl.toExternalForm();
|
|
||||||
context.setResourceBase(webContentPath);
|
|
||||||
context.addServlet(new ServletHolder("frontend", DefaultServlet.class), "/");
|
|
||||||
|
|
||||||
// Configure specific websocket behavior
|
|
||||||
JettyWebSocketServletContainerInitializer.configure(
|
|
||||||
context,
|
|
||||||
(servletContext, wsContainer) -> {
|
|
||||||
// Configure default max size
|
|
||||||
wsContainer.setMaxTextMessageSize(65535);
|
|
||||||
wsContainer.setIdleTimeout(Duration.ofDays(300000));
|
|
||||||
// Add websockets
|
|
||||||
wsContainer.addMapping("/schafkopf-events/*",
|
|
||||||
new FrontendEndpointCreator(this, messageListener));
|
|
||||||
});
|
|
||||||
|
|
||||||
if (openFrontend) {
|
|
||||||
URI uri = new URI("http://" + hostName + ":" + port); // Replace with your target URL
|
|
||||||
Desktop.getDesktop().browse(uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the server in a separate thread
|
|
||||||
Thread serverThread = new Thread(() -> {
|
|
||||||
try {
|
|
||||||
server.start();
|
|
||||||
server.join(); // Wait for server to finish execution
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
serverThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void configureCors(ServletContextHandler context) {
|
|
||||||
// Enable CORS for all paths
|
|
||||||
FilterHolder cors = context.addFilter(CrossOriginFilter.class, "/*", null);
|
|
||||||
|
|
||||||
// Configure allowed origins, headers, and methods
|
|
||||||
cors.setInitParameter("allowedOrigins", "*");
|
|
||||||
cors.setInitParameter("allowedHeaders", "X-Requested-With,Content-Type,Accept,Origin");
|
|
||||||
cors.setInitParameter("allowedMethods", "GET,POST,PUT,DELETE,OPTIONS");
|
|
||||||
|
|
||||||
// Add filter mappings
|
|
||||||
EnumSet<DispatcherType> types = EnumSet.of(DispatcherType.REQUEST);
|
|
||||||
context.addFilter(cors, "*", types);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void start() throws Exception {
|
|
||||||
server.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void join() throws InterruptedException {
|
|
||||||
server.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addFrontendEndpoint(FrontendEndpoint endpoint) {
|
|
||||||
frontendEndpoints.add(endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeFrontendEndpoint(FrontendEndpoint endpoint) {
|
|
||||||
frontendEndpoints.remove(endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends Message to all Frontend Instances.
|
|
||||||
*
|
|
||||||
* @param message Message to send (String).
|
|
||||||
*/
|
|
||||||
private void sendMessageToAllFrontendEndpoints(String message) {
|
|
||||||
for (FrontendEndpoint endpoint : frontendEndpoints) {
|
|
||||||
endpoint.sendMessage(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(SchafkopfBaseMessage message) {
|
|
||||||
sendMessageToAllFrontendEndpoints(
|
|
||||||
message.getBaseMessage().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void sendMessageTest(String message) {
|
|
||||||
sendMessageToAllFrontendEndpoints(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import org.eclipse.jetty.websocket.api.Session;
|
|
||||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
|
|
||||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
|
||||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
|
|
||||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
|
||||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
|
||||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
|
||||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfMessageOrigin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main Class that represents the Backend Server.
|
|
||||||
*/
|
|
||||||
@WebSocket
|
|
||||||
public class DedicatedServerConnection implements MessageSender {
|
|
||||||
|
|
||||||
private final MessageListener messageListener;
|
|
||||||
private final CountDownLatch closeLatch;
|
|
||||||
private final CountDownLatch connectionLatch;
|
|
||||||
private static Session session;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public DedicatedServerConnection(String address, MessageListener messageListener) {
|
|
||||||
this.messageListener = messageListener;
|
|
||||||
this.closeLatch = new CountDownLatch(1);
|
|
||||||
this.connectionLatch = new CountDownLatch(1);
|
|
||||||
|
|
||||||
connect("ws://" + address);
|
|
||||||
try {
|
|
||||||
connectionLatch.await(); // Wait until the connection is established
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
System.err.println("Error waiting for connection: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
@OnWebSocketConnect
|
|
||||||
public void onConnect(Session session) {
|
|
||||||
this.session = session;
|
|
||||||
connectionLatch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnWebSocketMessage
|
|
||||||
public void onMessage(String message) {
|
|
||||||
messageListener.receiveMessage(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnWebSocketClose
|
|
||||||
public void onClose(int statusCode, String reason) {
|
|
||||||
System.out.println("Connection closed: " + reason);
|
|
||||||
closeLatch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnWebSocketError
|
|
||||||
public void onError(Throwable cause) {
|
|
||||||
System.err.println("Error occurred: " + cause.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main Class that represents the Backend Server.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void sendMessage(SchafkopfBaseMessage message) {
|
|
||||||
try {
|
|
||||||
session.getRemote().sendString(
|
|
||||||
new SchafkopfMessage(SchafkopfMessageOrigin.BACKEND, message).getMessageAsString());
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Error sending message: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void awaitClose() throws InterruptedException {
|
|
||||||
closeLatch.await();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main Class that represents the Backend Server.
|
|
||||||
*/
|
|
||||||
public void connect(String serverUri) {
|
|
||||||
Thread connectionThread = new Thread(() -> {
|
|
||||||
try {
|
|
||||||
WebSocketClient client = new WebSocketClient();
|
|
||||||
try {
|
|
||||||
client.start();
|
|
||||||
HeartbeatSender heartbeatSender = new HeartbeatSender(this);
|
|
||||||
heartbeatSender.start(); // Start sending heartbeat messages
|
|
||||||
URI uri = new URI(serverUri);
|
|
||||||
ClientUpgradeRequest request = new ClientUpgradeRequest();
|
|
||||||
client.connect(this, uri, request);
|
|
||||||
|
|
||||||
System.out.println("Connecting to : " + uri);
|
|
||||||
this.awaitClose();
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Error connecting to server: " + e.getMessage());
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
client.stop();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Error starting dedicated server connection: " + e.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
connectionThread.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import org.eclipse.jetty.websocket.api.Session;
|
|
||||||
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public class FrontendEndpoint extends WebSocketAdapter {
|
|
||||||
|
|
||||||
private final CountDownLatch closureLatch = new CountDownLatch(1);
|
|
||||||
private BackendServer backendServer;
|
|
||||||
private final MessageListener messageListener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public FrontendEndpoint(BackendServer backendServer, MessageListener messageListener) {
|
|
||||||
this.messageListener = messageListener;
|
|
||||||
this.backendServer = backendServer;
|
|
||||||
System.out.println("new FrontendEndpoint");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onWebSocketConnect(Session session) {
|
|
||||||
super.onWebSocketConnect(session);
|
|
||||||
String clientIp = session.getRemoteAddress().toString();
|
|
||||||
System.out.println("Endpoint connected from ip: " + clientIp);
|
|
||||||
|
|
||||||
backendServer.addFrontendEndpoint(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onWebSocketText(String message) {
|
|
||||||
super.onWebSocketText(message);
|
|
||||||
if (messageListener != null) {
|
|
||||||
messageListener.receiveMessage(message); // Notify the listener
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onWebSocketClose(int statusCode, String reason) {
|
|
||||||
super.onWebSocketClose(statusCode, reason);
|
|
||||||
|
|
||||||
backendServer.removeFrontendEndpoint(this);
|
|
||||||
|
|
||||||
System.out.println("Socket Closed: [" + statusCode + "] " + reason);
|
|
||||||
closureLatch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onWebSocketError(Throwable cause) {
|
|
||||||
super.onWebSocketError(cause);
|
|
||||||
cause.printStackTrace(System.err);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* send a Message to the connected FrontEnd.
|
|
||||||
*/
|
|
||||||
public void sendMessage(String message) {
|
|
||||||
try {
|
|
||||||
getRemote().sendString(message);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.server.JettyServerUpgradeRequest;
|
|
||||||
import org.eclipse.jetty.websocket.server.JettyServerUpgradeResponse;
|
|
||||||
import org.eclipse.jetty.websocket.server.JettyWebSocketCreator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creater to make new Instances of the FrontendConnection.
|
|
||||||
*/
|
|
||||||
public class FrontendEndpointCreator implements JettyWebSocketCreator {
|
|
||||||
private BackendServer backendServer;
|
|
||||||
private final MessageListener messageListener;
|
|
||||||
|
|
||||||
public FrontendEndpointCreator(BackendServer backendServer, MessageListener messageListener) {
|
|
||||||
this.messageListener = messageListener;
|
|
||||||
this.backendServer = backendServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object createWebSocket(
|
|
||||||
JettyServerUpgradeRequest jettyServerUpgradeRequest,
|
|
||||||
JettyServerUpgradeResponse jettyServerUpgradeResponse) {
|
|
||||||
return new FrontendEndpoint(this.backendServer, messageListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an Instance of the Backend Server.
|
|
||||||
*/
|
|
||||||
public class HeartbeatSender {
|
|
||||||
|
|
||||||
private static final int HEARTBEAT_INTERVAL = 15000; // 1 minute
|
|
||||||
|
|
||||||
private final DedicatedServerConnection client;
|
|
||||||
|
|
||||||
public HeartbeatSender(DedicatedServerConnection client) {
|
|
||||||
this.client = client;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an Instance of the Backend Server.
|
|
||||||
*/
|
|
||||||
public void start() {
|
|
||||||
Timer timer = new Timer();
|
|
||||||
timer.scheduleAtFixedRate(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
client.sendMessage(
|
|
||||||
new SchafkopfBaseMessage(SchafkopfMessage.SchafkopfMessageType.HEARTBEAT_SYN));
|
|
||||||
}
|
|
||||||
}, HEARTBEAT_INTERVAL, HEARTBEAT_INTERVAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfMessageOrigin;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfMessageType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public class SchafkopfClient implements MessageListener {
|
|
||||||
|
|
||||||
private BackendServer backendServer;
|
|
||||||
private DedicatedServerConnection dedicatedServerConnection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public SchafkopfClient() throws Exception {
|
|
||||||
|
|
||||||
this.backendServer = new BackendServer("localhost", 8080, true, this);
|
|
||||||
|
|
||||||
System.out.println("Client started.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
|
||||||
new SchafkopfClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void receiveMessage(String jsonMessage) {
|
|
||||||
Gson gson = new Gson();
|
|
||||||
JsonObject jsonObject = gson.fromJson(jsonMessage, JsonObject.class);
|
|
||||||
|
|
||||||
// Check if the origin is "frontend" or "dedicated_server"
|
|
||||||
String origin = jsonObject.get("origin").getAsString();
|
|
||||||
switch (SchafkopfMessageOrigin.valueOf(origin)) {
|
|
||||||
case FRONTEND:
|
|
||||||
handleFrontendMessage(jsonObject);
|
|
||||||
break;
|
|
||||||
case DEDICATED_SERVER:
|
|
||||||
handleDedicatedServerMessage(jsonObject);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Handle messages from unknown origins
|
|
||||||
System.out.println("Received message from unknown origin: " + origin);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void handleFrontendMessage(JsonObject jsonObject) {
|
|
||||||
JsonObject message = jsonObject.getAsJsonObject("message");
|
|
||||||
JsonObject content = message.getAsJsonObject("content");
|
|
||||||
String messageType = message.get("message_type").getAsString();
|
|
||||||
|
|
||||||
switch (SchafkopfMessageType.valueOf(messageType)) {
|
|
||||||
case REQUEST_SERVER_CONNECTION:
|
|
||||||
dedicatedServerConnection = new DedicatedServerConnection(
|
|
||||||
content.get("serverAddress").getAsString(),
|
|
||||||
this);
|
|
||||||
break;
|
|
||||||
case PLAYER_CARD:
|
|
||||||
case CREATE_ONLINE_GAME:
|
|
||||||
case JOIN_ONLINE_GAME:
|
|
||||||
case SET_PLAYER_NAME:
|
|
||||||
dedicatedServerConnection.sendMessage(
|
|
||||||
new SchafkopfBaseMessage(SchafkopfMessageType.valueOf(messageType), content));
|
|
||||||
break;
|
|
||||||
case LIST_ONLINE_GAMES:
|
|
||||||
case GET_ONLINE_GAME:
|
|
||||||
case SET_STATUS_READY:
|
|
||||||
case LEAVE_ONLINE_GAME:
|
|
||||||
case START_DEDICATED_GAME:
|
|
||||||
dedicatedServerConnection.sendMessage(
|
|
||||||
new SchafkopfBaseMessage(SchafkopfMessageType.valueOf(messageType)));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Handle unknown message types
|
|
||||||
System.out.println("Received unknown message type from frontend server: " + messageType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Received message from frontend: " + jsonObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleDedicatedServerMessage(JsonObject jsonObject) {
|
|
||||||
JsonObject message = jsonObject.getAsJsonObject("message");
|
|
||||||
JsonObject content = message.getAsJsonObject("content");
|
|
||||||
String messageType = message.get("message_type").getAsString();
|
|
||||||
|
|
||||||
switch (SchafkopfMessageType.valueOf(messageType)) {
|
|
||||||
case GET_CARD_ONLINE_PLAYER:
|
|
||||||
case HEARTBEAT_ACK:
|
|
||||||
|
|
||||||
break;
|
|
||||||
case GAME_STATE:
|
|
||||||
case ONLINE_PLAYER_HAND:
|
|
||||||
case UNKNOWN_ERROR:
|
|
||||||
case INFO_MESSAGE:
|
|
||||||
case GET_ONLINE_GAME:
|
|
||||||
case LIST_ONLINE_GAMES:
|
|
||||||
backendServer.sendMessage(
|
|
||||||
new SchafkopfBaseMessage(SchafkopfMessageType.valueOf(messageType), content));
|
|
||||||
break;
|
|
||||||
case SERVER_CONNECTION_SUCCESSFUL:
|
|
||||||
case GAME_START_READY:
|
|
||||||
backendServer.sendMessage(
|
|
||||||
new SchafkopfBaseMessage(SchafkopfMessageType.valueOf(messageType)));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Handle unknown message types
|
|
||||||
System.out.println("Received unknown message type from dedicated server: " + messageType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!messageType.equals("HEARTBEAT_ACK")) {
|
|
||||||
System.out.println("Received message from dedicated server: " + jsonObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package org.schafkopf.cardreader;
|
|
||||||
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
|
|
||||||
/** Class that represents one Card Reader. */
|
|
||||||
public abstract class CardReader {
|
|
||||||
|
|
||||||
private CountDownLatch nfcLatch = new CountDownLatch(1);
|
|
||||||
private Boolean readingMode = false;
|
|
||||||
private String uidString = "";
|
|
||||||
|
|
||||||
public CardReader() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/** method to call to wait for NFC input. */
|
|
||||||
public String waitForCardScan() throws InterruptedException {
|
|
||||||
this.readingMode = true;
|
|
||||||
nfcLatch.await();
|
|
||||||
Thread.sleep(20);
|
|
||||||
this.readingMode = false;
|
|
||||||
nfcLatch = new CountDownLatch(1);
|
|
||||||
return this.uidString;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* checks uid of scanned card and do nothing if Server is not in reading mode.
|
|
||||||
*
|
|
||||||
* @param uidString uid to check.
|
|
||||||
*/
|
|
||||||
public void nfcGelesen(String uidString) {
|
|
||||||
if (this.uidString.equals(uidString)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this.readingMode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uidString = uidString;
|
|
||||||
nfcLatch.countDown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
package org.schafkopf.cardreader;
|
|
||||||
|
|
||||||
import com.fazecast.jSerialComm.SerialPort;
|
|
||||||
import com.sun.tools.jconsole.JConsoleContext;
|
|
||||||
import io.github.cdimascio.dotenv.Dotenv;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import org.schafkopf.BackendServer;
|
|
||||||
|
|
||||||
/** Class that represents the NFC Reader. */
|
|
||||||
public class UsbCardReader extends CardReader {
|
|
||||||
|
|
||||||
private volatile boolean isRunning = true;
|
|
||||||
Dotenv dotenv = Dotenv.configure().directory("./").load();
|
|
||||||
private String comPort = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an Instance of the KartenLeser.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public UsbCardReader() {
|
|
||||||
new Thread(this::run).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stop() {
|
|
||||||
isRunning = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Run the reader. */
|
|
||||||
public void run() {
|
|
||||||
// SerialPort[] ports = SerialPort.getCommPorts();
|
|
||||||
//
|
|
||||||
// for (SerialPort port : ports) {
|
|
||||||
// if (port.openPort()) {
|
|
||||||
// System.out.println(port.getSystemPortName());
|
|
||||||
// try {
|
|
||||||
// Thread.sleep(5000);
|
|
||||||
// } catch (InterruptedException e) {
|
|
||||||
// throw new RuntimeException(e);
|
|
||||||
// }
|
|
||||||
// // Read any data available on the serial port
|
|
||||||
// byte[] initialBuffer = new byte[port.bytesAvailable()];
|
|
||||||
// int initialBytesRead = port.readBytes(initialBuffer, initialBuffer.length);
|
|
||||||
// String initialData = null;
|
|
||||||
// try {
|
|
||||||
// initialData = new String(initialBuffer, 0, initialBytesRead, "UTF-8").trim();
|
|
||||||
// } catch (UnsupportedEncodingException e) {
|
|
||||||
// throw new RuntimeException(e);
|
|
||||||
// }
|
|
||||||
// System.out.print("Raw data: ");
|
|
||||||
// for (byte b : initialBuffer) {
|
|
||||||
// System.out.print(b + " ");
|
|
||||||
// }
|
|
||||||
// System.out.println(initialData);
|
|
||||||
// if (initialData.contains("Adafruit PN532 NFC Marker")) {
|
|
||||||
// comPort = port.getSystemPortName();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
comPort = dotenv.get("COM_PORT");
|
|
||||||
|
|
||||||
if (comPort == null) {
|
|
||||||
System.out.println("Adafruit PN532 NFC device not found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SerialPort serialPort = SerialPort.getCommPort(comPort);
|
|
||||||
serialPort.setBaudRate(115200);
|
|
||||||
|
|
||||||
if (serialPort.openPort()) {
|
|
||||||
System.out.println("Serial port opened successfully");
|
|
||||||
|
|
||||||
try {
|
|
||||||
while (isRunning) {
|
|
||||||
if (serialPort.bytesAvailable() > 0) {
|
|
||||||
byte[] buffer = new byte[serialPort.bytesAvailable()];
|
|
||||||
int bytesRead = serialPort.readBytes(buffer, buffer.length);
|
|
||||||
String data = new String(buffer, 0, bytesRead, "UTF-8").trim();
|
|
||||||
|
|
||||||
// Process the received data
|
|
||||||
this.nfcGelesen(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional: Add a delay to avoid consuming too much CPU
|
|
||||||
Thread.sleep(100);
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
serialPort.closePort();
|
|
||||||
System.out.println("Serial port closed");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
System.out.println("Failed to open serial port");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
package org.schafkopf.player;
|
|
||||||
|
|
||||||
import org.schafkopf.MessageSender;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage;
|
|
||||||
import org.schafkopf.cardreader.CardReader;
|
|
||||||
import org.schafkopf.karte.Karte;
|
|
||||||
import org.schafkopf.karte.KartenListe;
|
|
||||||
import org.schafkopf.karte.KartenUtil;
|
|
||||||
import org.schafkopf.spielcontroller.SpielController;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Player that plays in real life.
|
|
||||||
*/
|
|
||||||
public class LocalPlayer extends Player {
|
|
||||||
|
|
||||||
private final CardReader cardReader;
|
|
||||||
|
|
||||||
public LocalPlayer(CardReader cardReader, MessageSender messageSender) {
|
|
||||||
super("Local Player", messageSender);
|
|
||||||
this.cardReader = cardReader;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Karte play(SpielController spiel, KartenListe tischKarten, KartenListe gespielteKarten) {
|
|
||||||
return wartetAufKarte();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resetReady() {
|
|
||||||
// Not needed
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Waits for a Card and returns a Karte Object.
|
|
||||||
*/
|
|
||||||
private Karte wartetAufKarte() {
|
|
||||||
String uid = null;
|
|
||||||
System.out.println("Starte Warten auf Karte");
|
|
||||||
try {
|
|
||||||
uid = cardReader.waitForCardScan();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Karte karte = KartenUtil.getIdOfUid(uid);
|
|
||||||
|
|
||||||
if (karte == null) {
|
|
||||||
System.out.println("Ungültige Karte");
|
|
||||||
return wartetAufKarte();
|
|
||||||
}
|
|
||||||
System.out.println("Karte gescannt: " + karte.getName());
|
|
||||||
System.out.println("Beende Warten auf Karte");
|
|
||||||
return karte;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(SchafkopfBaseMessage message) {
|
|
||||||
System.out.println("LocalPlayer: " + message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
122
Backend/schafkopf-client/src/main/kotlin/CmdSchafkopfMessager.kt
Normal file
122
Backend/schafkopf-client/src/main/kotlin/CmdSchafkopfMessager.kt
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
import de.heiserer.CmdPlayer
|
||||||
|
import de.heiserer.SchafkopfMessager
|
||||||
|
import de.heiserer.cards.Card
|
||||||
|
import de.heiserer.cards.CardColor
|
||||||
|
import de.heiserer.cards.CardSymbol
|
||||||
|
import de.heiserer.player.Player
|
||||||
|
|
||||||
|
class CmdSchafkopfMessager: SchafkopfMessager {
|
||||||
|
override fun sendPlayerTurn(player: Player) {
|
||||||
|
if(player !is CmdPlayer){
|
||||||
|
clearConsole()
|
||||||
|
}
|
||||||
|
|
||||||
|
println("${player.getName()} ist am Zug")
|
||||||
|
Thread.sleep(1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sendPlayerWonTrick(player: Player) {
|
||||||
|
println("${player.getName()} hat den Stich gewonnen.")
|
||||||
|
println()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sendPlayerWonGame(player: Player) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sendCardPlayed(player: Player, card: Card) {
|
||||||
|
println("${player.getName()} hat ${card.displayName} gespielt.")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sendTableCards(cards: List<Card>) {
|
||||||
|
println("Tischkarten:")
|
||||||
|
CmdCard.printCards(cards)
|
||||||
|
|
||||||
|
Thread.sleep(3000)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class CmdCard(card: Card){
|
||||||
|
private val symbolMap = mapOf(
|
||||||
|
CardSymbol.SIEBEN to "7",
|
||||||
|
CardSymbol.ACHT to "8",
|
||||||
|
CardSymbol.NEUN to "9",
|
||||||
|
CardSymbol.ZEHN to "10",
|
||||||
|
CardSymbol.OBER to "O",
|
||||||
|
CardSymbol.UNTER to "U",
|
||||||
|
CardSymbol.KOENIG to "K",
|
||||||
|
CardSymbol.ASS to "A"
|
||||||
|
)
|
||||||
|
|
||||||
|
private val colorMap = mapOf(
|
||||||
|
CardColor.HERZ to "♥",
|
||||||
|
CardColor.SCHELL to "♦",
|
||||||
|
CardColor.EICHEL to "♣",
|
||||||
|
CardColor.BLATT to "♠"
|
||||||
|
)
|
||||||
|
|
||||||
|
private val symbol = symbolMap[card.symbol]?: "?"
|
||||||
|
private val color = colorMap[card.color]?: "?"
|
||||||
|
private val colorName = card.color.displayName
|
||||||
|
private val symbolName = card.symbol.displayName
|
||||||
|
|
||||||
|
private val cardWidth = 13
|
||||||
|
|
||||||
|
private val symbolLine = "│ ${symbol.padEnd(cardWidth-5)}$symbol │"
|
||||||
|
private val colorLine = "│ ${color.padEnd(cardWidth-5)}$color │"
|
||||||
|
|
||||||
|
private val cardLines = listOf(
|
||||||
|
"┌───────────┐",
|
||||||
|
symbolLine,
|
||||||
|
colorLine,
|
||||||
|
"│${colorName.padCenter(cardWidth-2)}│",
|
||||||
|
"│${symbolName.padCenter(cardWidth-2)}│",
|
||||||
|
colorLine,
|
||||||
|
symbolLine,
|
||||||
|
"└───────────┘"
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
fun print(){
|
||||||
|
cardLines.forEach {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCardLines(): List<String> {
|
||||||
|
return cardLines
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.padCenter(totalWidth: Int, padChar: Char = ' '): String {
|
||||||
|
if (this.length >= totalWidth) return this
|
||||||
|
val padding = totalWidth - this.length
|
||||||
|
val padStart = this.length + padding / 2
|
||||||
|
return this.padStart(padStart, padChar).padEnd(totalWidth, padChar)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun printCards(cards: List<Card>) {
|
||||||
|
val lastCardLines = CmdCard(cards.last()).getCardLines()
|
||||||
|
|
||||||
|
val cardLines = cards.map { CmdCard(it).getCardLines() }
|
||||||
|
|
||||||
|
// Loop through the indices of the lines that are present in all cards
|
||||||
|
for (i in lastCardLines.indices) {
|
||||||
|
// For other lines, print up to the first 5 characters
|
||||||
|
var line = cardLines.joinToString(" ") { it[i].take(5) }
|
||||||
|
line += lastCardLines[i].drop(5)
|
||||||
|
println(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearConsole() {
|
||||||
|
for (i in 1..100) {
|
||||||
|
println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package de.heiserer
|
||||||
|
|
||||||
|
import de.heiserer.cards.*
|
||||||
|
import de.heiserer.player.Player
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
class CmdPlayer(name: String) : Player(name) {
|
||||||
|
private val scanner = Scanner(System.`in`)
|
||||||
|
|
||||||
|
override fun playCard(tableCards: UnsortedCardList, gameType: GameType): Card {
|
||||||
|
val cardsCopy = cards.getCopyOfCards()
|
||||||
|
|
||||||
|
println("Available cards:")
|
||||||
|
CmdSchafkopfMessager.CmdCard.printCards(cardsCopy)
|
||||||
|
|
||||||
|
// Print cards with their respective index
|
||||||
|
cardsCopy.forEachIndexed { index, card ->
|
||||||
|
println("(${index + 1}) ${card.name}") // Prints card with index starting from 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prompt the user to enter a card number
|
||||||
|
println("Please enter the number of the card you want to play:")
|
||||||
|
|
||||||
|
val userInput = scanner.nextLine()
|
||||||
|
val cardToPlay = getCardByUserInput(userInput, tableCards, gameType)
|
||||||
|
|
||||||
|
return cards.remove(cardToPlay)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCardByUserInput(
|
||||||
|
userInput: String,
|
||||||
|
tableCards: UnsortedCardList,
|
||||||
|
gameType: GameType
|
||||||
|
): Card {
|
||||||
|
val cardsCopy = cards.getCopyOfCards()
|
||||||
|
return try {
|
||||||
|
// Convert user input to an integer and adjust for zero-based index
|
||||||
|
val cardIndex = userInput.toInt() - 1
|
||||||
|
|
||||||
|
// Ensure the index is within bounds
|
||||||
|
if (cardIndex in cardsCopy.indices) {
|
||||||
|
cardsCopy[cardIndex] // Return the selected card
|
||||||
|
if(validateCard(cardsCopy[cardIndex], tableCards, gameType)){
|
||||||
|
cardsCopy[cardIndex]
|
||||||
|
} else {
|
||||||
|
println("Invalid card. Please try again.")
|
||||||
|
playCard(tableCards, gameType) // Retry if the card is invalid
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println("Invalid card number. Please try again.")
|
||||||
|
playCard(tableCards, gameType) // Retry if the input was invalid
|
||||||
|
}
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
println("Invalid input. Please enter a number.")
|
||||||
|
playCard(tableCards, gameType) // Retry on invalid input
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package de.heiserer
|
||||||
|
|
||||||
|
import CmdSchafkopfMessager
|
||||||
|
import de.heiserer.player.NPCPlayer
|
||||||
|
import de.heiserer.plugins.*
|
||||||
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.engine.*
|
||||||
|
import io.ktor.server.netty.*
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
val test = SchafkopfGameController(listOf(NPCPlayer("NPC 1"), NPCPlayer("NPC 2"), CmdPlayer("Dev"), NPCPlayer("NPC 4")), CmdSchafkopfMessager())
|
||||||
|
|
||||||
|
test.playRound()
|
||||||
|
|
||||||
|
embeddedServer(Netty, port = 8080, host = "0.0.0.0", module = Application::module)
|
||||||
|
.start(wait = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Application.module() {
|
||||||
|
configureRouting()
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package de.heiserer.plugins
|
||||||
|
|
||||||
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.response.*
|
||||||
|
import io.ktor.server.routing.*
|
||||||
|
|
||||||
|
fun Application.configureRouting() {
|
||||||
|
routing {
|
||||||
|
get("/") {
|
||||||
|
call.respondText("Hello World!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>org.schafkopf</groupId>
|
|
||||||
<artifactId>schafkopf-backend-java</artifactId>
|
|
||||||
<version>1.0-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<artifactId>schafkopf-server</artifactId>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
</properties>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.schafkopf</groupId>
|
|
||||||
<artifactId>schafkopf-shared</artifactId>
|
|
||||||
<version>1.0-SNAPSHOT</version>
|
|
||||||
<scope>compile</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
<build>
|
|
||||||
<finalName>schafkopf-server-build</finalName>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
|
||||||
<version>3.3.0</version>
|
|
||||||
<configuration>
|
|
||||||
<descriptorRefs>
|
|
||||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
|
||||||
</descriptorRefs>
|
|
||||||
<archive>
|
|
||||||
<manifest>
|
|
||||||
<mainClass>org.schafkopf.DedicatedServer</mainClass>
|
|
||||||
</manifest>
|
|
||||||
</archive>
|
|
||||||
</configuration>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>make-assembly</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>single</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import jakarta.servlet.DispatcherType;
|
|
||||||
import java.net.DatagramSocket;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.SocketException;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.List;
|
|
||||||
import org.eclipse.jetty.server.Server;
|
|
||||||
import org.eclipse.jetty.server.ServerConnector;
|
|
||||||
import org.eclipse.jetty.servlet.FilterHolder;
|
|
||||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
|
||||||
import org.eclipse.jetty.servlets.CrossOriginFilter;
|
|
||||||
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
|
|
||||||
import org.schafkopf.SchafkopfException.NoGameSessionException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main Class that represents the Backend Server.
|
|
||||||
*/
|
|
||||||
public class DedicatedServer {
|
|
||||||
|
|
||||||
private final Server server;
|
|
||||||
private final ServerConnector connector;
|
|
||||||
|
|
||||||
private final List<SchafkopfClientConnection> clientConnections = new ArrayList<>();
|
|
||||||
|
|
||||||
private final List<OnlineGameSession> onlineGameSessions = new ArrayList<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an Instance of the Backend Server.
|
|
||||||
*/
|
|
||||||
public DedicatedServer() {
|
|
||||||
server = new Server();
|
|
||||||
InetAddress address;
|
|
||||||
|
|
||||||
try (final DatagramSocket socket = new DatagramSocket()) {
|
|
||||||
socket.connect(InetAddress.getByName("8.8.8.8"), 10002);
|
|
||||||
address = socket.getLocalAddress();
|
|
||||||
} catch (UnknownHostException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} catch (SocketException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
InetSocketAddress socketAddress = new InetSocketAddress(address.getHostAddress(), 8085);
|
|
||||||
System.out.println(
|
|
||||||
"Server started at: " + socketAddress.getAddress() + ":" + socketAddress.getPort());
|
|
||||||
connector = new ServerConnector(server);
|
|
||||||
connector.setHost(socketAddress.getHostName());
|
|
||||||
connector.setPort(socketAddress.getPort());
|
|
||||||
server.addConnector(connector);
|
|
||||||
|
|
||||||
// Setup the basic application "context" for this application at "/"
|
|
||||||
// This is also known as the handler tree (in jetty speak)
|
|
||||||
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
|
|
||||||
|
|
||||||
// Add the health check servlet to the servlet context
|
|
||||||
context.addServlet(HealthCheckServlet.class, "/health");
|
|
||||||
context.setContextPath("/");
|
|
||||||
server.setHandler(context);
|
|
||||||
// Configure CORS settings
|
|
||||||
configureCors(context);
|
|
||||||
// Configure specific websocket behavior
|
|
||||||
JettyWebSocketServletContainerInitializer.configure(
|
|
||||||
context,
|
|
||||||
(servletContext, wsContainer) -> {
|
|
||||||
// Configure default max size
|
|
||||||
wsContainer.setMaxTextMessageSize(65535);
|
|
||||||
wsContainer.setIdleTimeout(Duration.ofDays(300000));
|
|
||||||
// Add websockets
|
|
||||||
wsContainer.addMapping("/*", new SchafkopfClientConnectionCreator(this));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main entrypoint of the Application.
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) throws Exception {
|
|
||||||
DedicatedServer server = new DedicatedServer();
|
|
||||||
server.start();
|
|
||||||
server.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void configureCors(ServletContextHandler context) {
|
|
||||||
// Enable CORS for all paths
|
|
||||||
FilterHolder cors = context.addFilter(CrossOriginFilter.class, "/*", null);
|
|
||||||
|
|
||||||
// Configure allowed origins, headers, and methods
|
|
||||||
cors.setInitParameter("allowedOrigins", "*");
|
|
||||||
cors.setInitParameter("allowedHeaders", "X-Requested-With,Content-Type,Accept,Origin");
|
|
||||||
cors.setInitParameter("allowedMethods", "GET,POST,PUT,DELETE,OPTIONS");
|
|
||||||
|
|
||||||
// Add filter mappings
|
|
||||||
EnumSet<DispatcherType> types = EnumSet.of(DispatcherType.REQUEST);
|
|
||||||
context.addFilter(cors, "*", types);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void start() throws Exception {
|
|
||||||
server.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void join() throws InterruptedException {
|
|
||||||
server.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addFrontendEndpoint(SchafkopfClientConnection endpoint) {
|
|
||||||
clientConnections.add(endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeFrontendEndpoint(SchafkopfClientConnection endpoint) {
|
|
||||||
clientConnections.remove(endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addGameSession(OnlineGameSession onlineGameSession) {
|
|
||||||
onlineGameSessions.add(onlineGameSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<OnlineGameSession> getGameSessions() {
|
|
||||||
return onlineGameSessions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main entrypoint of the Application.
|
|
||||||
*/
|
|
||||||
public JsonArray getGameSessionsAsJson() throws NoGameSessionException {
|
|
||||||
if (onlineGameSessions.isEmpty()) {
|
|
||||||
throw new NoGameSessionException();
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonArray gameSessionsJson = new JsonArray();
|
|
||||||
for (OnlineGameSession onlineGameSession : onlineGameSessions) {
|
|
||||||
gameSessionsJson.add(onlineGameSession.getJson());
|
|
||||||
}
|
|
||||||
return gameSessionsJson;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main entrypoint of the Application.
|
|
||||||
*/
|
|
||||||
public OnlineGameSession getCurrentGameSession() throws NoGameSessionException {
|
|
||||||
if (onlineGameSessions.isEmpty()) {
|
|
||||||
throw new NoGameSessionException();
|
|
||||||
}
|
|
||||||
return onlineGameSessions.get(onlineGameSessions.size() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main entrypoint of the Application.
|
|
||||||
*/
|
|
||||||
public OnlineGameSession getGameSessionByName(String gameId) throws NoGameSessionException {
|
|
||||||
for (OnlineGameSession onlineGameSession : onlineGameSessions) {
|
|
||||||
if (onlineGameSession.getServerName().equals(gameId)) {
|
|
||||||
return onlineGameSession;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new NoGameSessionException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeGameSession(OnlineGameSession onlineGameSession) {
|
|
||||||
onlineGameSessions.remove(onlineGameSession);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.http.HttpServlet;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public class HealthCheckServlet extends HttpServlet {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
|
|
||||||
throws ServletException, IOException {
|
|
||||||
resp.setContentType("text/plain");
|
|
||||||
resp.getWriter().println("Backend server is up and running!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import org.schafkopf.SchafkopfException.PlayerNotReadyException;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfMessageType;
|
|
||||||
import org.schafkopf.player.BotPlayer;
|
|
||||||
import org.schafkopf.player.OnlinePlayer;
|
|
||||||
import org.schafkopf.player.Player;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main entrypoint of the Application.
|
|
||||||
*/
|
|
||||||
public class OnlineGameSession extends BaseGameSession {
|
|
||||||
|
|
||||||
private String serverName;
|
|
||||||
|
|
||||||
private DedicatedServer dedicatedServer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main entrypoint of the Application.
|
|
||||||
*/
|
|
||||||
public OnlineGameSession(String serverName, DedicatedServer dedicatedServer) {
|
|
||||||
this.players = new ArrayList<>();
|
|
||||||
this.dedicatedServer = dedicatedServer;
|
|
||||||
this.serverName = serverName;
|
|
||||||
logger.info(serverName + " created.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public void addPlayer(Player player) {
|
|
||||||
if (this.players.size() >= 4) {
|
|
||||||
throw new RuntimeException("Game is full");
|
|
||||||
}
|
|
||||||
logger.info("Adding player to game: " + player);
|
|
||||||
players.add(player);
|
|
||||||
|
|
||||||
this.sendSessionInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void startGame() throws PlayerNotReadyException {
|
|
||||||
logger.info("Starting game: " + serverName + " with " + players.size() + " Onlineplayers");
|
|
||||||
|
|
||||||
for (Player player : players) {
|
|
||||||
if (!player.isReady()) {
|
|
||||||
throw new PlayerNotReadyException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.GAME_START_READY));
|
|
||||||
|
|
||||||
//wait for 5 seconds
|
|
||||||
try {
|
|
||||||
Thread.sleep(5000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
super.startGame(players);
|
|
||||||
players.forEach(player -> player.resetReady());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public void removePlayer(OnlinePlayer player) {
|
|
||||||
logger.info("Removing player from game: " + player.getName());
|
|
||||||
players.remove(player);
|
|
||||||
|
|
||||||
if (this.getPlayerCount() == 0) {
|
|
||||||
logger.info("No players left in game: " + serverName);
|
|
||||||
if (spielThread != null) {
|
|
||||||
spielThread.interrupt();
|
|
||||||
}
|
|
||||||
this.dedicatedServer.removeGameSession(this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.sendSessionInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(SchafkopfBaseMessage message) {
|
|
||||||
for (Player player : players) {
|
|
||||||
player.sendMessage(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main entrypoint of the Application.
|
|
||||||
*/
|
|
||||||
public JsonObject getJson() {
|
|
||||||
JsonObject jsonObject = new JsonObject();
|
|
||||||
jsonObject.addProperty("serverName", serverName);
|
|
||||||
jsonObject.addProperty("playerCount", getPlayerCount());
|
|
||||||
|
|
||||||
// Create an array to hold player information
|
|
||||||
JsonArray playersArray = new JsonArray();
|
|
||||||
for (Player player : players) {
|
|
||||||
JsonObject playerObject = new JsonObject();
|
|
||||||
playerObject.addProperty("playerName",
|
|
||||||
player.getName()); // Assuming you have a method to get player name
|
|
||||||
playerObject.addProperty("isReady",
|
|
||||||
player.isReady()); // Assuming you have a method to check player readiness
|
|
||||||
playersArray.add(playerObject);
|
|
||||||
playerObject.addProperty("isBot",
|
|
||||||
player instanceof BotPlayer);
|
|
||||||
}
|
|
||||||
for (int i = players.size(); i < 4; i++) {
|
|
||||||
JsonObject playerObject = new JsonObject();
|
|
||||||
playerObject.addProperty("playerName",
|
|
||||||
"Bot " + i); // Assuming you have a method to get player name
|
|
||||||
playerObject.addProperty("isReady",
|
|
||||||
true);
|
|
||||||
playerObject.addProperty("isBot",
|
|
||||||
true);
|
|
||||||
playersArray.add(playerObject);
|
|
||||||
}
|
|
||||||
jsonObject.add("players", playersArray);
|
|
||||||
|
|
||||||
return jsonObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getServerName() {
|
|
||||||
return serverName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main entrypoint of the Application.
|
|
||||||
*/
|
|
||||||
public int getPlayerCount() {
|
|
||||||
int onlinePlayerCount = 0;
|
|
||||||
for (Player player : players) {
|
|
||||||
if (player instanceof OnlinePlayer) {
|
|
||||||
onlinePlayerCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return onlinePlayerCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public void sendSessionInfo() {
|
|
||||||
JsonObject messageObject2 = new JsonObject();
|
|
||||||
messageObject2.add("game", this.getJson());
|
|
||||||
sendMessage(
|
|
||||||
new SchafkopfBaseMessage(SchafkopfMessageType.GET_ONLINE_GAME,
|
|
||||||
messageObject2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import org.eclipse.jetty.websocket.api.Session;
|
|
||||||
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
|
|
||||||
import org.schafkopf.SchafkopfException.NoGameSessionException;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfMessageOrigin;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfMessageType;
|
|
||||||
import org.schafkopf.karte.Karte;
|
|
||||||
import org.schafkopf.player.OnlinePlayer;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public class SchafkopfClientConnection extends WebSocketAdapter implements MessageSender {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(SchafkopfClientConnection.class);
|
|
||||||
private final CountDownLatch connectionLatch;
|
|
||||||
private final CountDownLatch closureLatch = new CountDownLatch(1);
|
|
||||||
private DedicatedServer dedicatedServer;
|
|
||||||
|
|
||||||
private OnlineGameSession onlineGameSession;
|
|
||||||
|
|
||||||
private OnlinePlayer onlinePlayer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public SchafkopfClientConnection(DedicatedServer dedicatedServer) {
|
|
||||||
this.dedicatedServer = dedicatedServer;
|
|
||||||
this.connectionLatch = new CountDownLatch(1);
|
|
||||||
this.onlinePlayer = new OnlinePlayer(this, "DefaultName");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onWebSocketConnect(Session session) {
|
|
||||||
super.onWebSocketConnect(session);
|
|
||||||
String clientIp = session.getRemoteAddress().toString();
|
|
||||||
logger.info("Endpoint connected from ip: " + clientIp);
|
|
||||||
connectionLatch.countDown();
|
|
||||||
dedicatedServer.addFrontendEndpoint(this);
|
|
||||||
sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.SERVER_CONNECTION_SUCCESSFUL));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onWebSocketText(String jsonMessage) {
|
|
||||||
super.onWebSocketText(jsonMessage);
|
|
||||||
Gson gson = new Gson();
|
|
||||||
JsonObject jsonObject = gson.fromJson(jsonMessage, JsonObject.class);
|
|
||||||
|
|
||||||
// Check if the origin is "backend"
|
|
||||||
String origin = jsonObject.get("origin").getAsString();
|
|
||||||
if (!SchafkopfMessageOrigin.BACKEND.toString().equals(origin)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonObject message = jsonObject.getAsJsonObject("message");
|
|
||||||
JsonObject content = message.getAsJsonObject("content");
|
|
||||||
String messageType = message.get("message_type").getAsString();
|
|
||||||
|
|
||||||
switch (SchafkopfMessageType.valueOf(messageType)) {
|
|
||||||
case HEARTBEAT_SYN:
|
|
||||||
sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.HEARTBEAT_ACK));
|
|
||||||
break;
|
|
||||||
case JOIN_ONLINE_GAME:
|
|
||||||
OnlineGameSession onlineGameSession = null;
|
|
||||||
try {
|
|
||||||
onlineGameSession = dedicatedServer.getGameSessionByName(
|
|
||||||
content.get("serverName").getAsString());
|
|
||||||
} catch (NoGameSessionException e) {
|
|
||||||
JsonObject messageObject = new JsonObject();
|
|
||||||
messageObject.addProperty("error",
|
|
||||||
"No GameSession with name \"" + content.get("serverName").getAsString()
|
|
||||||
+ "\" found.");
|
|
||||||
sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.UNKNOWN_ERROR,
|
|
||||||
messageObject));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
joinGame(onlineGameSession);
|
|
||||||
sendServerList();
|
|
||||||
JsonObject messageObject = new JsonObject();
|
|
||||||
messageObject.addProperty("message",
|
|
||||||
"Joined GameSession \"" + onlineGameSession.getServerName() + "\".");
|
|
||||||
sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.INFO_MESSAGE,
|
|
||||||
messageObject));
|
|
||||||
break;
|
|
||||||
case START_DEDICATED_GAME:
|
|
||||||
try {
|
|
||||||
this.onlineGameSession.startGame();
|
|
||||||
} catch (SchafkopfException e) {
|
|
||||||
sendError(e);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SET_PLAYER_NAME:
|
|
||||||
String name = content.get("playerName").getAsString();
|
|
||||||
onlinePlayer.setName(name);
|
|
||||||
break;
|
|
||||||
case PLAYER_CARD:
|
|
||||||
onlinePlayer.receiveCard(Karte.valueOf(content.get("card").getAsString()));
|
|
||||||
break;
|
|
||||||
case LIST_ONLINE_GAMES:
|
|
||||||
sendServerList();
|
|
||||||
break;
|
|
||||||
case GET_ONLINE_GAME:
|
|
||||||
this.onlineGameSession.sendSessionInfo();
|
|
||||||
break;
|
|
||||||
case CREATE_ONLINE_GAME:
|
|
||||||
String servername = content.get("serverName").getAsString();
|
|
||||||
OnlineGameSession onlineGameSession2 = new OnlineGameSession(servername,
|
|
||||||
this.dedicatedServer);
|
|
||||||
dedicatedServer.addGameSession(onlineGameSession2);
|
|
||||||
joinGame(onlineGameSession2);
|
|
||||||
sendServerList();
|
|
||||||
break;
|
|
||||||
case SET_STATUS_READY:
|
|
||||||
onlinePlayer.setReady(!onlinePlayer.isReady());
|
|
||||||
this.onlineGameSession.sendSessionInfo();
|
|
||||||
break;
|
|
||||||
case LEAVE_ONLINE_GAME:
|
|
||||||
this.onlineGameSession.removePlayer(this.onlinePlayer);
|
|
||||||
this.onlineGameSession = null;
|
|
||||||
sendServerList();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Handle unknown message types
|
|
||||||
logger.warn("Received unknown message type: " + messageType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onWebSocketClose(int statusCode, String reason) {
|
|
||||||
if (this.onlineGameSession != null) {
|
|
||||||
this.onlineGameSession.removePlayer(this.onlinePlayer);
|
|
||||||
}
|
|
||||||
super.onWebSocketClose(statusCode, reason);
|
|
||||||
|
|
||||||
dedicatedServer.removeFrontendEndpoint(this);
|
|
||||||
|
|
||||||
logger.warn("Socket Closed: [" + statusCode + "] " + reason);
|
|
||||||
closureLatch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onWebSocketError(Throwable cause) {
|
|
||||||
super.onWebSocketError(cause);
|
|
||||||
cause.printStackTrace(System.err);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a message to the connected FrontEnd.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void sendMessage(SchafkopfBaseMessage message) {
|
|
||||||
SchafkopfMessage schafkopfMessage = new SchafkopfMessage(
|
|
||||||
SchafkopfMessageOrigin.DEDICATED_SERVER,
|
|
||||||
message);
|
|
||||||
try {
|
|
||||||
getRemote().sendString(schafkopfMessage.getMessageAsString());
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main entrypoint of the Application.
|
|
||||||
*/
|
|
||||||
public void joinGame(OnlineGameSession onlineGameSession) {
|
|
||||||
if (this.onlineGameSession != null) {
|
|
||||||
this.onlineGameSession.removePlayer(this.onlinePlayer);
|
|
||||||
}
|
|
||||||
this.onlineGameSession = onlineGameSession;
|
|
||||||
onlineGameSession.addPlayer(this.onlinePlayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendServerList() {
|
|
||||||
JsonObject messageObject = new JsonObject();
|
|
||||||
try {
|
|
||||||
messageObject.add("games", dedicatedServer.getGameSessionsAsJson());
|
|
||||||
} catch (NoGameSessionException e) {
|
|
||||||
JsonObject error = new JsonObject();
|
|
||||||
error.addProperty("error",
|
|
||||||
"No GameSessions found.");
|
|
||||||
sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.UNKNOWN_ERROR,
|
|
||||||
error));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.LIST_ONLINE_GAMES,
|
|
||||||
messageObject));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a message to the connected FrontEnd.
|
|
||||||
*/
|
|
||||||
public void sendError(SchafkopfException e) {
|
|
||||||
JsonObject messageObject = new JsonObject();
|
|
||||||
messageObject.addProperty("error",
|
|
||||||
e.getMessage());
|
|
||||||
sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.UNKNOWN_ERROR,
|
|
||||||
messageObject));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.server.JettyServerUpgradeRequest;
|
|
||||||
import org.eclipse.jetty.websocket.server.JettyServerUpgradeResponse;
|
|
||||||
import org.eclipse.jetty.websocket.server.JettyWebSocketCreator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creater to make new Instances of the FrontendConnection.
|
|
||||||
*/
|
|
||||||
public class SchafkopfClientConnectionCreator implements JettyWebSocketCreator {
|
|
||||||
|
|
||||||
private DedicatedServer dedicatedServer;
|
|
||||||
|
|
||||||
public SchafkopfClientConnectionCreator(DedicatedServer dedicatedServer) {
|
|
||||||
this.dedicatedServer = dedicatedServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object createWebSocket(
|
|
||||||
JettyServerUpgradeRequest jettyServerUpgradeRequest,
|
|
||||||
JettyServerUpgradeResponse jettyServerUpgradeResponse) {
|
|
||||||
return new SchafkopfClientConnection(this.dedicatedServer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +1,89 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.schafkopf</groupId>
|
<groupId>de.heiserer</groupId>
|
||||||
<artifactId>schafkopf-backend-java</artifactId>
|
<artifactId>schafkopf-2</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>0.0.1</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>schafkopf-shared</artifactId>
|
<artifactId>schafkopf-shared</artifactId>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
<kotlin.code.style>official</kotlin.code.style>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>mavenCentral</id>
|
||||||
|
<url>https://repo1.maven.org/maven2/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<sourceDirectory>src/main/kotlin</sourceDirectory>
|
||||||
|
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-maven-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>compile</id>
|
||||||
|
<phase>compile</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>compile</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>test-compile</id>
|
||||||
|
<phase>test-compile</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>test-compile</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>2.22.2</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-failsafe-plugin</artifactId>
|
||||||
|
<version>2.22.2</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>MainKt</mainClass>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-test-junit5</artifactId>
|
||||||
|
<version>2.0.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<version>5.10.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-stdlib</artifactId>
|
||||||
|
<version>2.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import org.schafkopf.SchafkopfException.NotEnoughPlayersException;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfMessageType;
|
|
||||||
import org.schafkopf.player.BotPlayer;
|
|
||||||
import org.schafkopf.player.Player;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main entrypoint of the Application.
|
|
||||||
*/
|
|
||||||
public abstract class BaseGameSession implements MessageSender {
|
|
||||||
|
|
||||||
protected static final Logger logger = LoggerFactory.getLogger(BaseGameSession.class);
|
|
||||||
protected Thread spielThread;
|
|
||||||
protected Schafkopf schafkopf;
|
|
||||||
protected List<Player> players;
|
|
||||||
|
|
||||||
void startGame(List<Player> players) {
|
|
||||||
logger.info("Starting game");
|
|
||||||
for (int i = players.size(); i < 4; i++) {
|
|
||||||
players.add(new BotPlayer("Bot " + i));
|
|
||||||
}
|
|
||||||
|
|
||||||
sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.GAME_START_READY));
|
|
||||||
|
|
||||||
//wait for 5 seconds
|
|
||||||
try {
|
|
||||||
Thread.sleep(5000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
spielThread = new Thread(() -> {
|
|
||||||
try {
|
|
||||||
schafkopf = new Schafkopf(players.toArray(Player[]::new), this);
|
|
||||||
schafkopf.startGame();
|
|
||||||
} catch (NotEnoughPlayersException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
spielThread.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import org.schafkopf.karte.Karte;
|
|
||||||
import org.schafkopf.karte.KartenFarbe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GameState.
|
|
||||||
*/
|
|
||||||
public class GameState {
|
|
||||||
|
|
||||||
public GamePhase getGamePhase() {
|
|
||||||
return this.gamePhase;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GamePhase.
|
|
||||||
*/
|
|
||||||
public enum GamePhase {
|
|
||||||
CHOOSE_GAME("Spiel muss gewählt werden"),
|
|
||||||
|
|
||||||
GAME_START("Warten auf das Legen einer Karte"),
|
|
||||||
|
|
||||||
TRICK_START("Warten auf das Legen einer Karte"),
|
|
||||||
WAIT_FOR_CARD("Warten auf das Legen einer Karte"),
|
|
||||||
PLAYER_CARD("Warten auf das Legen einer Karte"),
|
|
||||||
PLAYER_TRICK("Spieler sticht"),
|
|
||||||
GAME_STOP("Spieler sticht");
|
|
||||||
// Add more phases as needed
|
|
||||||
|
|
||||||
private final String description;
|
|
||||||
|
|
||||||
GamePhase(String description) {
|
|
||||||
this.description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDescription() {
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private GamePhase gamePhase;
|
|
||||||
private String currentPlayer; // Using Integer to allow for null
|
|
||||||
private Karte card;
|
|
||||||
private KartenFarbe color;
|
|
||||||
private boolean trumpf;
|
|
||||||
|
|
||||||
// Constructors, getters, and setters
|
|
||||||
|
|
||||||
public GameState(GamePhase phase) {
|
|
||||||
this.gamePhase = phase;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GameState(GamePhase phase, String player) {
|
|
||||||
this.gamePhase = phase;
|
|
||||||
this.currentPlayer = player;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GameState.
|
|
||||||
*/
|
|
||||||
public GameState(GamePhase phase, String player, Karte card, KartenFarbe color, boolean trumpf) {
|
|
||||||
this.gamePhase = phase;
|
|
||||||
this.currentPlayer = player;
|
|
||||||
this.card = card;
|
|
||||||
this.color = color;
|
|
||||||
this.trumpf = trumpf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GameState.
|
|
||||||
*/
|
|
||||||
public GameState(GamePhase phase, String player, Karte card) {
|
|
||||||
this.gamePhase = phase;
|
|
||||||
this.currentPlayer = player;
|
|
||||||
this.card = card;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GameState.
|
|
||||||
*/
|
|
||||||
public JsonObject getJson() {
|
|
||||||
Gson gson = new Gson();
|
|
||||||
JsonObject gameStateObject = new JsonObject();
|
|
||||||
|
|
||||||
if (this.currentPlayer != null) {
|
|
||||||
gameStateObject.addProperty("currentPlayer", this.currentPlayer);
|
|
||||||
}
|
|
||||||
if (this.card != null) {
|
|
||||||
gameStateObject.add("card", gson.toJsonTree(this.card));
|
|
||||||
}
|
|
||||||
gameStateObject.addProperty("gamePhase", this.gamePhase.name());
|
|
||||||
gameStateObject.addProperty("trumpf", this.trumpf);
|
|
||||||
if (this.color != null) {
|
|
||||||
gameStateObject.addProperty("color", this.color.name());
|
|
||||||
}
|
|
||||||
|
|
||||||
return gameStateObject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public interface MessageListener {
|
|
||||||
|
|
||||||
void receiveMessage(String message);
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main entrypoint of the Application.
|
|
||||||
*/
|
|
||||||
public interface MessageSender {
|
|
||||||
|
|
||||||
void sendMessage(SchafkopfBaseMessage message);
|
|
||||||
}
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
import org.schafkopf.GameState.GamePhase;
|
|
||||||
import org.schafkopf.SchafkopfException.NotEnoughPlayersException;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfMessageType;
|
|
||||||
import org.schafkopf.karte.KartenFarbe;
|
|
||||||
import org.schafkopf.karte.KartenListe;
|
|
||||||
import org.schafkopf.karte.KartenUtil;
|
|
||||||
import org.schafkopf.player.BotPlayer;
|
|
||||||
import org.schafkopf.player.OnlinePlayer;
|
|
||||||
import org.schafkopf.player.Player;
|
|
||||||
import org.schafkopf.spielcontroller.SauSpielController;
|
|
||||||
import org.schafkopf.spielcontroller.SpielController;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main class representing the Schafkopf game.
|
|
||||||
*/
|
|
||||||
public class Schafkopf {
|
|
||||||
|
|
||||||
private final MessageSender messageSender;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The game controller. This is the class that implements the game logic.
|
|
||||||
*/
|
|
||||||
private SpielController spiel = new SauSpielController(0, KartenFarbe.EICHEL);
|
|
||||||
|
|
||||||
private final Player[] player;
|
|
||||||
|
|
||||||
private GameState gameState = new GameState(GamePhase.GAME_STOP);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for the Schafkopf class.
|
|
||||||
*
|
|
||||||
* @param messageSender MessageSender
|
|
||||||
*/
|
|
||||||
public Schafkopf(Player[] player, MessageSender messageSender) throws NotEnoughPlayersException {
|
|
||||||
this.player = player;
|
|
||||||
|
|
||||||
if (player.length < 4) {
|
|
||||||
throw new NotEnoughPlayersException();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.messageSender = messageSender;
|
|
||||||
System.out.println("SchaffKopfGame erstellt");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Player[] getPlayer() {
|
|
||||||
return player;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set GameState to "started" and start Game Thread.
|
|
||||||
*/
|
|
||||||
public void startGame() throws InterruptedException {
|
|
||||||
if (gameState.getGamePhase() != GamePhase.GAME_STOP) {
|
|
||||||
System.out.println("Game already started!");
|
|
||||||
messageSender.sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.UNKNOWN_ERROR));
|
|
||||||
} else {
|
|
||||||
gameState = new GameState(GamePhase.GAME_START);
|
|
||||||
setAndSendGameState(gameState);
|
|
||||||
System.out.println("Start Game");
|
|
||||||
|
|
||||||
KartenListe austeilen = KartenUtil.initializeSchafKopfCardDeck();
|
|
||||||
austeilen.shuffle();
|
|
||||||
for (Player currentPlayer : player) {
|
|
||||||
if (currentPlayer instanceof BotPlayer botPlayer) {
|
|
||||||
KartenListe botHand = new KartenListe();
|
|
||||||
for (int i = 7; i >= 0; i--) {
|
|
||||||
botHand.addKarten(austeilen.removeKarten(austeilen.getByIndex(i)));
|
|
||||||
}
|
|
||||||
System.out.println("Bot Hand: " + botHand.getJson().toString());
|
|
||||||
botPlayer.setCards(botHand); // Replace with the actual method you want to call
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Player currentPlayer : player) {
|
|
||||||
if (currentPlayer instanceof OnlinePlayer onlinePlayer) {
|
|
||||||
KartenListe karten = new KartenListe();
|
|
||||||
for (int i = 7; i >= 0; i--) {
|
|
||||||
karten.addKarten(austeilen.removeKarten(austeilen.getByIndex(i)));
|
|
||||||
}
|
|
||||||
onlinePlayer.setAndSendPlayerCards(karten);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new Spielablauf(this, spiel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set GameState to "stopped" and interrupt Game Thread.
|
|
||||||
*/
|
|
||||||
public void stopGame() {
|
|
||||||
if (gameState.getGamePhase() == GamePhase.GAME_STOP) {
|
|
||||||
System.out.println("no active Game!");
|
|
||||||
messageSender.sendMessage(new SchafkopfBaseMessage(SchafkopfMessageType.UNKNOWN_ERROR));
|
|
||||||
} else {
|
|
||||||
gameState = new GameState(GamePhase.GAME_STOP);
|
|
||||||
setAndSendGameState(gameState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public void setAndSendGameState(GameState gameState) {
|
|
||||||
this.gameState = gameState;
|
|
||||||
this.messageSender.sendMessage(
|
|
||||||
new SchafkopfBaseMessage(SchafkopfMessageType.GAME_STATE, gameState.getJson()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public GameState getGameState() {
|
|
||||||
return this.gameState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public class SchafkopfException extends Exception {
|
|
||||||
|
|
||||||
private SchafkopfException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public static class NotEnoughPlayersException extends SchafkopfException {
|
|
||||||
|
|
||||||
public NotEnoughPlayersException() {
|
|
||||||
super("Not enough players to start the game");
|
|
||||||
}
|
|
||||||
|
|
||||||
// You can also include additional constructors or methods if needed
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main entrypoint of the Application.
|
|
||||||
*/
|
|
||||||
public static class NoGameSessionException extends SchafkopfException {
|
|
||||||
|
|
||||||
public NoGameSessionException() {
|
|
||||||
super("No game session available");
|
|
||||||
}
|
|
||||||
|
|
||||||
// You can also include additional constructors or methods if needed
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main entrypoint of the Application.
|
|
||||||
*/
|
|
||||||
public static class PlayerNotReadyException extends SchafkopfException {
|
|
||||||
|
|
||||||
public PlayerNotReadyException() {
|
|
||||||
super("Not all Players are in Ready State");
|
|
||||||
}
|
|
||||||
|
|
||||||
// You can also include additional constructors or methods if needed
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public class InvalidMoveException extends SchafkopfException {
|
|
||||||
|
|
||||||
// Constructor with a message
|
|
||||||
public InvalidMoveException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// You can also include additional constructors or methods if needed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public class SchafkopfMessage {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public static class SchafkopfBaseMessage {
|
|
||||||
|
|
||||||
private JsonObject message;
|
|
||||||
private SchafkopfMessageType messageType;
|
|
||||||
|
|
||||||
public SchafkopfBaseMessage(SchafkopfMessageType messageType, String content) {
|
|
||||||
this.messageType = messageType;
|
|
||||||
this.message = buildBaseMessage(messageType, content);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SchafkopfBaseMessage(SchafkopfMessageType messageType, JsonObject content) {
|
|
||||||
this.messageType = messageType;
|
|
||||||
this.message = buildBaseMessage(messageType, content);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SchafkopfBaseMessage(SchafkopfMessageType messageType) {
|
|
||||||
this.messageType = messageType;
|
|
||||||
this.message = buildBaseMessage(messageType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonObject getBaseMessage() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonObject message;
|
|
||||||
|
|
||||||
public SchafkopfMessage(SchafkopfMessageOrigin origin, SchafkopfBaseMessage baseMessage) {
|
|
||||||
this.message = buildWrapperMessage(origin, baseMessage.getBaseMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public SchafkopfMessage(SchafkopfMessageOrigin origin, SchafkopfMessageType messageType) {
|
|
||||||
|
|
||||||
JsonObject messageContentObject = new JsonObject();
|
|
||||||
messageContentObject.add("content", buildBaseMessage(messageType));
|
|
||||||
|
|
||||||
this.message = buildWrapperMessage(origin, messageContentObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
private SchafkopfMessage(SchafkopfMessageOrigin origin, SchafkopfMessageType messageType,
|
|
||||||
JsonObject messageContent) {
|
|
||||||
|
|
||||||
JsonObject messageContentObject = new JsonObject();
|
|
||||||
messageContentObject.add("content", buildBaseMessage(messageType, messageContent));
|
|
||||||
|
|
||||||
this.message = buildWrapperMessage(origin, messageContentObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public SchafkopfMessage(SchafkopfMessageOrigin origin, SchafkopfMessageType messageType,
|
|
||||||
String messageContent) {
|
|
||||||
|
|
||||||
JsonObject messageContentObject = new JsonObject();
|
|
||||||
messageContentObject.add("content", buildBaseMessage(messageType, messageContent));
|
|
||||||
|
|
||||||
this.message = buildWrapperMessage(origin, messageContentObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static JsonObject buildWrapperMessage(SchafkopfMessageOrigin origin, JsonObject message) {
|
|
||||||
JsonObject messageObject = new JsonObject();
|
|
||||||
messageObject.addProperty("origin", origin.toString());
|
|
||||||
messageObject.add("message", message);
|
|
||||||
|
|
||||||
return messageObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static JsonObject buildBaseMessage(SchafkopfMessageType messageType,
|
|
||||||
String messageContent) {
|
|
||||||
JsonObject messageContentObject = new JsonObject();
|
|
||||||
messageContentObject.addProperty("message_type", messageType.toString());
|
|
||||||
messageContentObject.addProperty("content", messageContent);
|
|
||||||
|
|
||||||
return messageContentObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static JsonObject buildBaseMessage(SchafkopfMessageType messageType,
|
|
||||||
JsonObject messageContent) {
|
|
||||||
JsonObject messageContentObject = new JsonObject();
|
|
||||||
messageContentObject.addProperty("message_type", messageType.toString());
|
|
||||||
messageContentObject.add("content", messageContent);
|
|
||||||
|
|
||||||
return messageContentObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static JsonObject buildBaseMessage(SchafkopfMessageType messageType) {
|
|
||||||
JsonObject messageContentObject = new JsonObject();
|
|
||||||
messageContentObject.addProperty("message_type", messageType.toString());
|
|
||||||
|
|
||||||
return messageContentObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonObject getMessage() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMessageAsString() {
|
|
||||||
return message.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public enum SchafkopfMessageType {
|
|
||||||
UNKNOWN_ERROR,
|
|
||||||
|
|
||||||
INFO_MESSAGE,
|
|
||||||
HEARTBEAT_SYN,
|
|
||||||
HEARTBEAT_ACK,
|
|
||||||
GET_CARD_ONLINE_PLAYER,
|
|
||||||
ONLINE_PLAYER_HAND,
|
|
||||||
GAME_STATE,
|
|
||||||
SERVER_CONNECTION_SUCCESSFUL,
|
|
||||||
REQUEST_SERVER_CONNECTION,
|
|
||||||
JOIN_ONLINE_GAME,
|
|
||||||
START_DEDICATED_GAME,
|
|
||||||
PLAYER_CARD,
|
|
||||||
LIST_ONLINE_GAMES,
|
|
||||||
|
|
||||||
GET_ONLINE_GAME,
|
|
||||||
CREATE_ONLINE_GAME,
|
|
||||||
SET_STATUS_READY,
|
|
||||||
SET_PLAYER_NAME,
|
|
||||||
GAME_START_READY,
|
|
||||||
LEAVE_ONLINE_GAME
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public enum SchafkopfMessageOrigin {
|
|
||||||
FRONTEND,
|
|
||||||
BACKEND,
|
|
||||||
DEDICATED_SERVER
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
package org.schafkopf;
|
|
||||||
|
|
||||||
import org.schafkopf.GameState.GamePhase;
|
|
||||||
import org.schafkopf.karte.Karte;
|
|
||||||
import org.schafkopf.karte.KartenListe;
|
|
||||||
import org.schafkopf.player.Player;
|
|
||||||
import org.schafkopf.spielcontroller.SpielController;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main class that controlls the game flow.
|
|
||||||
*/
|
|
||||||
public class Spielablauf {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(Spielablauf.class);
|
|
||||||
private final KartenListe gespielteKarten = new KartenListe();
|
|
||||||
|
|
||||||
private final KartenListe tischKarten = new KartenListe();
|
|
||||||
|
|
||||||
private final SpielController spiel;
|
|
||||||
|
|
||||||
private final Player[] players;
|
|
||||||
|
|
||||||
private final Schafkopf schafkopf;
|
|
||||||
|
|
||||||
Spielablauf(Schafkopf schafkopf, SpielController spiel) throws InterruptedException {
|
|
||||||
this.schafkopf = schafkopf;
|
|
||||||
this.spiel = spiel;
|
|
||||||
this.players = schafkopf.getPlayer();
|
|
||||||
|
|
||||||
playRound();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void playRound() throws InterruptedException {
|
|
||||||
int startingPlayer = 0;
|
|
||||||
|
|
||||||
logger.info("Starte Stiche");
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
logger.info("Stich: {}", i);
|
|
||||||
startingPlayer = playTrick(startingPlayer);
|
|
||||||
}
|
|
||||||
schafkopf.stopGame();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int playTrick(int startingPlayer) throws InterruptedException {
|
|
||||||
schafkopf.setAndSendGameState(new GameState(GamePhase.TRICK_START));
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
int currentPlayer = (i + startingPlayer) % 4;
|
|
||||||
|
|
||||||
logger.info("Spieler ist dran: {}", players[currentPlayer].getName());
|
|
||||||
schafkopf.setAndSendGameState(
|
|
||||||
new GameState(GamePhase.WAIT_FOR_CARD, players[currentPlayer].getName()));
|
|
||||||
|
|
||||||
Karte playedCard = players[currentPlayer].play(spiel, tischKarten, gespielteKarten);
|
|
||||||
tischKarten.addKarten(playedCard);
|
|
||||||
|
|
||||||
schafkopf.setAndSendGameState(
|
|
||||||
new GameState(
|
|
||||||
GamePhase.PLAYER_CARD,
|
|
||||||
players[currentPlayer].getName(),
|
|
||||||
playedCard,
|
|
||||||
tischKarten.getByIndex(0).getFarbe(),
|
|
||||||
spiel.isTrumpf(tischKarten.getByIndex(0))));
|
|
||||||
}
|
|
||||||
int stichSpieler = SpielController.welcheKarteSticht(tischKarten);
|
|
||||||
|
|
||||||
logger.info("Stiche ende");
|
|
||||||
|
|
||||||
int winningPlayerIndex = (startingPlayer + stichSpieler) % 4;
|
|
||||||
logger.warn("Karte sticht: {}", winningPlayerIndex);
|
|
||||||
|
|
||||||
schafkopf.setAndSendGameState(
|
|
||||||
new GameState(
|
|
||||||
GamePhase.PLAYER_TRICK, players[winningPlayerIndex].getName(),
|
|
||||||
tischKarten.getByIndex(stichSpieler)));
|
|
||||||
|
|
||||||
try {
|
|
||||||
Thread.sleep(3000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
logger.error("error sleep");
|
|
||||||
}
|
|
||||||
|
|
||||||
gespielteKarten.addKarten(tischKarten);
|
|
||||||
tischKarten.clear();
|
|
||||||
|
|
||||||
return winningPlayerIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
package org.schafkopf.karte;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
|
|
||||||
/** enum to represent all cards in the game. */
|
|
||||||
public enum Karte {
|
|
||||||
SCHELL_7(KartenFarbe.SCHELL, KartenSymbol.SEVEN),
|
|
||||||
SCHELL_8(KartenFarbe.SCHELL, KartenSymbol.EIGHT),
|
|
||||||
SCHELL_9(KartenFarbe.SCHELL, KartenSymbol.NINE),
|
|
||||||
SCHELL_U(KartenFarbe.SCHELL, KartenSymbol.UNTER),
|
|
||||||
SCHELL_O(KartenFarbe.SCHELL, KartenSymbol.OBER),
|
|
||||||
SCHELL_K(KartenFarbe.SCHELL, KartenSymbol.KOENIG),
|
|
||||||
SCHELL_X(KartenFarbe.SCHELL, KartenSymbol.TEN),
|
|
||||||
SCHELL_A(KartenFarbe.SCHELL, KartenSymbol.ASS),
|
|
||||||
HERZ_7(KartenFarbe.HERZ, KartenSymbol.SEVEN),
|
|
||||||
HERZ_8(KartenFarbe.HERZ, KartenSymbol.EIGHT),
|
|
||||||
HERZ_9(KartenFarbe.HERZ, KartenSymbol.NINE),
|
|
||||||
HERZ_U(KartenFarbe.HERZ, KartenSymbol.UNTER),
|
|
||||||
HERZ_O(KartenFarbe.HERZ, KartenSymbol.OBER),
|
|
||||||
HERZ_K(KartenFarbe.HERZ, KartenSymbol.KOENIG),
|
|
||||||
HERZ_X(KartenFarbe.HERZ, KartenSymbol.TEN),
|
|
||||||
HERZ_A(KartenFarbe.HERZ, KartenSymbol.ASS),
|
|
||||||
|
|
||||||
BLATT_7(KartenFarbe.BLATT, KartenSymbol.SEVEN),
|
|
||||||
BLATT_8(KartenFarbe.BLATT, KartenSymbol.EIGHT),
|
|
||||||
BLATT_9(KartenFarbe.BLATT, KartenSymbol.NINE),
|
|
||||||
BLATT_U(KartenFarbe.BLATT, KartenSymbol.UNTER),
|
|
||||||
BLATT_O(KartenFarbe.BLATT, KartenSymbol.OBER),
|
|
||||||
BLATT_K(KartenFarbe.BLATT, KartenSymbol.KOENIG),
|
|
||||||
BLATT_X(KartenFarbe.BLATT, KartenSymbol.TEN),
|
|
||||||
BLATT_A(KartenFarbe.BLATT, KartenSymbol.ASS),
|
|
||||||
EICHEL_7(KartenFarbe.EICHEL, KartenSymbol.SEVEN),
|
|
||||||
EICHEL_8(KartenFarbe.EICHEL, KartenSymbol.EIGHT),
|
|
||||||
EICHEL_9(KartenFarbe.EICHEL, KartenSymbol.NINE),
|
|
||||||
EICHEL_U(KartenFarbe.EICHEL, KartenSymbol.UNTER),
|
|
||||||
EICHEL_O(KartenFarbe.EICHEL, KartenSymbol.OBER),
|
|
||||||
EICHEL_K(KartenFarbe.EICHEL, KartenSymbol.KOENIG),
|
|
||||||
EICHEL_X(KartenFarbe.EICHEL, KartenSymbol.TEN),
|
|
||||||
EICHEL_A(KartenFarbe.EICHEL, KartenSymbol.ASS);
|
|
||||||
|
|
||||||
|
|
||||||
private final String id;
|
|
||||||
private final KartenFarbe farbe;
|
|
||||||
private final KartenSymbol symbol;
|
|
||||||
|
|
||||||
private final String displayName;
|
|
||||||
|
|
||||||
private final int punkte;
|
|
||||||
|
|
||||||
Karte(KartenFarbe farbe, KartenSymbol symbol) {
|
|
||||||
this.farbe = farbe;
|
|
||||||
this.symbol = symbol;
|
|
||||||
this.id = this.name().toLowerCase();
|
|
||||||
this.displayName = farbe.getDisplayName() + " " + symbol.getDisplayName();
|
|
||||||
this.punkte = symbol.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getId() {
|
|
||||||
return this.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return this.displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KartenFarbe getFarbe() {
|
|
||||||
return this.farbe;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KartenSymbol getSymbol() {
|
|
||||||
return this.symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPunkte() {
|
|
||||||
return this.punkte;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** get the Card as a Json Object. */
|
|
||||||
public JsonObject getJson() {
|
|
||||||
Gson gson = new Gson();
|
|
||||||
JsonObject jsonObject = new JsonObject();
|
|
||||||
jsonObject.add("card", gson.toJsonTree(this));
|
|
||||||
|
|
||||||
return jsonObject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package org.schafkopf.karte;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enum for all possible Card Colors.
|
|
||||||
*/
|
|
||||||
public enum KartenFarbe {
|
|
||||||
EICHEL("Eichel"),
|
|
||||||
BLATT("Blatt"),
|
|
||||||
HERZ("Herz"),
|
|
||||||
SCHELL("Schell");
|
|
||||||
|
|
||||||
private final String displayName;
|
|
||||||
|
|
||||||
KartenFarbe(String displayName) {
|
|
||||||
this.displayName = displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDisplayName() {
|
|
||||||
return displayName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,238 +0,0 @@
|
|||||||
package org.schafkopf.karte;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Class that represents a list of Cards.
|
|
||||||
*/
|
|
||||||
public class KartenListe {
|
|
||||||
|
|
||||||
private List<Karte> kartenListe;
|
|
||||||
|
|
||||||
public KartenListe() {
|
|
||||||
this.kartenListe = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public KartenListe(KartenListe liste) {
|
|
||||||
this.kartenListe = new ArrayList<>(liste.getKartenListe());
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Karte> getKartenListe() {
|
|
||||||
return this.kartenListe;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Class that represents a list of Cards.
|
|
||||||
*/
|
|
||||||
public void sort() {
|
|
||||||
KartenListe completeDeck = KartenUtil.initializeSchafKopfCardDeck();
|
|
||||||
completeDeck.removeKarten(this);
|
|
||||||
|
|
||||||
KartenListe completeDeck2 = KartenUtil.initializeSchafKopfCardDeck();
|
|
||||||
completeDeck2.removeKarten(completeDeck);
|
|
||||||
|
|
||||||
this.kartenListe = completeDeck2.getKartenListe();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void shuffle() {
|
|
||||||
Collections.shuffle(this.kartenListe);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Class that represents a list of Cards.
|
|
||||||
*/
|
|
||||||
public void addKarten(Karte karte) {
|
|
||||||
if (!this.containsKarte(karte)) {
|
|
||||||
this.kartenListe.add(karte);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new RuntimeException("Karte bereits vorhanden: " + karte.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
// methoden zum hinzufügen von karten
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Class that represents a list of Cards.
|
|
||||||
*/
|
|
||||||
public void addKarten(KartenListe karten) {
|
|
||||||
for (Karte karte : karten.getKartenListe()) {
|
|
||||||
this.addKarten(karte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Class that represents a list of Cards.
|
|
||||||
*/
|
|
||||||
public KartenListe removeKarten(KartenListe karten) {
|
|
||||||
KartenListe result = new KartenListe();
|
|
||||||
for (Karte karteWeg : karten.getKartenListe()) {
|
|
||||||
for (Karte karte : this.kartenListe) {
|
|
||||||
if (karte.getId().equals(karteWeg.getId())) {
|
|
||||||
result.addKarten(karte);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.kartenListe.removeAll(result.getKartenListe());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// methoden zum entfernen von karten
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Class that represents a list of Cards.
|
|
||||||
*/
|
|
||||||
public KartenListe removeKarten(KartenFarbe farbe) {
|
|
||||||
KartenListe result = new KartenListe();
|
|
||||||
for (Karte karte : this.kartenListe) {
|
|
||||||
if (karte.getFarbe().equals(farbe)) {
|
|
||||||
result.addKarten(karte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.kartenListe.removeAll(result.getKartenListe());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Class that represents a list of Cards.
|
|
||||||
*/
|
|
||||||
public KartenListe removeKarten(KartenSymbol symbol) {
|
|
||||||
KartenListe result = new KartenListe();
|
|
||||||
for (Karte karte : this.kartenListe) {
|
|
||||||
if (karte.getSymbol().equals(symbol)) {
|
|
||||||
result.addKarten(karte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.kartenListe.removeAll(result.getKartenListe());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Class that represents a list of Cards.
|
|
||||||
*/
|
|
||||||
public Karte removeKarten(Karte karteToRemove) {
|
|
||||||
for (Karte karte : this.kartenListe) {
|
|
||||||
if (karte.getId().equals(karteToRemove.getId())) {
|
|
||||||
this.kartenListe.remove(karte);
|
|
||||||
return karte;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Class that represents a list of Cards.
|
|
||||||
*/
|
|
||||||
public Karte removeKarten(String idToRemove) {
|
|
||||||
for (Karte karte : this.kartenListe) {
|
|
||||||
if (karte.getId().equals(idToRemove)) {
|
|
||||||
this.kartenListe.remove(karte);
|
|
||||||
return karte;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Class that represents a list of Cards.
|
|
||||||
*/
|
|
||||||
public boolean containsKarte(Karte karte) {
|
|
||||||
for (Karte karteInListe : this.kartenListe) {
|
|
||||||
if (karteInListe.getId().equals(karte.getId())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get Karten
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Class that represents a list of Cards.
|
|
||||||
*/
|
|
||||||
public KartenListe getKarten(KartenFarbe farbe) {
|
|
||||||
KartenListe result = new KartenListe();
|
|
||||||
for (Karte karte : this.kartenListe) {
|
|
||||||
if (karte.getFarbe().equals(farbe)) {
|
|
||||||
result.addKarten(karte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Class that represents a list of Cards.
|
|
||||||
*/
|
|
||||||
public KartenListe getKarten(KartenSymbol symbol) {
|
|
||||||
KartenListe result = new KartenListe();
|
|
||||||
for (Karte karte : this.kartenListe) {
|
|
||||||
if (karte.getSymbol().equals(symbol)) {
|
|
||||||
result.addKarten(karte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public Karte getKarten(KartenSymbol symbol, KartenFarbe farbe) {
|
|
||||||
KartenListe result = new KartenListe();
|
|
||||||
for (Karte karte : this.kartenListe) {
|
|
||||||
if (karte.getSymbol().equals(symbol)) {
|
|
||||||
result.addKarten(karte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Karte karte : result.kartenListe) {
|
|
||||||
if (karte.getFarbe().equals(farbe)) {
|
|
||||||
result.addKarten(karte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.getByIndex(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Class that represents a list of Cards.
|
|
||||||
*/
|
|
||||||
public JsonElement getJson() {
|
|
||||||
Gson gson = new Gson();
|
|
||||||
|
|
||||||
return gson.toJsonTree(this.kartenListe);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return this.kartenListe.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Karte getLast() {
|
|
||||||
return this.kartenListe.getLast();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Karte getByIndex(int index) {
|
|
||||||
return this.kartenListe.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int size() {
|
|
||||||
return this.kartenListe.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Class that represents a list of Cards.
|
|
||||||
*/
|
|
||||||
public int indexOf(Karte karte) {
|
|
||||||
for (Karte karteInListe : this.kartenListe) {
|
|
||||||
if (karteInListe.getId().equals(karte.getId())) {
|
|
||||||
return this.kartenListe.indexOf(karteInListe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
this.kartenListe.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package org.schafkopf.karte;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enum for all possible Card Symbols.
|
|
||||||
*/
|
|
||||||
public enum KartenSymbol {
|
|
||||||
SIX("6", "6", 0),
|
|
||||||
SEVEN("7", "7", 0),
|
|
||||||
EIGHT("8", "8", 0),
|
|
||||||
NINE("9", "9", 0),
|
|
||||||
UNTER("u", "Unter", 2),
|
|
||||||
OBER("o", "Ober", 3),
|
|
||||||
KOENIG("k", "König", 4),
|
|
||||||
TEN("x", "10", 10),
|
|
||||||
ASS("a", "Ass", 11);
|
|
||||||
|
|
||||||
private final String displayName;
|
|
||||||
private final String id;
|
|
||||||
private final int value;
|
|
||||||
|
|
||||||
KartenSymbol(String id, String displayName, int value) {
|
|
||||||
this.displayName = displayName;
|
|
||||||
this.value = value;
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDisplayName() {
|
|
||||||
return displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
package org.schafkopf.karte;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
/** Class that brings usefully functions for Card/s. */
|
|
||||||
public class KartenUtil {
|
|
||||||
|
|
||||||
/** initialize a normal Card Deck. It will be in the standard order. */
|
|
||||||
public static KartenListe initializeSchafKopfCardDeck() {
|
|
||||||
KartenListe deck = new KartenListe();
|
|
||||||
|
|
||||||
for (Karte karte : Karte.values()) {
|
|
||||||
deck.addKarten(karte);
|
|
||||||
}
|
|
||||||
|
|
||||||
deck.removeKarten(KartenSymbol.SIX);
|
|
||||||
return deck;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a List of Random Cards.
|
|
||||||
*
|
|
||||||
* @param anzahl count of random cards.
|
|
||||||
*/
|
|
||||||
public static KartenListe zieheZufallsHand(int anzahl) {
|
|
||||||
KartenListe karten = initializeSchafKopfCardDeck();
|
|
||||||
KartenListe gezogeneKarten = new KartenListe();
|
|
||||||
Random random = new Random();
|
|
||||||
|
|
||||||
// Ziehe zufällige Karten
|
|
||||||
for (int i = 0; i < anzahl; i++) {
|
|
||||||
int zufallsIndex = random.nextInt(karten.size());
|
|
||||||
Karte gezogeneKarte = karten.getByIndex(zufallsIndex);
|
|
||||||
gezogeneKarten.addKarten(gezogeneKarte);
|
|
||||||
karten.removeKarten(gezogeneKarte);
|
|
||||||
}
|
|
||||||
return gezogeneKarten;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* converts Uid from a NFC Card to a card ID.
|
|
||||||
*
|
|
||||||
* @param uid uId to get the Card ID from.
|
|
||||||
*/
|
|
||||||
public static Karte getIdOfUid(String uid) {
|
|
||||||
return switch (uid) {
|
|
||||||
case "04E7A9C2126F80" -> Karte.EICHEL_7;
|
|
||||||
case "04A46BB4780000" -> Karte.EICHEL_8;
|
|
||||||
case "04A26BB4780000" -> Karte.EICHEL_9;
|
|
||||||
case "04A16BB4780000" -> Karte.EICHEL_X;
|
|
||||||
case "049E6BB4780000" -> Karte.EICHEL_K;
|
|
||||||
case "04A86BB4780000" -> Karte.EICHEL_A;
|
|
||||||
case "04A06BB4780000" -> Karte.EICHEL_U;
|
|
||||||
case "049F6BB4780000" -> Karte.EICHEL_O;
|
|
||||||
case "04F26BB4780000" -> Karte.BLATT_7;
|
|
||||||
case "04A76BB4780000" -> Karte.BLATT_8;
|
|
||||||
case "049B6BB4780000" -> Karte.BLATT_9;
|
|
||||||
case "04996BB4780000" -> Karte.BLATT_X;
|
|
||||||
case "041CD2C2126F81" -> Karte.BLATT_K;
|
|
||||||
case "04A96BB4780000" -> Karte.BLATT_A;
|
|
||||||
case "049A6BB4780000" -> Karte.BLATT_U;
|
|
||||||
case "049D6BB4780000" -> Karte.BLATT_O;
|
|
||||||
case "04936BB4780000" -> Karte.SCHELL_7;
|
|
||||||
case "04F697C2126F80" -> Karte.SCHELL_8;
|
|
||||||
case "04946BB4780000" -> Karte.SCHELL_9;
|
|
||||||
case "04956BB4780000" -> Karte.SCHELL_X;
|
|
||||||
case "04986BB4780000" -> Karte.SCHELL_K;
|
|
||||||
case "04AA6BB4780000" -> Karte.SCHELL_A;
|
|
||||||
case "04966BB4780000" -> Karte.SCHELL_U;
|
|
||||||
case "04976BB4780000" -> Karte.SCHELL_O;
|
|
||||||
case "04F36BB4780000" -> Karte.HERZ_7;
|
|
||||||
case "04B06BB4780000" -> Karte.HERZ_8;
|
|
||||||
case "04AF6BB4780000" -> Karte.HERZ_9;
|
|
||||||
case "04AE6BB4780000" -> Karte.HERZ_X;
|
|
||||||
case "04AB6BB4780000" -> Karte.HERZ_K;
|
|
||||||
case "049C6BB4780000" -> Karte.HERZ_A;
|
|
||||||
case "04AD6BB4780000" -> Karte.HERZ_U;
|
|
||||||
case "04AC6BB4780000" -> Karte.HERZ_O;
|
|
||||||
default -> null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
package org.schafkopf.player;
|
|
||||||
|
|
||||||
import org.schafkopf.MessageSender;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage;
|
|
||||||
import org.schafkopf.karte.Karte;
|
|
||||||
import org.schafkopf.karte.KartenListe;
|
|
||||||
import org.schafkopf.karte.KartenUtil;
|
|
||||||
import org.schafkopf.spielcontroller.SpielController;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Player that represents the Bot.
|
|
||||||
*/
|
|
||||||
public class BotPlayer extends Player {
|
|
||||||
|
|
||||||
private KartenListe eigeneKarten;
|
|
||||||
private KartenListe unbekannteKarten = KartenUtil.initializeSchafKopfCardDeck();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for the BotPlayer.
|
|
||||||
*/
|
|
||||||
public BotPlayer(String name) {
|
|
||||||
super(name, new MessageSender() {
|
|
||||||
@Override
|
|
||||||
public void sendMessage(SchafkopfBaseMessage message) {
|
|
||||||
System.out.println("BotPlayer: " + message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.setReady(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Karte play(SpielController spiel, KartenListe tischKarten, KartenListe gespielteKarten) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
Thread.sleep(1000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Karte card = spiel.welcheKarteSpielIch(true, gespielteKarten, eigeneKarten, tischKarten);
|
|
||||||
|
|
||||||
eigeneKarten.removeKarten(card);
|
|
||||||
|
|
||||||
System.out.println("Eigene Karte legen");
|
|
||||||
return card;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the Cards of the Player.
|
|
||||||
*/
|
|
||||||
public void setCards(KartenListe cards) {
|
|
||||||
System.out.println("Eigene Karte setzen");
|
|
||||||
cards.sort();
|
|
||||||
this.eigeneKarten = cards;
|
|
||||||
this.unbekannteKarten = KartenUtil.initializeSchafKopfCardDeck();
|
|
||||||
this.unbekannteKarten.removeKarten(eigeneKarten);
|
|
||||||
System.out.println("Eigene Karte fertig");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(SchafkopfBaseMessage message) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resetReady() {
|
|
||||||
// Not needed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
package org.schafkopf.player;
|
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
import org.schafkopf.MessageSender;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfBaseMessage;
|
|
||||||
import org.schafkopf.SchafkopfMessage.SchafkopfMessageType;
|
|
||||||
import org.schafkopf.karte.Karte;
|
|
||||||
import org.schafkopf.karte.KartenListe;
|
|
||||||
import org.schafkopf.spielcontroller.SpielController;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Player that plays in real life.
|
|
||||||
*/
|
|
||||||
public class OnlinePlayer extends Player {
|
|
||||||
|
|
||||||
private final BlockingQueue<Karte> receivedCardQueue = new LinkedBlockingQueue<>();
|
|
||||||
|
|
||||||
private KartenListe karten = new KartenListe();
|
|
||||||
|
|
||||||
public OnlinePlayer(MessageSender messageSender, String name) {
|
|
||||||
super(name, messageSender);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Class that represents a list of Cards.
|
|
||||||
*/
|
|
||||||
public void setAndSendPlayerCards(KartenListe karten) {
|
|
||||||
karten.sort();
|
|
||||||
this.karten = karten;
|
|
||||||
|
|
||||||
sendPlayerCards();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Karte play(SpielController spiel, KartenListe tischKarten, KartenListe gespielteKarten)
|
|
||||||
throws InterruptedException {
|
|
||||||
sendPlayerCards();
|
|
||||||
Karte spielKarte = null;
|
|
||||||
|
|
||||||
// Send the message to request the card from the frontend
|
|
||||||
messageSender.sendMessage(
|
|
||||||
new SchafkopfBaseMessage(SchafkopfMessageType.GET_CARD_ONLINE_PLAYER));
|
|
||||||
|
|
||||||
spielKarte = receivedCardQueue.take();
|
|
||||||
|
|
||||||
this.karten.removeKarten(spielKarte);
|
|
||||||
sendPlayerCards();
|
|
||||||
System.out.println("Karte gespielt: " + spielKarte);
|
|
||||||
return spielKarte;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resetReady() {
|
|
||||||
this.setReady(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendPlayerCards() {
|
|
||||||
JsonObject messageObject = new JsonObject();
|
|
||||||
messageObject.add("cards", this.karten.getJson());
|
|
||||||
|
|
||||||
messageSender.sendMessage(
|
|
||||||
new SchafkopfBaseMessage(SchafkopfMessageType.ONLINE_PLAYER_HAND, messageObject));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Frontend Connection.
|
|
||||||
*/
|
|
||||||
public void receiveCard(Karte receivedCard) {
|
|
||||||
System.out.println("Received Card before Queue: " + receivedCard.getName());
|
|
||||||
receivedCardQueue.add(receivedCard);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(SchafkopfBaseMessage message) {
|
|
||||||
messageSender.sendMessage(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package org.schafkopf.player;
|
|
||||||
|
|
||||||
import org.schafkopf.MessageSender;
|
|
||||||
import org.schafkopf.karte.Karte;
|
|
||||||
import org.schafkopf.karte.KartenListe;
|
|
||||||
import org.schafkopf.spielcontroller.SpielController;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents one Player of the game.
|
|
||||||
*/
|
|
||||||
public abstract class Player implements MessageSender {
|
|
||||||
|
|
||||||
protected MessageSender messageSender;
|
|
||||||
private boolean ready = false;
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
protected Player(String name, MessageSender messageSender) {
|
|
||||||
this.messageSender = messageSender;
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract Karte play(
|
|
||||||
SpielController spiel, KartenListe tischKarten, KartenListe gespielteKarten)
|
|
||||||
throws InterruptedException;
|
|
||||||
|
|
||||||
public abstract void resetReady();
|
|
||||||
|
|
||||||
public void setReady(boolean ready) {
|
|
||||||
this.ready = ready;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isReady() {
|
|
||||||
return ready;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package org.schafkopf.spielcontroller;
|
|
||||||
|
|
||||||
import org.schafkopf.karte.Karte;
|
|
||||||
import org.schafkopf.karte.KartenFarbe;
|
|
||||||
import org.schafkopf.karte.KartenListe;
|
|
||||||
import org.schafkopf.karte.KartenSymbol;
|
|
||||||
import org.schafkopf.karte.KartenUtil;
|
|
||||||
|
|
||||||
/** SpielController that implements Logic of a Farb Geier. */
|
|
||||||
public class FarbGeierController extends SoloController {
|
|
||||||
/**
|
|
||||||
* Create instance of SpielController.
|
|
||||||
*
|
|
||||||
* @param farbe Trumpffarbe of the Farb Geier.
|
|
||||||
*/
|
|
||||||
public FarbGeierController(int activePlayer, KartenFarbe farbe) {
|
|
||||||
super(activePlayer);
|
|
||||||
KartenListe kartenList = KartenUtil.initializeSchafKopfCardDeck();
|
|
||||||
KartenListe oberKarten = kartenList.getKarten(KartenSymbol.OBER);
|
|
||||||
KartenListe farbTrumpfKarten = kartenList.getKarten(farbe);
|
|
||||||
farbTrumpfKarten.removeKarten(KartenSymbol.OBER);
|
|
||||||
farbTrumpfKarten.addKarten(oberKarten);
|
|
||||||
kartenList.removeKarten(farbTrumpfKarten);
|
|
||||||
|
|
||||||
this.trumpfKarten = new KartenListe(farbTrumpfKarten);
|
|
||||||
this.farbKarten = new KartenListe(kartenList);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Karte welcheKarteSpielIch(
|
|
||||||
boolean istSpieler,
|
|
||||||
KartenListe gespielteKarten,
|
|
||||||
KartenListe meineHand,
|
|
||||||
KartenListe tischKarten) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
package org.schafkopf.spielcontroller;
|
|
||||||
|
|
||||||
import org.schafkopf.karte.Karte;
|
|
||||||
import org.schafkopf.karte.KartenFarbe;
|
|
||||||
import org.schafkopf.karte.KartenListe;
|
|
||||||
import org.schafkopf.karte.KartenSymbol;
|
|
||||||
import org.schafkopf.karte.KartenUtil;
|
|
||||||
|
|
||||||
/** SpielController that implements Logic of a Farb Solo. */
|
|
||||||
public class FarbSoloController extends SoloController {
|
|
||||||
/**
|
|
||||||
* Create instance of SpielController.
|
|
||||||
*
|
|
||||||
* @param farbe Trumpffarbe of the Farb Solo.
|
|
||||||
*/
|
|
||||||
public FarbSoloController(int activePlayer, KartenFarbe farbe) {
|
|
||||||
super(activePlayer);
|
|
||||||
KartenListe kartenList = KartenUtil.initializeSchafKopfCardDeck();
|
|
||||||
KartenListe unterKarten = kartenList.getKarten(KartenSymbol.UNTER);
|
|
||||||
|
|
||||||
KartenListe farbTrumpfKarten = kartenList.getKarten(farbe);
|
|
||||||
farbTrumpfKarten.removeKarten(KartenSymbol.UNTER);
|
|
||||||
farbTrumpfKarten.removeKarten(KartenSymbol.OBER);
|
|
||||||
farbTrumpfKarten.addKarten(kartenList.getKarten(KartenSymbol.UNTER));
|
|
||||||
farbTrumpfKarten.addKarten(kartenList.getKarten(KartenSymbol.OBER));
|
|
||||||
|
|
||||||
kartenList.removeKarten(farbTrumpfKarten);
|
|
||||||
|
|
||||||
this.trumpfKarten = new KartenListe(farbTrumpfKarten);
|
|
||||||
this.farbKarten = new KartenListe(kartenList);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Karte welcheKarteSpielIch(
|
|
||||||
boolean istSpieler,
|
|
||||||
KartenListe gespielteKarten,
|
|
||||||
KartenListe meineHand,
|
|
||||||
KartenListe tischKarten) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package org.schafkopf.spielcontroller;
|
|
||||||
|
|
||||||
import org.schafkopf.karte.Karte;
|
|
||||||
import org.schafkopf.karte.KartenFarbe;
|
|
||||||
import org.schafkopf.karte.KartenListe;
|
|
||||||
import org.schafkopf.karte.KartenSymbol;
|
|
||||||
import org.schafkopf.karte.KartenUtil;
|
|
||||||
|
|
||||||
/** SpielController that implements Logic of a Farb Wenz. */
|
|
||||||
public class FarbWenzController extends SoloController {
|
|
||||||
/**
|
|
||||||
* Create instance of SpielController.
|
|
||||||
*
|
|
||||||
* @param farbe Trumpffarbe of the Farb Wenz.
|
|
||||||
*/
|
|
||||||
public FarbWenzController(int activePlayer, KartenFarbe farbe) {
|
|
||||||
super(activePlayer);
|
|
||||||
KartenListe kartenList = KartenUtil.initializeSchafKopfCardDeck();
|
|
||||||
KartenListe unterKarten = kartenList.getKarten(KartenSymbol.UNTER);
|
|
||||||
KartenListe farbTrumpfKarten = kartenList.getKarten(farbe);
|
|
||||||
farbTrumpfKarten.removeKarten(KartenSymbol.UNTER);
|
|
||||||
farbTrumpfKarten.addKarten(unterKarten);
|
|
||||||
kartenList.removeKarten(farbTrumpfKarten);
|
|
||||||
|
|
||||||
this.trumpfKarten = new KartenListe(farbTrumpfKarten);
|
|
||||||
this.farbKarten = new KartenListe(kartenList);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Karte welcheKarteSpielIch(
|
|
||||||
boolean istSpieler,
|
|
||||||
KartenListe gespielteKarten,
|
|
||||||
KartenListe meineHand,
|
|
||||||
KartenListe tischKarten) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package org.schafkopf.spielcontroller;
|
|
||||||
|
|
||||||
import org.schafkopf.karte.KartenListe;
|
|
||||||
import org.schafkopf.karte.KartenSymbol;
|
|
||||||
import org.schafkopf.karte.KartenUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpielController that implements Logic of a Geier Game.
|
|
||||||
*/
|
|
||||||
public class GeierController extends GeierWenzController {
|
|
||||||
/**
|
|
||||||
* Create instance of Geier Game.
|
|
||||||
*/
|
|
||||||
public GeierController(int activePlayer) {
|
|
||||||
super(activePlayer);
|
|
||||||
KartenListe kartenList = KartenUtil.initializeSchafKopfCardDeck();
|
|
||||||
KartenListe oberKarten = kartenList.getKarten(KartenSymbol.OBER);
|
|
||||||
|
|
||||||
kartenList.removeKarten(oberKarten);
|
|
||||||
|
|
||||||
this.trumpfKarten = new KartenListe(oberKarten);
|
|
||||||
this.farbKarten = new KartenListe(kartenList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package org.schafkopf.spielcontroller;
|
|
||||||
|
|
||||||
import org.schafkopf.karte.Karte;
|
|
||||||
import org.schafkopf.karte.KartenListe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpielController that implements Logic of a Geier/Wenz Game.
|
|
||||||
*/
|
|
||||||
public class GeierWenzController extends SoloController {
|
|
||||||
|
|
||||||
public GeierWenzController(int activePlayer) {
|
|
||||||
super(activePlayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Karte welcheKarteSpielIch(boolean istSpieler, KartenListe gespielteKarten,
|
|
||||||
KartenListe meineHand, KartenListe tischKarten) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
package org.schafkopf.spielcontroller;
|
|
||||||
|
|
||||||
import org.schafkopf.karte.Karte;
|
|
||||||
import org.schafkopf.karte.KartenFarbe;
|
|
||||||
import org.schafkopf.karte.KartenListe;
|
|
||||||
|
|
||||||
/** SpielController that implements Logic of a Sau Spiel Game. */
|
|
||||||
public class SauSpielController extends StandardController {
|
|
||||||
|
|
||||||
KartenFarbe suchFarbe;
|
|
||||||
|
|
||||||
/** Class that represents one Card of the game. */
|
|
||||||
public SauSpielController(int activePlayer, KartenFarbe farbe) {
|
|
||||||
super(activePlayer);
|
|
||||||
this.suchFarbe = farbe;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** choose witch Card should be played with the right Game logic. */
|
|
||||||
public Karte welcheKarteSpielIch(
|
|
||||||
boolean istSpieler,
|
|
||||||
KartenListe gespielteKarten,
|
|
||||||
KartenListe meineHand,
|
|
||||||
KartenListe tischKarten) {
|
|
||||||
System.out.println("Ich spiele eine Karte Sauspiel");
|
|
||||||
|
|
||||||
int spielerNummer = tischKarten.size();
|
|
||||||
|
|
||||||
switch (spielerNummer) {
|
|
||||||
case 0:
|
|
||||||
if (istSpieler) {
|
|
||||||
return meineHand.getLast();
|
|
||||||
} else {
|
|
||||||
return meineHand.getByIndex(0);
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
if (istSpieler) {
|
|
||||||
return farbeZugeben(meineHand, tischKarten.getByIndex(0), 2);
|
|
||||||
} else {
|
|
||||||
return farbeZugeben(meineHand, tischKarten.getByIndex(0), 0);
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
if (istSpieler) {
|
|
||||||
return farbeZugeben(meineHand, tischKarten.getByIndex(0), 2);
|
|
||||||
} else {
|
|
||||||
return farbeZugeben(meineHand, tischKarten.getByIndex(0), 0);
|
|
||||||
}
|
|
||||||
case 3:
|
|
||||||
if (istSpieler) {
|
|
||||||
return farbeZugeben(meineHand, tischKarten.getByIndex(0), 2);
|
|
||||||
} else {
|
|
||||||
return farbeZugeben(meineHand, tischKarten.getByIndex(0), 0);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
System.out.println("Ungültige SpielerNummer");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package org.schafkopf.spielcontroller;
|
|
||||||
|
|
||||||
import org.schafkopf.karte.Karte;
|
|
||||||
import org.schafkopf.karte.KartenListe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* abstract Class that represents Logic of a Solo like Game.
|
|
||||||
*/
|
|
||||||
public abstract class SoloController extends SpielController {
|
|
||||||
|
|
||||||
SoloController(int activePlayer) {
|
|
||||||
super(activePlayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Karte welcheKarteSpielIch(
|
|
||||||
KartenListe gespielteKarten, KartenListe meineHand, KartenListe tischKarten) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
package org.schafkopf.spielcontroller;
|
|
||||||
|
|
||||||
import org.schafkopf.karte.Karte;
|
|
||||||
import org.schafkopf.karte.KartenFarbe;
|
|
||||||
import org.schafkopf.karte.KartenListe;
|
|
||||||
import org.schafkopf.karte.KartenUtil;
|
|
||||||
|
|
||||||
/** Base Class of Game Controllers. */
|
|
||||||
public abstract class SpielController {
|
|
||||||
protected static KartenListe trumpfKarten;
|
|
||||||
protected static KartenListe farbKarten;
|
|
||||||
|
|
||||||
protected static int activePlayer;
|
|
||||||
|
|
||||||
public SpielController(int activePlayer) {
|
|
||||||
this.activePlayer = activePlayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create instance of SpielController.
|
|
||||||
*
|
|
||||||
* @param meineHand Cards one Player holds.
|
|
||||||
* @param ersteKarte color the Player has to play.
|
|
||||||
* @param mode Mode the player chooses a Card if multiple are available.
|
|
||||||
*/
|
|
||||||
public static Karte farbeZugeben(KartenListe meineHand, Karte ersteKarte, int mode) {
|
|
||||||
KartenListe hand = new KartenListe(meineHand);
|
|
||||||
sortiereKarten(hand);
|
|
||||||
|
|
||||||
boolean trumpfGespielt = trumpfKarten.containsKarte(ersteKarte);
|
|
||||||
|
|
||||||
KartenListe handTrumpfKarten = hand.removeKarten(trumpfKarten);
|
|
||||||
KartenListe handfarbKarten;
|
|
||||||
|
|
||||||
if (trumpfGespielt) {
|
|
||||||
handfarbKarten = handTrumpfKarten;
|
|
||||||
} else {
|
|
||||||
handfarbKarten = hand.getKarten(ersteKarte.getFarbe());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handfarbKarten.size() == 1) {
|
|
||||||
return handfarbKarten.getByIndex(0);
|
|
||||||
} else if (handfarbKarten.size() > 1) {
|
|
||||||
return switch (mode) {
|
|
||||||
case 0 -> // Abspatzen
|
|
||||||
handfarbKarten.getByIndex(0);
|
|
||||||
case 1, 2 -> // Stechen // Schmieren
|
|
||||||
handfarbKarten.getLast();
|
|
||||||
default -> null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (handfarbKarten.isEmpty()) {
|
|
||||||
switch (mode) {
|
|
||||||
case 0: // Abspatzen
|
|
||||||
return hand.getByIndex(0);
|
|
||||||
case 1: // Schmieren
|
|
||||||
return hand.getLast();
|
|
||||||
case 2: // Stechen
|
|
||||||
if (!handTrumpfKarten.isEmpty()) {
|
|
||||||
return handTrumpfKarten.getLast(); // trumpf reinspielen
|
|
||||||
} else {
|
|
||||||
return hand.getByIndex(0); // wenn kein Trumpf und farblos, abschpatzen
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* sorts Cards, so they are in the right order for the active game.
|
|
||||||
*
|
|
||||||
* @param karten Trumpffarbe of the Farb Geier.
|
|
||||||
*/
|
|
||||||
public static void sortiereKarten(KartenListe karten) {
|
|
||||||
KartenListe kartenReihenfolge = new KartenListe(farbKarten);
|
|
||||||
kartenReihenfolge.addKarten(trumpfKarten);
|
|
||||||
|
|
||||||
KartenListe kartenListe = KartenUtil.initializeSchafKopfCardDeck();
|
|
||||||
|
|
||||||
kartenListe.removeKarten(karten);
|
|
||||||
kartenReihenfolge.removeKarten(kartenListe);
|
|
||||||
|
|
||||||
karten.clear();
|
|
||||||
karten.addKarten(kartenReihenfolge);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* checks, which card has the highest strength and will win one Stich.
|
|
||||||
*
|
|
||||||
* @param karten Cards to check.
|
|
||||||
*/
|
|
||||||
public static int welcheKarteSticht(KartenListe karten) {
|
|
||||||
KartenListe kartenNew = new KartenListe(karten);
|
|
||||||
sortiereKarten(kartenNew);
|
|
||||||
KartenListe farbTischKarten = kartenNew.removeKarten(trumpfKarten);
|
|
||||||
System.out.println("trumpfKarten:");
|
|
||||||
System.out.println(trumpfKarten.getJson());
|
|
||||||
|
|
||||||
if (!farbTischKarten.isEmpty()) {
|
|
||||||
System.out.println("trumpfkarten:");
|
|
||||||
System.out.println(farbTischKarten.getJson());
|
|
||||||
return karten.indexOf(farbTischKarten.getLast());
|
|
||||||
} else {
|
|
||||||
KartenFarbe firstColor = karten.getByIndex(0).getFarbe();
|
|
||||||
KartenListe firstColorCards = kartenNew.removeKarten(firstColor);
|
|
||||||
|
|
||||||
System.out.println("firstcolor:");
|
|
||||||
System.out.println(firstColorCards.getJson());
|
|
||||||
|
|
||||||
return karten.indexOf(firstColorCards.getLast());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract Karte welcheKarteSpielIch(
|
|
||||||
boolean istSpieler,
|
|
||||||
KartenListe gespielteKarten,
|
|
||||||
KartenListe meineHand,
|
|
||||||
KartenListe tischKarten);
|
|
||||||
|
|
||||||
public KartenListe getTrumpfKarten() {
|
|
||||||
return trumpfKarten;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isTrumpf(Karte card) {
|
|
||||||
return trumpfKarten.containsKarte(card);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KartenListe getFarbKarten() {
|
|
||||||
return farbKarten;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package org.schafkopf.spielcontroller;
|
|
||||||
|
|
||||||
import org.schafkopf.karte.Karte;
|
|
||||||
import org.schafkopf.karte.KartenFarbe;
|
|
||||||
import org.schafkopf.karte.KartenListe;
|
|
||||||
import org.schafkopf.karte.KartenSymbol;
|
|
||||||
import org.schafkopf.karte.KartenUtil;
|
|
||||||
|
|
||||||
/** SpielController that has the standard Card Deck for Sauspiel, Bettel und Co. */
|
|
||||||
public abstract class StandardController extends SpielController {
|
|
||||||
|
|
||||||
StandardController(int activePlayer) {
|
|
||||||
super(activePlayer);
|
|
||||||
KartenListe kartenList = KartenUtil.initializeSchafKopfCardDeck();
|
|
||||||
KartenListe herzKarten = kartenList.getKarten(KartenFarbe.HERZ);
|
|
||||||
herzKarten.removeKarten(KartenSymbol.UNTER);
|
|
||||||
herzKarten.removeKarten(KartenSymbol.OBER);
|
|
||||||
|
|
||||||
herzKarten.addKarten(kartenList.getKarten(KartenSymbol.UNTER));
|
|
||||||
herzKarten.addKarten(kartenList.getKarten(KartenSymbol.OBER));
|
|
||||||
|
|
||||||
kartenList.removeKarten(herzKarten);
|
|
||||||
|
|
||||||
this.trumpfKarten = new KartenListe(herzKarten);
|
|
||||||
this.farbKarten = new KartenListe(kartenList);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract Karte welcheKarteSpielIch(
|
|
||||||
boolean istSpieler,
|
|
||||||
KartenListe gespielteKarten,
|
|
||||||
KartenListe meineHand,
|
|
||||||
KartenListe tischKarten);
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package org.schafkopf.spielcontroller;
|
|
||||||
|
|
||||||
import org.schafkopf.karte.KartenListe;
|
|
||||||
import org.schafkopf.karte.KartenSymbol;
|
|
||||||
import org.schafkopf.karte.KartenUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SpielController that implements Logic of a Wenz Game.
|
|
||||||
*/
|
|
||||||
public class WenzController extends GeierWenzController {
|
|
||||||
/**
|
|
||||||
* Create instance of Wenz Game.
|
|
||||||
*/
|
|
||||||
public WenzController(int activePlayer) {
|
|
||||||
super(activePlayer);
|
|
||||||
this.activePlayer = activePlayer;
|
|
||||||
KartenListe kartenList = KartenUtil.initializeSchafKopfCardDeck();
|
|
||||||
KartenListe unterKarten = kartenList.getKarten(KartenSymbol.UNTER);
|
|
||||||
|
|
||||||
kartenList.removeKarten(unterKarten);
|
|
||||||
|
|
||||||
this.trumpfKarten = new KartenListe(unterKarten);
|
|
||||||
this.farbKarten = new KartenListe(kartenList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package de.heiserer
|
||||||
|
|
||||||
|
import card.CardList
|
||||||
|
import card.CardToolkit
|
||||||
|
import de.heiserer.cards.*
|
||||||
|
import de.heiserer.player.Player
|
||||||
|
|
||||||
|
class SchafkopfGameController(palyers: List<Player>, private val messager: SchafkopfMessager) {
|
||||||
|
private val players = palyers
|
||||||
|
|
||||||
|
private lateinit var gameType: GameType
|
||||||
|
private val playedCards: CardList = UnsortedCardList()
|
||||||
|
|
||||||
|
fun playRound(startingOffset: Int = 0){
|
||||||
|
serveCards()
|
||||||
|
|
||||||
|
gameType = GameType.SAU_SPIEL
|
||||||
|
players.forEach { it.sortCards(gameType) }
|
||||||
|
|
||||||
|
var startingPlayer: Player = players[startingOffset % 4]
|
||||||
|
|
||||||
|
for(i in 0 until 8){
|
||||||
|
startingPlayer = playTrick(startingPlayer)
|
||||||
|
messager.sendPlayerWonTrick(startingPlayer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun playTrick(startingPlayer: Player): Player{
|
||||||
|
val tableCards = UnsortedCardList()
|
||||||
|
|
||||||
|
for(i in 0 until 4){
|
||||||
|
val currentPlayer = calculatePlayerOffset(startingPlayer, i)
|
||||||
|
messager.sendPlayerTurn(currentPlayer)
|
||||||
|
val card = currentPlayer.playCard(tableCards, gameType)
|
||||||
|
messager.sendCardPlayed(currentPlayer, card)
|
||||||
|
tableCards.add(card)
|
||||||
|
messager.sendTableCards(tableCards.getCopyOfCards())
|
||||||
|
}
|
||||||
|
|
||||||
|
playedCards.add(tableCards)
|
||||||
|
|
||||||
|
val trickOffset = CardToolkit.whoTricks(gameType, tableCards)
|
||||||
|
return calculatePlayerOffset(startingPlayer, trickOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun calculatePlayerOffset(startingPlayer: Player, i: Int): Player = players[(players.indexOf(startingPlayer) + i) % 4]
|
||||||
|
|
||||||
|
private fun serveCards(){
|
||||||
|
val deck = UnsortedCardList(true)
|
||||||
|
deck.shuffle()
|
||||||
|
|
||||||
|
for(i in 0 until 4){
|
||||||
|
val playerCards = UnsortedCardList()
|
||||||
|
|
||||||
|
for(j in 0 until 8){
|
||||||
|
playerCards.add(deck.removeLast())
|
||||||
|
}
|
||||||
|
|
||||||
|
players[i % 4].serveCards(playerCards)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package de.heiserer
|
||||||
|
|
||||||
|
import de.heiserer.cards.Card
|
||||||
|
import de.heiserer.player.Player
|
||||||
|
|
||||||
|
interface SchafkopfMessager {
|
||||||
|
fun sendPlayerTurn(player: Player)
|
||||||
|
fun sendPlayerWonTrick(player: Player)
|
||||||
|
fun sendPlayerWonGame(player: Player)
|
||||||
|
fun sendCardPlayed(player: Player, card: Card)
|
||||||
|
fun sendTableCards(cards: List<Card>)
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package de.heiserer.cards
|
||||||
|
|
||||||
|
class CardAlreadyAddedException(message: String, val card: Card) : RuntimeException(message) {
|
||||||
|
|
||||||
|
// You can add additional constructors or methods if needed, but for now, this is sufficient to handle the scenario of adding a card that's already in the list.
|
||||||
|
}
|
||||||
25
Backend/schafkopf-shared/src/main/kotlin/card/CardList.kt
Normal file
25
Backend/schafkopf-shared/src/main/kotlin/card/CardList.kt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package card
|
||||||
|
|
||||||
|
import de.heiserer.cards.Card
|
||||||
|
import de.heiserer.cards.GameType
|
||||||
|
import de.heiserer.cards.SortedCardList
|
||||||
|
|
||||||
|
interface CardList {
|
||||||
|
fun add(card: Card)
|
||||||
|
fun add(cards: CardList)
|
||||||
|
|
||||||
|
fun remove(card: Card): Card
|
||||||
|
fun remove(cards: CardList)
|
||||||
|
fun removeLast(): Card
|
||||||
|
fun removeFirst(): Card
|
||||||
|
|
||||||
|
fun get(index: Int): Card
|
||||||
|
fun getLast(): Card
|
||||||
|
fun getCopyOfCards(): List<Card>
|
||||||
|
|
||||||
|
fun indexOf(card: Card): Int
|
||||||
|
operator fun contains(card: Card): Boolean
|
||||||
|
fun size(): Int
|
||||||
|
fun print()
|
||||||
|
fun asSortedCardList(type: GameType): SortedCardList
|
||||||
|
}
|
||||||
40
Backend/schafkopf-shared/src/main/kotlin/card/CardToolkit.kt
Normal file
40
Backend/schafkopf-shared/src/main/kotlin/card/CardToolkit.kt
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package card
|
||||||
|
|
||||||
|
import de.heiserer.cards.Card
|
||||||
|
import de.heiserer.cards.GameType
|
||||||
|
import de.heiserer.cards.UnsortedCardList
|
||||||
|
|
||||||
|
class CardToolkit private constructor(private val gameType: GameType) {
|
||||||
|
private val sortedCardList = UnsortedCardList(true).asSortedCardList(gameType)
|
||||||
|
|
||||||
|
fun isTrumpf(card: Card): Boolean = card in sortedCardList.getTrumpf()
|
||||||
|
|
||||||
|
fun whoTricks(cards: CardList): Int {
|
||||||
|
if(cards.size() != 4){
|
||||||
|
throw IllegalArgumentException("Es müssen 4 Karten auf dem Tisch liegen.")
|
||||||
|
}
|
||||||
|
|
||||||
|
val sortedCards = cards.asSortedCardList(gameType)
|
||||||
|
|
||||||
|
if(sortedCards.getTrumpf().size() > 0){
|
||||||
|
return cards.indexOf(sortedCards.getTrumpf().getLast())
|
||||||
|
} else {
|
||||||
|
val firstColor = cards.get(0).color
|
||||||
|
val colorCards = sortedCards.getCardsWithoutTrumpf(firstColor)
|
||||||
|
|
||||||
|
return cards.indexOf(colorCards.getLast())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun isTrumpf(gameType: GameType, card: Card): Boolean {
|
||||||
|
val toolkit = CardToolkit(gameType)
|
||||||
|
return toolkit.isTrumpf(card)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun whoTricks(gameType: GameType, cards: CardList): Int {
|
||||||
|
val toolkit = CardToolkit(gameType)
|
||||||
|
return toolkit.whoTricks(cards)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package de.heiserer.cards
|
||||||
|
|
||||||
|
enum class Card(val color: CardColor, val symbol: CardSymbol) {
|
||||||
|
SCHELL_7(CardColor.SCHELL, CardSymbol.SIEBEN),
|
||||||
|
SCHELL_8(CardColor.SCHELL, CardSymbol.ACHT),
|
||||||
|
SCHELL_9(CardColor.SCHELL, CardSymbol.NEUN),
|
||||||
|
SCHELL_U(CardColor.SCHELL, CardSymbol.UNTER),
|
||||||
|
SCHELL_O(CardColor.SCHELL, CardSymbol.OBER),
|
||||||
|
SCHELL_K(CardColor.SCHELL, CardSymbol.KOENIG),
|
||||||
|
SCHELL_X(CardColor.SCHELL, CardSymbol.ZEHN),
|
||||||
|
SCHELL_A(CardColor.SCHELL, CardSymbol.ASS),
|
||||||
|
HERZ_7(CardColor.HERZ, CardSymbol.SIEBEN),
|
||||||
|
HERZ_8(CardColor.HERZ, CardSymbol.ACHT),
|
||||||
|
HERZ_9(CardColor.HERZ, CardSymbol.NEUN),
|
||||||
|
HERZ_U(CardColor.HERZ, CardSymbol.UNTER),
|
||||||
|
HERZ_O(CardColor.HERZ, CardSymbol.OBER),
|
||||||
|
HERZ_K(CardColor.HERZ, CardSymbol.KOENIG),
|
||||||
|
HERZ_X(CardColor.HERZ, CardSymbol.ZEHN),
|
||||||
|
HERZ_A(CardColor.HERZ, CardSymbol.ASS),
|
||||||
|
BLATT_7(CardColor.BLATT, CardSymbol.SIEBEN),
|
||||||
|
BLATT_8(CardColor.BLATT, CardSymbol.ACHT),
|
||||||
|
BLATT_9(CardColor.BLATT, CardSymbol.NEUN),
|
||||||
|
BLATT_U(CardColor.BLATT, CardSymbol.UNTER),
|
||||||
|
BLATT_O(CardColor.BLATT, CardSymbol.OBER),
|
||||||
|
BLATT_K(CardColor.BLATT, CardSymbol.KOENIG),
|
||||||
|
BLATT_X(CardColor.BLATT, CardSymbol.ZEHN),
|
||||||
|
BLATT_A(CardColor.BLATT, CardSymbol.ASS),
|
||||||
|
EICHEL_7(CardColor.EICHEL, CardSymbol.SIEBEN),
|
||||||
|
EICHEL_8(CardColor.EICHEL, CardSymbol.ACHT),
|
||||||
|
EICHEL_9(CardColor.EICHEL, CardSymbol.NEUN),
|
||||||
|
EICHEL_U(CardColor.EICHEL, CardSymbol.UNTER),
|
||||||
|
EICHEL_O(CardColor.EICHEL, CardSymbol.OBER),
|
||||||
|
EICHEL_K(CardColor.EICHEL, CardSymbol.KOENIG),
|
||||||
|
EICHEL_X(CardColor.EICHEL, CardSymbol.ZEHN),
|
||||||
|
EICHEL_A(CardColor.EICHEL, CardSymbol.ASS);
|
||||||
|
|
||||||
|
val id = name.lowercase()
|
||||||
|
val displayName = "${color.displayName} ${symbol.displayName}"
|
||||||
|
val points = symbol.value
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class CardColor(val order: Int, val displayName: String) {
|
||||||
|
SCHELL(0,"Schell"),
|
||||||
|
HERZ(1,"Herz"),
|
||||||
|
BLATT(2,"Blatt"),
|
||||||
|
EICHEL(3,"Eichel")
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class CardSymbol(val order: Int, val displayName: String, val value: Int) {
|
||||||
|
SIEBEN(0,"7", 0),
|
||||||
|
ACHT(1,"8", 0),
|
||||||
|
NEUN(2,"9", 0),
|
||||||
|
UNTER(3,"Unter", 2),
|
||||||
|
OBER(4,"Ober", 3),
|
||||||
|
KOENIG(5,"König", 4),
|
||||||
|
ZEHN(6,"10", 10),
|
||||||
|
ASS(7,"Ass", 11)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class GameType(val color: CardColor?, val symbol: CardSymbol?){
|
||||||
|
RAMSCH(CardColor.HERZ, null),
|
||||||
|
SAU_SPIEL(CardColor.HERZ, null),
|
||||||
|
BETTEL(CardColor.HERZ, null),
|
||||||
|
|
||||||
|
SCHELL_GEIER(CardColor.SCHELL, CardSymbol.OBER),
|
||||||
|
HERZ_GEIER(CardColor.HERZ, CardSymbol.OBER),
|
||||||
|
BLATT_GEIER(CardColor.BLATT, CardSymbol.OBER),
|
||||||
|
EICHEL_GEIER(CardColor.EICHEL, CardSymbol.OBER),
|
||||||
|
|
||||||
|
SCHELL_WENZ(CardColor.SCHELL, CardSymbol.UNTER),
|
||||||
|
HERZ_WENZ(CardColor.HERZ, CardSymbol.UNTER),
|
||||||
|
BLATT_WENZ(CardColor.BLATT, CardSymbol.UNTER),
|
||||||
|
EICHEL_WENZ(CardColor.EICHEL, CardSymbol.UNTER),
|
||||||
|
|
||||||
|
GEIER(null, CardSymbol.OBER),
|
||||||
|
WENZ(null, CardSymbol.UNTER),
|
||||||
|
|
||||||
|
SCHELL_SOLO(CardColor.SCHELL, null),
|
||||||
|
BLATT_SOLO(CardColor.BLATT, null),
|
||||||
|
EICHEL_SOLO(CardColor.EICHEL, null),
|
||||||
|
HERZ_SOLO(CardColor.HERZ, null),
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package de.heiserer.cards
|
||||||
|
|
||||||
|
class SortedCardList(private val gameType: GameType, withAllCards: Boolean = false) : UnsortedCardList(withAllCards) {
|
||||||
|
override fun add(card: Card) {
|
||||||
|
super.add(card)
|
||||||
|
sort()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCardsWithoutTrumpf(color: CardColor? = null): SortedCardList {
|
||||||
|
val cardsWithoutTrumpf = SortedCardList(gameType)
|
||||||
|
|
||||||
|
color?.let { cardsWithoutTrumpf.add(get(it)) }?: cardsWithoutTrumpf.add(this)
|
||||||
|
try {
|
||||||
|
cardsWithoutTrumpf.remove(getTrumpf())
|
||||||
|
} catch (_: IllegalArgumentException) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return cardsWithoutTrumpf
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTrumpf(): SortedCardList {
|
||||||
|
val trumpf = SortedCardList(gameType)
|
||||||
|
|
||||||
|
gameType.symbol?.let {
|
||||||
|
trumpf.add(get(it))
|
||||||
|
} ?: run {
|
||||||
|
trumpf.add(get(CardSymbol.OBER))
|
||||||
|
trumpf.add(get(CardSymbol.UNTER))
|
||||||
|
}
|
||||||
|
|
||||||
|
gameType.color?.let {
|
||||||
|
try{ trumpf.add(get(it)) } catch (_: CardAlreadyAddedException) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return trumpf
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sort() {
|
||||||
|
super.sortInternal(gameType.symbol, gameType.color)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
package de.heiserer.cards
|
||||||
|
|
||||||
|
import card.CardList
|
||||||
|
|
||||||
|
open class UnsortedCardList(withAllCards: Boolean = false): CardList {
|
||||||
|
private val cards: MutableList<Card> = if(withAllCards){
|
||||||
|
enumValues<Card>().toMutableList()
|
||||||
|
} else {
|
||||||
|
mutableListOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun contains(card: Card): Boolean {
|
||||||
|
return cards.contains(card)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun add(card: Card) {
|
||||||
|
if (card !in cards) {
|
||||||
|
cards.add(card)
|
||||||
|
} else {
|
||||||
|
throw CardAlreadyAddedException("Karte $card is already in the deck.", card)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun add(cards: CardList) {
|
||||||
|
cards.getCopyOfCards().forEach { card -> add(card) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(card: Card) : Card {
|
||||||
|
if (card !in cards) {
|
||||||
|
throw IllegalArgumentException("Karte $card is not in the deck.")
|
||||||
|
} else {
|
||||||
|
cards.remove(card)
|
||||||
|
return card
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove(cards: CardList) {
|
||||||
|
cards.getCopyOfCards().forEach { card -> remove(card) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeFirst(): Card {
|
||||||
|
if (cards.isEmpty()) {
|
||||||
|
throw IllegalArgumentException("Deck is empty.")
|
||||||
|
}
|
||||||
|
return cards.removeFirst()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeLast(): Card {
|
||||||
|
if (cards.isEmpty()) {
|
||||||
|
throw IllegalArgumentException("Deck is empty.")
|
||||||
|
}
|
||||||
|
return cards.removeLast()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun indexOf(card: Card): Int {
|
||||||
|
return cards.indexOf(card)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun get(index: Int): Card {
|
||||||
|
if(index < 0 || index >= cards.size){
|
||||||
|
throw IllegalArgumentException("Index $index is out of bounds.")
|
||||||
|
}
|
||||||
|
return cards[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLast(): Card {
|
||||||
|
return cards[cards.size - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun get(color: CardColor): CardList {
|
||||||
|
val list = UnsortedCardList()
|
||||||
|
cards.forEach { card ->
|
||||||
|
if (card.color == color) {
|
||||||
|
list.add(card)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun get(symbol: CardSymbol): CardList {
|
||||||
|
val list = UnsortedCardList()
|
||||||
|
cards.forEach { card ->
|
||||||
|
if (card.symbol == symbol) {
|
||||||
|
list.add(card)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCopyOfCards(): List<Card> {
|
||||||
|
return cards.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun size(): Int {
|
||||||
|
return cards.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun print() {
|
||||||
|
println("KartenDeck:")
|
||||||
|
cards.forEach { karte ->
|
||||||
|
println(karte.displayName)
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shuffle() {
|
||||||
|
cards.shuffle()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun asSortedCardList(type: GameType): SortedCardList {
|
||||||
|
val sortedList = SortedCardList(type)
|
||||||
|
cards.forEach { card -> sortedList.add(card) }
|
||||||
|
return sortedList
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun sortInternal(symbol: CardSymbol? = null, color: CardColor? = null) {
|
||||||
|
if (symbol != null && symbol != CardSymbol.OBER && symbol != CardSymbol.UNTER) {
|
||||||
|
throw IllegalArgumentException("Symbol $symbol is not accepted. Only OBER and UNTER are allowed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
cards.sortWith(compareBy<Card> {
|
||||||
|
when {
|
||||||
|
symbol == null && it.symbol == CardSymbol.OBER -> 3
|
||||||
|
symbol == null && it.symbol == CardSymbol.UNTER -> 2
|
||||||
|
symbol != null && it.symbol == symbol -> 2
|
||||||
|
color != null && it.color == color -> 1
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
}.thenComparing(compareBy({ it.color.order }, { it.symbol.order })))
|
||||||
|
}
|
||||||
|
}
|
||||||
39
Backend/schafkopf-shared/src/main/kotlin/player/NPCPlayer.kt
Normal file
39
Backend/schafkopf-shared/src/main/kotlin/player/NPCPlayer.kt
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package de.heiserer.player
|
||||||
|
|
||||||
|
import card.CardToolkit
|
||||||
|
import de.heiserer.cards.*
|
||||||
|
|
||||||
|
class NPCPlayer(name: String) : Player(name){
|
||||||
|
override fun playCard(tableCards: UnsortedCardList, gameType: GameType): Card {
|
||||||
|
if(tableCards.size() == 0){
|
||||||
|
return playTrumpf()
|
||||||
|
}
|
||||||
|
|
||||||
|
val firstCard = tableCards.get(0)
|
||||||
|
return if(CardToolkit.isTrumpf(gameType, firstCard)){
|
||||||
|
playTrumpf()
|
||||||
|
} else {
|
||||||
|
playColor(firstCard.color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun playTrumpf(): Card {
|
||||||
|
val trumpfCards = cards.getTrumpf()
|
||||||
|
if (trumpfCards.size() > 0) {
|
||||||
|
val card = trumpfCards.removeLast()
|
||||||
|
return cards.remove(card)
|
||||||
|
} else {
|
||||||
|
return cards.removeFirst()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun playColor(color: CardColor): Card {
|
||||||
|
val colorCards = cards.getCardsWithoutTrumpf(color)
|
||||||
|
if (colorCards.size() > 0) {
|
||||||
|
val card = colorCards.removeLast()
|
||||||
|
return cards.remove(card)
|
||||||
|
} else {
|
||||||
|
return cards.removeFirst()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
Backend/schafkopf-shared/src/main/kotlin/player/Player.kt
Normal file
36
Backend/schafkopf-shared/src/main/kotlin/player/Player.kt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package de.heiserer.player
|
||||||
|
|
||||||
|
import card.CardToolkit
|
||||||
|
import de.heiserer.cards.*
|
||||||
|
|
||||||
|
abstract class Player(private var name: String){
|
||||||
|
protected var cards: SortedCardList = SortedCardList(GameType.SAU_SPIEL)
|
||||||
|
|
||||||
|
fun serveCards(cards: UnsortedCardList){
|
||||||
|
this.cards = cards.asSortedCardList(GameType.SAU_SPIEL)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sortCards(gameType: GameType){
|
||||||
|
cards = cards.asSortedCardList(gameType)
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun playCard(tableCards: UnsortedCardList, gameType: GameType): Card
|
||||||
|
|
||||||
|
fun getName(): String{
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun validateCard(card: Card, tableCards: UnsortedCardList, gameType: GameType): Boolean{
|
||||||
|
if(tableCards.size() == 0){
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
val firstCard = tableCards.get(0)
|
||||||
|
|
||||||
|
return if(CardToolkit.isTrumpf(gameType, firstCard)){
|
||||||
|
CardToolkit.isTrumpf(gameType, card) || cards.getTrumpf().size() == 0
|
||||||
|
} else {
|
||||||
|
card.color == firstCard.color || cards.getCardsWithoutTrumpf(firstCard.color).size() == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
479
Backend/schafkopf-shared/src/test/kotlin/CardListTest.kt
Normal file
479
Backend/schafkopf-shared/src/test/kotlin/CardListTest.kt
Normal file
@@ -0,0 +1,479 @@
|
|||||||
|
import de.heiserer.cards.Card
|
||||||
|
import de.heiserer.cards.CardColor
|
||||||
|
import de.heiserer.cards.GameType
|
||||||
|
import de.heiserer.cards.UnsortedCardList
|
||||||
|
import org.junit.jupiter.api.Assertions.*
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource
|
||||||
|
|
||||||
|
class UnsortedListTest {
|
||||||
|
@Test
|
||||||
|
fun `init with all cards`() {
|
||||||
|
assertEquals(32, UnsortedCardList(true).size())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SortingCardsTest {
|
||||||
|
private val defaultList = listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun gameTypeProvider() = GameType.values().asList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun testCardSorting(expectedCards: List<Card>, gameType: GameType) {
|
||||||
|
// ARRANGE
|
||||||
|
var cards = UnsortedCardList()
|
||||||
|
defaultList.shuffled().forEach {
|
||||||
|
cards.add(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
cards = cards.asSortedCardList(gameType)
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
assertEquals(expectedCards.size, cards.size())
|
||||||
|
expectedCards.forEachIndexed { index, card ->
|
||||||
|
assertEquals(card, cards.get(index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun expectedCardsForGameType(gameType: GameType): List<Card> {
|
||||||
|
return when(gameType) {
|
||||||
|
GameType.RAMSCH -> defaultList
|
||||||
|
GameType.SAU_SPIEL -> defaultList
|
||||||
|
GameType.BETTEL -> defaultList
|
||||||
|
|
||||||
|
GameType.SCHELL_GEIER -> listOf(
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
GameType.HERZ_GEIER -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
GameType.BLATT_GEIER -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
GameType.EICHEL_GEIER -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
|
||||||
|
GameType.SCHELL_WENZ -> listOf(
|
||||||
|
Card.HERZ_O,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
)
|
||||||
|
GameType.HERZ_WENZ -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
Card.HERZ_O,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.EICHEL_U
|
||||||
|
)
|
||||||
|
GameType.BLATT_WENZ -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_O,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
)
|
||||||
|
GameType.EICHEL_WENZ -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_O,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
Card.EICHEL_U
|
||||||
|
)
|
||||||
|
|
||||||
|
GameType.GEIER -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
GameType.WENZ -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_O,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
)
|
||||||
|
|
||||||
|
GameType.SCHELL_SOLO -> listOf(
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
GameType.BLATT_SOLO -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
GameType.EICHEL_SOLO -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
GameType.HERZ_SOLO -> defaultList
|
||||||
|
else -> throw IllegalArgumentException("No expected cards defined for gameType: $gameType")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "test sorting for {0}") // This gives a clear name in the test output
|
||||||
|
@MethodSource("gameTypeProvider")
|
||||||
|
fun `test sorting for all game types`(gameType: GameType) {
|
||||||
|
// ARRANGE
|
||||||
|
val expectedCards = expectedCardsForGameType(gameType)
|
||||||
|
|
||||||
|
// ACT & ASSERT
|
||||||
|
testCardSorting(expectedCards, gameType)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private fun testGetTrumpfCard(expectedCards: List<Card>, gameType: GameType) {
|
||||||
|
// ARRANGE
|
||||||
|
var cards = UnsortedCardList()
|
||||||
|
defaultList.shuffled().forEach {
|
||||||
|
cards.add(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
cards = cards.asSortedCardList(gameType).getTrumpf()
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
expectedCards.forEachIndexed { index, card ->
|
||||||
|
assertEquals(card, cards.get(index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun expectedTrumpfCardsForGameType(gameType: GameType): List<Card> {
|
||||||
|
val herzList = listOf(
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
return when(gameType) {
|
||||||
|
GameType.RAMSCH -> herzList
|
||||||
|
GameType.SAU_SPIEL -> herzList
|
||||||
|
GameType.BETTEL -> herzList
|
||||||
|
|
||||||
|
GameType.SCHELL_GEIER -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
GameType.HERZ_GEIER -> listOf(
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
GameType.BLATT_GEIER -> listOf(
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
GameType.EICHEL_GEIER -> listOf(
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
|
||||||
|
GameType.SCHELL_WENZ -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
)
|
||||||
|
GameType.HERZ_WENZ -> listOf(
|
||||||
|
Card.HERZ_O,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.EICHEL_U
|
||||||
|
)
|
||||||
|
GameType.BLATT_WENZ -> listOf(
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
)
|
||||||
|
GameType.EICHEL_WENZ -> listOf(
|
||||||
|
Card.EICHEL_A,
|
||||||
|
Card.EICHEL_U
|
||||||
|
)
|
||||||
|
|
||||||
|
GameType.GEIER -> listOf(
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
GameType.WENZ -> listOf(
|
||||||
|
Card.EICHEL_U,
|
||||||
|
)
|
||||||
|
|
||||||
|
GameType.SCHELL_SOLO -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
GameType.BLATT_SOLO -> listOf(
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
GameType.EICHEL_SOLO -> listOf(
|
||||||
|
Card.EICHEL_A,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.HERZ_O
|
||||||
|
)
|
||||||
|
GameType.HERZ_SOLO -> herzList
|
||||||
|
else -> throw IllegalArgumentException("No expected cards defined for gameType: $gameType")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "test get Trumpfcards for {0}") // This gives a clear name in the test output
|
||||||
|
@MethodSource("gameTypeProvider")
|
||||||
|
fun `test get Trumpfcards for all game types`(gameType: GameType) {
|
||||||
|
// ARRANGE
|
||||||
|
val expectedCards = expectedTrumpfCardsForGameType(gameType)
|
||||||
|
|
||||||
|
// ACT & ASSERT
|
||||||
|
testGetTrumpfCard(expectedCards, gameType)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private fun testCardsWithoutTrumpf(expectedCards: List<Card>, gameType: GameType) {
|
||||||
|
// ARRANGE
|
||||||
|
var cards = UnsortedCardList()
|
||||||
|
defaultList.shuffled().forEach {
|
||||||
|
cards.add(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
cards = cards.asSortedCardList(gameType).getCardsWithoutTrumpf()
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
expectedCards.forEachIndexed { index, card ->
|
||||||
|
assertEquals(card, cards.get(index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun expectedCardsWithoutTrumpfForGameType(gameType: GameType): List<Card> {
|
||||||
|
val herzList = listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
)
|
||||||
|
return when(gameType) {
|
||||||
|
GameType.RAMSCH -> herzList
|
||||||
|
GameType.SAU_SPIEL -> herzList
|
||||||
|
GameType.BETTEL -> herzList
|
||||||
|
|
||||||
|
GameType.SCHELL_GEIER -> listOf(
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
)
|
||||||
|
GameType.HERZ_GEIER -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
)
|
||||||
|
GameType.BLATT_GEIER -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
)
|
||||||
|
GameType.EICHEL_GEIER -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
)
|
||||||
|
|
||||||
|
GameType.SCHELL_WENZ -> listOf(
|
||||||
|
Card.HERZ_O,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
)
|
||||||
|
GameType.HERZ_WENZ -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
)
|
||||||
|
GameType.BLATT_WENZ -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_O,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
)
|
||||||
|
GameType.EICHEL_WENZ -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_O,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
)
|
||||||
|
|
||||||
|
GameType.GEIER -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_U,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
)
|
||||||
|
GameType.WENZ -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_O,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
)
|
||||||
|
|
||||||
|
GameType.SCHELL_SOLO -> listOf(
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
)
|
||||||
|
GameType.BLATT_SOLO -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.EICHEL_A,
|
||||||
|
)
|
||||||
|
GameType.EICHEL_SOLO -> listOf(
|
||||||
|
Card.SCHELL_7,
|
||||||
|
Card.SCHELL_K,
|
||||||
|
Card.HERZ_X,
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
)
|
||||||
|
GameType.HERZ_SOLO -> herzList
|
||||||
|
else -> throw IllegalArgumentException("No expected cards defined for gameType: $gameType")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "test get Cards without Trumpf for {0}") // This gives a clear name in the test output
|
||||||
|
@MethodSource("gameTypeProvider")
|
||||||
|
fun `test get Cards without Trumpf for all game types`(gameType: GameType) {
|
||||||
|
// ARRANGE
|
||||||
|
val expectedCards = expectedCardsWithoutTrumpfForGameType(gameType)
|
||||||
|
|
||||||
|
// ACT & ASSERT
|
||||||
|
testCardsWithoutTrumpf(expectedCards, gameType)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test get Cards without Trumpf with Color`() {
|
||||||
|
// ARRANGE
|
||||||
|
var cards = UnsortedCardList()
|
||||||
|
defaultList.shuffled().forEach {
|
||||||
|
cards.add(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
val expectedCards = listOf(
|
||||||
|
Card.BLATT_8,
|
||||||
|
Card.BLATT_9,
|
||||||
|
)
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
cards = cards.asSortedCardList(GameType.SAU_SPIEL).getCardsWithoutTrumpf(CardColor.BLATT)
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
expectedCards.forEachIndexed { index, card ->
|
||||||
|
assertEquals(card, cards.get(index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
Backend/schafkopf-shared/src/test/kotlin/CardToolkitTest.kt
Normal file
26
Backend/schafkopf-shared/src/test/kotlin/CardToolkitTest.kt
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import card.CardToolkit
|
||||||
|
import de.heiserer.cards.*
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
class CardToolkitTest {
|
||||||
|
@Test
|
||||||
|
fun `test isTrumpf`() {
|
||||||
|
// ASSERT AND ACT
|
||||||
|
assertEquals(true, CardToolkit.isTrumpf(GameType.SAU_SPIEL, Card.HERZ_O))
|
||||||
|
assertEquals(false, CardToolkit.isTrumpf(GameType.SAU_SPIEL, Card.SCHELL_7))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whoTricks() {
|
||||||
|
// ARRANGE
|
||||||
|
val cards = UnsortedCardList()
|
||||||
|
cards.add(Card.SCHELL_7)
|
||||||
|
cards.add(Card.SCHELL_8)
|
||||||
|
cards.add(Card.SCHELL_K)
|
||||||
|
cards.add(Card.SCHELL_O)
|
||||||
|
|
||||||
|
// ASSERT AND ACT
|
||||||
|
assertEquals(3, CardToolkit.whoTricks(GameType.SAU_SPIEL, cards))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user