Mono Repo

* moveBackend

* added Frontend

* added env support for COM port

* added frontend into monorepo
This commit is contained in:
Valentin Heiserer
2024-04-16 22:24:11 +02:00
committed by GitHub
parent fb7ae31a0d
commit 09c38c81dd
138 changed files with 4410 additions and 5 deletions

228
Frontend/src/App.vue Normal file
View File

@@ -0,0 +1,228 @@
<script lang="ts" setup>
import {onMounted, ref} from 'vue';
import CardComp from './components/CardComponent.vue';
import {BackendMessage, Card, GamePhase, GameState} from "./BackendMessage";
const messageFromServer = ref<string[]>([]);
const gameStateText = ref<string>("Schafkopf");
const gameInfoText = ref<string>("");
const socket = ref<WebSocket | null>();
const tableCards = ref<Card[]>([]);
const botCards = ref(0);
const trickCard = ref<Card>();
const showGameSelect = ref(true);
function startSimulation(): void {
sendMessageToServer("startsimulation");
}
function stopSimulation(): void {
sendMessageToServer("stopsimulation");
}
function showTrumpf(): void {
tableCards.value = [];
sendMessageToServer("showtrumpf");
}
function showFarben(): void {
tableCards.value = [];
sendMessageToServer("showfarben");
}
function setGame(game: string): void {
sendMessageToServer(game);
}
function sendMessageToServer(message: string) {
if (socket.value) {
socket.value.send(message);
console.log("Sent message to server:", message);
}
}
function showGameState(gamestate: GameState) {
switch (gamestate.gamePhase) {
case GamePhase.GAME_START:
gameStateText.value = "Spiel startet";
showGameSelect.value = false;
botCards.value = 8;
break;
case GamePhase.TRICK_START:
gameStateText.value = "Runde startet";
tableCards.value = [];
trickCard.value = undefined
gameInfoText.value = "";
break;
case GamePhase.WAIT_FOR_CARD:
gameStateText.value = "Spieler " + gamestate.currentPlayer + " muss eine Karte legen.";
break;
case GamePhase.PLAYER_CARD:
gameStateText.value = "Spieler " + gamestate.currentPlayer + " hat eine Karte gespielt.";
if (gamestate.currentPlayer === 0) {
botCards.value--
}
if (gamestate.trumpf) {
gameInfoText.value = "TRUMPF";
} else {
gameInfoText.value = gamestate.color?.toString() ?? "ERROR";
}
tableCards.value.push(gamestate.card!);
break;
case GamePhase.PLAYER_TRICK:
gameStateText.value = "Spieler " + gamestate.currentPlayer + " sticht.";
trickCard.value = gamestate.card
break;
case GamePhase.GAME_STOP:
showGameSelect.value = true;
break;
default:
gameStateText.value = "Fehler";
}
}
onMounted(() => {
const websocketIp = import.meta.env.VITE_APP_WEBSOCKET_IP;
// Open a WebSocket connection when the component is mounted
socket.value = new WebSocket("ws://" + websocketIp + ":8080/schafkopf-events/");
// Handle connection opened
socket.value.addEventListener("open", (event) => {
console.log("WebSocket connection opened:", event);
});
// Handle messages received from the server
socket.value.addEventListener("message", (event) => {
const message: BackendMessage = JSON.parse(event.data);
console.log(message)
if ('gamestate' in message) {
console.log(message.gamestate)
showGameState(message.gamestate)
} else {
console.log("Invalid BackendMessage format: ", event);
}
});
// Handle connection closed
socket.value.addEventListener("close", (event) => {
console.log("WebSocket connection closed:", event);
});
// Handle errors
socket.value.addEventListener("error", (event) => {
console.error("WebSocket error:", event);
});
});
</script>
<template>
<div>
<div v-for="message in messageFromServer" :key="message">{{ message }}</div>
<div v-if="showGameSelect">
<div class="flex gap-2 place-content-center">
<button @click="setGame('setgame:sauspiel')">Sauspiel</button>
<button @click="setGame('setgame:herzsolo')">herzsolo</button>
<button @click="setGame('setgame:eichelsolo')">eichelsolo</button>
<button @click="setGame('setgame:blattsolo')">blattsolo</button>
<button @click="setGame('setgame:schellsolo')">schellsolo</button>
<button @click="setGame('setgame:eichelwenz')">eichelwenz</button>
<button @click="setGame('setgame:blattwenz')">blattwenz</button>
<button @click="setGame('setgame:herzwenz')">herzwenz</button>
<button @click="setGame('setgame:schellwenz')">schellwenz</button>
<button @click="setGame('setgame:eichelgeier')">eichelgeier</button>
<button @click="setGame('setgame:blattgeier')">blattgeier</button>
<button @click="setGame('setgame:herzgeier')">herzgeier</button>
<button @click="setGame('setgame:schellgeier')">schellgeier</button>
<button @click="setGame('setgame:geier')">Geier</button>
<button @click="setGame('setgame:wenz')">Wenz</button>
</div>
<div class="flex gap-2 place-content-center">
<button @click="showFarben">Zeige alle Farben</button>
<button @click="showTrumpf">Zeige alle Trumpfkarten</button>
</div>
<div class="flex gap-2 place-content-center">
<button class="v-button" @click="startSimulation">Starten</button>
<button class="v-button" @click="stopSimulation">Stoppen</button>
</div>
</div>
<div v-else>
<div class="flex gap-2 place-content-center">
<button class="v-button" @click="stopSimulation">Stoppen</button>
</div>
<h1 class=" top-52 text-white font-bold text-6xl absolute text-center w-full">{{ gameInfoText }}</h1>
<h1 class=" top-64 text-white font-bold text-6xl absolute text-center w-full">{{ gameStateText }}</h1>
<div v-if="tableCards.length > 0">
<!-- <div class="grid grid-cols-4 place-content-center">-->
<!-- <CardComp v-for="card in tableCards" :card="card" class="md" />-->
<!-- </div>-->
<CardComp v-if="tableCards.length > 0" :card="tableCards[0]" class="absolute card1 md"/>
<CardComp v-if="tableCards.length > 1" :card="tableCards[1]" class="absolute card2 md"/>
<CardComp v-if="tableCards.length > 2" :card="tableCards[2]" class="absolute card3 md"/>
<CardComp v-if="tableCards.length > 3" :card="tableCards[3]" class="absolute card4 md"/>
</div>
<div class="absolute left-0 top-1/2 transform -translate-y-1/2">
<CardComp v-if="trickCard" :card="trickCard" class="xl"/>
</div>
<div class="absolute bottom-0 w-full">
<div class="flex flex-row gap-3 w-fit mx-auto justify-center">
<CardComp v-for="i in botCards" :key="i" :card="Card.BACK" class="sm"/>
</div>
</div>
</div>
</div>
</template>
<style lang="scss">
$card-height: 24rem;
.card0 {
z-index: 1;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.card1 {
z-index: 1;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.card2 {
z-index: 2;
top: calc(50% + ($card-height * 0.10));
left: calc(50% - ($card-height * 0.3));
transform: rotate(50deg) translate(-50%, -50%);
}
.card3 {
z-index: 3;
top: calc(50% - ($card-height * 0.125));
left: calc(50% + ($card-height * 0.35));
transform: rotate(-30deg) translate(-50%, -50%);
}
.card4 {
z-index: 4;
top: calc(50% - ($card-height * 0.4));
left: calc(50% + ($card-height * 0.35));
transform: rotate(-60deg) translate(-50%, -50%);
}
</style>

View File

@@ -0,0 +1,98 @@
// Enum for KartenSymbol
enum KartenFarbe {
SCHELL = "SCH",
HERZ = "HERZ",
BLATT = "BLATT",
EICHEL = "EICHEL",
TRUMPF = "TRUMPF",
}
enum KartenSymbol {
SEVEN = "7",
EIGHT = "8",
NINE = "9",
TEN = "X",
UNTER = "U",
OBER = "O",
KOENIG = "K",
ASS = "A",
}
export enum Card {
EICHEL_7 = 'EICHEL_7',
EICHEL_8 = 'EICHEL_8',
EICHEL_9 = 'EICHEL_9',
EICHEL_X = 'EICHEL_X',
EICHEL_K = 'EICHEL_K',
EICHEL_A = 'EICHEL_A',
BLATT_7 = 'BLATT_7',
BLATT_8 = 'BLATT_8',
BLATT_9 = 'BLATT_9',
BLATT_X = 'BLATT_X',
BLATT_K = 'BLATT_K',
BLATT_A = 'BLATT_A',
SCHELL_7 = 'SCHELL_7',
SCHELL_8 = 'SCHELL_8',
SCHELL_9 = 'SCHELL_9',
SCHELL_X = 'SCHELL_X',
SCHELL_K = 'SCHELL_K',
SCHELL_A = 'SCHELL_A',
HERZ_7 = 'HERZ_7',
HERZ_8 = 'HERZ_8',
HERZ_9 = 'HERZ_9',
HERZ_X = 'HERZ_X',
HERZ_K = 'HERZ_K',
HERZ_A = 'HERZ_A',
SCHELL_U = 'SCHELL_U',
HERZ_U = 'HERZ_U',
BLATT_U = 'BLATT_U',
EICHEL_U = 'EICHEL_U',
SCHELL_O = 'SCHELL_O',
HERZ_O = 'HERZ_O',
BLATT_O = 'BLATT_O',
EICHEL_O = 'EICHEL_O',
BACK = "CARD_BACK"
// Add other card combinations as needed
}
export enum GamePhase {
CHOOSE_GAME = "CHOOSE_GAME",
GAME_START = "GAME_START",
GAME_STOP = "GAME_STOP",
TRICK_START = "TRICK_START",
WAIT_FOR_CARD = "WAIT_FOR_CARD",
PLAYER_CARD = "PLAYER_CARD",
PLAYER_TRICK = "PLAYER_TRICK"
}
// Define the interface for an array of cards
export interface CardArray {
cards: Card[];
}
export interface CardObject {
card: Card;
}
// Define the interface for the game state
export interface GameState {
gamePhase: GamePhase;
currentPlayer?: number;
card?: Card;
color?: KartenFarbe;
trumpf?: boolean;
}
export interface GameStateJson {
gamestate : GameState
}
// Define a union type for all possible message types
export type BackendMessage = CardObject | CardArray | GameStateJson;

View File

@@ -0,0 +1,52 @@
<script lang="ts" setup>
import {onBeforeMount, ref, watch} from 'vue';
import {Card} from "../BackendMessage";
const props = defineProps<{
card: Card;
}>();
const imgSrc = ref('/assets/card_back.png')
const focus = ref(false);
onBeforeMount(() => {
imgSrc.value = "/assets/" + props.card.toString().toLowerCase() + ".png"
})
watch(() => props.card, (newCard) => {
imgSrc.value = `/assets/${newCard.toString().toLowerCase()}.png`;
});
</script>
<template>
<div
:class="{'!scale-105 !z-10 !top-1/2 !left-1/2' : focus}" class="card transition overflow-hidden"
@click="focus=!focus">
<img class="h-full rounded-[1rem] mx-auto" :src="imgSrc" alt="card">
</div>
</template>
<style lang="scss">
.card {
&.xl {
@apply h-[48rem];
img {
@apply rounded-[2rem]
}
}
&.md {
@apply h-96;
img {
@apply rounded-2xl
}
}
&.sm {
@apply h-64;
img {
@apply rounded-xl
}
}
}
</style>

5
Frontend/src/main.ts Normal file
View File

@@ -0,0 +1,5 @@
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
createApp(App).mount('#app')

8
Frontend/src/style.css Normal file
View File

@@ -0,0 +1,8 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
.v-button{
@apply bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full transition duration-300 ease-in-out;
}

1
Frontend/src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />