From 031e8eef7901e9fde6f98e1a166545fe7be2dbe9 Mon Sep 17 00:00:00 2001 From: Christian Kauer Date: Sun, 17 Dec 2023 17:29:56 +0100 Subject: [PATCH] Add project files. --- GameContract/Class1.cs | 7 + GameContract/GameContract.csproj | 9 + .../DeathGame/DeathGameHandlerTests.cs | 114 ++++++++++++ .../GameHandler.UnitTests.csproj | 26 +++ GameHandler.UnitTests/GlobalUsings.cs | 1 + GameHandler/Class1.cs | 7 + GameHandler/DeathGame/DeathGameHandler.cs | 176 ++++++++++++++++++ GameHandler/GameHandler.csproj | 14 ++ GameModel/Class1.cs | 7 + GameModel/DeathGame/Coffin.cs | 11 ++ GameModel/DeathGame/DeathGameModel.cs | 10 + GameModel/DeathGame/DeathGameSettings.cs | 11 ++ GameModel/GameModel.csproj | 9 + KoogleV4.sln | 43 +++++ 14 files changed, 445 insertions(+) create mode 100644 GameContract/Class1.cs create mode 100644 GameContract/GameContract.csproj create mode 100644 GameHandler.UnitTests/DeathGame/DeathGameHandlerTests.cs create mode 100644 GameHandler.UnitTests/GameHandler.UnitTests.csproj create mode 100644 GameHandler.UnitTests/GlobalUsings.cs create mode 100644 GameHandler/Class1.cs create mode 100644 GameHandler/DeathGame/DeathGameHandler.cs create mode 100644 GameHandler/GameHandler.csproj create mode 100644 GameModel/Class1.cs create mode 100644 GameModel/DeathGame/Coffin.cs create mode 100644 GameModel/DeathGame/DeathGameModel.cs create mode 100644 GameModel/DeathGame/DeathGameSettings.cs create mode 100644 GameModel/GameModel.csproj create mode 100644 KoogleV4.sln diff --git a/GameContract/Class1.cs b/GameContract/Class1.cs new file mode 100644 index 0000000..1f41244 --- /dev/null +++ b/GameContract/Class1.cs @@ -0,0 +1,7 @@ +namespace GameContract +{ + public class Class1 + { + + } +} \ No newline at end of file diff --git a/GameContract/GameContract.csproj b/GameContract/GameContract.csproj new file mode 100644 index 0000000..cfadb03 --- /dev/null +++ b/GameContract/GameContract.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/GameHandler.UnitTests/DeathGame/DeathGameHandlerTests.cs b/GameHandler.UnitTests/DeathGame/DeathGameHandlerTests.cs new file mode 100644 index 0000000..78fadba --- /dev/null +++ b/GameHandler.UnitTests/DeathGame/DeathGameHandlerTests.cs @@ -0,0 +1,114 @@ +using GameHandler.DeathGame; +using GameModel.DeathGame; +using NuGet.Frameworks; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime; +using System.Text; +using System.Threading.Tasks; + +namespace GameHandler.UnitTests.DeathGame +{ + [TestFixture] + public class DeathGameHandlerTests + { + private DeathGameSettings _settings; + private DeathGameHandler _gh; + private int[] _players; + + [SetUp] + public void SetUp() + { + _settings = new DeathGameSettings(6); + _gh = new DeathGameHandler(); + _players = new[] { 1, 2, 3, 4, 5, 6 }; + } + + [Test] + public void CreateGameModelWithDuplicatePlayers_ThrowsException() + { + var players = new[] { 1, 2, 3, 1 }; + Assert.That(() => _gh.InitGameModel(players,_settings), Throws.TypeOf()); + } + + [Test] + public void CreateGameModelWithTooManyPlayers_ThrowsException() + { + var players = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; + Assert.That(() => _gh.InitGameModel(players, _settings), Throws.TypeOf()); + } + + [Test] + public void CreateGameModelWithTooLessPlayers_ThrowsException() + { + var players = new[] { 1, 2 }; + Assert.That(() => _gh.InitGameModel(players, _settings), Throws.TypeOf()); + } + + [Test] + [TestCase(2)] + [TestCase(13)] + public void InvalidMaxCoffinSize_ThrowsException(int maxCoffinSize) + { + var players = new[] { 1, 2, 3, 4 }; + var invalidSettings = new DeathGameSettings(maxCoffinSize); + Assert.That(() => _gh.InitGameModel(players, invalidSettings), Throws.TypeOf()); + } + + + [Test] + public void NewGameModel_IsWithoutXandLineCount() + { + var gm = _gh.InitGameModel(_players, _settings); + + Assert.That(gm.Coffins.Count, Is.EqualTo(_players.Count())); + Assert.That(gm.Coffins.Any(_ => _.XCount > 0), Is.False); + Assert.That(gm.Coffins.Any(_ => _.LineCount > 0), Is.False); + } + + + [Test] + public void InvalidFirstThrow_RaisesExpense() + { + var gm = _gh.InitGameModel(_players, _settings); + + var gmAfter = _gh.CalcNextModel(gm, 1, false, false); + } + + + [Test] + public void AfterFirstThrow_SomeFirstPlayerHasAnX() + { + var gm = _gh.InitGameModel(_players, _settings); + + + } + + [Test] + public void FirstPlayerIsLast_AfterModelUpdate() + { + var gm = _gh.InitGameModel(_players, _settings); + + var first = gm.Coffins.First(); + DeathGameModel? newGm = null; + + Assert.That(() => { newGm = _gh.CalcNextModel(gm, 2, false, false); }, Throws.Nothing); + + Assert.That(first.PlayerId,Is.EqualTo(newGm.Coffins.Last().PlayerId)); + Assert.That(gm.Coffins.Count, Is.EqualTo(newGm.Coffins.Count())); + } + + [Test] + public void AllPins_CausesLineAtPreviousPlayer() + { + var gm = _gh.InitGameModel(_players, _settings); + + var prev = gm.Coffins.Last(); + var newGm = _gh.CalcNextModel(gm, 2, false, true); + var prevNew = newGm.Coffins.First(_ => _.PlayerId == prev.PlayerId); + + Assert.That(prevNew.LineCount, Is.EqualTo(prev.LineCount + 1)); + } + } +} diff --git a/GameHandler.UnitTests/GameHandler.UnitTests.csproj b/GameHandler.UnitTests/GameHandler.UnitTests.csproj new file mode 100644 index 0000000..6bea1db --- /dev/null +++ b/GameHandler.UnitTests/GameHandler.UnitTests.csproj @@ -0,0 +1,26 @@ + + + + net7.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + diff --git a/GameHandler.UnitTests/GlobalUsings.cs b/GameHandler.UnitTests/GlobalUsings.cs new file mode 100644 index 0000000..cefced4 --- /dev/null +++ b/GameHandler.UnitTests/GlobalUsings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file diff --git a/GameHandler/Class1.cs b/GameHandler/Class1.cs new file mode 100644 index 0000000..7627cf6 --- /dev/null +++ b/GameHandler/Class1.cs @@ -0,0 +1,7 @@ +namespace GameHandler +{ + public class Class1 + { + + } +} \ No newline at end of file diff --git a/GameHandler/DeathGame/DeathGameHandler.cs b/GameHandler/DeathGame/DeathGameHandler.cs new file mode 100644 index 0000000..42533fa --- /dev/null +++ b/GameHandler/DeathGame/DeathGameHandler.cs @@ -0,0 +1,176 @@ +using GameModel.DeathGame; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameHandler.DeathGame +{ + public delegate void CoffinCompleted(Coffin coffin); + public class DeathGameHandler + { + const int MIN_PLAYER_COUNT = 3; + const int MAX_PLAYER_COUNT = 12; + const int MIN_COFFIN_SIZE = 6; + const int MAX_COFFIN_SIZE = 12; + + public event CoffinCompleted CoffinCompleted; + + private static Random random = new Random(); + + + private void ValidatePlayerCount(int playerCount) + { + if (playerCount < MIN_PLAYER_COUNT) + { + throw new InvalidDataException($"MaxPlayerCount must be at least {MIN_PLAYER_COUNT}"); + } + if (playerCount > MAX_PLAYER_COUNT) + { + throw new InvalidDataException($"MaxPlayerCount cannot be greater than {MAX_PLAYER_COUNT}"); + } + } + + private void ValidateGameSettings(DeathGameSettings deathGameSettings) + { + if (deathGameSettings.MaxCoffinSize < MIN_COFFIN_SIZE || deathGameSettings.MaxCoffinSize > MAX_COFFIN_SIZE ) + { + throw new InvalidDataException($"Max coffin size must be from {MIN_COFFIN_SIZE} to {MAX_COFFIN_SIZE}"); + } + } + + public DeathGameHandler() + { + } + + public DeathGameModel InitGameModel(int[] playerIds, DeathGameSettings deathGameSettings) + { + ValidatePlayerCount(playerIds.Count()); + ValidateGameSettings(deathGameSettings); + + var coffins = new List(); + foreach (var playerId in playerIds) + { + if (coffins.Any(_ => _.PlayerId.Equals(playerId))) + { + throw new InvalidDataException($"player {playerId} already exists"); + } + coffins.Add(new Coffin(playerId,0,0)); + } + return new DeathGameModel(1, coffins.OrderBy(_ => random.Next()).ToList().ToArray(), deathGameSettings); + } + + public DeathGameModel CalcNextModel(DeathGameModel gm, int pinCount, bool isTrowIntoAllPins, bool boardCleared) + { + var incLine = false; + var incX = false; + if (gm.Id == 1) // first round in game + { + incX = true; + if (pinCount < 3) + { + incLine = true; + } + } + + if (isTrowIntoAllPins) + { + incX = true; + } + + if (pinCount == 0) + { + incLine = true; + } + + var current = gm.Coffins.First(); + var next = gm.Coffins.Skip(1).First(); + var previous = gm.Coffins.Last(); + Coffin? currentUpdated = null; + Coffin? nextUpdated = next; + Coffin? previousUpdated = previous; + + + var xCount = current.XCount; + var lineCount = current.LineCount; + + + if ((current.XCount == 2) && incX) + { + xCount = 0; + lineCount++; + } + + if (incLine) + { + lineCount++; + } + + currentUpdated = current with { LineCount = lineCount, XCount = xCount }; + + if (lineCount > gm.deathGameSettings.MaxCoffinSize) + { + CoffinCompleted?.Invoke(currentUpdated); + currentUpdated = null; + } + + + if (boardCleared) + { + previousUpdated = previous with { LineCount = previous.LineCount + 1 }; + if (previousUpdated.LineCount > gm.deathGameSettings.MaxCoffinSize) + { + CoffinCompleted?.Invoke(previousUpdated); + previousUpdated = null; + } + + + if (next.XCount < 2) + { + nextUpdated = next with { XCount = next.XCount + 1 }; + } + else + { + nextUpdated = next with { LineCount = previous.LineCount + 1, XCount = 0 }; + if (nextUpdated.LineCount > gm.deathGameSettings.MaxCoffinSize) + { + CoffinCompleted?.Invoke(nextUpdated); + nextUpdated = null; + } + } + + } + + var coffins = gm.Coffins.Skip(1).ToList(); + if (currentUpdated != null) + { + coffins.Add(currentUpdated); + } + + if (nextUpdated == null) + { + coffins.Remove(next); + } + + if (previousUpdated == null) + { + coffins.Remove(previous); + } + else + { + var idx = coffins.IndexOf(previous); + + coffins.Remove(previous); + coffins.Insert(idx, previousUpdated); + } + + + + + var result = gm with { Coffins = coffins.ToArray(), Id = gm.Id + 1 }; + + return result; + } + } +} diff --git a/GameHandler/GameHandler.csproj b/GameHandler/GameHandler.csproj new file mode 100644 index 0000000..36eb0b9 --- /dev/null +++ b/GameHandler/GameHandler.csproj @@ -0,0 +1,14 @@ + + + + net7.0 + enable + enable + + + + + + + + diff --git a/GameModel/Class1.cs b/GameModel/Class1.cs new file mode 100644 index 0000000..29ef0c3 --- /dev/null +++ b/GameModel/Class1.cs @@ -0,0 +1,7 @@ +namespace GameModel +{ + public class Class1 + { + + } +} \ No newline at end of file diff --git a/GameModel/DeathGame/Coffin.cs b/GameModel/DeathGame/Coffin.cs new file mode 100644 index 0000000..2f38e8b --- /dev/null +++ b/GameModel/DeathGame/Coffin.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameModel.DeathGame +{ + public record Coffin(int PlayerId, int XCount, int LineCount); + +} diff --git a/GameModel/DeathGame/DeathGameModel.cs b/GameModel/DeathGame/DeathGameModel.cs new file mode 100644 index 0000000..b0e8296 --- /dev/null +++ b/GameModel/DeathGame/DeathGameModel.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameModel.DeathGame +{ + public record DeathGameModel(int Id, Coffin[] Coffins, DeathGameSettings deathGameSettings); +} diff --git a/GameModel/DeathGame/DeathGameSettings.cs b/GameModel/DeathGame/DeathGameSettings.cs new file mode 100644 index 0000000..4bbc3fb --- /dev/null +++ b/GameModel/DeathGame/DeathGameSettings.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GameModel.DeathGame +{ + public record DeathGameSettings(int MaxCoffinSize); + +} diff --git a/GameModel/GameModel.csproj b/GameModel/GameModel.csproj new file mode 100644 index 0000000..a1ed5b3 --- /dev/null +++ b/GameModel/GameModel.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/KoogleV4.sln b/KoogleV4.sln new file mode 100644 index 0000000..acde62f --- /dev/null +++ b/KoogleV4.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34202.233 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameHandler.UnitTests", "GameHandler.UnitTests\GameHandler.UnitTests.csproj", "{E2F3CE36-0051-4C9A-B3FF-0BB44292B756}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameHandler", "GameHandler\GameHandler.csproj", "{4A541722-86AD-492F-AADA-CFB4935CDB83}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameModel", "GameModel\GameModel.csproj", "{9B3CADB6-C335-46D1-B98B-07E73D53E16B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameContract", "GameContract\GameContract.csproj", "{68897747-A9D4-4E45-A20C-6AB7E7AB22FD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E2F3CE36-0051-4C9A-B3FF-0BB44292B756}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2F3CE36-0051-4C9A-B3FF-0BB44292B756}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2F3CE36-0051-4C9A-B3FF-0BB44292B756}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2F3CE36-0051-4C9A-B3FF-0BB44292B756}.Release|Any CPU.Build.0 = Release|Any CPU + {4A541722-86AD-492F-AADA-CFB4935CDB83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4A541722-86AD-492F-AADA-CFB4935CDB83}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4A541722-86AD-492F-AADA-CFB4935CDB83}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4A541722-86AD-492F-AADA-CFB4935CDB83}.Release|Any CPU.Build.0 = Release|Any CPU + {9B3CADB6-C335-46D1-B98B-07E73D53E16B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B3CADB6-C335-46D1-B98B-07E73D53E16B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B3CADB6-C335-46D1-B98B-07E73D53E16B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B3CADB6-C335-46D1-B98B-07E73D53E16B}.Release|Any CPU.Build.0 = Release|Any CPU + {68897747-A9D4-4E45-A20C-6AB7E7AB22FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68897747-A9D4-4E45-A20C-6AB7E7AB22FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68897747-A9D4-4E45-A20C-6AB7E7AB22FD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68897747-A9D4-4E45-A20C-6AB7E7AB22FD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {00DAFA57-4F14-4807-886E-392D4E67BA46} + EndGlobalSection +EndGlobal