diff --git a/source/Trek-12/ConsoleApp/ConsoleApp.csproj b/source/Trek-12/ConsoleApp/ConsoleApp.csproj index 229387b..5d6a28e 100644 --- a/source/Trek-12/ConsoleApp/ConsoleApp.csproj +++ b/source/Trek-12/ConsoleApp/ConsoleApp.csproj @@ -8,6 +8,7 @@ + diff --git a/source/Trek-12/ConsoleApp/Program.cs b/source/Trek-12/ConsoleApp/Program.cs index cb57385..5f96c6c 100644 --- a/source/Trek-12/ConsoleApp/Program.cs +++ b/source/Trek-12/ConsoleApp/Program.cs @@ -4,6 +4,8 @@ using Models; using Models.Events; using Models.Exceptions; using Models.Game; +using Models.Interfaces; +using DataContractPersistence; namespace ConsoleApp; @@ -19,10 +21,11 @@ class Program string? pseudo = Console.ReadLine(); if (pseudo != null) { + IPersistence persistence = new DataContractXml(); Player player = new Player(pseudo); Map map = new Map("background"); - Game game = new Game(player, map); + Game game = new Game(persistence); // Abonnement aux événements game.GameStarted += OnGameStarted!; @@ -33,7 +36,7 @@ class Program game.CellChosen += OnCellChosen!; // Initialisation - game.InitializeGame(); + game.InitializeGame(map, player); } } diff --git a/source/Trek-12/Models/Game/Dice.cs b/source/Trek-12/Models/Game/Dice.cs index 2d47fe3..36bfd33 100644 --- a/source/Trek-12/Models/Game/Dice.cs +++ b/source/Trek-12/Models/Game/Dice.cs @@ -14,12 +14,12 @@ namespace Models.Game /// /// Lowest number on the dice. /// - public int NbMin { get; private set; } + public int MinVal { get; private set; } /// /// Highest number on the dice. /// - public int NbMax { get; private set; } + public int MaxVal { get; private set; } /// /// Value of the dice. @@ -29,9 +29,9 @@ namespace Models.Game get => _value; private set { - if (value < NbMin || value > NbMax) + if (value < MinVal || value > MaxVal) { - value = NbMin; + value = MinVal; } _value = value; } @@ -40,13 +40,13 @@ namespace Models.Game /// /// Initializes a new instance of the class. /// - /// The lowest number on the dice, on which the highest number is set - public Dice(int nbmin) + /// The lowest number on the dice, on which the highest number is set + public Dice(int minval) { - if (nbmin < 0) nbmin = 0; - if (nbmin > 1) nbmin = 1; - NbMin = nbmin; - NbMax = nbmin + 5; + if (minval < 0) minval = 0; + if (minval > 1) minval = 1; + MinVal = minval; + MaxVal = minval + 5; } /// @@ -54,13 +54,13 @@ namespace Models.Game /// public Dice() { - NbMin = 0; - NbMax = 5; + MinVal = 0; + MaxVal = 5; } public override string ToString() { - return $"Ce dé a pour valeur {Value} et est entre {NbMin} et {NbMax}"; + return $"Ce dé a pour valeur {Value} et est entre {MinVal} et {MaxVal}"; } /// @@ -68,7 +68,7 @@ namespace Models.Game /// public void Roll() { - Value = new Random().Next(NbMin, NbMax + 1); + Value = new Random().Next(MinVal, MaxVal + 1); } /// diff --git a/source/Trek-12/Models/Game/Game.cs b/source/Trek-12/Models/Game/Game.cs index 982b2af..c4f4460 100644 --- a/source/Trek-12/Models/Game/Game.cs +++ b/source/Trek-12/Models/Game/Game.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -27,14 +28,20 @@ namespace Models.Game public List BestScores { get; set; } private bool _isRunning; + public bool IsRunning + { + get => _isRunning; + private set => _isRunning = value; + } + public Player CurrentPlayer { get; private set; } public Map UsedMap { get; private set; } - private Dice Dice1 { get; } - private Dice Dice2 { get; } + public Dice Dice1 { get; private set; } + public Dice Dice2 { get; private set; } - private int Turn { get; set; } + public int Turn { get; private set; } public Rules.Rules GameRules { get; } @@ -98,25 +105,10 @@ namespace Models.Game Games = new List(); Maps = new List(); BestScores = new List(); - - _isRunning = false; - } - - - /// - /// Initializes a new instance of the class. - /// - /// The player who play the game. - /// The map to be used in the game. - public Game(Player player, Map map) - { - _isRunning = false; - UsedMap = map; - CurrentPlayer = player; - Dice1 = new Dice(); - Dice2 = new Dice(1); - Turn = 1; + GameRules = new Rules.Rules(); + + IsRunning = false; } /// @@ -240,13 +232,30 @@ namespace Models.Game } } } - + /// /// Initializes the game. /// - public void InitializeGame() + public void InitializeGame(Map map, Player player, bool startImmediately = true) + { + UsedMap = map; + CurrentPlayer = player; + Turn = 1; + Dice1 = new Dice(); + Dice2 = new Dice(1); + + if (startImmediately) + { + StartGame(); + } + } + + /// + /// Starts the game. + /// + private void StartGame() { - _isRunning = true; + IsRunning = true; GameStarted?.Invoke(this, new GameStartedEventArgs(CurrentPlayer)); GameLoop(); } @@ -256,7 +265,7 @@ namespace Models.Game /// private void EndGame(int? pts) { - _isRunning = false; + IsRunning = false; GameEnded?.Invoke(this, new GameEndedEventArgs(CurrentPlayer, pts)); } @@ -265,7 +274,7 @@ namespace Models.Game /// private void GameLoop() { - while (_isRunning) + while (IsRunning) { if (Turn == 20) { diff --git a/source/Trek-12/Models/Rules/Rules.cs b/source/Trek-12/Models/Rules/Rules.cs index bd8c8cf..341d1ff 100644 --- a/source/Trek-12/Models/Rules/Rules.cs +++ b/source/Trek-12/Models/Rules/Rules.cs @@ -209,6 +209,8 @@ namespace Models.Rules public List EveryAdjacentCells(Cell choosenCell, List cells) { List adjacentCells = new List(); + + if (choosenCell == null || cells == null) return adjacentCells; foreach (var cell in cells) { @@ -230,7 +232,7 @@ namespace Models.Rules calculus += zones[i].Count - 1 + zones[i][0].Value; if (zones[i].Count > 9) { - calculus += (zones[i].Count - 9) * 5; + calculus += (zones[i].Count - 9) * 5 - (zones[i].Count - 9); } } return calculus; diff --git a/source/Trek-12/Tests/DiceTests.cs b/source/Trek-12/Tests/DiceTests.cs index 7831db6..22ded18 100644 --- a/source/Trek-12/Tests/DiceTests.cs +++ b/source/Trek-12/Tests/DiceTests.cs @@ -13,30 +13,30 @@ public class DiceTests public void Constructor_WithNegativeNbMin_SetsNbMinToZero() { Dice dice = new Dice(-1); - Assert.Equal(0, dice.NbMin); + Assert.Equal(0, dice.MinVal); } [Fact] public void Constructor_WithNbMinGreaterThanOne_SetsNbMinToOne() { Dice dice = new Dice(2); - Assert.Equal(1, dice.NbMin); + Assert.Equal(1, dice.MinVal); } [Fact] public void Constructor_WithValidNbMin_SetsNbMinAndNbMaxCorrectly() { Dice dice = new Dice(1); - Assert.Equal(1, dice.NbMin); - Assert.Equal(6, dice.NbMax); + Assert.Equal(1, dice.MinVal); + Assert.Equal(6, dice.MaxVal); } [Fact] public void DefaultConstructor_SetsNbMinToZeroAndNbMaxToFive() { Dice dice = new Dice(); - Assert.Equal(0, dice.NbMin); - Assert.Equal(5, dice.NbMax); + Assert.Equal(0, dice.MinVal); + Assert.Equal(5, dice.MaxVal); } [Fact] @@ -44,6 +44,6 @@ public class DiceTests { Dice dice = new Dice(); dice.Roll(); - Assert.True(dice.Value >= dice.NbMin && dice.Value <= dice.NbMax); + Assert.True(dice.Value >= dice.MinVal && dice.Value <= dice.MaxVal); } } diff --git a/source/Trek-12/Tests/GameTests.cs b/source/Trek-12/Tests/GameTests.cs new file mode 100644 index 0000000..066aeff --- /dev/null +++ b/source/Trek-12/Tests/GameTests.cs @@ -0,0 +1,246 @@ +using Moq; +using Models.Game; +using Models.Interfaces; +using System.Reflection; +using static System.Type; + +namespace Tests; + +public class GameTests +{ + private readonly Mock _mockPersistence; // Mocking (faking) the persistence layer to allow for testing without a persistent connection + private readonly Game _game; + + public GameTests() + { + _mockPersistence = new Mock(); + _game = new Game(_mockPersistence.Object); + } + + private void SetDiceValues(Game game, int value1, int value2) + { + var dice1Field = typeof(Game).GetProperty("Dice1", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + var dice2Field = typeof(Game).GetProperty("Dice2", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + + if (dice1Field != null && dice2Field != null) + { + var dice1 = (Dice)dice1Field?.GetValue(game)!; + var dice2 = (Dice)dice2Field?.GetValue(game)!; + + var valueField = typeof(Dice).GetField("_value", BindingFlags.Instance | BindingFlags.NonPublic); + valueField?.SetValue(dice1, value1); + valueField?.SetValue(dice2, value2); + } + } + + [Fact] + public void AddPlayer_ShouldAddPlayerToList() + { + var player = new Player(); + + _game.AddPlayer(player); + + Assert.Contains(player, _game.Players); + } + + [Fact] + public void AddGame_ShouldAddGameToList() + { + var game = new Game(_mockPersistence.Object); + + _game.AddGame(game); + + Assert.Contains(game, _game.Games); + } + + [Fact] + public void AddMap_ShouldAddMapToList() + { + var map = new Map("test_background.png"); + + _game.AddMap(map); + + Assert.Contains(map, _game.Maps); + } + + [Fact] + public void AddBestScore_ShouldAddBestScoreToList() + { + var bestScore = new BestScore(3, 125); + + _game.AddBestScore(bestScore); + + Assert.Contains(bestScore, _game.BestScores); + } + + [Fact] + public void LoadData_ShouldLoadDataFromPersistence() + { + var players = new List { new Player() }; + var games = new List { new Game(_mockPersistence.Object) }; + var maps = new List { new Map("test_background") }; + var bestScores = new List { new BestScore(1, 26) }; + + _mockPersistence.Setup(p => p.LoadData()).Returns((players, games, maps, bestScores)); + + _game.LoadData(); + + Assert.Equal(players, _game.Players); + Assert.Equal(games, _game.Games); + Assert.Equal(maps, _game.Maps); + Assert.Equal(bestScores, _game.BestScores); + } + + [Fact] + public void SaveData_ShouldCallPersistenceSaveData() + { + _game.SaveData(); + + _mockPersistence.Verify(p => p.SaveData(_game.Players, _game.Games, _game.Maps, _game.BestScores), Times.Once); // Times.Once is to verify if the method is called exactly one time + } + + [Fact] + public void InitializeGame_ShouldInitializeGameAndNotTriggerEventWhenNotStarted() + { + var player = new Player(); + var map = new Map("test_background"); + bool eventTriggered = false; + + _game.GameStarted += (sender, args) => + { + eventTriggered = true; + }; + + _game.InitializeGame(map, player, false); + Assert.False(eventTriggered); + Assert.False(_game.IsRunning); + Assert.Equal(map, _game.UsedMap); + Assert.Equal(player, _game.CurrentPlayer); + } + + [Fact] + public void InitializeGame_ShouldInitializeGameAndTriggerEventWhenStarted() + { + var player = new Player(); + var map = new Map("test_background"); + bool eventTriggered = false; + + _game.GameEnded += (sender, args) => + { + eventTriggered = true; + }; + + _game.InitializeGame(map, player, true); + Assert.True(eventTriggered); + Assert.False(_game.IsRunning); + Assert.Equal(map, _game.UsedMap); + Assert.Equal(player, _game.CurrentPlayer); + } + + [Theory] + [InlineData(Operation.ADDITION, 3, 4, 7)] + [InlineData(Operation.SUBTRACTION, 6, 4, 2)] + [InlineData(Operation.MULTIPLICATION, 2, 3, 6)] + [InlineData(Operation.LOWER, 1, 4, 1)] + [InlineData(Operation.HIGHER, 2, 5, 5)] + public void HandlePlayerOperation_ShouldPerformCorrectOperationAndTriggerEvent(Operation operation, int value1, int value2, int expectedResult) + { + var player = new Player(); + var map = new Map("background"); + _game.InitializeGame(map, player, false); + + bool eventTriggered = false; + int actualResult = 0; + + _game.OperationChosen += (sender, args) => + { + eventTriggered = true; + actualResult = args.Result; + }; + + SetDiceValues(_game, value1, value2); + + + _game.HandlePlayerOperation(operation); + + + Assert.True(eventTriggered); + Assert.Equal(expectedResult, actualResult); + } + + [Fact] + public void Game_Initialization_SetsMap() + { + var player = new Player("test_player"); + var map = new Map("background"); + + _game.InitializeGame(map, player); + + Assert.Equal(map, _game.UsedMap); + } + + [Fact] + public void Game_Initialization_SetsPlayer() + { + var player = new Player("test_player"); + var map = new Map("background"); + + _game.InitializeGame(map, player); + + Assert.Equal(player, _game.CurrentPlayer); + } + + [Fact] + public void Game_Initialization_SetsDice() + { + Player player = new Player(); + Map map = new Map("test_background"); + + _game.InitializeGame(map, player); + + Assert.NotNull(_game.Dice1); + Assert.NotNull(_game.Dice2); + } + + [Fact] + public void Game_Initialization_SetsGameRules() + { + var game = new Game(_mockPersistence.Object); + + Assert.NotNull(game.GameRules); + } + + [Fact] + public void MarkOperationAsChecked_Check_Well() + { + var player = new Player(); + var map = new Map("test_background"); + + _game.InitializeGame(map, player); + + // Use of reflection to call private method + var methodInfo = typeof(Game).GetMethod("MarkOperationAsChecked", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(methodInfo); + + var operation = Operation.ADDITION; + + + methodInfo.Invoke(_game, new object[] { operation }); + + + int operationIndex = (int)operation; + int operationsPerType = 4; + bool isChecked = false; + + for (int i = operationIndex * operationsPerType; i < (operationIndex + 1) * operationsPerType; i++) + { + if (_game.UsedMap.OperationGrid[i].IsChecked) + { + isChecked = true; + break; + } + } + + Assert.True(isChecked); + } +} diff --git a/source/Trek-12/Tests/RulesTests.cs b/source/Trek-12/Tests/RulesTests.cs index 56ec387..7cfe05a 100644 --- a/source/Trek-12/Tests/RulesTests.cs +++ b/source/Trek-12/Tests/RulesTests.cs @@ -232,4 +232,294 @@ public class RulesTests rules.IsZoneValidAndAddToZones(cell, map); Assert.Contains(cell, map.Zones[0]); } + + [Fact] + public void IsValueInZones_ReturnsFalse_WhenCellIsNull() + { + Rules rules = new Rules(); + Assert.False(rules.IsValueInZones(null, new List>())); + } + + [Fact] + public void IsValueInZones_ReturnsFalse_WhenZonesAreEmpty() + { + Rules rules = new Rules(); + Cell cell = new Cell(0, 0); + cell.Value = 1; + Assert.False(rules.IsValueInZones(cell, new List>())); + } + + [Fact] + public void IsValueInZones_ReturnsFalse_WhenNoZoneContainsCellWithSameValue() + { + Rules rules = new Rules(); + Cell cell = new Cell(0, 0); + cell.Value = 1; + List> zones = new List> + { + new List { new Cell(0, 1) { Value = 2 } }, + new List { new Cell(1, 0) { Value = 3 } } + }; + Assert.False(rules.IsValueInZones(cell, zones)); + } + + [Fact] + public void IsValueInZones_ReturnsTrue_WhenZoneContainsCellWithSameValue() + { + Rules rules = new Rules(); + Cell cell = new Cell(0, 0); + cell.Value = 1; + List> zones = new List> + { + new List { new Cell(0, 1) { Value = 1 } }, + new List { new Cell(1, 0) { Value = 3 } } + }; + Assert.True(rules.IsValueInZones(cell, zones)); + } + + [Fact] + public void IsCellInZone_ReturnsFalse_WhenCellIsNull() + { + Rules rules = new Rules(); + Assert.False(rules.IsCellInZone(null, new List>())); + } + + [Fact] + public void IsCellInZone_ReturnsFalse_WhenZonesAreEmpty() + { + Rules rules = new Rules(); + Cell cell = new Cell(0, 0); + Assert.False(rules.IsCellInZone(cell, new List>())); + } + + [Fact] + public void IsCellInZone_ReturnsFalse_WhenNoZoneContainsCell() + { + Rules rules = new Rules(); + Cell cell = new Cell(0, 0); + List> zones = new List> + { + new List { new Cell(0, 1) }, + new List { new Cell(1, 0) } + }; + Assert.False(rules.IsCellInZone(cell, zones)); + } + + [Fact] + public void IsCellInZone_ReturnsTrue_WhenZoneContainsCell() + { + Rules rules = new Rules(); + Cell cell = new Cell(0, 0); + List> zones = new List> + { + new List { new Cell(0, 1) }, + new List { cell } + }; + Assert.True(rules.IsCellInZone(cell, zones)); + } + + [Fact] + public void AddToZone_DoesNothing_WhenCellIsNull() + { + Rules rules = new Rules(); + List> zones = new List>(); + rules.AddToZone(null, zones); + Assert.Empty(zones); + } + + [Fact] + public void AddToZone_DoesNothing_WhenCellValueIsNull() + { + Rules rules = new Rules(); + Cell cell = new Cell(0, 0); + List> zones = new List>(); + rules.AddToZone(cell, zones); + Assert.Empty(zones); + } + + [Fact] + public void AddToZone_DoesNothing_WhenCellIsAlreadyInZone() + { + Rules rules = new Rules(); + Cell cell = new Cell(0, 0); + cell.Value = 1; + List> zones = new List> + { + new List { cell } + }; + rules.AddToZone(cell, zones); + Assert.Single(zones[0]); // Verify if the List has still only one element + } + + [Fact] + public void AddToZone_AddsCellToExistingZone_WhenCellHasSameValueAsZone() + { + Rules rules = new Rules(); + Cell cell = new Cell(0, 0); + cell.Value = 1; + List> zones = new List> + { + new List { new Cell(0, 1) { Value = 1 } } + }; + rules.AddToZone(cell, zones); + Assert.Contains(cell, zones[0]); + } + + [Fact] + public void NewZoneIsCreated_DoesNothing_WhenFirstCellIsNull() + { + Rules rules = new Rules(); + Map map = new Map("background"); + rules.NewZoneIsCreated(null, new Cell(0, 0), map); + Assert.Empty(map.Zones); + } + + [Fact] + public void NewZoneIsCreated_DoesNothing_WhenSecondCellIsNull() + { + Rules rules = new Rules(); + Map map = new Map("background"); + rules.NewZoneIsCreated(new Cell(0, 0), null, map); + Assert.Empty(map.Zones); + } + + [Fact] + public void NewZoneIsCreated_DoesNothing_WhenFirstCellValueIsNull() + { + Rules rules = new Rules(); + Map map = new Map("background"); + Cell firstCell = new Cell(0, 0); + rules.NewZoneIsCreated(firstCell, new Cell(0, 1), map); + Assert.Empty(map.Zones); + } + + [Fact] + public void NewZoneIsCreated_DoesNothing_WhenSecondCellValueIsNull() + { + Rules rules = new Rules(); + Map map = new Map("background"); + Cell secondCell = new Cell(0, 0); + rules.NewZoneIsCreated(new Cell(0, 1), secondCell, map); + Assert.Empty(map.Zones); + } + + [Fact] + public void NewZoneIsCreated_CreatesNewZone_WhenBothCellsAreNotNullAndHaveValues() + { + Rules rules = new Rules(); + Map map = new Map("background"); + Cell firstCell = new Cell(0, 0); + firstCell.Value = 1; + Cell secondCell = new Cell(0, 1); + secondCell.Value = 1; + rules.NewZoneIsCreated(firstCell, secondCell, map); + Assert.Single(map.Zones); + Assert.Contains(firstCell, map.Zones[0]); + Assert.Contains(secondCell, map.Zones[0]); + } + + [Fact] + public void EveryAdjacentCells_ReturnsEmptyList_WhenCellIsNull() + { + Rules rules = new Rules(); + List cells = new List { new Cell(0, 0), new Cell(0, 1) }; + Assert.Empty(rules.EveryAdjacentCells(null, cells)); + } + + [Fact] + public void EveryAdjacentCells_ReturnsEmptyList_WhenCellsAreEmpty() + { + Rules rules = new Rules(); + Cell cell = new Cell(0, 0); + Assert.Empty(rules.EveryAdjacentCells(cell, new List())); + } + + [Fact] + public void EveryAdjacentCells_ReturnsEmptyList_WhenNoAdjacentCells() + { + Rules rules = new Rules(); + Cell cell = new Cell(0, 0); + List cells = new List { new Cell(2, 2), new Cell(3, 3) }; + Assert.Empty(rules.EveryAdjacentCells(cell, cells)); + } + + [Fact] + public void EveryAdjacentCells_ReturnsListWithAdjacentCells_WhenAdjacentCellsExist() + { + Rules rules = new Rules(); + Cell cell = new Cell(0, 0); + List cells = new List { new Cell(0, 1), new Cell(1, 0), new Cell(2, 2) }; + List result = rules.EveryAdjacentCells(cell, cells); + Assert.Equal(2, result.Count); + Assert.Contains(new Cell(0, 1), result); + Assert.Contains(new Cell(1, 0), result); + } + + [Fact] + public void FinalCalculusOfZones_ReturnsZero_WhenZonesAreEmpty() + { + Rules rules = new Rules(); + Assert.Equal(0, rules.FinalCalculusOfZones(new List>())); + } + + [Fact] + public void FinalCalculusOfZones_ReturnsCorrectValue_WhenZonesAreNotEmpty() + { + Rules rules = new Rules(); + Cell cell = new Cell(0, 0); + cell.Value = 1; + Cell cell1 = new Cell(0, 1); + cell1.Value = 1; + Cell cell2 = new Cell(0, 2); + cell2.Value = 1; + Cell uncell = new Cell(0, 0); + uncell.Value = 4; + Cell deuxcell = new Cell(0, 1); + deuxcell.Value = 4; + Cell troiscell = new Cell(0, 2); + troiscell.Value = 4; + List> zones = new List> + { + new List { cell, cell1, cell2 }, + new List { uncell, deuxcell, troiscell } + }; + + Assert.Equal(9, rules.FinalCalculusOfZones(zones)); + } + + [Fact] + public void FinalCalculusOfZones_ReturnsCorrectValue_WhenZoneCountIsGreaterThanNine() + { + Rules rules = new Rules(); + Cell cell = new Cell(0, 0); + cell.Value = 5; + Cell cell1 = new Cell(0, 1); + cell1.Value = 5; + Cell cell2 = new Cell(0, 2); + cell2.Value = 5; + Cell cell3 = new Cell(0, 3); + cell3.Value = 5; + Cell cell4 = new Cell(0, 4); + cell4.Value = 5; + Cell cell5 = new Cell(0, 5); + cell5.Value = 5; + Cell cell6 = new Cell(1, 0); + cell6.Value = 5; + Cell cell7 = new Cell(1, 1); + cell7.Value = 5; + Cell cell8 = new Cell(1, 2); + cell8.Value = 5; + Cell cell9 = new Cell(1, 3); + cell9.Value = 5; + Cell cell10 = new Cell(1, 4); + cell10.Value = 5; + + List> zones = new List> + { + new List { cell, cell1, cell2, cell3, cell4, cell5, cell6, cell7, cell8, cell9, cell10 } + }; + + Assert.Equal(23, rules.FinalCalculusOfZones(zones)); + } + } \ No newline at end of file diff --git a/source/Trek-12/Tests/Tests.csproj b/source/Trek-12/Tests/Tests.csproj index 2fea905..4ea534a 100644 --- a/source/Trek-12/Tests/Tests.csproj +++ b/source/Trek-12/Tests/Tests.csproj @@ -10,8 +10,9 @@ - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all