You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
450 lines
16 KiB
450 lines
16 KiB
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.Serialization;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Threading.Tasks.Dataflow;
|
|
using Models.Events;
|
|
using Models.Exceptions;
|
|
using Models.Interfaces;
|
|
using Models.Rules;
|
|
|
|
namespace Models.Game
|
|
{
|
|
/// <summary>
|
|
/// The Game class represents a game session in the application.
|
|
/// It contains all the necessary properties and methods to manage a game, including the game loop, dice rolling, and use of the game rules.
|
|
/// </summary>
|
|
[DataContract]
|
|
public class Game : INotifyPropertyChanged
|
|
{
|
|
/* Persistence */
|
|
public IPersistence PersistenceManager { get; set; }
|
|
|
|
/* List for the game and persistence */
|
|
private ObservableCollection<Player> _players;
|
|
|
|
public ObservableCollection<Player> Players
|
|
{
|
|
get => _players;
|
|
set
|
|
{
|
|
_players = value;
|
|
OnPropertyChanged(nameof(Players));
|
|
}
|
|
}
|
|
|
|
private ObservableCollection<Game> _games;
|
|
|
|
public ObservableCollection<Game> Games
|
|
{
|
|
get => _games;
|
|
set
|
|
{
|
|
_games = value;
|
|
OnPropertyChanged(nameof(Games));
|
|
}
|
|
}
|
|
|
|
private ObservableCollection<Map> _maps;
|
|
|
|
public ObservableCollection<Map> Maps
|
|
{
|
|
get => _maps;
|
|
set
|
|
{
|
|
_maps = value;
|
|
OnPropertyChanged(nameof(Maps));
|
|
}
|
|
}
|
|
|
|
private ObservableCollection<BestScore> _bestScores;
|
|
|
|
public ObservableCollection<BestScore> BestScores
|
|
{
|
|
get => _bestScores;
|
|
set
|
|
{
|
|
_bestScores = value;
|
|
OnPropertyChanged(nameof(BestScores));
|
|
}
|
|
}
|
|
|
|
private bool _isRunning;
|
|
[DataMember]
|
|
public bool IsRunning
|
|
{
|
|
get => _isRunning;
|
|
private set => _isRunning = value;
|
|
}
|
|
|
|
[DataMember]
|
|
public Player? CurrentPlayer { get; private set; }
|
|
|
|
[DataMember]
|
|
public Map? UsedMap { get; private set; }
|
|
|
|
public Dice Dice1 { get; private set;}
|
|
|
|
public Dice Dice2 { get; private set; }
|
|
|
|
[DataMember]
|
|
public int Turn { get; private set; }
|
|
|
|
public Rules.Rules GameRules { get; }
|
|
|
|
|
|
// == Events ==
|
|
public event EventHandler<GameStartedEventArgs> GameStarted;
|
|
public event EventHandler<GameEndedEventArgs> GameEnded;
|
|
public event EventHandler<BoardsUpdateEventArgs> BoardUpdated;
|
|
public event EventHandler<DiceRolledEventArgs> DiceRolled;
|
|
public event EventHandler<OperationChosenEventArgs> OperationChosen;
|
|
public event EventHandler<CellChosenEventArgs> CellChosen;
|
|
|
|
public void AddPlayer(Player player)
|
|
{
|
|
Players.Add(player);
|
|
}
|
|
|
|
public void AddGame(Game game)
|
|
{
|
|
Games.Add(game);
|
|
}
|
|
|
|
public void AddMap(Map map)
|
|
{
|
|
Maps.Add(map);
|
|
}
|
|
|
|
public void AddBestScore(BestScore bestScore)
|
|
{
|
|
BestScores.Add(bestScore);
|
|
}
|
|
|
|
public bool RemovePlayer(string playerName)
|
|
{
|
|
Player player = Players.FirstOrDefault(p => p.Pseudo == playerName);
|
|
if (player == null)
|
|
{
|
|
return false;
|
|
}
|
|
Players.Remove(player);
|
|
return true;
|
|
}
|
|
|
|
public bool ModifyPlayer(string pseudo, string newpseudo)
|
|
{
|
|
foreach (var index in Players)
|
|
{
|
|
if (index.Pseudo == pseudo)
|
|
{
|
|
index.Pseudo = newpseudo;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void LoadData()
|
|
{
|
|
var data = PersistenceManager.LoadData();
|
|
foreach (var player in data.Item1)
|
|
{
|
|
Players.Add(player);
|
|
}
|
|
foreach (var game in data.Item2)
|
|
{
|
|
Games.Add(game);
|
|
}
|
|
foreach (var map in data.Item3)
|
|
{
|
|
Maps.Add(map);
|
|
}
|
|
foreach (var bestScore in data.Item4)
|
|
{
|
|
BestScores.Add(bestScore);
|
|
}
|
|
}
|
|
|
|
public void SaveData() => PersistenceManager.SaveData(Players, Games, Maps, BestScores);
|
|
|
|
public Game(IPersistence persistenceManager)
|
|
{
|
|
PersistenceManager = persistenceManager;
|
|
|
|
Players = new ObservableCollection<Player>();
|
|
Games = new ObservableCollection<Game>();
|
|
Maps = new ObservableCollection<Map>();
|
|
BestScores = new ObservableCollection<BestScore>();
|
|
|
|
GameRules = new Rules.Rules();
|
|
|
|
IsRunning = false;
|
|
}
|
|
|
|
public Game()
|
|
{
|
|
Players = new ObservableCollection<Player>();
|
|
Games = new ObservableCollection<Game>();
|
|
Maps = new ObservableCollection<Map>();
|
|
BestScores = new ObservableCollection<BestScore>();
|
|
|
|
GameRules = new Rules.Rules();
|
|
UsedMap = new Map("test");
|
|
IsRunning = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rolls all the dice.
|
|
/// </summary>
|
|
public void RollAllDice()
|
|
{
|
|
Dice1.Roll();
|
|
Dice2.Roll();
|
|
DiceRolled?.Invoke(this, new DiceRolledEventArgs(Dice1.Value, Dice2.Value));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Marks an operation as checked in the operation grid of the game.
|
|
/// </summary>
|
|
/// <param name="operation"></param>
|
|
private void MarkOperationAsChecked(Operation operation)
|
|
{
|
|
int operationIndex = (int)operation;
|
|
int operationsPerType = 4; // Chaque type d'opération peut être fait 4 fois
|
|
|
|
for (int i = operationIndex * operationsPerType; i < (operationIndex + 1) * operationsPerType; i++)
|
|
{
|
|
if (!UsedMap.OperationGrid[i].IsChecked)
|
|
{
|
|
UsedMap.OperationGrid[i].Check();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Performs an operation on the values of two dice based on the provided operation.
|
|
/// </summary>
|
|
/// <param name="o">The operation to perform. This can be LOWER, HIGHER, SUBTRACTION, ADDITION, or MULTIPLICATION.</param>
|
|
/// <returns>
|
|
/// The result of the operation. If the operation is LOWER or HIGHER, it returns the lower or higher value of the two dice respectively.
|
|
/// If the operation is SUBTRACTION, it returns the difference between the higher and lower value of the two dice.
|
|
/// If the operation is ADDITION, it returns the sum of the values of the two dice.
|
|
/// If the operation is MULTIPLICATION, it returns the product of the values of the two dice.
|
|
/// If the operation is not one of the operations, it throws an ArgumentOutOfRangeException.
|
|
/// </returns>
|
|
public int ResultOperation(Operation o)
|
|
{
|
|
int result = o switch
|
|
{
|
|
Operation.LOWER => Dice1.IsLower(Dice2) ? Dice1.Value : Dice2.Value,
|
|
Operation.HIGHER => Dice1.IsLower(Dice2) ? Dice2.Value : Dice1.Value,
|
|
Operation.SUBTRACTION => Dice1.IsLower(Dice2) ? Dice2.Value - Dice1.Value : Dice1.Value - Dice2.Value,
|
|
Operation.ADDITION => Dice2.Value + Dice1.Value,
|
|
Operation.MULTIPLICATION => Dice2.Value * Dice1.Value,
|
|
_ => throw new ArgumentOutOfRangeException()
|
|
};
|
|
|
|
MarkOperationAsChecked(o);
|
|
return result;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Places the result of a dice operation into a chosen cell on the game board.
|
|
/// The result can be placed in the chosen cell if it's the first turn or if the chosen cell is valid according to the game rules.
|
|
/// </summary>
|
|
/// <param name="playerChoice">The cell chosen by the player to place the result.</param>
|
|
/// <param name="result">The result of the dice operation to be placed in the cell.</param>
|
|
private bool PlaceResult(Cell playerChoice, int result)
|
|
{
|
|
if (Turn == 1 || GameRules.NearCellIsValid(playerChoice, UsedMap.Boards.ToList()))
|
|
{
|
|
for (int i = 0; i < UsedMap.Boards.Count; i++)
|
|
{
|
|
if (UsedMap.Boards[i].X == playerChoice.X && UsedMap.Boards[i].Y == playerChoice.Y)
|
|
{
|
|
if (UsedMap.Boards[i].Value != null)
|
|
return false;
|
|
UsedMap.Boards[i].Value = result;
|
|
BoardUpdated?.Invoke(this, new BoardsUpdateEventArgs(UsedMap.Boards.ToList()));
|
|
return true;
|
|
}
|
|
}
|
|
//playerChoice.Value = result;
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add the choosen cell to a rope path if it's possible.
|
|
/// </summary>
|
|
/// <param name="playerChoice"></param>
|
|
/// <param name="adjacentes"></param>
|
|
private void AddToRopePath(Cell playerChoice,List<Cell> adjacentes)
|
|
{
|
|
int index =0;
|
|
|
|
foreach (var cells in adjacentes.Where(cells => cells.Value - playerChoice.Value == 1 || cells.Value - playerChoice.Value == -1))
|
|
{
|
|
// Le cas si il n'existe aucun chemin de corde
|
|
if (UsedMap.RopePaths.Count == 0)
|
|
{
|
|
// Creer un nouveau chemin de corde avec la cellule choisi par le joueur et celle adjacente
|
|
UsedMap.RopePaths.Add(new List<Cell> {playerChoice, cells});
|
|
}
|
|
|
|
|
|
// A modifier dans le cas ou il est possible de fusionner deux chemins de corde
|
|
if (GameRules.IsInRopePaths(playerChoice, UsedMap.RopePaths, index)) break;
|
|
|
|
// Le cas si il existe des chemins de corde
|
|
|
|
// Est-ce que la cellule adjacentes fait parti d'un chemin de corde
|
|
if (!GameRules.IsInRopePaths(cells,UsedMap.RopePaths,index))
|
|
{
|
|
UsedMap.RopePaths.Add(new List<Cell> { playerChoice, cells });
|
|
continue;
|
|
}
|
|
|
|
if (!GameRules.AsValue(playerChoice,UsedMap.RopePaths,index))
|
|
{
|
|
UsedMap.RopePaths[index].Add(playerChoice);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the game.
|
|
/// </summary>
|
|
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();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Starts the game.
|
|
/// </summary>
|
|
private void StartGame()
|
|
{
|
|
IsRunning = true;
|
|
GameStarted?.Invoke(this, new GameStartedEventArgs(CurrentPlayer));
|
|
GameLoop();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ends the game.
|
|
/// </summary>
|
|
private void EndGame(int? pts)
|
|
{
|
|
IsRunning = false;
|
|
GameEnded?.Invoke(this, new GameEndedEventArgs(CurrentPlayer, pts));
|
|
}
|
|
|
|
/// <summary>
|
|
/// The main game loop that runs while the game is active.
|
|
/// </summary>
|
|
private void GameLoop()
|
|
{
|
|
while (IsRunning)
|
|
{
|
|
if (Turn == 20)
|
|
{
|
|
foreach(var cells in UsedMap.Boards.ToList())
|
|
{
|
|
GameRules.IsZoneValidAndAddToZones(cells, UsedMap);
|
|
AddToRopePath(cells, GameRules.EveryAdjacentCells(cells, UsedMap.Boards.ToList()));
|
|
}
|
|
int? points = GameRules.FinalCalculusOfZones(UsedMap.Zones);
|
|
for (int i = 0; i < UsedMap.RopePaths.Count; i++)
|
|
{
|
|
points += GameRules.ScoreRopePaths(UsedMap.RopePaths[i]);
|
|
}
|
|
EndGame(points);
|
|
break;
|
|
}
|
|
|
|
RollAllDice();
|
|
|
|
Turn++;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles the player's choice of an operation based on the dice values.s
|
|
/// </summary>
|
|
/// <param name="operation">The operation chosen by the player.</param>
|
|
public void HandlePlayerOperation(Operation operation)
|
|
{
|
|
int result = ResultOperation(operation);
|
|
OperationChosen?.Invoke(this, new OperationChosenEventArgs(operation, result));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles the player's choice of a cell on the game board.
|
|
/// </summary>
|
|
/// <param name="cell"></param>
|
|
/// <param name="result"></param>
|
|
/// <exception cref="InvalidCellCoordinatesException"></exception>
|
|
/// <exception cref="InvalidCellException"></exception>
|
|
public bool HandlePlayerChoice(Cell cell, int result)
|
|
{
|
|
if (cell.X < 0 || cell.X >= UsedMap.Boards.Count / 6 || cell.Y < 0 || cell.Y >= 6)
|
|
{
|
|
return false;
|
|
//throw new InvalidCellCoordinatesException("Invalid cell coordinates. Please choose again.");
|
|
}
|
|
|
|
if (!GameRules.IsCellValid(cell, UsedMap.Boards.ToList()))
|
|
{
|
|
return false;
|
|
//throw new InvalidCellException("Cell is not valid. Please choose again.");
|
|
}
|
|
|
|
bool res = PlaceResult(cell, result);
|
|
if (!res)
|
|
{
|
|
return false;
|
|
//throw new InvalidPlaceResultException("Cell is not valid for place result. Please choose again.");
|
|
}
|
|
GameRules.IsZoneValidAndAddToZones(cell, UsedMap);
|
|
AddToRopePath(cell, GameRules.EveryAdjacentCells(cell, UsedMap.Boards.ToList()));
|
|
CellChosen?.Invoke(this, new CellChosenEventArgs(cell, result));
|
|
return true;
|
|
//BoardUpdated?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Event raised when a property is changed to notify the view.
|
|
/// </summary>
|
|
public event PropertyChangedEventHandler? PropertyChanged;
|
|
|
|
/// <summary>
|
|
/// Trigger the PropertyChanged event for a specific property.
|
|
/// </summary>
|
|
/// <param name="propertyName">Name of the property that changed.</param>
|
|
public virtual void OnPropertyChanged(string propertyName)
|
|
{
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
}
|
|
}
|
|
}
|