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..61fea580 --- /dev/null +++ b/src/services/gameapi/Codebreaker.GameAPIs.Analyzers.Tests/Analyzers/SimpleGameGuessAnalyzerTests.cs @@ -0,0 +1,247 @@ +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 SetMove_ShouldThrowOnInvalidGuessCount() + { + Assert.Throws(() => + TestSkeleton( + ["Black", "Black", "Black", "Black"], + ["Black"] + )); + } + + [Fact] + public void SetMove_ShouldThrowOnInvalidGuessValues() + { + Assert.Throws(() => + TestSkeleton( + ["Black", "Black", "Black", "Black"], + ["Black", "Der", "Blue", "Yellow"] // "Der" is the wrong value + )); + } + + [Fact] + public void SetMove_ShouldThrowOnInvalidMoveNumber() + { + Assert.Throws(() => + TestSkeleton( + [Green, Yellow, Green, Black], + [Yellow, Green, Black, Blue], moveNumber: 2)); + } + + [Fact] + public void GetResult_ShouldNotIncrementMoveNumberOnInvalidMove() + { + IGame game = TestSkeletonWithGame( + [Green, Yellow, Green, Black], + [Yellow, Green, Black, Blue], moveNumber: 2); + + 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() + { + GameType = GameTypes.Game6x4Mini, + NumberCodes = 4, + MaxMoves = 12, + IsVictory = false, + FieldValues = new Dictionary>() + { + [FieldCategories.Colors] = [.. TestData6x4.Colors6] + }, + 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] + }, + 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(); +} 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/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/Analyzers/SimpleGameGuessAnalyzer.cs b/src/services/gameapi/Codebreaker.GameAPIs.Analyzers/Analyzers/SimpleGameGuessAnalyzer.cs index 5e768ced..6ec65e51 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.CorrectPositionAndColor); if (allCorrect || _game.LastMoveNumber >= _game.MaxMoves) { 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. 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