Skip to content

Commit

Permalink
Merge pull request #180 from CodebreakerApp/156-fix_game6x4mini
Browse files Browse the repository at this point in the history
156 fix game6x4mini
  • Loading branch information
christiannagel authored May 25, 2024
2 parents d8fb793 + af43847 commit 1cf3ea0
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -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<ArgumentException>(() =>
TestSkeleton(
["Black", "Black", "Black", "Black"],
["Black"]
));
}

[Fact]
public void SetMove_ShouldThrowOnInvalidGuessValues()
{
Assert.Throws<ArgumentException>(() =>
TestSkeleton(
["Black", "Black", "Black", "Black"],
["Black", "Der", "Blue", "Yellow"] // "Der" is the wrong value
));
}

[Fact]
public void SetMove_ShouldThrowOnInvalidMoveNumber()
{
Assert.Throws<ArgumentException>(() =>
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<string, IEnumerable<string>>()
{
[FieldCategories.Colors] = [.. TestData6x4.Colors6]
},
Codes = ["Red", "Blue", "Green", "Yellow"]
};

var guesses = new string[] { "Red", "Blue", "Green", "Yellow" }.ToPegs<ColorField>().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<string, IEnumerable<string>>()
{
[FieldCategories.Colors] = [.. TestData6x4.Colors6]
},
Codes = ["Red", "Blue", "Green", "Yellow"]
};

var guesses = new string[] { "Red", "Yellow", "Green", "Yellow" }.ToPegs<ColorField>().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<string, IEnumerable<string>>()
{
[FieldCategories.Colors] = [.. TestData6x4.Colors6]
},
Codes = codes
};

SimpleGameGuessAnalyzer analyzer = new(game,guesses.ToPegs<ColorField>().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<string, IEnumerable<string>>()
{
[FieldCategories.Colors] = [.. TestData6x4.Colors6]
},
Codes = codes
};

SimpleGameGuessAnalyzer analyzer = new(game, guesses.ToPegs<ColorField>().ToArray(), moveNumber);
try
{
analyzer.GetResult();
}
catch (ArgumentException)
{

}
return game;
}
}

public class TestData6x4Mini : IEnumerable<object[]>
{
public static readonly string[] Colors6 = [Red, Green, Blue, Yellow, Black, White];

public IEnumerator<object[]> 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();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,13 @@
/// </summary>
/// <typeparam name="TField">The type for guesses.</typeparam>
/// <typeparam name="TResult">The type returned from the analysis.</typeparam>
public abstract class GameGuessAnalyzer<TField, TResult> : IGameGuessAnalyzer<TResult>
public abstract class GameGuessAnalyzer<TField, TResult>(IGame game, TField[] guesses, int moveNumber) : IGameGuessAnalyzer<TResult>
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;

/// <summary>
/// Override this method to return the result of the guess analysis.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,40 @@ protected override void ValidateGuessValues()
protected override SimpleColorResult GetCoreResult()
{
// Check black and white keyPegs
List<ColorField> codesToCheck = new(_game.Codes.ToPegs<ColorField>());
List<ColorField> guessPegsToCheck = new(Guesses);
List<string> codesToCheck = [.. _game.Codes.ToPegs<ColorField>().Select(cf => cf.ToString()) ];
List<string> guessPegsToCheck = [.. Guesses.Select(g => g.ToString())];
List<int> 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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<PackageTags>
Codebreaker;CNinnovation;GameAnalyzers;
Codebreaker;CNinnovation;GameAnalyzers
</PackageTags>
<Description>
This library contains game analyzers for the Codebreaker app. Reference this library when creating a Codebreaker service.
Expand Down

0 comments on commit 1cf3ea0

Please sign in to comment.