1er essais
All checks were successful
check main state / build (8.0.x) (push) Successful in 1m42s
Main Build Process / Build & Test (pull_request) Successful in 2m12s

This commit is contained in:
2024-08-30 16:08:51 +02:00
parent b5a6b1c653
commit 4a5fc49c31
9 changed files with 213 additions and 16 deletions

View File

@@ -0,0 +1,36 @@
using LittleTown.Core.Actions;
using LittleTown.StaticDataAcces;
namespace LittleTown.Core.Tests;
public class MatchUpdaterTesting
{
private class TestAction : IAction
{
public Action<MatchUpdater> CB { get; init; } = delegate { };
public void Execute(MatchUpdater updater)
{
CB(updater);
}
}
[Fact]
public void EnforcePlayerCountInMatchCreation()
{
StaticDataGetter getter = new StaticDataGetter();
Match match = new Match(getter);
match.AddPlayer("Player1");
match.AddPlayer("Player2");
match.Init();
TestAction ta = new TestAction()
{
CB = (updater) =>
{
Assert.True(updater.GetMatch() == match);
}
};
}
}

View File

@@ -0,0 +1,34 @@
using LittleTown.Core.Actions;
using LittleTown.Core.Exceptions;
using LittleTown.StaticDataAcces;
namespace LittleTown.Core.Tests;
public class MatchWorkflowTesting
{
[Fact]
public void Simple2PlayerGame()
{
StaticDataGetter getter = new StaticDataGetter();
Match match = new Match(getter);
match.AddPlayer("Player1");
match.AddPlayer("Player2");
match.Init();
int count = 0;
while (!match.IsDone)
{
EmptyAction action = new EmptyAction();
match.ExecuteAction(action);
count++;
if (count > 40)
{
Assert.Fail("Trop d'action pour une partie vide");
}
}
Assert.Throws<MatchFinishedException>(() => match.ExecuteAction(new EmptyAction()));
}
}

View File

@@ -0,0 +1,21 @@
namespace LittleTown.Core.Exceptions;
/// <summary>
/// Exception levee quand on essais d'executer une actin dans un match terminé
/// </summary>
public class MatchFinishedException : Exception
{
/// <summary> constructeur de base </summary>
public MatchFinishedException() : base() { }
/// <summary> Constructeur avec un message d'erreur </summary>
/// <param name="message">le message decrivant l'exception en detail</param>
public MatchFinishedException(string message) : base(message) { }
/// <summary>
/// Constructeur avec un message et une exception interne
/// </summary>
/// <param name="message">Le message de l'erreur</param>
/// <param name="innerException">l'exception encapsulée</param>
public MatchFinishedException(string message, Exception innerException) : base(message, innerException) { }
}

View File

@@ -9,8 +9,6 @@
<EnableNETAnalyzers>true</EnableNETAnalyzers> <EnableNETAnalyzers>true</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild> <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<AnalysisMode>All</AnalysisMode> <AnalysisMode>All</AnalysisMode>
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@@ -0,0 +1,15 @@
namespace LittleTown.Core.Actions;
/// <summary>
/// Un action de test qui permet de faire le déroulé d'une partie, cette action ne fait rien qu'utiliser un ouvrier du joueur et passer au suivant
/// </summary>
public class EmptyAction : IAction
{
/// <inheritdoc/>
public void Execute(MatchUpdater updater)
{
ArgumentNullException.ThrowIfNull(updater);
updater.NextPlayer();
}
}

View File

@@ -0,0 +1,13 @@
namespace LittleTown.Core.Actions;
/// <summary>
/// Interface qui représente ce que doivent implémenter les actions
/// </summary>
public interface IAction
{
/// <summary>
/// Methode demandant a l'action d'appliquer ses changement au match
/// </summary>
/// <param name="updater">une interface permettant de modifier le match, elle est valide que la durée de l'appel d'execute</param>
public void Execute(MatchUpdater updater);
}

View File

@@ -0,0 +1,21 @@
namespace LittleTown.Core.Actions;
using LittleTown.Core;
using LittleTown.Core.Enums;
/// <summary>
/// Ceci est une structure offrant des méthodes pour modifier les données d'un match
/// Il permet aux actions de modifier des valeurs qui sont privées
/// </summary>
public class MatchUpdater
{
/// <summary> Permet de récupérer le match qui sera modifié par cet updater </summary>
public required Func<Match> GetMatch { get; init; }
/// <summary> Permet d'ajouter des ressources a un joueur </summary>
public required Action<ResourceType> AddRessourceToPlayer { get; init; }
/// <summary> demande au match de changer de joueur en cours </summary>
public required Action NextPlayer { get; init; }
}

View File

@@ -1,3 +1,4 @@
using LittleTown.Core.Actions;
using LittleTown.Core.Exceptions; using LittleTown.Core.Exceptions;
using LittleTown.Core.Ports; using LittleTown.Core.Ports;
@@ -8,6 +9,18 @@ namespace LittleTown.Core;
/// </summary> /// </summary>
public class Match public class Match
{ {
/// <summary> LE numero du tour en cours (Partant de 1) </summary>
public int CurrentTurn { get; private set; } = 1;
/// <summary> l'id du joueur a qui c'est le tour de jouer</summary>
public string CurrentPlayer { get => _players[_playerTurnsOrder[_currentPlayerIndex]].PlayerName; }
/// <summary> Indique si le match est terminé </summary>
public bool IsDone { get; private set; }
/// <summary>la liste indiquant l'ordre des joueurs, _playerTurnsOrder[0] donne l'index du 1er joueur, _playerTurnsOrder[1] du second..... </summary>
private List<int> _playerTurnsOrder = new List<int>();
private const int _minPlayerCount = 2; private const int _minPlayerCount = 2;
private const int _maxPlayerCount = 4; private const int _maxPlayerCount = 4;
@@ -20,18 +33,12 @@ public class Match
private Random _random = new Random(); private Random _random = new Random();
private Dictionary<string, PlayerZone> _players = new(); private List<PlayerZone> _players = new();
private int _currentPlayerIndex;
/// <summary> LE numero du tour en cours (Partant de 1) </summary>
public int CurrentTurn { get; private set; } = 1;
/// <summary>la liste indiquant l'ordre des joueurs, _playerTurnsOrder[0] donne l'index du 1er joueur, _playerTurnsOrder[1] du second..... </summary>
private List<int> _playerTurnsOrder = new List<int>();
/// <summary> /// <summary>
/// Constructeur d'une nouvelle partie avec un nombre de joueurs données en parametres /// Constructeur d'une nouvelle partie avec un nombre de joueurs données en parametres
/// </summary> /// </summary>
/// <param name="nbPlayer"></param>
/// <param name="staticData">un objet permettant de récupérer les données statiques du jeu</param> /// <param name="staticData">un objet permettant de récupérer les données statiques du jeu</param>
public Match(IStaticDataGetter staticData) public Match(IStaticDataGetter staticData)
{ {
@@ -48,11 +55,14 @@ public class Match
{ {
if (_players.Count < _maxPlayerCount) if (_players.Count < _maxPlayerCount)
{ {
if (_players.ContainsKey(playerName)) if (_players.Any(p => p.PlayerName == playerName))
{ {
throw new MatchConfigException("Un joueur existe déjà avec ce nom"); throw new MatchConfigException("Un joueur existe déjà avec ce nom");
} }
_players.Add(playerName, new PlayerZone()); _players.Add(new PlayerZone()
{
PlayerName = playerName
});
} }
else else
{ {
@@ -60,6 +70,30 @@ public class Match
} }
} }
/// <summary>
/// Demander au match d'executer une action si elle est autorisée
/// </summary>
/// <param name="action">l'action a réaliser</param>
public void ExecuteAction(IAction action)
{
ArgumentNullException.ThrowIfNull(action);
//quelques vérification génériques pour savoir si l'action peut être jouée
if (IsDone)
{
throw new MatchFinishedException("Impossible d'effectuer une action sur un match terminé");
}
//autoriser l'action a s'executer en lui donner les getters dont elle a besoin
action.Execute(new MatchUpdater()
{
GetMatch = () => { return this; },
AddRessourceToPlayer = (resourceType) => { },
NextPlayer = NextPlayer
});
}
/// <summary> Initialiser la partie, il faut avoir ajouté les joueurs au préalable </summary> /// <summary> Initialiser la partie, il faut avoir ajouté les joueurs au préalable </summary>
/// <exception cref="MatchConfigException"></exception> /// <exception cref="MatchConfigException"></exception>
public void Init() public void Init()
@@ -70,13 +104,14 @@ public class Match
ArgumentOutOfRangeException.ThrowIfGreaterThan(nbPlayer, _maxPlayerCount); ArgumentOutOfRangeException.ThrowIfGreaterThan(nbPlayer, _maxPlayerCount);
List<int> freeObjectiveIndexs = Enumerable.Range(0, _objectives.Count).ToList(); List<int> freeObjectiveIndexs = Enumerable.Range(0, _objectives.Count).ToList();
foreach (PlayerZone zone in _players.Values) foreach (PlayerZone zone in _players)
{ {
zone.AddObjectives(GetRandomObjectives(nbPlayer switch zone.AddObjectives(GetRandomObjectives(nbPlayer switch
{ {
2 => 4, 2 => 4,
3 => 3, 3 => 3,
4 => 2, 4 => 2,
_ => throw new MatchConfigException("Mauvais nombre de joueurs lors Workers")
}, freeObjectiveIndexs)); }, freeObjectiveIndexs));
zone.AddRessources(Enums.ResourceType.Piece, 3); zone.AddRessources(Enums.ResourceType.Piece, 3);
} }
@@ -106,6 +141,8 @@ public class Match
index = 0; index = 0;
} }
_currentPlayerIndex = 0;
} }
/// <summary> Permet de récuperer une player zone(une copie) </summary> /// <summary> Permet de récuperer une player zone(une copie) </summary>
@@ -113,7 +150,10 @@ public class Match
/// <returns></returns> /// <returns></returns>
public PlayerZone GetPlayerZone(string playerName) public PlayerZone GetPlayerZone(string playerName)
{ {
if (!_players.TryGetValue(playerName, out PlayerZone? value)) var value = _players.Where(p => p.PlayerName == playerName).FirstOrDefault();
if (null == value)
throw new ArgumentException("playerID is out of bound"); throw new ArgumentException("playerID is out of bound");
return value.Clone() as PlayerZone ?? throw new ArgumentException("Cast exception in GetPlayerZone"); ; return value.Clone() as PlayerZone ?? throw new ArgumentException("Cast exception in GetPlayerZone"); ;
@@ -131,4 +171,17 @@ public class Match
} }
return result; return result;
} }
private void NextPlayer()
{
_currentPlayerIndex++;
if (_currentPlayerIndex >= _players.Count)
{
_currentPlayerIndex = 0;
CurrentTurn++;
}
if (CurrentTurn >= 4)
{
IsDone = true;
}
}
} }

View File

@@ -7,7 +7,8 @@ public class PlayerZone : ICloneable
/// <summary> Les ressources que possede le joueur </summary> /// <summary> Les ressources que possede le joueur </summary>
public IDictionary<ResourceType, int> Ressources { get; init; } = new Dictionary<ResourceType, int>(); public IDictionary<ResourceType, int> Ressources { get; init; } = new Dictionary<ResourceType, int>();
/// <summary> l'id du joueur a cette zone </summary>
public required string PlayerName { get; init; }
/// <summary> La liste des objectifs que le joueur possede/// </summary> /// <summary> La liste des objectifs que le joueur possede/// </summary>
public IReadOnlyCollection<Objective> Objectives { get => _objectives.AsReadOnly(); init => _objectives = new List<Objective>(value); } public IReadOnlyCollection<Objective> Objectives { get => _objectives.AsReadOnly(); init => _objectives = new List<Objective>(value); }
@@ -32,6 +33,10 @@ public class PlayerZone : ICloneable
} }
} }
/// <summary>
/// Assigner des objectifs au joueur
/// </summary>
/// <param name="objectives"></param>
public void AddObjectives(ICollection<Objective> objectives) public void AddObjectives(ICollection<Objective> objectives)
{ {
_objectives.AddRange(objectives); _objectives.AddRange(objectives);
@@ -45,7 +50,8 @@ public class PlayerZone : ICloneable
{ {
Ressources = new Dictionary<ResourceType, int>(Ressources), Ressources = new Dictionary<ResourceType, int>(Ressources),
Objectives = new List<Objective>(Objectives), Objectives = new List<Objective>(Objectives),
ScoreMarker = ScoreMarker ScoreMarker = ScoreMarker,
PlayerName = PlayerName
}; };
return result; return result;