From 4a167bf3b472b6e6c69b5e6d9eca790bac3aa583 Mon Sep 17 00:00:00 2001 From: Valentin Heiserer Date: Wed, 17 Jul 2024 00:25:50 +0200 Subject: [PATCH] init kotlin backend --- Backend/pom.xml | 281 +++++------ Backend/schafkopf-client/.gitignore | 38 ++ Backend/schafkopf-client/pom.xml | 198 +++++--- .../java/org/schafkopf/BackendServer.java | 139 ----- .../schafkopf/DedicatedServerConnection.java | 117 ----- .../java/org/schafkopf/FrontendEndpoint.java | 72 --- .../schafkopf/FrontendEndpointCreator.java | 25 - .../java/org/schafkopf/HeartbeatSender.java | 33 -- .../java/org/schafkopf/SchafkopfClient.java | 122 ----- .../org/schafkopf/cardreader/CardReader.java | 42 -- .../schafkopf/cardreader/UsbCardReader.java | 99 ---- .../org/schafkopf/player/LocalPlayer.java | 60 --- .../kotlin/de/heiserer/SchafkopfClient.kt | 19 + .../kotlin/de/heiserer/plugins/Routing.kt | 13 + Backend/schafkopf-server/pom.xml | 55 -- .../java/org/schafkopf/DedicatedServer.java | 167 ------ .../org/schafkopf/HealthCheckServlet.java | 23 - .../java/org/schafkopf/OnlineGameSession.java | 155 ------ .../schafkopf/SchafkopfClientConnection.java | 209 -------- .../SchafkopfClientConnectionCreator.java | 24 - Backend/schafkopf-shared/pom.xml | 97 +++- .../java/org/schafkopf/BaseGameSession.java | 51 -- .../main/java/org/schafkopf/GameState.java | 101 ---- .../java/org/schafkopf/MessageListener.java | 9 - .../java/org/schafkopf/MessageSender.java | 11 - .../main/java/org/schafkopf/Schafkopf.java | 116 ----- .../org/schafkopf/SchafkopfException.java | 62 --- .../java/org/schafkopf/SchafkopfMessage.java | 156 ------ .../main/java/org/schafkopf/Spielablauf.java | 90 ---- .../main/java/org/schafkopf/karte/Karte.java | 87 ---- .../java/org/schafkopf/karte/KartenFarbe.java | 21 - .../java/org/schafkopf/karte/KartenListe.java | 238 --------- .../org/schafkopf/karte/KartenSymbol.java | 38 -- .../java/org/schafkopf/karte/KartenUtil.java | 82 --- .../java/org/schafkopf/player/BotPlayer.java | 69 --- .../org/schafkopf/player/OnlinePlayer.java | 79 --- .../java/org/schafkopf/player/Player.java | 43 -- .../spielcontroller/FarbGeierController.java | 36 -- .../spielcontroller/FarbSoloController.java | 40 -- .../spielcontroller/FarbWenzController.java | 36 -- .../spielcontroller/GeierController.java | 24 - .../spielcontroller/GeierWenzController.java | 20 - .../spielcontroller/SauSpielController.java | 58 --- .../spielcontroller/SoloController.java | 19 - .../spielcontroller/SpielController.java | 133 ----- .../spielcontroller/StandardController.java | 33 -- .../spielcontroller/WenzController.java | 25 - .../main/kotlin/SchafkopfGameController.kt | 113 +++++ .../src/main/kotlin/Schafkopfkarte.kt | 82 +++ .../src/main/kotlin/UnsortedCardList.kt | 180 +++++++ .../src/test/kotlin/CardListTest.kt | 476 ++++++++++++++++++ 51 files changed, 1284 insertions(+), 3232 deletions(-) create mode 100644 Backend/schafkopf-client/.gitignore delete mode 100644 Backend/schafkopf-client/src/main/java/org/schafkopf/BackendServer.java delete mode 100644 Backend/schafkopf-client/src/main/java/org/schafkopf/DedicatedServerConnection.java delete mode 100644 Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpoint.java delete mode 100644 Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpointCreator.java delete mode 100644 Backend/schafkopf-client/src/main/java/org/schafkopf/HeartbeatSender.java delete mode 100644 Backend/schafkopf-client/src/main/java/org/schafkopf/SchafkopfClient.java delete mode 100644 Backend/schafkopf-client/src/main/java/org/schafkopf/cardreader/CardReader.java delete mode 100644 Backend/schafkopf-client/src/main/java/org/schafkopf/cardreader/UsbCardReader.java delete mode 100644 Backend/schafkopf-client/src/main/java/org/schafkopf/player/LocalPlayer.java create mode 100644 Backend/schafkopf-client/src/main/kotlin/de/heiserer/SchafkopfClient.kt create mode 100644 Backend/schafkopf-client/src/main/kotlin/de/heiserer/plugins/Routing.kt delete mode 100644 Backend/schafkopf-server/pom.xml delete mode 100644 Backend/schafkopf-server/src/main/java/org/schafkopf/DedicatedServer.java delete mode 100644 Backend/schafkopf-server/src/main/java/org/schafkopf/HealthCheckServlet.java delete mode 100644 Backend/schafkopf-server/src/main/java/org/schafkopf/OnlineGameSession.java delete mode 100644 Backend/schafkopf-server/src/main/java/org/schafkopf/SchafkopfClientConnection.java delete mode 100644 Backend/schafkopf-server/src/main/java/org/schafkopf/SchafkopfClientConnectionCreator.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/BaseGameSession.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/GameState.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageListener.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageSender.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/Schafkopf.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/SchafkopfException.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/SchafkopfMessage.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/Spielablauf.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/Karte.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenFarbe.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenListe.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenSymbol.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenUtil.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/player/BotPlayer.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/player/OnlinePlayer.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/player/Player.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbGeierController.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbSoloController.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbWenzController.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/GeierController.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/GeierWenzController.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SauSpielController.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SoloController.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SpielController.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/StandardController.java delete mode 100644 Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/WenzController.java create mode 100644 Backend/schafkopf-shared/src/main/kotlin/SchafkopfGameController.kt create mode 100644 Backend/schafkopf-shared/src/main/kotlin/Schafkopfkarte.kt create mode 100644 Backend/schafkopf-shared/src/main/kotlin/UnsortedCardList.kt create mode 100644 Backend/schafkopf-shared/src/test/kotlin/CardListTest.kt diff --git a/Backend/pom.xml b/Backend/pom.xml index 6322fd7..c8d0a03 100644 --- a/Backend/pom.xml +++ b/Backend/pom.xml @@ -1,145 +1,142 @@ - + + 4.0.0 + de.heiserer + schafkopf-2 + 0.0.1 + pom + schafkopf-2 + schafkopf-2 + + schafkopf-shared + + + 2.3.12 + official + 2.0.0 + 1.4.14 + 2.0.9 + UTF-8 + true + de.heiserer.ApplicationKt + + + + + + io.ktor + ktor-server-core-jvm + ${ktor_version} + + + io.ktor + ktor-server-netty-jvm + ${ktor_version} + + + ch.qos.logback + logback-classic + ${logback_version} + + + org.slf4j + slf4j-api + ${slf4j_version} + + + io.ktor + ktor-server-tests-jvm + ${ktor_version} + test + + + org.jetbrains.kotlin + kotlin-test-junit + ${kotlin_version} + test + + + org.jetbrains.kotlinx + kotlinx-coroutines-debug + 1.6.4 + test + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + ${project.basedir}/src/main/resources + + - schafkopf-backend-java - pom - - schafkopf-client - schafkopf-server - schafkopf-shared - - org.schafkopf - 4.0.0 - 1.0-SNAPSHOT - - - 3.5.1 - 21 - 21 - 2.4.0 - UTF-8 - 1.7.32 - - - - - schafkopf-backend-build - - - - maven-clean-plugin - 3.1.0 - - - clean-initial - pre-clean - - clean - - - - - - maven-checkstyle-plugin - - - - google_checks.xml - true - true - warning - - - check - - validate - validate - - - org.apache.maven.plugins - 3.3.1 - - - maven-jar-plugin - - - - org.schafkopf.DedicatedServer - - - - 3.3.0 - - - maven-compiler-plugin - - true - true - false - - 3.11.0 - - - - - - - - com.fazecast - jSerialComm - 2.6.0 - - - - slf4j-api - org.slf4j - ${slf4j.version} - - - slf4j-simple - org.slf4j - ${slf4j.version} - - - - websocket-jetty-api - org.eclipse.jetty.websocket - 11.0.15 - - - - websocket-jetty-server - org.eclipse.jetty.websocket - 11.0.20 - - - jetty-servlets - org.eclipse.jetty - 11.0.19 - - - - websocket-jetty-client - org.eclipse.jetty.websocket - 11.0.15 - - - gson - com.google.code.gson - 2.10.1 - - - dotenv-java - io.github.cdimascio - 3.0.0 - - - - org.json - json - 20240303 - - + + + kotlin-maven-plugin + org.jetbrains.kotlin + ${kotlin_version} + + 1.8 + + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + + java + + + + + ${main.class} + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.6 + + + jar-with-dependencies + + + + true + ${main.class} + + + + + + assemble-all + package + + single + + + + + + \ No newline at end of file diff --git a/Backend/schafkopf-client/.gitignore b/Backend/schafkopf-client/.gitignore new file mode 100644 index 0000000..7fc7f7a --- /dev/null +++ b/Backend/schafkopf-client/.gitignore @@ -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 \ No newline at end of file diff --git a/Backend/schafkopf-client/pom.xml b/Backend/schafkopf-client/pom.xml index 61979e7..a5660ea 100644 --- a/Backend/schafkopf-client/pom.xml +++ b/Backend/schafkopf-client/pom.xml @@ -1,60 +1,144 @@ - - 4.0.0 - - org.schafkopf - schafkopf-backend-java - 1.0-SNAPSHOT - + + 4.0.0 + de.heiserer + schafkopf-client + 0.0.1 + schafkopf-client + schafkopf-client + + 2.3.12 + official + 2.0.0 + 1.4.14 + 2.0.9 + UTF-8 + true + de.heiserer.SchafkopfClientKt + + + + + + io.ktor + ktor-server-core-jvm + ${ktor_version} + + + io.ktor + ktor-server-netty-jvm + ${ktor_version} + + + ch.qos.logback + logback-classic + ${logback_version} + + + org.slf4j + slf4j-api + ${slf4j_version} + + + io.ktor + ktor-server-tests-jvm + ${ktor_version} + test + + + org.jetbrains.kotlin + kotlin-test-junit + ${kotlin_version} + test + + + org.jetbrains.kotlinx + kotlinx-coroutines-debug + 1.6.4 + test + + + de.heiserer + schafkopf-shared + 0.0.1 + compile + + + + src/main/kotlin + ${project.basedir}/src/test/kotlin + + + src/main/resources + + - schafkopf-client - - - 21 - 21 - UTF-8 - - - - org.schafkopf - schafkopf-shared - 1.0-SNAPSHOT - compile - - - - schafkopf-client-build - - - src/main/resources - - - - - maven-assembly-plugin - 3.3.0 - - - jar-with-dependencies - - - - org.schafkopf.SchafkopfClient - - - - - - make-assembly - package - - single - - - - - - + + + kotlin-maven-plugin + org.jetbrains.kotlin + ${kotlin_version} + + 1.8 + + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + + java + + + + + ${main.class} + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.6 + + + jar-with-dependencies + + + + true + ${main.class} + + + + + + assemble-all + package + + single + + + + + + \ No newline at end of file diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/BackendServer.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/BackendServer.java deleted file mode 100644 index 200608c..0000000 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/BackendServer.java +++ /dev/null @@ -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 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 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); - } -} diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/DedicatedServerConnection.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/DedicatedServerConnection.java deleted file mode 100644 index be97e0f..0000000 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/DedicatedServerConnection.java +++ /dev/null @@ -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(); - } -} diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpoint.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpoint.java deleted file mode 100644 index 79d2836..0000000 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpoint.java +++ /dev/null @@ -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(); - } - } -} diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpointCreator.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpointCreator.java deleted file mode 100644 index f2f9620..0000000 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/FrontendEndpointCreator.java +++ /dev/null @@ -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); - } -} diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/HeartbeatSender.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/HeartbeatSender.java deleted file mode 100644 index cb52c67..0000000 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/HeartbeatSender.java +++ /dev/null @@ -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); - } -} diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/SchafkopfClient.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/SchafkopfClient.java deleted file mode 100644 index e4fbf41..0000000 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/SchafkopfClient.java +++ /dev/null @@ -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); - } - } -} - diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/cardreader/CardReader.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/cardreader/CardReader.java deleted file mode 100644 index 97cc6f6..0000000 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/cardreader/CardReader.java +++ /dev/null @@ -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(); - } -} diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/cardreader/UsbCardReader.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/cardreader/UsbCardReader.java deleted file mode 100644 index 9d20af9..0000000 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/cardreader/UsbCardReader.java +++ /dev/null @@ -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"); - } - } -} diff --git a/Backend/schafkopf-client/src/main/java/org/schafkopf/player/LocalPlayer.java b/Backend/schafkopf-client/src/main/java/org/schafkopf/player/LocalPlayer.java deleted file mode 100644 index 18f5928..0000000 --- a/Backend/schafkopf-client/src/main/java/org/schafkopf/player/LocalPlayer.java +++ /dev/null @@ -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); - } -} diff --git a/Backend/schafkopf-client/src/main/kotlin/de/heiserer/SchafkopfClient.kt b/Backend/schafkopf-client/src/main/kotlin/de/heiserer/SchafkopfClient.kt new file mode 100644 index 0000000..b246dae --- /dev/null +++ b/Backend/schafkopf-client/src/main/kotlin/de/heiserer/SchafkopfClient.kt @@ -0,0 +1,19 @@ +package de.heiserer + +import de.heiserer.plugins.* +import io.ktor.server.application.* +import io.ktor.server.engine.* +import io.ktor.server.netty.* + +fun main() { + val test = SchafkopfGameController() + + test.playRound() + + embeddedServer(Netty, port = 8080, host = "0.0.0.0", module = Application::module) + .start(wait = true) +} + +fun Application.module() { + configureRouting() +} diff --git a/Backend/schafkopf-client/src/main/kotlin/de/heiserer/plugins/Routing.kt b/Backend/schafkopf-client/src/main/kotlin/de/heiserer/plugins/Routing.kt new file mode 100644 index 0000000..144f68b --- /dev/null +++ b/Backend/schafkopf-client/src/main/kotlin/de/heiserer/plugins/Routing.kt @@ -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!") + } + } +} diff --git a/Backend/schafkopf-server/pom.xml b/Backend/schafkopf-server/pom.xml deleted file mode 100644 index acd62be..0000000 --- a/Backend/schafkopf-server/pom.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - 4.0.0 - - org.schafkopf - schafkopf-backend-java - 1.0-SNAPSHOT - - - schafkopf-server - - - 21 - 21 - UTF-8 - - - - org.schafkopf - schafkopf-shared - 1.0-SNAPSHOT - compile - - - - schafkopf-server-build - - - maven-assembly-plugin - 3.3.0 - - - jar-with-dependencies - - - - org.schafkopf.DedicatedServer - - - - - - make-assembly - package - - single - - - - - - - \ No newline at end of file diff --git a/Backend/schafkopf-server/src/main/java/org/schafkopf/DedicatedServer.java b/Backend/schafkopf-server/src/main/java/org/schafkopf/DedicatedServer.java deleted file mode 100644 index 22273d0..0000000 --- a/Backend/schafkopf-server/src/main/java/org/schafkopf/DedicatedServer.java +++ /dev/null @@ -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 clientConnections = new ArrayList<>(); - - private final List 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 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 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); - } -} diff --git a/Backend/schafkopf-server/src/main/java/org/schafkopf/HealthCheckServlet.java b/Backend/schafkopf-server/src/main/java/org/schafkopf/HealthCheckServlet.java deleted file mode 100644 index 828af19..0000000 --- a/Backend/schafkopf-server/src/main/java/org/schafkopf/HealthCheckServlet.java +++ /dev/null @@ -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!"); - } -} diff --git a/Backend/schafkopf-server/src/main/java/org/schafkopf/OnlineGameSession.java b/Backend/schafkopf-server/src/main/java/org/schafkopf/OnlineGameSession.java deleted file mode 100644 index 665ac3c..0000000 --- a/Backend/schafkopf-server/src/main/java/org/schafkopf/OnlineGameSession.java +++ /dev/null @@ -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)); - } -} diff --git a/Backend/schafkopf-server/src/main/java/org/schafkopf/SchafkopfClientConnection.java b/Backend/schafkopf-server/src/main/java/org/schafkopf/SchafkopfClientConnection.java deleted file mode 100644 index ae6590e..0000000 --- a/Backend/schafkopf-server/src/main/java/org/schafkopf/SchafkopfClientConnection.java +++ /dev/null @@ -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)); - } -} diff --git a/Backend/schafkopf-server/src/main/java/org/schafkopf/SchafkopfClientConnectionCreator.java b/Backend/schafkopf-server/src/main/java/org/schafkopf/SchafkopfClientConnectionCreator.java deleted file mode 100644 index ea46e4e..0000000 --- a/Backend/schafkopf-server/src/main/java/org/schafkopf/SchafkopfClientConnectionCreator.java +++ /dev/null @@ -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); - } -} diff --git a/Backend/schafkopf-shared/pom.xml b/Backend/schafkopf-shared/pom.xml index 5ba1c2c..3f45f11 100644 --- a/Backend/schafkopf-shared/pom.xml +++ b/Backend/schafkopf-shared/pom.xml @@ -1,20 +1,89 @@ - 4.0.0 - - org.schafkopf - schafkopf-backend-java - 1.0-SNAPSHOT - + 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"> + 4.0.0 + + de.heiserer + schafkopf-2 + 0.0.1 + - schafkopf-shared + schafkopf-shared - - 21 - 21 - UTF-8 - + + UTF-8 + official + 1.8 + + + + + mavenCentral + https://repo1.maven.org/maven2/ + + + + + src/main/kotlin + src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + maven-surefire-plugin + 2.22.2 + + + maven-failsafe-plugin + 2.22.2 + + + org.codehaus.mojo + exec-maven-plugin + + MainKt + + + + + + + + org.jetbrains.kotlin + kotlin-test-junit5 + 2.0.0 + test + + + org.junit.jupiter + junit-jupiter + 5.10.0 + test + + + org.jetbrains.kotlin + kotlin-stdlib + 2.0.0 + + \ No newline at end of file diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/BaseGameSession.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/BaseGameSession.java deleted file mode 100644 index 8d35337..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/BaseGameSession.java +++ /dev/null @@ -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 players; - - void startGame(List 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(); - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/GameState.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/GameState.java deleted file mode 100644 index ea3725e..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/GameState.java +++ /dev/null @@ -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; - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageListener.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageListener.java deleted file mode 100644 index 03497b5..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageListener.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.schafkopf; - -/** - * Class that represents one Frontend Connection. - */ -public interface MessageListener { - - void receiveMessage(String message); -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageSender.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageSender.java deleted file mode 100644 index a8425af..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/MessageSender.java +++ /dev/null @@ -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); -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/Schafkopf.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/Schafkopf.java deleted file mode 100644 index 038a1ac..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/Schafkopf.java +++ /dev/null @@ -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; - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/SchafkopfException.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/SchafkopfException.java deleted file mode 100644 index dc4f775..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/SchafkopfException.java +++ /dev/null @@ -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 - } -} - diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/SchafkopfMessage.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/SchafkopfMessage.java deleted file mode 100644 index 321f664..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/SchafkopfMessage.java +++ /dev/null @@ -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 - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/Spielablauf.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/Spielablauf.java deleted file mode 100644 index 581d04e..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/Spielablauf.java +++ /dev/null @@ -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; - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/Karte.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/Karte.java deleted file mode 100644 index c7de614..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/Karte.java +++ /dev/null @@ -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; - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenFarbe.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenFarbe.java deleted file mode 100644 index cfd7e84..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenFarbe.java +++ /dev/null @@ -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; - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenListe.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenListe.java deleted file mode 100644 index 09639f5..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenListe.java +++ /dev/null @@ -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 kartenListe; - - public KartenListe() { - this.kartenListe = new ArrayList<>(); - } - - public KartenListe(KartenListe liste) { - this.kartenListe = new ArrayList<>(liste.getKartenListe()); - } - - public List 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(); - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenSymbol.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenSymbol.java deleted file mode 100644 index b8e2c91..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenSymbol.java +++ /dev/null @@ -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; - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenUtil.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenUtil.java deleted file mode 100644 index 199008f..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/karte/KartenUtil.java +++ /dev/null @@ -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; - }; - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/BotPlayer.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/BotPlayer.java deleted file mode 100644 index afd14a7..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/BotPlayer.java +++ /dev/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 - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/OnlinePlayer.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/OnlinePlayer.java deleted file mode 100644 index 1da2a92..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/OnlinePlayer.java +++ /dev/null @@ -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 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); - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/Player.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/Player.java deleted file mode 100644 index 9e0dc50..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/player/Player.java +++ /dev/null @@ -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; - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbGeierController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbGeierController.java deleted file mode 100644 index 0cdb3b5..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbGeierController.java +++ /dev/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 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; - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbSoloController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbSoloController.java deleted file mode 100644 index 4eba882..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbSoloController.java +++ /dev/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; - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbWenzController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbWenzController.java deleted file mode 100644 index 50788c5..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/FarbWenzController.java +++ /dev/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; - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/GeierController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/GeierController.java deleted file mode 100644 index 67888c0..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/GeierController.java +++ /dev/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); - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/GeierWenzController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/GeierWenzController.java deleted file mode 100644 index 5003859..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/GeierWenzController.java +++ /dev/null @@ -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; - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SauSpielController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SauSpielController.java deleted file mode 100644 index a402257..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SauSpielController.java +++ /dev/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; - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SoloController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SoloController.java deleted file mode 100644 index 200d46c..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SoloController.java +++ /dev/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; - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SpielController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SpielController.java deleted file mode 100644 index e43cf1e..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/SpielController.java +++ /dev/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; - } -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/StandardController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/StandardController.java deleted file mode 100644 index 162c193..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/StandardController.java +++ /dev/null @@ -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); -} diff --git a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/WenzController.java b/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/WenzController.java deleted file mode 100644 index 70cff99..0000000 --- a/Backend/schafkopf-shared/src/main/java/org/schafkopf/spielcontroller/WenzController.java +++ /dev/null @@ -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); - } -} diff --git a/Backend/schafkopf-shared/src/main/kotlin/SchafkopfGameController.kt b/Backend/schafkopf-shared/src/main/kotlin/SchafkopfGameController.kt new file mode 100644 index 0000000..f893c11 --- /dev/null +++ b/Backend/schafkopf-shared/src/main/kotlin/SchafkopfGameController.kt @@ -0,0 +1,113 @@ +package de.heiserer + +class SchafkopfGameController { + private val players = listOf(NPCPlayer("NPC 1"), NPCPlayer("NPC 2"), NPCPlayer("NPC 3"), NPCPlayer("NPC 4")) + + 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) + } + } + + private fun playTrick(startingPlayer: Player): Player{ + val tableCards = UnsortedCardList() + + for(i in 0 until 4){ + val currentPlayer = calculatePlayerOffset(startingPlayer, i) + currentPlayer.printName() + + val card = currentPlayer.playCard(tableCards, gameType) + tableCards.add(card) + } + + tableCards.print() + + return startingPlayer + } + + 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) + } + } +} + +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) + println("Spieler $name hat folgende Karten sortiert:") + cards.print() + println() + println("Trumpf:") + cards.getTrumpf().print() + println() + println("Farbe:") + cards.getCardsWithoutTrumpf().print() + println() + } + + abstract fun playCard(tableCards: UnsortedCardList, gameType: GameType): Card + + fun printName(){ + println(name) + } +} + +class NPCPlayer(name: String) : Player(name){ + override fun playCard(tableCards: UnsortedCardList, gameType: GameType): Card { + if(tableCards.size() == 0){ + println("Erster Spieler") + val card = cards.removeLast() + println("Spielt ${card.displayName}") + println() + return card + } + + val firstCard = tableCards.get(0) + if(cards.farbFreiOrTrumpf(firstCard, gameType)){ + println("farbFrei Or Trumpf") + return playTrumpf() + } + } + + private fun playTrumpf(): Card { + val trumpf = cards.getTrumpf() + if (trumpf.size() > 0) { + println("play Trumpf") + val card = trumpf.removeLast() + println("Spielt ${card.displayName}") + println() + return cards.remove(card) + } else { + println("abspatzen") + return cards.removeFirst() + } + } +} \ No newline at end of file diff --git a/Backend/schafkopf-shared/src/main/kotlin/Schafkopfkarte.kt b/Backend/schafkopf-shared/src/main/kotlin/Schafkopfkarte.kt new file mode 100644 index 0000000..416e1f6 --- /dev/null +++ b/Backend/schafkopf-shared/src/main/kotlin/Schafkopfkarte.kt @@ -0,0 +1,82 @@ +package de.heiserer + +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.NINE), + 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.NINE), + 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.NINE), + 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.NINE), + 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), + NINE(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), +} \ No newline at end of file diff --git a/Backend/schafkopf-shared/src/main/kotlin/UnsortedCardList.kt b/Backend/schafkopf-shared/src/main/kotlin/UnsortedCardList.kt new file mode 100644 index 0000000..60576f7 --- /dev/null +++ b/Backend/schafkopf-shared/src/main/kotlin/UnsortedCardList.kt @@ -0,0 +1,180 @@ +package de.heiserer + +interface CardList { + fun add(card: Card) + fun add(cards: CardList) + fun remove(card: Card): Card + fun remove(cards: CardList) + fun removeLast(): Card + fun get(index: Int): Card + fun getCopyOfCards(): List + fun size(): Int + fun print() + fun asSortedCardList(type: GameType): SortedCardList + fun removeFirst(): Card +} + +open class UnsortedCardList(withAllCards: Boolean = false): CardList { + private val cards: MutableList = if(withAllCards){ + enumValues().toMutableList() + } else { + mutableListOf() + } + + 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 get(index: Int): Card { + if(index < 0 || index >= cards.size){ + throw IllegalArgumentException("Index $index is out of bounds.") + } + return cards[index] + } + + 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 { + 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 { + 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 }))) + } +} + +class SortedCardList(private val gameType: GameType, withAllCards: Boolean = false) : UnsortedCardList(withAllCards) { + private fun sort() { + super.sortInternal(gameType.symbol, gameType.color) + } + + 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 + } + + fun farbFreiOrTrumpf(firstCard: Card, gameType: GameType) = + firstCard.color == gameType.color || getCardsWithoutTrumpf(gameType.color).size() == 0 + + override fun add(card: Card) { + super.add(card) + sort() + } +} + +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. +} \ No newline at end of file diff --git a/Backend/schafkopf-shared/src/test/kotlin/CardListTest.kt b/Backend/schafkopf-shared/src/test/kotlin/CardListTest.kt new file mode 100644 index 0000000..a59dbcd --- /dev/null +++ b/Backend/schafkopf-shared/src/test/kotlin/CardListTest.kt @@ -0,0 +1,476 @@ +import de.heiserer.* +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, 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 { + 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, 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 { + 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, 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 { + 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)) + } + } +} \ No newline at end of file