diff --git a/API/API.csproj b/API/API.csproj
index 7330b1b..e51252d 100644
--- a/API/API.csproj
+++ b/API/API.csproj
@@ -12,9 +12,7 @@
-
-
diff --git a/API/Controllers/GameController.cs b/API/Controllers/GameHubSocket.cs
similarity index 79%
rename from API/Controllers/GameController.cs
rename to API/Controllers/GameHubSocket.cs
index 971d3e4..eede8c9 100644
--- a/API/Controllers/GameController.cs
+++ b/API/Controllers/GameHubSocket.cs
@@ -2,9 +2,9 @@ using Microsoft.AspNetCore.SignalR;
namespace API.Controllers;
-public class GameController : Hub
+public class GameHubSocket : Hub
{
- public async Task JoinGame(string gameId, string playerName)
+ public async Task CreateGame(string playerName, )
{
await Groups.AddToGroupAsync(Context.ConnectionId, gameId);
await Clients.Group(gameId).SendAsync("PlayerJoined", new
@@ -13,4 +13,9 @@ public class GameController : Hub
PlayerName = playerName
});
}
+
+ public async Task JoinGame(string gameId, string playerName)
+ {
+
+ }
}
\ No newline at end of file
diff --git a/API/Models/DataClasses/SixDigitInt.cs b/API/Models/DataClasses/SixDigitInt.cs
new file mode 100644
index 0000000..0ee9112
--- /dev/null
+++ b/API/Models/DataClasses/SixDigitInt.cs
@@ -0,0 +1,20 @@
+namespace API.Models.DataClasses
+{
+ public readonly record struct SixDigitInt
+ {
+ public int Value { get; }
+
+ public SixDigitInt(int value)
+ {
+ if (value < 0 || value > 999999)
+ throw new ArgumentOutOfRangeException(nameof(value),
+ "Wert muss zwischen 0 und 999999 liegen.");
+
+ Value = value;
+ }
+
+ public override string ToString() => Value.ToString("D6");
+
+ public static implicit operator int(SixDigitInt v) => v.Value;
+ }
+}
diff --git a/API/Models/Game/Game.cs b/API/Models/Game/Game.cs
new file mode 100644
index 0000000..7f3359a
--- /dev/null
+++ b/API/Models/Game/Game.cs
@@ -0,0 +1,26 @@
+using API.Models.DataClasses;
+
+namespace API.Models.Game
+{
+ public enum GameState
+ {
+ Lobby,
+ Running,
+ Ended
+ }
+
+ public class Game
+ {
+ public string Id { get; init; } = Guid.NewGuid().ToString();
+ public SixDigitInt GameCode { get; }
+ public string?[] PlayerConnectionIds { get; set; } = new string?[2];
+ public GameState State { get; private set; } = GameState.Lobby;
+ public GameField Field { get; }
+
+ public Game(Coordinates gFs, SixDigitInt gameCode)
+ {
+ Field = new GameField(gFs);
+ GameCode = gameCode;
+ }
+ }
+}
diff --git a/API/Models/Game/GameField.cs b/API/Models/Game/GameField.cs
new file mode 100644
index 0000000..98377c3
--- /dev/null
+++ b/API/Models/Game/GameField.cs
@@ -0,0 +1,86 @@
+namespace API.Models.Game
+{
+ public class Coordinates
+ {
+ public int X;
+ public int Y;
+ }
+
+ public enum PlaceResult
+ {
+ OutOfGameField,
+ NotAllowedPlayer,
+ OccupiedRed,
+ OccupiedYellow,
+ InvalidFieldValue,
+ Placed
+ }
+
+ public enum FieldState
+ {
+ OutOfGameField,
+ Empty,
+ OccupiedRed,
+ OccupiedYellow,
+ InvalidFieldValue
+ }
+
+ public class GameField(Coordinates gFs)
+ {
+ public int[,] CurrentField { get; } = new int[gFs.Y, gFs.X];
+ public int[,] BackupField { get; } = new int[gFs.Y, gFs.X];
+
+ public PlaceResult Place(Coordinates coordinates, int player)
+ {
+ if (coordinates.X < 0 || coordinates.X >= CurrentField.GetLength(1) ||
+ coordinates.Y < 0 || coordinates.Y >= CurrentField.GetLength(0))
+ {
+ return PlaceResult.OutOfGameField;
+ }
+
+ if (player != 1 && player != 2)
+ return PlaceResult.NotAllowedPlayer;
+
+ int currentValue = CurrentField[coordinates.Y, coordinates.X];
+
+ if (currentValue != 0)
+ {
+ return currentValue switch
+ {
+ 1 => PlaceResult.OccupiedRed,
+ 2 => PlaceResult.OccupiedYellow,
+ _ => PlaceResult.InvalidFieldValue
+ };
+ }
+
+ CreateSave();
+ CurrentField[coordinates.Y, coordinates.X] = player;
+
+ return PlaceResult.Placed;
+ }
+
+ public FieldState CheckField(Coordinates coordinates)
+ {
+ if (coordinates.X < 0 || coordinates.X >= CurrentField.GetLength(1) ||
+ coordinates.Y < 0 || coordinates.Y >= CurrentField.GetLength(0))
+ {
+ return FieldState.OutOfGameField;
+ }
+
+ int currentValue = CurrentField[coordinates.Y, coordinates.X];
+
+ return currentValue switch
+ {
+ 0 => FieldState.Empty,
+ 1 => FieldState.OccupiedRed,
+ 2 => FieldState.OccupiedYellow,
+ _ => FieldState.InvalidFieldValue
+ };
+ }
+
+ private void CreateSave()
+ {
+ Array.Copy(CurrentField, BackupField, CurrentField.Length);
+ }
+ }
+}
\ No newline at end of file
diff --git a/API/Program.cs b/API/Program.cs
index d65a162..0e8ac50 100644
--- a/API/Program.cs
+++ b/API/Program.cs
@@ -1,3 +1,5 @@
+using API.Services.GameManager;
+
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
@@ -7,6 +9,8 @@ builder.Services.AddSignalR();
builder.Services.AddOpenApi();
builder.Services.AddSwaggerGen();
+builder.Services.AddSingleton();
+
var app = builder.Build();
// Configure the HTTP request pipeline.
@@ -24,9 +28,8 @@ app.UseStaticFiles();
app.UseAuthorization();
app.MapControllers();
-app.MapHub("/api/game");
+app.MapHub("/api/gamehub");
-// SPA fallback: serve index.html for routes not matched by API or static files
app.MapFallbackToFile("index.html");
app.Run();
diff --git a/API/Services/GameManager/GameManager.cs b/API/Services/GameManager/GameManager.cs
new file mode 100644
index 0000000..2813ee5
--- /dev/null
+++ b/API/Services/GameManager/GameManager.cs
@@ -0,0 +1,20 @@
+namespace API.Services.GameManager
+{
+ public class GameManager : IGameManager
+ {
+ public int CreateGame(string playerName)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool JoinGame(string playerName, int gameCode)
+ {
+ throw new NotImplementedException();
+ }
+
+ private int GenerateNonExistingGameCode()
+ {
+ return 0;
+ }
+ }
+}
diff --git a/API/Services/GameManager/IGameManager.cs b/API/Services/GameManager/IGameManager.cs
new file mode 100644
index 0000000..dbb18d7
--- /dev/null
+++ b/API/Services/GameManager/IGameManager.cs
@@ -0,0 +1,8 @@
+namespace API.Services.GameManager
+{
+ public interface IGameManager
+ {
+ public int CreateGame(string playerName);
+ public bool JoinGame(string playerName, int gameCode);
+ }
+}
diff --git a/GUI/package-lock.json b/GUI/package-lock.json
index 9d310fa..4c13612 100644
--- a/GUI/package-lock.json
+++ b/GUI/package-lock.json
@@ -62,7 +62,6 @@
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -1386,7 +1385,6 @@
"integrity": "sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"undici-types": "~7.16.0"
}
@@ -1850,7 +1848,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -2977,7 +2974,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true,
"license": "Apache-2.0",
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -3085,7 +3081,6 @@
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"esbuild": "^0.27.0",
"fdir": "^6.5.0",
@@ -3271,7 +3266,6 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.28.tgz",
"integrity": "sha512-BRdrNfeoccSoIZeIhyPBfvWSLFP4q8J3u8Ju8Ug5vu3LdD+yTM13Sg4sKtljxozbnuMu1NB1X5HBHRYUzFocKg==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@vue/compiler-dom": "3.5.28",
"@vue/compiler-sfc": "3.5.28",
diff --git a/GUI/src/scripts/logic/localMode/LocalGame.ts b/GUI/src/scripts/logic/localMode/LocalGame.ts
index 33b0b2e..f45fcb2 100644
--- a/GUI/src/scripts/logic/localMode/LocalGame.ts
+++ b/GUI/src/scripts/logic/localMode/LocalGame.ts
@@ -1,10 +1,11 @@
import type { FieldSize } from '@/scripts/interfaces/FieldSize.ts'
+import buildFieldArray from '@/scripts/utils/BuildFieldArray'
class LocalGame {
public field: number[][]
constructor(fieldSize: FieldSize) {
- this.field = Array.from({ length: fieldSize.y }, () => Array(fieldSize.x).fill(0))
+ this.field = buildFieldArray(fieldSize);
}
}
diff --git a/GUI/src/scripts/logic/signalR/GameConnection.ts b/GUI/src/scripts/logic/signalR/GameConnection.ts
index 47af739..98a6d2a 100644
--- a/GUI/src/scripts/logic/signalR/GameConnection.ts
+++ b/GUI/src/scripts/logic/signalR/GameConnection.ts
@@ -2,9 +2,9 @@ import * as signalR from '@microsoft/signalr';
class GameConnection{
public connection: signalR.HubConnection
- constructor(gameSession: string) {
+ constructor(playerName: string, joinCode: number | undefined) {
this.connection = new signalR.HubConnectionBuilder()
- .withUrl(`/gameHub?gameSession=${gameSession}`)
+ .withUrl("/api/gamehub")
.withAutomaticReconnect()
.build();
}
diff --git a/GUI/src/scripts/utils/BuildFieldArray.ts b/GUI/src/scripts/utils/BuildFieldArray.ts
new file mode 100644
index 0000000..2db5728
--- /dev/null
+++ b/GUI/src/scripts/utils/BuildFieldArray.ts
@@ -0,0 +1,7 @@
+import type { FieldSize } from "../interfaces/FieldSize";
+
+function buildFieldArray(fieldSize: FieldSize): number[][]{
+ return Array.from({ length: fieldSize.y }, () => Array(fieldSize.x).fill(0));
+}
+
+export default buildFieldArray;
\ No newline at end of file