From 8b45e21f812df9eb9751d0a20c2e735bbb5df3d5 Mon Sep 17 00:00:00 2001 From: Christian Nagel Date: Sat, 25 May 2024 12:07:26 -0700 Subject: [PATCH 1/6] rename global usings --- .../{Usings.cs => GlobalUsings.cs} | 0 .../Codebreaker.GameAPIs.Analyzers/{Usings.cs => GlobalUsings.cs} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/services/gameapi/Codebreaker.GameAPIs.Analyzers.Tests/{Usings.cs => GlobalUsings.cs} (100%) rename src/services/gameapi/Codebreaker.GameAPIs.Analyzers/{Usings.cs => GlobalUsings.cs} (100%) diff --git a/src/services/gameapi/Codebreaker.GameAPIs.Analyzers.Tests/Usings.cs b/src/services/gameapi/Codebreaker.GameAPIs.Analyzers.Tests/GlobalUsings.cs similarity index 100% rename from src/services/gameapi/Codebreaker.GameAPIs.Analyzers.Tests/Usings.cs rename to src/services/gameapi/Codebreaker.GameAPIs.Analyzers.Tests/GlobalUsings.cs diff --git a/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Usings.cs b/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/GlobalUsings.cs similarity index 100% rename from src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Usings.cs rename to src/services/gameapi/Codebreaker.GameAPIs.Analyzers/GlobalUsings.cs From bbbd5161be133bc49950787673e6449f1fbe185b Mon Sep 17 00:00:00 2001 From: Christian Nagel Date: Sat, 25 May 2024 12:08:24 -0700 Subject: [PATCH 2/6] add unit tests for Game6x4Mini #156 --- .../Analyzers/SimpleGameGuessAnalyzerTests.cs | 190 ++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 src/services/gameapi/Codebreaker.GameAPIs.Analyzers.Tests/Analyzers/SimpleGameGuessAnalyzerTests.cs diff --git a/src/services/gameapi/Codebreaker.GameAPIs.Analyzers.Tests/Analyzers/SimpleGameGuessAnalyzerTests.cs b/src/services/gameapi/Codebreaker.GameAPIs.Analyzers.Tests/Analyzers/SimpleGameGuessAnalyzerTests.cs new file mode 100644 index 00000000..9aaa2443 --- /dev/null +++ b/src/services/gameapi/Codebreaker.GameAPIs.Analyzers.Tests/Analyzers/SimpleGameGuessAnalyzerTests.cs @@ -0,0 +1,190 @@ +using System.Collections; + +using static Codebreaker.GameAPIs.Models.Colors; + +namespace Codebreaker.GameAPIs.Analyzer.Tests; + +public class SimpleGameGuessAnalyzerTests +{ + [Fact] + public void SetMoveShouldReturnThreeCorrectColor() + { + SimpleColorResult expectedKeyPegs = new( + [ + ResultValue.CorrectColor, + ResultValue.CorrectColor, + ResultValue.CorrectColor, + ResultValue.Incorrect + ]); + SimpleColorResult? resultKeyPegs = TestSkeleton( + [Green, Yellow, Green, Black], + [Yellow, Green, Black, Blue] + ); + + Assert.Equal(expectedKeyPegs, resultKeyPegs); + } + + [Theory] + [ClassData(typeof(TestData6x4Mini))] + public void SetMoveUsingVariousDataUsingDataClass(string[] code, string[] guess, SimpleColorResult expectedKeyPegs) + { + SimpleColorResult actualKeyPegs = TestSkeleton(code, guess); + Assert.Equal(expectedKeyPegs, actualKeyPegs); + } + + [Fact] + public void ShouldThrowOnInvalidGuessCount() + { + Assert.Throws(() => + TestSkeleton( + ["Black", "Black", "Black", "Black"], + ["Black"] + )); + } + + [Fact] + public void ShouldThrowOnInvalidGuessValues() + { + Assert.Throws(() => + TestSkeleton( + ["Black", "Black", "Black", "Black"], + ["Black", "Der", "Blue", "Yellow"] // "Der" is the wrong value + )); + } + + [Fact] + public void ShouldThrowOnInvalidMoveNumber() + { + Assert.Throws(() => + TestSkeleton( + [Green, Yellow, Green, Black], + [Yellow, Green, Black, Blue], moveNumber: 2)); + } + + [Fact] + public void ShouldNotIncrementMoveNumberOnInvalidMove() + { + IGame game = TestSkeletonWithGame( + [Green, Yellow, Green, Black], + [Yellow, Green, Black, Blue], moveNumber: 2); + + Assert.Equal(0, game?.LastMoveNumber); + } + + private static SimpleColorResult TestSkeleton(string[] codes, string[] guesses, int moveNumber = 1) + { + MockColorGame game = new() + { + GameType = GameTypes.Game6x4Mini, + NumberCodes = 4, + MaxMoves = 12, + IsVictory = false, + FieldValues = new Dictionary>() + { + [FieldCategories.Colors ] = TestData6x4.Colors6.ToList() + }, + Codes = codes + }; + + SimpleGameGuessAnalyzer analyzer = new(game,guesses.ToPegs().ToArray(), moveNumber); + return analyzer.GetResult(); + } + + private static IGame TestSkeletonWithGame(string[] codes, string[] guesses, int moveNumber = 1) + { + MockColorGame game = new() + { + GameType = GameTypes.Game6x4, + NumberCodes = 4, + MaxMoves = 12, + IsVictory = false, + FieldValues = new Dictionary>() + { + [FieldCategories.Colors] = TestData6x4.Colors6.ToList() + }, + Codes = codes + }; + + SimpleGameGuessAnalyzer analyzer = new(game, guesses.ToPegs().ToArray(), moveNumber); + try + { + analyzer.GetResult(); + } + catch (ArgumentException) + { + + } + return game; + } +} + +public class TestData6x4Mini : IEnumerable +{ + public static readonly string[] Colors6 = [Red, Green, Blue, Yellow, Black, White]; + + public IEnumerator GetEnumerator() + { + yield return new object[] + { + new string[] { Green, Blue, Green, Yellow }, // code + new string[] { Green, Green, Black, White }, // inputdata + new SimpleColorResult( + [ + ResultValue.CorrectPositionAndColor, + ResultValue.CorrectColor, + ResultValue.Incorrect, + ResultValue.Incorrect + ]) // expected + }; + yield return new object[] + { + new string[] { Red, Blue, Black, White }, + new string[] { Black, Black, Red, Yellow }, + new SimpleColorResult( + [ + ResultValue.CorrectColor, + ResultValue.Incorrect, + ResultValue.CorrectColor, + ResultValue.Incorrect + ]) + }; + yield return new object[] + { + new string[] { Yellow, Black, Yellow, Green }, + new string[] { Black, Black, Black, Black }, + new SimpleColorResult( + [ + ResultValue.Incorrect, + ResultValue.CorrectPositionAndColor, + ResultValue.Incorrect, + ResultValue.Incorrect + ]) + }; + yield return new object[] + { + new string[] { Yellow, Yellow, White, Red }, + new string[] { Green, Yellow, White, Red }, + new SimpleColorResult( + [ + ResultValue.Incorrect, + ResultValue.CorrectPositionAndColor, + ResultValue.CorrectPositionAndColor, + ResultValue.CorrectPositionAndColor + ]) + }; + yield return new object[] + { + new string[] { White, Black, Yellow, Black }, + new string[] { Black, Blue, Black, White }, + new SimpleColorResult( + [ + ResultValue.CorrectColor, + ResultValue.Incorrect, + ResultValue.CorrectColor, + ResultValue.CorrectColor + ]) + }; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +} From c67b87d48f49060e62a8aa473206a3fcda0e5328 Mon Sep 17 00:00:00 2001 From: Christian Nagel Date: Sat, 25 May 2024 12:08:51 -0700 Subject: [PATCH 3/6] fix Game6x4 implementation --- .../Analyzers/SimpleGameGuessAnalyzer.cs | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Analyzers/SimpleGameGuessAnalyzer.cs b/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Analyzers/SimpleGameGuessAnalyzer.cs index 5e768ced..cc4593af 100644 --- a/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Analyzers/SimpleGameGuessAnalyzer.cs +++ b/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Analyzers/SimpleGameGuessAnalyzer.cs @@ -11,39 +11,40 @@ protected override void ValidateGuessValues() protected override SimpleColorResult GetCoreResult() { // Check black and white keyPegs - List codesToCheck = new(_game.Codes.ToPegs()); - List guessPegsToCheck = new(Guesses); + List codesToCheck = [.. _game.Codes.ToPegs().Select(cf => cf.ToString()) ]; + List guessPegsToCheck = [.. Guesses.Select(g => g.ToString())]; + List positionsToIgnore = []; - var results = Enumerable.Repeat(ResultValue.Incorrect, 4).ToArray(); - - for (int i = 0; i < results.Length; i++) - { - results[i] = ResultValue.Incorrect; - } + ResultValue[] results = [.. Enumerable.Repeat(ResultValue.Incorrect, 4)]; // check black - for (int i = 0; i < _game.Codes.Length; i++) + for (int i = 0; i < guessPegsToCheck.Count; i++) { // check black if (guessPegsToCheck[i] == codesToCheck[i]) { results[i] = ResultValue.CorrectPositionAndColor; - } - else // check white - { - if (codesToCheck.Contains(codesToCheck[i]) && results[i] == ResultValue.Incorrect) - { - results[i] = ResultValue.CorrectColor; - } + positionsToIgnore.Add(i); + codesToCheck[i] = string.Empty; } } + // check white + for (int i = 0; i < guessPegsToCheck.Count; i++) + { + if (positionsToIgnore.Contains(i)) continue; + int ix = codesToCheck.IndexOf(guessPegsToCheck[i]); + if (ix == -1) continue; + results[i] = ResultValue.CorrectColor; + codesToCheck[ix] = string.Empty; + } + return new SimpleColorResult(results); } protected override void SetGameEndInformation(SimpleColorResult result) { - bool allCorrect = result.Results.Any(r => r == ResultValue.CorrectColor); + bool allCorrect = result.Results.All(r => r == ResultValue.CorrectColor); if (allCorrect || _game.LastMoveNumber >= _game.MaxMoves) { From be17bc92b753f5b993418936a7be3bfb9d382a42 Mon Sep 17 00:00:00 2001 From: Christian Nagel Date: Sat, 25 May 2024 12:50:18 -0700 Subject: [PATCH 4/6] add tests for result checks --- .../Analyzers/SimpleGameGuessAnalyzerTests.cs | 71 +++++++++++++++++-- 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/src/services/gameapi/Codebreaker.GameAPIs.Analyzers.Tests/Analyzers/SimpleGameGuessAnalyzerTests.cs b/src/services/gameapi/Codebreaker.GameAPIs.Analyzers.Tests/Analyzers/SimpleGameGuessAnalyzerTests.cs index 9aaa2443..61fea580 100644 --- a/src/services/gameapi/Codebreaker.GameAPIs.Analyzers.Tests/Analyzers/SimpleGameGuessAnalyzerTests.cs +++ b/src/services/gameapi/Codebreaker.GameAPIs.Analyzers.Tests/Analyzers/SimpleGameGuessAnalyzerTests.cs @@ -16,7 +16,7 @@ public void SetMoveShouldReturnThreeCorrectColor() ResultValue.CorrectColor, ResultValue.Incorrect ]); - SimpleColorResult? resultKeyPegs = TestSkeleton( + SimpleColorResult resultKeyPegs = TestSkeleton( [Green, Yellow, Green, Black], [Yellow, Green, Black, Blue] ); @@ -33,7 +33,7 @@ public void SetMoveUsingVariousDataUsingDataClass(string[] code, string[] guess, } [Fact] - public void ShouldThrowOnInvalidGuessCount() + public void SetMove_ShouldThrowOnInvalidGuessCount() { Assert.Throws(() => TestSkeleton( @@ -43,7 +43,7 @@ public void ShouldThrowOnInvalidGuessCount() } [Fact] - public void ShouldThrowOnInvalidGuessValues() + public void SetMove_ShouldThrowOnInvalidGuessValues() { Assert.Throws(() => TestSkeleton( @@ -53,7 +53,7 @@ public void ShouldThrowOnInvalidGuessValues() } [Fact] - public void ShouldThrowOnInvalidMoveNumber() + public void SetMove_ShouldThrowOnInvalidMoveNumber() { Assert.Throws(() => TestSkeleton( @@ -62,7 +62,7 @@ public void ShouldThrowOnInvalidMoveNumber() } [Fact] - public void ShouldNotIncrementMoveNumberOnInvalidMove() + public void GetResult_ShouldNotIncrementMoveNumberOnInvalidMove() { IGame game = TestSkeletonWithGame( [Green, Yellow, Green, Black], @@ -71,6 +71,63 @@ public void ShouldNotIncrementMoveNumberOnInvalidMove() Assert.Equal(0, game?.LastMoveNumber); } + [Fact] + public void GetResult_WithGameWon_ShouldSetCorrectGameEndInformation() + { + // Arrange + var game = new MockColorGame + { + GameType = GameTypes.Game6x4Mini, + NumberCodes = 4, + MaxMoves = 12, + IsVictory = false, + FieldValues = new Dictionary>() + { + [FieldCategories.Colors] = [.. TestData6x4.Colors6] + }, + Codes = ["Red", "Blue", "Green", "Yellow"] + }; + + var guesses = new string[] { "Red", "Blue", "Green", "Yellow" }.ToPegs().ToArray(); + var analyzer = new SimpleGameGuessAnalyzer(game, guesses, 1); + + // Act + var result = analyzer.GetResult(); + + // Assert + Assert.NotNull(game.EndTime); + Assert.NotNull(game.Duration); + Assert.True(game.IsVictory); + } + + [Fact] + public void GetResult_WithGameNotComplete_ShouldSetCorrectGameEndInformation() + { + // Arrange + var game = new MockColorGame + { + GameType = GameTypes.Game6x4Mini, + NumberCodes = 4, + MaxMoves = 12, + IsVictory = false, + FieldValues = new Dictionary>() + { + [FieldCategories.Colors] = [.. TestData6x4.Colors6] + }, + Codes = ["Red", "Blue", "Green", "Yellow"] + }; + + var guesses = new string[] { "Red", "Yellow", "Green", "Yellow" }.ToPegs().ToArray(); + var analyzer = new SimpleGameGuessAnalyzer(game, guesses, 1); + + // Act + var result = analyzer.GetResult(); + + // Assert + Assert.Null(game.EndTime); + Assert.False(game.IsVictory); + } + private static SimpleColorResult TestSkeleton(string[] codes, string[] guesses, int moveNumber = 1) { MockColorGame game = new() @@ -81,7 +138,7 @@ private static SimpleColorResult TestSkeleton(string[] codes, string[] guesses, IsVictory = false, FieldValues = new Dictionary>() { - [FieldCategories.Colors ] = TestData6x4.Colors6.ToList() + [FieldCategories.Colors] = [.. TestData6x4.Colors6] }, Codes = codes }; @@ -100,7 +157,7 @@ private static IGame TestSkeletonWithGame(string[] codes, string[] guesses, int IsVictory = false, FieldValues = new Dictionary>() { - [FieldCategories.Colors] = TestData6x4.Colors6.ToList() + [FieldCategories.Colors] = [.. TestData6x4.Colors6] }, Codes = codes }; From 533a1158f22847651bd55f4e29c39a653766c876 Mon Sep 17 00:00:00 2001 From: Christian Nagel Date: Sat, 25 May 2024 12:50:51 -0700 Subject: [PATCH 5/6] fix simple game result settings --- .../Analyzers/SimpleGameGuessAnalyzer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Analyzers/SimpleGameGuessAnalyzer.cs b/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Analyzers/SimpleGameGuessAnalyzer.cs index cc4593af..6ec65e51 100644 --- a/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Analyzers/SimpleGameGuessAnalyzer.cs +++ b/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Analyzers/SimpleGameGuessAnalyzer.cs @@ -44,7 +44,7 @@ protected override SimpleColorResult GetCoreResult() protected override void SetGameEndInformation(SimpleColorResult result) { - bool allCorrect = result.Results.All(r => r == ResultValue.CorrectColor); + bool allCorrect = result.Results.All(r => r == ResultValue.CorrectPositionAndColor); if (allCorrect || _game.LastMoveNumber >= _game.MaxMoves) { From af43847b213d4982b5384f2282f082a8bdc448e5 Mon Sep 17 00:00:00 2001 From: Christian Nagel Date: Sat, 25 May 2024 12:51:52 -0700 Subject: [PATCH 6/6] cleanup --- .../Analyzers/GameGuessAnalyzer.cs | 15 ++++----------- .../Codebreaker.Analyzers.csproj | 2 +- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Analyzers/GameGuessAnalyzer.cs b/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Analyzers/GameGuessAnalyzer.cs index 06bedf62..2fab97bd 100644 --- a/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Analyzers/GameGuessAnalyzer.cs +++ b/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Analyzers/GameGuessAnalyzer.cs @@ -5,20 +5,13 @@ /// /// The type for guesses. /// The type returned from the analysis. -public abstract class GameGuessAnalyzer : IGameGuessAnalyzer +public abstract class GameGuessAnalyzer(IGame game, TField[] guesses, int moveNumber) : IGameGuessAnalyzer where TResult : struct { - protected readonly IGame _game; - private readonly int _moveNumber; + protected readonly IGame _game = game; + private readonly int _moveNumber = moveNumber; - protected TField[] Guesses { get; private set; } - - protected GameGuessAnalyzer(IGame game, TField[] guesses, int moveNumber) - { - _game = game; - Guesses = guesses; - _moveNumber = moveNumber; - } + protected TField[] Guesses { get; private set; } = guesses; /// /// Override this method to return the result of the guess analysis. diff --git a/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Codebreaker.Analyzers.csproj b/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Codebreaker.Analyzers.csproj index b02b71bb..0a4806ab 100644 --- a/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Codebreaker.Analyzers.csproj +++ b/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Codebreaker.Analyzers.csproj @@ -7,7 +7,7 @@ enable latest - Codebreaker;CNinnovation;GameAnalyzers; + Codebreaker;CNinnovation;GameAnalyzers This library contains game analyzers for the Codebreaker app. Reference this library when creating a Codebreaker service.