split Project into multiple Modules to prepare for development of Servers

This commit is contained in:
Valentin Heiserer
2024-04-18 00:26:11 +02:00
committed by GitHub
parent 949c00bb28
commit c94127acf1
79 changed files with 200 additions and 155 deletions

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.schafkopf</groupId>
<artifactId>schafkopf-backend-java</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>schafkopf-client</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.schafkopf</groupId>
<artifactId>schafkopf-shared</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>schafkopf-client-build</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>org.schafkopf.BackendServer</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,237 @@
package org.schafkopf;
import com.google.gson.JsonObject;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import jakarta.servlet.DispatcherType;
import java.awt.Desktop;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.time.Duration;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.CountDownLatch;
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.cardreader.UsbCardReader;
/** Main Class that represents the Backend Server. */
public class BackendServer {
private final Server server;
private final ServerConnector connector;
private CountDownLatch nfcLatch = new CountDownLatch(1);
private Boolean readingMode = false;
private String uidString = "";
/** Important variables. */
public final Schafkopf schafkopfGame;
private final List<FrontendEndpoint> frontendEndpoints = new ArrayList<>();
/** Creates an Instance of the Backend Server. */
public BackendServer() throws URISyntaxException, IOException {
server = new Server();
InetSocketAddress address = new InetSocketAddress("localhost", 8080);
connector = new ServerConnector(server);
connector.setHost(address.getHostName());
connector.setPort(address.getPort());
server.addConnector(connector);
schafkopfGame = new Schafkopf(this);
new UsbCardReader(this);
// 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);
System.out.println("Web Content Path: " + webContentPath);
// 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));
});
// Integrate simple HTTP server
startHttpServer();
URI uri = new URI("http://localhost:8081"); // Replace with your target URL
Desktop.getDesktop().browse(uri);
}
private void startHttpServer() {
try {
HttpServer httpServer = HttpServer.create(new InetSocketAddress(8081), 0);
httpServer.createContext("/", new MyHandler());
httpServer.setExecutor(null);
httpServer.start();
System.out.println("HTTP Server started on port 8081");
} catch (IOException e) {
e.printStackTrace();
}
}
static class MyHandler implements HttpHandler {
@Override
public void handle(HttpExchange t) throws IOException {
String path = t.getRequestURI().getPath();
if ("/".equals(path)) {
path = "/index.html"; // default to index.html
}
try {
InputStream fileStream =
getClass().getClassLoader().getResourceAsStream("web-content" + path);
if (fileStream != null) {
byte[] data = fileStream.readAllBytes();
// Set the appropriate MIME type for JavaScript files
String mimeType = getMimeType(path);
t.getResponseHeaders().set("Content-Type", mimeType);
t.sendResponseHeaders(200, data.length);
try (OutputStream os = t.getResponseBody()) {
os.write(data);
}
} else {
// File not found
t.sendResponseHeaders(404, -1);
}
} catch (IOException e) {
e.printStackTrace();
t.sendResponseHeaders(500, -1);
}
}
private String getMimeType(String path) {
if (path.endsWith(".js")) {
return "application/javascript";
} else if (path.endsWith(".html")) {
return "text/html";
} else if (path.endsWith(".css")) {
return "text/css";
}
// Add more MIME types as needed
return "application/octet-stream";
}
}
/** The main entrypoint of the Application. */
public static void main(String[] args) throws Exception {
BackendServer server = new BackendServer();
server.setPort(8080);
server.start();
server.join();
}
private void configureCors(ServletContextHandler context) {
// Enable CORS for all paths
FilterHolder cors = context.addFilter(CrossOriginFilter.class, "/*", null);
// Configure allowed origins, headers, and methods
cors.setInitParameter("allowedOrigins", "*");
cors.setInitParameter("allowedHeaders", "X-Requested-With,Content-Type,Accept,Origin");
cors.setInitParameter("allowedMethods", "GET,POST,PUT,DELETE,OPTIONS");
// Add filter mappings
EnumSet<DispatcherType> types = EnumSet.of(DispatcherType.REQUEST);
context.addFilter(cors, "*", types);
}
private void setPort(int port) {
connector.setPort(port);
}
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).
*/
public void sendMessageToAllFrontendEndpoints(String message) {
for (FrontendEndpoint endpoint : frontendEndpoints) {
endpoint.sendMessage(message);
}
}
/**
* Sends Message to all Frontend Instances.
*
* @param message Message to send (JsonObject).
*/
public void sendMessageToAllFrontendEndpoints(JsonObject message) {
for (FrontendEndpoint endpoint : frontendEndpoints) {
endpoint.sendMessage(message.toString());
}
}
/** 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();
}
}

View File

@@ -0,0 +1,77 @@
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;
public FrontendEndpoint(BackendServer backendServer) {
this.backendServer = backendServer;
System.out.println("new FrontendEndpoint");
}
@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);
System.out.println("Received TEXT message:" + message);
if (message.contains("startsimulation")) {
backendServer.schafkopfGame.startGame();
}
if (message.contains("stopsimulation")) {
backendServer.schafkopfGame.stopGame();
}
if (message.contains("showtrumpf")) {
backendServer.schafkopfGame.showTrumpf();
}
if (message.contains("showfarben")) {
backendServer.schafkopfGame.showFarbe();
}
if (message.contains("setgame")) {
backendServer.schafkopfGame.setGame(message);
}
}
@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();
}
}
}

View File

@@ -0,0 +1,23 @@
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;
public FrontendEndpointCreator(BackendServer backendServer) {
this.backendServer = backendServer;
}
@Override
public Object createWebSocket(
JettyServerUpgradeRequest jettyServerUpgradeRequest,
JettyServerUpgradeResponse jettyServerUpgradeResponse) {
return new FrontendEndpoint(this.backendServer);
}
}

View File

@@ -0,0 +1,80 @@
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 Integer 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, Integer player) {
this.gamePhase = phase;
this.currentPlayer = player;
}
/** GameState. */
public GameState(GamePhase phase, Integer 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, Integer player, Karte card) {
this.gamePhase = phase;
this.currentPlayer = player;
this.card = card;
}
/** GameState. */
public JsonObject getJson() {
Gson gson = new Gson();
JsonObject jsonObject = new JsonObject();
jsonObject.add("gamestate", gson.toJsonTree(this));
return jsonObject;
}
}

View File

@@ -0,0 +1,193 @@
package org.schafkopf;
import org.schafkopf.GameState.GamePhase;
import org.schafkopf.karte.Karte;
import org.schafkopf.karte.KartenFarbe;
import org.schafkopf.karte.KartenListe;
import org.schafkopf.karte.KartenUtil;
import org.schafkopf.player.BotPlayer;
import org.schafkopf.player.LocalPlayer;
import org.schafkopf.player.Player;
import org.schafkopf.spielcontroller.FarbGeierController;
import org.schafkopf.spielcontroller.FarbSoloController;
import org.schafkopf.spielcontroller.FarbWenzController;
import org.schafkopf.spielcontroller.GeierController;
import org.schafkopf.spielcontroller.SauSpielController;
import org.schafkopf.spielcontroller.SpielController;
import org.schafkopf.spielcontroller.WenzController;
/** The main class representing the Schafkopf game. */
public class Schafkopf {
private final BackendServer server;
/** The game controller. This is the class that implements the game logic. */
private SpielController spiel = new SauSpielController(0, KartenFarbe.EICHEL);
private final Player[] player = {
new BotPlayer(), new LocalPlayer(this), new LocalPlayer(this), new LocalPlayer(this)
};
private GameState gameState = new GameState(GamePhase.GAME_STOP);
private Thread spielThread;
/**
* Constructor for the Schafkopf class.
*
* @param server The backend server associated with the game.
*/
Schafkopf(BackendServer server) {
this.server = server;
System.out.println("SchaffKopfGame erstellt");
}
public Player[] getPlayer() {
return player;
}
/** Sends all Trumpf Karten of the current GameType to the Frontend. */
public void showTrumpf() {
server.sendMessageToAllFrontendEndpoints(spiel.getTrumpfKarten().getJson());
}
/** Sends all Farb Karten of the current GameType to the Frontend. */
public void showFarbe() {
server.sendMessageToAllFrontendEndpoints(spiel.getFarbKarten().getJson());
}
/** Waits for a Card and returns a Karte Object. */
public Karte wartetAufKarte() {
String uid = null;
System.out.println("Starte Warten auf Karte");
try {
uid = server.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;
}
/** Set GameState to "started" and start Game Thread. */
public void startGame() {
if (gameState.getGamePhase() != GamePhase.GAME_STOP) {
System.out.println("Game already started!");
server.sendMessageToAllFrontendEndpoints("Game already started!");
} else {
gameState = new GameState(GamePhase.GAME_START);
setAndSendGameState(gameState);
System.out.println("Start Game");
// KartenListe botHand = KartenUtil.zieheZufallsHand(8);
KartenListe botHand = new KartenListe();
botHand.addKarten(Karte.EICHEL_7);
botHand.addKarten(Karte.SCHELL_7);
botHand.addKarten(Karte.BLATT_7);
botHand.addKarten(Karte.EICHEL_X);
botHand.addKarten(Karte.HERZ_X);
botHand.addKarten(Karte.HERZ_7);
botHand.addKarten(Karte.EICHEL_U);
botHand.addKarten(Karte.EICHEL_O);
for (Player currentPlayer : player) {
if (currentPlayer instanceof BotPlayer botPlayer) {
// Perform actions specific to BotPlayer
botPlayer.setCards(botHand); // Replace with the actual method you want to call
}
}
spielThread = new Thread(() -> new Spielablauf(this, spiel));
spielThread.start();
}
}
/** Set GameState to "stopped" and interrupt Game Thread. */
public void stopGame() {
if (gameState.getGamePhase() == GamePhase.GAME_STOP) {
System.out.println("no active Game!");
server.sendMessageToAllFrontendEndpoints("no active Game!");
} else {
gameState = new GameState(GamePhase.GAME_STOP);
setAndSendGameState(gameState);
}
spielThread.interrupt();
}
/** Set GameType. */
public void setGame(String message) {
System.out.println("Set Game: " + message);
server.sendMessageToAllFrontendEndpoints("Set Game: " + message);
switch (message) {
case "setgame:herzsolo":
this.spiel = new FarbSoloController(0, KartenFarbe.HERZ);
break;
case "setgame:blattsolo":
this.spiel = new FarbSoloController(0, KartenFarbe.BLATT);
break;
case "setgame:eichelsolo":
this.spiel = new FarbSoloController(0, KartenFarbe.EICHEL);
break;
case "setgame:schellsolo":
this.spiel = new FarbSoloController(0, KartenFarbe.SCHELL);
break;
case "setgame:wenz":
this.spiel = new WenzController(0);
break;
case "setgame:geier":
this.spiel = new GeierController(0);
break;
case "setgame:eichelwenz":
this.spiel = new FarbWenzController(0, KartenFarbe.EICHEL);
break;
case "setgame:herzwenz":
this.spiel = new FarbWenzController(0, KartenFarbe.HERZ);
break;
case "setgame:blattwenz":
this.spiel = new FarbWenzController(0, KartenFarbe.BLATT);
break;
case "setgame:schellwenz":
this.spiel = new FarbWenzController(0, KartenFarbe.SCHELL);
break;
case "setgame:eichelgeier":
this.spiel = new FarbGeierController(0, KartenFarbe.EICHEL);
break;
case "setgame:herzgeier":
this.spiel = new FarbGeierController(0, KartenFarbe.HERZ);
break;
case "setgame:blattgeier":
this.spiel = new FarbGeierController(0, KartenFarbe.BLATT);
break;
case "setgame:schellgeier":
this.spiel = new FarbGeierController(0, KartenFarbe.SCHELL);
break;
case "setgame:sauspiel":
this.spiel = new SauSpielController(0, KartenFarbe.EICHEL);
break;
default:
System.out.println("Ungültiges Spiel");
}
}
public void setAndSendGameState(GameState gameState) {
this.gameState = gameState;
this.server.sendMessageToAllFrontendEndpoints(this.gameState.getJson());
}
public GameState getGameState() {
return this.gameState;
}
}

View File

@@ -0,0 +1,86 @@
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) {
this.schafkopf = schafkopf;
this.spiel = spiel;
this.players = schafkopf.getPlayer();
playRound();
}
private void playRound() {
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) {
schafkopf.setAndSendGameState(new GameState(GamePhase.TRICK_START));
for (int i = 0; i < 4; i++) {
int currentPlayer = (i + startingPlayer) % 4;
logger.info("Spieler ist dran: {}", currentPlayer);
schafkopf.setAndSendGameState(new GameState(GamePhase.WAIT_FOR_CARD, currentPlayer));
Karte playedCard = players[currentPlayer].play(spiel, tischKarten, gespielteKarten);
tischKarten.addKarten(playedCard);
schafkopf.setAndSendGameState(
new GameState(
GamePhase.PLAYER_CARD,
currentPlayer,
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, winningPlayerIndex, tischKarten.getByIndex(stichSpieler)));
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
logger.error("error sleep");
}
gespielteKarten.addKarten(tischKarten);
tischKarten.clear();
return winningPlayerIndex;
}
}

View File

@@ -0,0 +1,13 @@
package org.schafkopf.cardreader;
import org.schafkopf.BackendServer;
/** Class that represents one Card Reader. */
public abstract class CardReader {
protected static BackendServer server;
public CardReader(BackendServer server) {
this.server = server;
}
}

View File

@@ -0,0 +1,83 @@
package org.schafkopf.cardreader;
import com.fazecast.jSerialComm.SerialPort;
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 final String comPort = dotenv.get("COM_PORT");
/**
* Creates an Instance of the KartenLeser.
*
* @param server Backend Server to call methods on.
*/
public UsbCardReader(BackendServer server) {
super(server);
new Thread(this::run).start();
}
public void stop() {
isRunning = false;
}
/** run the reader. */
public void run() {
SerialPort[] ports = SerialPort.getCommPorts();
SerialPort selectedPort = null;
for (SerialPort port : ports) {
if (port.getSystemPortName().equals(this.comPort)) {
selectedPort = port;
break;
}
}
if (selectedPort == null) {
System.out.println(this.comPort + " not found");
return;
}
if (ports.length == 0) {
System.out.println("No serial ports found");
return;
}
SerialPort serialPort = selectedPort; // You may need to adjust this based on your setup
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();
server.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");
}
}
}

View File

@@ -0,0 +1,43 @@
package org.schafkopf.player;
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();
public BotPlayer() {
// TODO document why this constructor is empty
}
@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");
this.eigeneKarten = cards;
this.unbekannteKarten = KartenUtil.initializeSchafKopfCardDeck();
this.unbekannteKarten.removeKarten(eigeneKarten);
System.out.println("Eigene Karte fertig");
}
}

View File

@@ -0,0 +1,23 @@
package org.schafkopf.player;
import org.schafkopf.Schafkopf;
import org.schafkopf.karte.Karte;
import org.schafkopf.karte.KartenListe;
import org.schafkopf.spielcontroller.SpielController;
/**
* Player that plays in real life.
*/
public class LocalPlayer extends Player {
private final Schafkopf schafkopf;
public LocalPlayer(Schafkopf schafkopf) {
this.schafkopf = schafkopf;
}
@Override
public Karte play(SpielController spiel, KartenListe tischKarten, KartenListe gespielteKarten) {
return schafkopf.wartetAufKarte();
}
}

View File

@@ -0,0 +1,11 @@
package org.schafkopf.player;
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 {
public abstract Karte play(
SpielController spiel, KartenListe tischKarten, KartenListe gespielteKarten);
}

View File

@@ -0,0 +1,36 @@
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;
}
}

View File

@@ -0,0 +1,40 @@
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;
}
}

View File

@@ -0,0 +1,36 @@
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;
}
}

View File

@@ -0,0 +1,24 @@
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);
}
}

View File

@@ -0,0 +1,20 @@
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;
}
}

View File

@@ -0,0 +1,58 @@
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;
}
}

View File

@@ -0,0 +1,19 @@
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;
}
}

View File

@@ -0,0 +1,133 @@
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;
}
}

View File

@@ -0,0 +1,33 @@
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);
}

View File

@@ -0,0 +1,25 @@
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);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 815 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 971 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 797 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 646 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1008 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 870 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 957 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 959 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 741 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 881 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1009 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1015 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 958 KiB

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Vite + Vue + TS</title>
<script type="module" crossorigin src="/assets/index-DZ_vcdmw.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-8PJRhba5.css">
</head>
<body class="bg-zinc-800">
<div id="app"></div>
</body>