Merge pull request 'v0.9 - Application MAUI' (#112) from dev into master
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details

Reviewed-on: #112
Reviewed-by: Remi NEVEU <remi.neveu@etu.uca.fr>
master v0.9
Rémi LAVERGNE 11 months ago
commit 4ea6ec094b

@ -8,6 +8,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DataContractPersistence\DataContractPersistence.csproj" />
<ProjectReference Include="..\Models\Models.csproj" />
</ItemGroup>

@ -1,246 +1,416 @@
// See https://aka.ms/new-console-template for more information
using Models;
using Models.Events;
using Models.Exceptions;
using Models.Game;
namespace ConsoleApp;
class Program
{
/// <summary>
/// Main function of the console app
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
Console.WriteLine("Enter your pseudo:");
string? pseudo = Console.ReadLine();
if (pseudo != null)
{
Player player = new Player(pseudo);
Map map = new Map("background");
Game game = new Game(player, map);
// Abonnement aux événements
game.GameStarted += OnGameStarted!;
game.GameEnded += OnGameEnded!;
game.BoardUpdated += OnBoardUpdated!;
game.DiceRolled += OnDiceRolled!;
game.OperationChosen += OnOperationChosen!;
game.CellChosen += OnCellChosen!;
// Initialisation
game.InitializeGame();
}
}
/// <summary>
/// Handles the event when the game has started.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnGameStarted(object sender, GameStartedEventArgs e)
{
Console.WriteLine($"The game has started! Player: {e.CurrentPlayer.Pseudo}");
}
/// <summary>
/// Handles the event when the game has ended.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnGameEnded(object sender, GameEndedEventArgs e)
{
Console.WriteLine($"The game has ended! Player: {e.CurrentPlayer.Pseudo}");
}
/// <summary>
/// Handles the event when the board is updated.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnBoardUpdated(object sender, EventArgs e)
{
DisplayBoard(((Game)sender).UsedMap);
DisplayOperationTable(((Game)sender).UsedMap.OperationGrid);
}
/// <summary>
/// Handles the event when the dice are rolled.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnDiceRolled(object sender, DiceRolledEventArgs e)
{
Console.WriteLine($"Dice 1: {e.Dice1Value} | Dice 2: {e.Dice2Value}");
Operation playerOperation = GetPlayerOperation();
((Game)sender).HandlePlayerOperation(playerOperation);
}
/// <summary>
/// Handles the event when an operation is chosen by the player.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnOperationChosen(object sender, OperationChosenEventArgs e)
{
Console.WriteLine($"Operation: {e.Operation}, Result: {e.Result}");
DisplayOperationTable(((Game)sender).UsedMap.OperationGrid);
Cell playerChoice = GetPlayerChoice();
try
{
((Game)sender).HandlePlayerChoice(playerChoice, e.Result);
}
catch (InvalidCellCoordinatesException err)
{
Console.WriteLine(err.Message);
playerChoice = GetPlayerChoice();
((Game)sender).HandlePlayerChoice(playerChoice, e.Result);
}
catch (InvalidCellException err)
{
Console.WriteLine(err.Message);
playerChoice = GetPlayerChoice();
((Game)sender).HandlePlayerChoice(playerChoice, e.Result);
}
}
/// <summary>
/// Handles the event when a cell is chosen by the player.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnCellChosen(object sender, CellChosenEventArgs e)
{
Console.WriteLine($"Cell chosen: ({e.Cell.X}, {e.Cell.Y}) with result: {e.Result}");
DisplayBoard(((Game)sender).UsedMap);
}
/// <summary>
/// Displays the game board.
/// </summary>
/// <param name="map">Used map to display</param>
static void DisplayBoard(Map map)
{
int cpt = 0;
for (int i = 0; i < map.Boards.Count; i++)
{
if (cpt % 6 == 0)
{
Console.Write("| ");
}
if (map.Boards[i].Value.HasValue)
{
Console.Write(map.Boards[i].Value?.ToString().PadLeft(2));
}
else
{
Console.Write(" O");
}
cpt++;
if (cpt % 6 == 0)
{
Console.Write(" |");
Console.WriteLine();
}
}
}
/// <summary>
/// Displays the operation table.
/// </summary>
/// <param name="operationTable">The operation table to display.</param>
static void DisplayOperationTable(List<OperationCell> operationTable)
{
Console.WriteLine("Operation Table:");
string[] operations = { "Addition (+)", "Subtraction (-)", "Multiplication (*)", "Lower Dice (less)", "Higher Dice (high)" };
for (int i = 0; i < operations.Length; i++)
{
Console.Write(operations[i].PadRight(18));
for (int j = 0; j < 4; j++)
{
int index = i * 4 + j;
if (index < operationTable.Count)
{
string status = operationTable[index].IsChecked ? "X" : " ";
Console.Write($" | {status}");
}
}
Console.WriteLine();
}
}
/// <summary>
/// Gets the cell chosen by the player.
/// </summary>
/// <returns>The cell chosen by the player.</returns>
static Cell GetPlayerChoice()
{
int row, column;
while (true)
{
Console.WriteLine("Enter the position of the cell you want to play");
Console.WriteLine("Enter the row number (0-5)");
if (!int.TryParse(Console.ReadLine(), out row) || row < 0 || row >= 6)
{
Console.WriteLine("Invalid row number. Please enter a number between 0 and 5.");
continue;
}
Console.WriteLine("Enter the column number (0-5)");
if (!int.TryParse(Console.ReadLine(), out column) || column < 0 || column >= 6)
{
Console.WriteLine("Invalid column number. Please enter a number between 0 and 5.");
continue;
}
return new Cell(row, column);
}
}
/// <summary>
/// Gets the operation chosen by the player.
/// </summary>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
static Operation GetPlayerOperation()
{
DisplayOperationOptions();
string? op = Console.ReadLine();
while (op != "1" && op != "2" && op != "3" && op != "4" && op != "5")
{
Console.WriteLine("Invalid operation. Please choose again.");
op = Console.ReadLine();
}
return op switch
{
"1" => Operation.ADDITION,
"2" => Operation.SUBTRACTION,
"3" => Operation.MULTIPLICATION,
"4" => Operation.LOWER,
"5" => Operation.HIGHER,
_ => throw new ArgumentOutOfRangeException()
};
}
/// <summary>
/// Displays the operation options for the player to choose from.
/// </summary>
static void DisplayOperationOptions()
{
Console.WriteLine("Choose an operation:");
Console.WriteLine("1: Addition (+)");
Console.WriteLine("2: Subtraction (-)");
Console.WriteLine("3: Multiplication (*)");
Console.WriteLine("4: Lower Dice (less)");
Console.WriteLine("5: Higher Dice (high)");
}
// See https://aka.ms/new-console-template for more information
using Models;
using Models.Events;
using Models.Game;
using Models.Interfaces;
using DataContractPersistence;
using Microsoft.Extensions.Logging;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace ConsoleApp;
class Program
{
static Game Game { get; set; }
/// <summary>
/// Main function of the console app
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
Console.WriteLine("Enter your pseudo:");
string? pseudo = Console.ReadLine();
if (pseudo != null)
{
IPersistence persistence = new DataContractXml();
Player player = new Player(pseudo, "test.png");
Map map = new Map("Dunai","background");
Game = new Game(persistence);
// Abonnement aux événements
Game.GameStarted += OnGameStarted!;
Game.GameEnded += OnGameEnded!;
Game.BoardUpdated += OnBoardUpdated!;
Game.DiceRolled += OnDiceRolled!;
Game.OperationChosen += OnOperationChosen!;
Game.CellChosen += OnCellChosen!;
Game.PlayerChooseOp += OnPlayerSelectionOp!;
Game.PlayerOption += OnPlayerOption!;
Game.PlayerChooseCell += OnPlayerSelectionCell!;
// Initialisation
Game.InitializeGame(map, player);
Game.GameLoop();
}
}
static void OnPlayerSelectionCell(Object sender, PlayerChooseCellEventArgs e)
{
int row, column;
while (true)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Enter the position of the cell you want to play");
Console.WriteLine("Enter the row number (0-6)");
Console.ResetColor();
if (!int.TryParse(Console.ReadLine(), out row) || row < 0 || row >= 7)
{
Console.ForegroundColor= ConsoleColor.Red;
Console.WriteLine("Invalid row number. Please enter a number between 0 and 6.");
Console.ResetColor();
continue;
}
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Enter the column number (0-6)");
Console.ResetColor();
if (!int.TryParse(Console.ReadLine(), out column) || column < 0 || column >= 7)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Invalid column number. Please enter a number between 0 and 6.");
Console.ResetColor();
continue;
}
Game.PlayerCell = new Cell(row, column);
break;
}
}
static void OnPlayerOption(object sender, PlayerOptionEventArgs e)
{
Console.WriteLine();
if (e.Turn != 1)
{
IEnumerable<Cell> PlayedCellsQuery =
from cell in e.Board
where cell.Valid == true
where cell.Value != null
select cell;
foreach (var item in e.Board)
{
if (item.X == 6)
Console.WriteLine();
if (!item.Valid)
Console.Write(" ");
else if (item.Value != null)
Console.Write($"{item.Value}");
else
{
foreach (var item1 in PlayedCellsQuery)
{
if (Math.Abs(item.X - item1.X) <= 1 && Math.Abs(item.Y - item1.Y) <= 1)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.Write($"{e.Resultat}");
Console.ResetColor();
}
}
}
}
return;
}
foreach (var item in e.Board)
{
if (!item.Valid)
Console.Write(" ");
else
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.Write($"{e.Resultat}");
Console.ResetColor();
}
if (item.X == 6)
Console.WriteLine();
}
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Choose an Available cell.");
Console.ResetColor();
}
static void OnPlayerSelectionOp(object sender, PlayerChooseOperationEventArgs e)
{
Console.WriteLine();
DisplayOperationTable(((Game)sender).UsedMap.OperationGrid.ToList());
Console.WriteLine();
Console.WriteLine("0. Lower | 1. Higher | 2. Substraction | 3. Addition | 4. Multiplication");
string? op = Console.ReadLine();
while (op != "0" && op != "1" && op != "2" && op != "3" && op != "4")
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Invalid operation. Please choose again.");
Console.ResetColor();
op = Console.ReadLine();
}
int test = Convert.ToInt32(op);
Game.PlayerOperation = (Operation)test;
}
/// <summary>
/// Handles the event when the game has started.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnGameStarted(object sender, GameStartedEventArgs e)
{
Console.WriteLine($"The game has started! Player: {e.CurrentPlayer.Pseudo}");
}
/// <summary>
/// Handles the event when the game has ended.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnGameEnded(object sender, GameEndedEventArgs e)
{
Console.WriteLine($"The game has ended! Player: {e.CurrentPlayer.Pseudo}");
Console.WriteLine($"Points: {e.Point}");
}
/// <summary>
/// Handles the event when the board is updated.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnBoardUpdated(object sender, BoardsUpdateEventArgs e)
{
foreach (var item in e.Boards)
{
if (!item.Valid)
Console.Write(" ");
else if (item.Value != null)
Console.Write($"{item.Value}");
else Console.Write("O");
if (item.X == 6)
Console.WriteLine();
}
}
/// <summary>
/// Handles the event when the dice are rolled.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnDiceRolled(object sender, DiceRolledEventArgs e)
{
// Console.Clear();
Console.WriteLine($"Dice 1: {e.Dice1Value} | Dice 2: {e.Dice2Value}");
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Choose an operation.");
Console.ResetColor();
}
/// <summary>
/// Handles the event when an operation is chosen by the player.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnOperationChosen(object sender, OperationChosenEventArgs e)
{
Console.WriteLine($"Operation: {e.Operation}, Result: {e.Result}");
DisplayOperationTable(((Game)sender).UsedMap.OperationGrid.ToList());
Cell playerChoice = GetPlayerChoice();
bool test = ((Game)sender).HandlePlayerChoice(playerChoice, e.Result);
if(!test)
{
Console.WriteLine("Invalid cell. Please choose again.");
Console.WriteLine();
Console.WriteLine();
DisplayBoard(((Game)sender).UsedMap);
OnOperationChosen(sender, e);
}
/*
try
{
((Game)sender).HandlePlayerChoice(playerChoice, e.Result);
}
catch (InvalidCellCoordinatesException err)
{
Console.WriteLine(err.Message);
playerChoice = GetPlayerChoice();
((Game)sender).HandlePlayerChoice(playerChoice, e.Result);
}
catch (InvalidCellException err)
{
Console.WriteLine(err.Message);
playerChoice = GetPlayerChoice();
((Game)sender).HandlePlayerChoice(playerChoice, e.Result);
}
catch (InvalidPlaceResultException err)
{
Console.WriteLine(err.Message);
playerChoice = GetPlayerChoice();
((Game)sender).HandlePlayerChoice(playerChoice, e.Result);
}
*/
}
/// <summary>
/// Handles the event when a cell is chosen by the player.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
static void OnCellChosen(object sender, CellChosenEventArgs e)
{
Console.WriteLine($"Cell chosen: ({e.Cell.X}, {e.Cell.Y}) with result: {e.Result}");
DisplayBoard(((Game)sender).UsedMap);
}
/// <summary>
/// Displays the game board.
/// </summary>
/// <param name="map">Used map to display</param>
static void DisplayBoard(Map map)
{
int cpt = 0;
for (int i = 0; i < map.Boards.Count; i++)
{
if (cpt % 6 == 0)
{
Console.Write("| ");
}
if (map.Boards[i].Value.HasValue)
{
Console.Write(map.Boards[i].Value?.ToString().PadLeft(2));
}
else
{
Console.Write(" O");
}
cpt++;
if (cpt % 6 == 0)
{
Console.Write(" |");
Console.WriteLine();
}
}
}
/// <summary>
/// Displays the operation table.
/// </summary>
/// <param name="operationTable">The operation table to display.</param>
static void DisplayOperationTable(List<OperationCell> operationTable)
{
Console.ForegroundColor = ConsoleColor.Yellow;
foreach ( var cell in operationTable)
{
if (cell.X == 0 && cell.Y == 0)
Console.Write("Lower => ");
if (cell.X == 0 && cell.Y == 1)
Console.Write("Higher => ");
if (cell.X == 0 && cell.Y == 2)
Console.Write("Substraction => ");
if (cell.X == 0 && cell.Y == 3)
Console.Write("Addition => ");
if (cell.X == 0 && cell.Y == 4)
Console.Write("Multiplication => ");
if (cell.IsChecked)
Console.Write("X | ");
if (!cell.IsChecked)
Console.Write(" | ");
if (cell.X == 3)
Console.WriteLine();
}
Console.ResetColor();
}
/// <summary>
/// Gets the cell chosen by the player.
/// </summary>
/// <returns>The cell chosen by the player.</returns>
static Cell GetPlayerChoice()
{
int row, column;
while (true)
{
Console.WriteLine("Enter the position of the cell you want to play");
Console.WriteLine("Enter the row number (0-5)");
if (!int.TryParse(Console.ReadLine(), out row) || row < 0 || row >= 6)
{
Console.WriteLine("Invalid row number. Please enter a number between 0 and 5.");
continue;
}
Console.WriteLine("Enter the column number (0-5)");
if (!int.TryParse(Console.ReadLine(), out column) || column < 0 || column >= 6)
{
Console.WriteLine("Invalid column number. Please enter a number between 0 and 5.");
continue;
}
return new Cell(row, column);
}
}
/// <summary>
/// Gets the operation chosen by the player.
/// </summary>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
static Operation GetPlayerOperation(object? sender)
{
DisplayOperationOptions();
string? op = Console.ReadLine();
while (op != "1" && op != "2" && op != "3" && op != "4" && op != "5")
{
Console.WriteLine("Invalid operation. Please choose again.");
op = Console.ReadLine();
}
int test = Convert.ToInt32(op);
while(((Game)sender).UsedMap.CheckOperationPossible(test-1))
{
Console.WriteLine("Invalid operation. Please choose again.");
Console.WriteLine();
op = Console.ReadLine();
while (op != "1" && op != "2" && op != "3" && op != "4" && op != "5")
{
Console.WriteLine("Invalid operation. Please choose again.");
op = Console.ReadLine();
}
test = Convert.ToInt32(op);
}
return op switch
{
"1" => Operation.ADDITION,
"2" => Operation.SUBTRACTION,
"3" => Operation.MULTIPLICATION,
"4" => Operation.LOWER,
"5" => Operation.HIGHER,
_ => throw new ArgumentOutOfRangeException()
};
}
/// <summary>
/// Displays the operation options for the player to choose from.
/// </summary>
static void DisplayOperationOptions()
{
Console.WriteLine("Choose an operation:");
Console.WriteLine("1: Addition (+)");
Console.WriteLine("2: Subtraction (-)");
Console.WriteLine("3: Multiplication (*)");
Console.WriteLine("4: Lower Dice (less)");
Console.WriteLine("5: Higher Dice (high)");
}
}

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Xml.Serialization;
using System.Collections.ObjectModel;
using System.Text.Json;
using Models.Game;
using Models.Interfaces;
namespace DataContractPersistence
{
public class DataContractJson : IPersistence
{
/// <summary>
/// Name of the file where the data will be saved
/// </summary>
public string FileName { get; set; } = "data.json";
/// <summary>
/// Path (relative to the project)
/// </summary>
public string FilePath { get; set; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Trek_12");
/// <summary>
/// Load all the data from JSON file
/// </summary>
/// <returns>A tuple with the lists of players, games, maps and bestScores</returns>
public (ObservableCollection<Player>, ObservableCollection<Game>, ObservableCollection<Map>, ObservableCollection<BestScore>) LoadData()
{
var JsonSerializer = new DataContractJsonSerializer(typeof(DataToPersist));
DataToPersist? data = new DataToPersist();
using (Stream s = File.OpenRead(Path.Combine(FilePath, FileName)))
{
data = JsonSerializer.ReadObject(s) as DataToPersist;
}
Console.WriteLine("Data loaded: " + Path.Combine(FilePath, FileName));
return (data.Players, data.Games, data.Maps, data.BestScores);
}
/// <summary>
/// Save all the data to a JSON file
/// </summary>
/// <param name="players"></param>
/// <param name="games"></param>
/// <param name="maps"></param>
/// <param name="bestScores"></param>
public void SaveData(ObservableCollection<Player> players, ObservableCollection<Game> games, ObservableCollection<Map> maps, ObservableCollection<BestScore> bestScores)
{
Debug.WriteLine("Saving data at " + Path.Combine(FilePath, FileName));
if (!Directory.Exists(FilePath))
{
Debug.WriteLine("Directory created");
Debug.WriteLine(Directory.GetDirectoryRoot(FilePath));
Debug.WriteLine(FilePath);
Directory.CreateDirectory(FilePath);
}
var JsonSerializer = new DataContractJsonSerializer(typeof(DataToPersist));
DataToPersist? data = new DataToPersist();
data.Players = players;
data.Games = games;
data.Maps = maps;
data.BestScores = bestScores;
var settings = new XmlWriterSettings() { Indent = true };
using (FileStream stream = File.Create(Path.Combine(FilePath, FileName)))
{
using (var writer = JsonReaderWriterFactory.CreateJsonWriter(
stream,
Encoding.UTF8,
false,
true))
{
JsonSerializer.WriteObject(writer, data);
writer.Flush();
}
}
}
}
}

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Models\Models.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;
using Models.Game;
using Models.Interfaces;
namespace DataContractPersistence
{
public class DataContractXml : IPersistence
{
/// <summary>
/// Name of the file where the data will be saved
/// </summary>
public string FileName { get; set; } = "data.xml";
/// <summary>
/// Path (relative to the project)
/// </summary>
public string FilePath { get; set; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Trek_12");
/// <summary>
/// Load all the data from XML file
/// </summary>
/// <returns>A tuple with the lists of players, games, maps and bestScores</returns>
public (ObservableCollection<Player>, ObservableCollection<Game>, ObservableCollection<Map>, ObservableCollection<BestScore>) LoadData()
{
var serializer = new DataContractSerializer(typeof(DataToPersist));
DataToPersist? data = new DataToPersist();
using (Stream s = File.OpenRead(Path.Combine(FilePath, FileName)))
{
data = serializer.ReadObject(s) as DataToPersist;
}
Console.WriteLine("Data loaded: " + Path.Combine(FilePath, FileName));
return (data.Players, data.Games, data.Maps, data.BestScores);
}
/// <summary>
/// Save all the data to a XML file
/// </summary>
/// <param name="players"></param>
/// <param name="games"></param>
/// <param name="maps"></param>
/// <param name="bestScores"></param>
public void SaveData(ObservableCollection<Player> players, ObservableCollection<Game> games, ObservableCollection<Map> maps, ObservableCollection<BestScore> bestScores)
{
if(!Directory.Exists(FilePath))
{
Debug.WriteLine("Directory created");
Debug.WriteLine(Directory.GetDirectoryRoot(FilePath));
Debug.WriteLine(FilePath);
Directory.CreateDirectory(FilePath);
}
var serializer = new DataContractSerializer(typeof(DataToPersist));
DataToPersist? data = new DataToPersist();
data.Players = players;
data.Games = games;
data.Maps = maps;
data.BestScores = bestScores;
var settings = new XmlWriterSettings() { Indent = true };
using (TextWriter tw = File.CreateText(Path.Combine(FilePath, FileName)))
{
using (XmlWriter writer = XmlWriter.Create(tw, settings))
{
serializer.WriteObject(writer, data);
Console.WriteLine("Data saved: " + Path.Combine(FilePath, FileName));
}
}
}
}
}

@ -0,0 +1,28 @@
using System.Collections.ObjectModel;
using Models.Game;
namespace DataContractPersistence
{
public class DataToPersist
{
/// <summary>
/// List of players
/// </summary>
public ObservableCollection<Player> Players { get; set; }
/// <summary>
/// List of games (with all the data in it to be able to recreate the game)
/// </summary>
public ObservableCollection<Game> Games { get; set; }
/// <summary>
/// List of maps with their boards
/// </summary>
public ObservableCollection<Map> Maps { get; set; }
/// <summary>
/// List of best scores
/// </summary>
public ObservableCollection<BestScore> BestScores { get; set; }
}
}

@ -0,0 +1,69 @@
using SQLite;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Threading.Tasks;
namespace DataContractPersistence
{
using Models.Game;
using Models.Interfaces;
public class SqLitePersistence : IPersistence
{
private readonly SQLiteConnection _database;
private readonly string _databasePath;
public SqLitePersistence()
{
_databasePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Trek_12.db");
_database = new SQLiteConnection(_databasePath);
_database.CreateTable<Player>();
_database.CreateTable<Map>();
_database.CreateTable<Game>();
_database.CreateTable<BestScore>();
}
public (ObservableCollection<Player>, ObservableCollection<Game>, ObservableCollection<Map>, ObservableCollection<BestScore>) LoadData()
{
var players = new ObservableCollection<Player>(_database.Table<Player>());
var games = new ObservableCollection<Game>(_database.Table<Game>());
var maps = new ObservableCollection<Map>(_database.Table<Map>());
var bestScores = new ObservableCollection<BestScore>(_database.Table<BestScore>());
return (players, games, maps, bestScores);
}
public void SaveData(ObservableCollection<Player> players, ObservableCollection<Game> games, ObservableCollection<Map> maps, ObservableCollection<BestScore> bestScores)
{
_database.RunInTransaction(() =>
{
_database.DeleteAll<Player>();
_database.DeleteAll<Game>();
_database.DeleteAll<Map>();
_database.DeleteAll<BestScore>();
});
foreach (var player in players)
{
_database.Insert(player);
}
foreach (var game in games)
{
_database.Insert(game);
}
foreach (var map in maps)
{
_database.Insert(map);
}
foreach (var bestScore in bestScores)
{
_database.Insert(bestScore);
}
}
}
}

@ -0,0 +1,17 @@
using Models.Game;
namespace Models.Events
{
/// <summary>
/// Event arguments for when the board change.
/// </summary>
public class BoardsUpdateEventArgs : EventArgs
{
public List<Cell> Boards { get; set; }
public BoardsUpdateEventArgs(List<Cell> board)
{
Boards = board;
}
}
}

@ -9,9 +9,12 @@ namespace Models.Events
{
public Player CurrentPlayer { get; }
public GameEndedEventArgs(Player winner)
public int? Point { get; }
public GameEndedEventArgs(Player winner, int? point)
{
CurrentPlayer = winner;
Point = point;
}
}
}

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Models.Game;
namespace Models.Events
{
public class PlayerChooseCellEventArgs : EventArgs
{
public Cell Cell { get; set; }
public PlayerChooseCellEventArgs(Cell cell)
{
Cell = cell;
}
}
}

@ -0,0 +1,19 @@
using Models.Game;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Models.Events
{
public class PlayerChooseOperationEventArgs : EventArgs
{
public Operation PlayerOp { get; set; }
public PlayerChooseOperationEventArgs(Operation op)
{
PlayerOp = op;
}
}
}

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Models.Game;
namespace Models.Events
{
public class PlayerOptionEventArgs : EventArgs
{
public List<Cell> Board { get; set; }
public int Resultat { get; set; }
public int Turn { get; set; }
public PlayerOptionEventArgs(List<Cell> boards, int resultat, int turn)
{
Board = boards;
Resultat = resultat;
Turn = turn;
}
}
}

@ -1,11 +0,0 @@
namespace Models.Exceptions
{
/// <summary>
/// Exception for when the cell coordinates are invalid.
/// </summary>
public class InvalidCellCoordinatesException : Exception
{
public InvalidCellCoordinatesException(string message) : base(message) { }
}
}

@ -1,11 +0,0 @@
namespace Models.Exceptions
{
/// <summary>
/// Exception for when the cell is invalid.
/// </summary>
public class InvalidCellException : Exception
{
public InvalidCellException(string message) : base(message) { }
}
}

@ -0,0 +1,41 @@
using System;
using Microsoft.Maui.Controls;
using Models.Interfaces;
namespace Models.Game
{
/// <summary>
/// Converter to Base64
/// </summary>
public class Base64Converter : IImageConverter
{
/// <summary>
/// Converts an image to a base64 string
/// </summary>
/// <param name="imagePath">The path to access the image</param>
/// <returns>The base64 string representation of the image</returns>
/// <exception cref="FileNotFoundException">Native .NET exception</exception>
public string ConvertImage(string imagePath)
{
if (!File.Exists(imagePath))
{
// native .NET exception
throw new FileNotFoundException("Image file not found", imagePath);
}
byte[] imageBytes = File.ReadAllBytes(imagePath);
return Convert.ToBase64String(imageBytes);
}
/// <summary>
/// Retrieve an image from a string encoded in base64
/// </summary>
/// <param name="imageString"></param>
/// <returns></returns>
public ImageSource RetrieveImage(string imageString)
{
byte[] imageBytes = Convert.FromBase64String(imageString);
return ImageSource.FromStream(() => new MemoryStream(imageBytes));
}
}
}

@ -2,53 +2,83 @@
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using SQLite;
namespace Models.Game
{
/// <summary>
/// This class represents the best score of a player.
/// </summary>
[DataContract, SQLite.Table("BestScores")]
public class BestScore
{
/// <summary>
/// Initialize a new instance of the BestScore class.
/// </summary>
/// <param name="gamesPlayed">Number of games played by the new user</param>
/// <param name="score">Best score</param>
public BestScore(int gamesPlayed, int score)
{
GamesPlayed = gamesPlayed;
Score = score;
if (GamesPlayed < 0)
GamesPlayed = 0;
if (Score < 0)
Score = 0;
}
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
/// <summary>
/// Number of games played by the user.
/// Name of the map.
/// </summary>
public int GamesPlayed { get; private set; }
[DataMember]
public string MapName { get; private set; }
[DataMember]
public Player ThePlayer { get; private set; }
/// <summary>
/// Best score of the player.
/// Number of games played by the user (on a specific map).
/// </summary>
private int _score;
public int Score
{
get
private int _gamesPlayed;
[DataMember]
public int GamesPlayed
{
get => _gamesPlayed;
private set
{
return _score;
if (value >= 0)
_gamesPlayed = value;
else _gamesPlayed = 0;
}
}
/// <summary>
/// Best score of the player (on a specific map).
/// </summary>
private int _score;
[DataMember]
public int Score
{
get => _score;
private set
{
if (value > _score)
if (value >= 0)
_score = value;
else _score = 0;
}
}
/// <summary>
/// Initialize a new instance of the BestScore class.
/// </summary>
/// <param name="gamesPlayed">Number of games played by the new user</param>
/// <param name="score">Best score</param>
/// <param name="map">The name of the map</param>
public BestScore(string map, Player player, int gamesPlayed, int score)
{
MapName = map;
ThePlayer = player;
GamesPlayed = gamesPlayed;
Score = score;
}
/// <summary>
/// SQLite constructor
/// </summary>
public BestScore() { }
/// <summary>
/// Increment the number of games played by the user.
/// </summary>
@ -63,7 +93,31 @@ namespace Models.Game
/// <param name="newScore">New best score</param>
public void UpdateScore(int newScore)
{
Score = newScore;
Score += newScore;
}
/// <summary>
/// Redefine the equal operation between BestScore.
/// </summary>
/// <param name="obj">The object to compare with the current BestScore.</param>
/// <returns>true if the specified object is equal to the current BestScore; otherwise, false.</returns>
public override bool Equals(object? obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
BestScore c = (BestScore)obj;
return (MapName == c.MapName && ThePlayer == c.ThePlayer);
}
/// <summary>
/// Returns the hash code for the current BestScore, in order for the Equals operation to work
/// </summary>
/// <returns>The hash code for the current BestScore.</returns>
public override int GetHashCode()
{
return MapName.GetHashCode() ^ ThePlayer.GetHashCode();
}
}
}

@ -1,14 +1,20 @@
namespace Models.Game
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
namespace Models.Game
{
/// <summary>
/// The Cell class represents a cell in the application.
/// </summary>
public class Cell : Position, IEquatable<Cell>
[DataContract]
public class Cell : Position, IEquatable<Cell>, INotifyPropertyChanged
{
/// <summary>
/// The value of the cell.
/// </summary>
private int? _value;
[DataMember]
public int? Value {
get => _value;
set
@ -18,18 +24,31 @@
throw new Exception("La valeur doit être supérieure à 0");
}
this._value = value;
OnPropertyChanged();
}
}
/// <summary>
/// The fact that the cell is dangerous or not.
/// </summary>
private bool IsDangerous { get; set; }
[DataMember]
public bool IsDangerous { get; set; }
[DataMember]
public bool Valid { get; set; }
/// <summary>
/// Atribute to know if the cell is a penalty cell.
/// </summary>
private bool Penalty { get; set; }
[DataMember]
public bool Penalty { get; private set; }
public void SetPenalty()
{
Penalty = true;
}
/// <summary>
/// Constructor of the Cell class.
@ -41,6 +60,7 @@
{
IsDangerous = isDangerous;
Penalty = false;
Valid = false;
}
/// <summary>
@ -60,5 +80,13 @@
if (this.X == other.X && this.Y == other.Y) return true;
return false;
}
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

@ -14,12 +14,12 @@ namespace Models.Game
/// <summary>
/// Lowest number on the dice.
/// </summary>
public int NbMin { get; private set; }
public int MinVal { get; private set; }
/// <summary>
/// Highest number on the dice.
/// </summary>
public int NbMax { get; private set; }
public int MaxVal { get; private set; }
/// <summary>
/// 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
/// <summary>
/// Initializes a new instance of the <see cref="Dice"/> class.
/// </summary>
/// <param name="nbmin">The lowest number on the dice, on which the highest number is set</param>
public Dice(int nbmin)
/// <param name="minval">The lowest number on the dice, on which the highest number is set</param>
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;
}
/// <summary>
@ -54,13 +54,8 @@ namespace Models.Game
/// </summary>
public Dice()
{
NbMin = 0;
NbMax = 5;
}
public override string ToString()
{
return $"Ce dé a pour valeur {Value} et est entre {NbMin} et {NbMax}";
MinVal = 0;
MaxVal = 5;
}
/// <summary>
@ -68,7 +63,7 @@ namespace Models.Game
/// </summary>
public void Roll()
{
Value = new Random().Next(NbMin, NbMax + 1);
Value = new Random().Next(MinVal, MaxVal + 1);
}
/// <summary>

@ -1,12 +1,19 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
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;
using SQLite;
namespace Models.Game
{
@ -14,54 +21,326 @@ namespace Models.Game
/// 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>
public class Game
[DataContract, SQLite.Table("Games")]
public class Game : INotifyPropertyChanged
{
public bool IsPreviousGameNotFinished { get; private set; }
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
/* Persistence Interface */
[Ignore]
public IPersistence PersistenceManager { get; set; }
/* List for the game and persistence */
private ObservableCollection<Player> _players;
[Ignore]
public ObservableCollection<Player> Players
{
get => _players;
set
{
_players = value;
OnPropertyChanged(nameof(Players));
}
}
private ObservableCollection<Game> _games;
[Ignore]
public ObservableCollection<Game> Games
{
get => _games;
set
{
_games = value;
OnPropertyChanged(nameof(Games));
}
}
private ObservableCollection<Map> _maps;
[Ignore]
public ObservableCollection<Map> Maps
{
get => _maps;
set
{
_maps = value;
OnPropertyChanged(nameof(Maps));
}
}
private ObservableCollection<BestScore> _bestScores;
[Ignore]
public ObservableCollection<BestScore> BestScores
{
get => _bestScores;
set
{
_bestScores = value;
OnPropertyChanged(nameof(BestScores));
}
}
private bool _isRunning;
public Player CurrentPlayer { get; private set; }
[DataMember]
public bool IsRunning
{
get => _isRunning;
private set => _isRunning = value;
}
public Map UsedMap { get; private set; }
[DataMember]
public Player? CurrentPlayer { get; private set; }
private Dice Dice1 { get; }
private Dice Dice2 { get; }
private Map? _usedMap;
[DataMember]
public Map? UsedMap
{
get => _usedMap;
set
{
_usedMap = value;
OnPropertyChanged(nameof(UsedMap));
}
}
[Ignore]
public Dice Dice1 { get; private set;}
[Ignore]
public Dice Dice2 { get; private set; }
[DataMember]
public int Turn { get; set; }
[Ignore]
public Operation PlayerOperation { get; set; }
private Cell _playerCell;
[Ignore]
public Cell PlayerCell {
get => _playerCell;
set
{
_playerCell = value;
OnPropertyChanged(nameof(PlayerCell));
}
}
private int _resultat;
public int Resultat {
get => _resultat;
set
{
_resultat = value;
OnPropertyChanged(nameof(Resultat));
}
}
private int Turn { get; set; }
public bool DiceRolledFlag { get; private set; }
public bool OperationChosenFlag { get; private set; }
[Ignore]
public Rules.Rules GameRules { get; }
// == Events ==
public event EventHandler<GameStartedEventArgs> GameStarted;
public event EventHandler<GameEndedEventArgs> GameEnded;
public event EventHandler BoardUpdated;
public event EventHandler<BoardsUpdateEventArgs> BoardUpdated;
public event EventHandler<DiceRolledEventArgs> DiceRolled;
public event EventHandler<OperationChosenEventArgs> OperationChosen;
public event EventHandler<CellChosenEventArgs> CellChosen;
public event EventHandler<PlayerChooseOperationEventArgs> PlayerChooseOp;
public event EventHandler<PlayerOptionEventArgs> PlayerOption;
public event EventHandler<PlayerChooseCellEventArgs> PlayerChooseCell;
public void AddPlayer(Player player)
{
Players.Add(player);
}
public void AddGame(Game game)
{
Games.Add(game);
}
public void AddMap(Map map)
{
Maps.Add(map);
}
/// <summary>
/// Initializes a new instance of the <see cref="Game"/> class.
/// Adds a new best score to the list of best scores. Or updates it if it already exists.
/// </summary>
/// <param name="player">The player who play the game.</param>
/// <param name="map">The map to be used in the game.</param>
public Game(Player player, Map map)
/// <param name="finalScore">The final score of the game.</param>
public void AddBestScore(int finalScore)
{
_isRunning = false;
UsedMap = map;
CurrentPlayer = player;
Dice1 = new Dice();
Dice2 = new Dice(1);
Turn = 1;
BestScore bs = new BestScore(UsedMap.Name, CurrentPlayer, 1, finalScore);
var existingScore = BestScores.FirstOrDefault(score => score.Equals(bs));
if (existingScore != null)
{
existingScore.IncrGamesPlayed();
existingScore.UpdateScore(finalScore);
}
else
{
BestScores.Add(bs);
}
// Sorting the best scores
List<BestScore> sortedScores = BestScores.OrderByDescending(score => score.Score).ToList();
for (int i = 0; i < sortedScores.Count; i++)
{
if (!BestScores[i].Equals(sortedScores[i]))
{
BestScores.Move(BestScores.IndexOf(sortedScores[i]), i);
}
}
}
/// <summary>
/// Removes a player from the list of players.
/// </summary>
/// <param name="playerName"></param>
/// <returns>True if the player was removed successfully, false otherwise.</returns>
public bool RemovePlayer(string playerName)
{
Player? player = Players.FirstOrDefault(p => p.Pseudo == playerName);
if (player == null)
{
return false;
}
Players.Remove(player);
CheckAndRemoveBestScoresDependencies(player.Pseudo);
return true;
}
/// <summary>
/// Modifies the pseudo of a player.
/// </summary>
/// <param name="pseudo"></param>
/// <param name="newpseudo"></param>
/// <returns></returns>
public bool ModifyPlayer(string pseudo, string newpseudo)
{
foreach (var index in Players)
{
if (index.Pseudo == pseudo)
{
CheckAndChangeBestScoresDependencies(index.Pseudo, newpseudo);
index.Pseudo = newpseudo;
return true;
}
}
return false;
}
/// <summary>
/// Removes a game from the list of games.
/// </summary>
/// <param name="playerName"></param>
public void CheckAndRemoveBestScoresDependencies(string playerName)
{
List<BestScore> bs = new List<BestScore>();
foreach (var bestScore in BestScores)
{
if (!bestScore.ThePlayer.Pseudo.Equals(playerName)) continue;
bs.Add(bestScore);
}
foreach (var score in bs)
{
BestScores.Remove(score);
}
}
/// <summary>
/// Modifies the pseudo of a player in the best scores.
/// </summary>
/// <param name="playerName"></param>
/// <param name="newPlayerName"></param>
public void CheckAndChangeBestScoresDependencies(string playerName, string newPlayerName)
{
foreach (var bestScore in BestScores)
{
if (!bestScore.ThePlayer.Pseudo.Equals(playerName)) continue;
bestScore.ThePlayer.Pseudo = newPlayerName;
}
}
public void LoadData()
{
var data = PersistenceManager.LoadData();
foreach (var player in data.Item1)
{
Players.Add(player);
}
foreach (var game in data.Item2)
{
if (game.IsRunning)
{
IsPreviousGameNotFinished = true;
}
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("temp","test");
IsRunning = false;
}
/// <summary>
/// Rolls all the dice.
/// </summary>
private void RollAllDice()
public void RollAllDice()
{
Dice1.Roll();
Dice2.Roll();
DiceRolledFlag = true;
OperationChosenFlag = false;
DiceRolled?.Invoke(this, new DiceRolledEventArgs(Dice1.Value, Dice2.Value));
OnPropertyChanged(nameof(Dice1));
OnPropertyChanged(nameof(Dice2));
}
/// <summary>
@ -71,13 +350,16 @@ namespace Models.Game
private void MarkOperationAsChecked(Operation operation)
{
int operationIndex = (int)operation;
int operationsPerType = 4; // Chaque type d'opération peut être fait 4 fois
IEnumerable<OperationCell> sortPaths =
from cell in UsedMap.OperationGrid
where cell.Y == operationIndex
select cell;
for (int i = operationIndex * operationsPerType; i < (operationIndex + 1) * operationsPerType; i++)
foreach (var item in sortPaths)
{
if (!UsedMap.OperationGrid[i].IsChecked)
if (!item.IsChecked)
{
UsedMap.OperationGrid[i].Check();
item.Check();
break;
}
}
@ -94,7 +376,7 @@ namespace Models.Game
/// 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>
private int ResultOperation(Operation o)
public int ResultOperation(Operation o)
{
int result = o switch
{
@ -105,8 +387,6 @@ namespace Models.Game
Operation.MULTIPLICATION => Dice2.Value * Dice1.Value,
_ => throw new ArgumentOutOfRangeException()
};
MarkOperationAsChecked(o);
return result;
}
@ -119,10 +399,26 @@ namespace Models.Game
/// <param name="result">The result of the dice operation to be placed in the cell.</param>
private void PlaceResult(Cell playerChoice, int result)
{
if (Turn == 1 || GameRules.NearCellIsValid(playerChoice, UsedMap.Boards))
IEnumerable<Cell> ValidCell =
from cell in UsedMap.Boards
where cell.Value == null
where cell.Valid == true
select cell;
foreach (var item in ValidCell)
{
playerChoice.Value = result;
BoardUpdated?.Invoke(this, EventArgs.Empty);
if (item.X == playerChoice.X && item.Y == playerChoice.Y)
{
if (result > 12 || (result > 6 && item.IsDangerous == true))
{
item.SetPenalty();
PlayerCell.SetPenalty();
}
item.Value = result;
OnPropertyChanged(nameof(UsedMap.Boards));
return;
}
}
}
@ -131,87 +427,126 @@ namespace Models.Game
/// </summary>
/// <param name="playerChoice"></param>
/// <param name="adjacentes"></param>
private void AddToRopePath(Cell playerChoice,List<Cell> adjacentes)
private void AddToRopePath(Cell playerChoice)
{
int index =0;
foreach (var cells in adjacentes)
{
// La cellule choisi peut creer/s'ajouter a un chemin de corde
if (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});
}
if(Turn==1) return;
int index = 0;
// A modifier dans le cas ou il est possible de fusionner deux chemins de corde
if (GameRules.IsInRopePaths(playerChoice, UsedMap.RopePaths, index)) break;
IEnumerable<Cell> ValidCell =
from cell in UsedMap.Boards
where cell.Value != null && cell.Valid == true && cell != playerChoice
select cell;
// Le cas si il existe des chemins de corde
foreach (var item in ValidCell)
{
if (!GameRules.IsCellAdjacent(playerChoice, item))
continue;
if (!((playerChoice.Value - item.Value) == 1 || (playerChoice.Value - item.Value) == -1))
continue;
if (!GameRules.IsInRopePaths(item,UsedMap.RopePaths,index))
{
UsedMap.RopePaths.Add(new List<Cell> { playerChoice, item });
return;
}
if (!GameRules.AsValue(playerChoice, UsedMap.RopePaths, index))
{
UsedMap.RopePaths[index].Add(playerChoice);
return;
}
}
}
// 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;
}
/// <summary>
/// Initializes the game.
/// </summary>
public void InitializeGame(Map map, Player player, bool startImmediately = true)
{
var runningGames = Games.Where(g => g.IsRunning).ToList();
foreach (var game in runningGames)
{
Games.Remove(game);
}
OnPropertyChanged(nameof(Games));
if (!GameRules.AsValue(playerChoice,UsedMap.RopePaths,index))
{
UsedMap.RopePaths[index].Add(playerChoice);
}
UsedMap = map;
CurrentPlayer = player;
Turn = 1;
Dice1 = new Dice();
Dice2 = new Dice(1);
// Si oui, est-ce que le chemin possede deja la valeur correspondante a la valeur de la cellule du joueur choisi
// {playerChoice.Value} n'est pas dans le chemin de corde
// Ajouter au chemin
// {playerChoice.Value} existe dans le chemin de corde pas possible
IsPreviousGameNotFinished = false;
OnPropertyChanged(nameof(UsedMap));
OnPropertyChanged(nameof(CurrentPlayer));
OnPropertyChanged(nameof(Turn));
}
if (startImmediately)
{
StartGame();
}
SaveData();
}
/// <summary>
/// Initializes the game.
/// Starts the game.
/// </summary>
public void InitializeGame()
private void StartGame()
{
_isRunning = true;
IsRunning = true;
GameStarted?.Invoke(this, new GameStartedEventArgs(CurrentPlayer));
GameLoop();
SaveData();
}
/// <summary>
/// Ends the game.
/// </summary>
private void EndGame()
private void EndGame(int? pts)
{
_isRunning = false;
GameEnded?.Invoke(this, new GameEndedEventArgs(CurrentPlayer));
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()
public void GameLoop()
{
while (_isRunning)
while (IsRunning)
{
if (Turn == 20)
{
EndGame();
break;
}
RollAllDice();
Resultat = PlayerChooseOperation();
PlayerSelectionCell();
BoardUpdated?.Invoke(this, new BoardsUpdateEventArgs(UsedMap.Boards.ToList()));
Turn++;
}
}
public int PlayerChooseOperation()
{
PlayerChooseOp?.Invoke(this, new PlayerChooseOperationEventArgs(PlayerOperation));
while(!GameRules.OperationAvailable(PlayerOperation,UsedMap.OperationGrid.ToList()))
{
PlayerChooseOp?.Invoke(this, new PlayerChooseOperationEventArgs(PlayerOperation));
}
PlayerOption?.Invoke(this, new PlayerOptionEventArgs(UsedMap.Boards.ToList(), ResultOperation(PlayerOperation), Turn));
OperationChosenFlag = true;
return ResultOperation(PlayerOperation);
}
public void PlayerSelectionCell()
{
PlayerChooseCell?.Invoke(this,new PlayerChooseCellEventArgs(PlayerCell));
while(!GameRules.NearCellIsValid(PlayerCell,UsedMap.Boards.ToList()))
{
PlayerChooseCell?.Invoke(this, new PlayerChooseCellEventArgs(PlayerCell));
}
MarkOperationAsChecked(PlayerOperation);
PlaceResult(PlayerCell, Resultat);
DiceRolledFlag = false;
OperationChosenFlag = false;
}
/// <summary>
/// Handles the player's choice of an operation based on the dice values.s
@ -220,6 +555,7 @@ namespace Models.Game
public void HandlePlayerOperation(Operation operation)
{
int result = ResultOperation(operation);
OperationChosenFlag = true;
OperationChosen?.Invoke(this, new OperationChosenEventArgs(operation, result));
}
@ -230,23 +566,72 @@ namespace Models.Game
/// <param name="result"></param>
/// <exception cref="InvalidCellCoordinatesException"></exception>
/// <exception cref="InvalidCellException"></exception>
public void HandlePlayerChoice(Cell cell, int result)
public bool HandlePlayerChoice(Cell cell, int result)
{
if (cell.X < 0 || cell.X >= UsedMap.Boards.Count / 6 || cell.Y < 0 || cell.Y >= 6)
{
throw new InvalidCellCoordinatesException("Invalid cell coordinates. Please choose again.");
return false;
}
if (!GameRules.IsCellValid(cell, UsedMap.Boards))
if (!GameRules.IsCellValid(cell, UsedMap.Boards.ToList()))
{
throw new InvalidCellException("Cell is not valid. Please choose again.");
return false;
}
PlaceResult(cell, result);
GameRules.IsZoneValidAndAddToZones(cell, UsedMap);
AddToRopePath(cell, GameRules.EveryAdjacentCells(cell, UsedMap.Boards));
AddToRopePath(cell);
CellChosen?.Invoke(this, new CellChosenEventArgs(cell, result));
BoardUpdated?.Invoke(this, EventArgs.Empty);
return true;
}
/// <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));
}
public int CalculusOfPenalty(ReadOnlyCollection<Cell> Boards)
{
int result = 0;
foreach (var cells in Boards)
if (cells.Penalty)
{
if (!cells.Valid || cells.Value == null)
continue;
result += 3;
}
return result;
}
public void PutPenaltyForLostCells(ReadOnlyCollection<Cell> Boards)
{
foreach (var cells in Boards)
{
if (cells == null || cells.Value == null || !cells.Valid)
continue;
if (!UsedMap.IsCellInZones(cells) && !UsedMap.IsCellInRopePath(cells))
cells.SetPenalty();
}
}
public int FinalCalculusOfPoints()
{
int? points = GameRules.FinalCalculusOfZones(UsedMap.Zones);
for (int i = 0; i < UsedMap.RopePaths.Count; i++)
{
points += GameRules.ScoreRopePaths(UsedMap.RopePaths[i]);
}
points -= CalculusOfPenalty(UsedMap.Boards);
return points ?? 0;
}
}
}

@ -1,78 +1,152 @@
namespace Models.Game
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using SQLite;
namespace Models.Game
{
/// <summary>
/// The Map class is the representation of the game map with the board and the operations table.
/// </summary>
[DataContract, SQLite.Table("Maps")]
public class Map
{
/// <summary>
/// The displaying name of the map
/// </summary>
[DataMember, PrimaryKey]
public string Name { get; private set; }
/// <summary>
/// It is the list of cells on the map.
/// </summary>
public List<Cell> Boards { get; private set; }
[DataMember]
[Ignore]
public ReadOnlyObservableCollection<Cell> Boards { get; private set; }
private readonly ObservableCollection<Cell> _board = new ObservableCollection<Cell>();
/// <summary>
/// It is the backgrond image of the map
/// </summary>
[DataMember]
public string Background { get; private set; }
/// <summary>
/// It is the grid of the possible operation in the game
/// </summary>
public List<OperationCell> OperationGrid { get; private set; }
[DataMember]
public ReadOnlyObservableCollection<OperationCell> OperationGrid { get; private set; }
private readonly ObservableCollection<OperationCell> _operationGrid = new ObservableCollection<OperationCell>();
/// <summary>
/// It is a list of a list containing user's rope paths in the current game
/// </summary>
[DataMember]
public List<List<Cell>> RopePaths { get; private set; }
/// <summary>
/// It is a list of a list containing user's zones in the current game
/// </summary>
[DataMember]
public List<List<Cell>> Zones { get; private set; }
/// <summary>
/// Initializes a new instance of the Map class.
/// </summary>
/// <param name="name"></param>
/// <param name="background">The background of the map.</param>
public Map(string background)
public Map(string name, string background)
{
Boards = InitializeBoards();
Name = name;
Background = background;
OperationGrid = InitializeOperationGrid();
Boards = new ReadOnlyObservableCollection<Cell>(_board);
InitializeBoards(_board);
OperationGrid = new ReadOnlyObservableCollection<OperationCell>(_operationGrid);
InitializeOperationGrid(_operationGrid);
RopePaths = new List<List<Cell>>();
Zones = new List<List<Cell>>();
}
/// <summary>
/// SQLite constructor
/// </summary>
public Map() { }
/// <summary>
/// Clone the map to avoid reference problems.
/// </summary>
/// <returns></returns>
public Map Clone()
{
Map map = new Map(Name, Background);
return map;
}
/// <summary>
/// Initializes the boards of the map.
/// </summary>
/// <returns>Return the boards</returns>
private List<Cell> InitializeBoards()
private void InitializeBoards(ObservableCollection<Cell> board)
{
var boards = new List<Cell>();
for (int i = 0; i < 36; i++) // 6x6 board
for (int i = 0; i < 49; i++) // 7x7 board
{
boards.Add(new Cell(i / 6, i % 6));
board.Add(new Cell(i % 7, i / 7));
}
return boards;
board[1].Valid = true;
board[3].Valid = true;
board[4].Valid = true;
board[5].Valid = true;
board[9].Valid = true;
board[12].Valid = true;
board[15].Valid = true;
board[16].Valid = true;
board[17].Valid = true;
board[21].Valid = true;
board[24].Valid = true;
board[25].Valid = true;
board[28].Valid = true;
board[30].Valid = true;
board[31].Valid = true;
board[32].Valid = true;
board[40].Valid = true;
board[41].Valid = true;
board[48].Valid = true;
board[3].IsDangerous = true;
board[15].IsDangerous = true;
board[16].IsDangerous = true;
}
/// <summary>
/// Initializes the operation grid of the map.
/// </summary>
/// <returns>Return the operation grid</returns>
private List<OperationCell> InitializeOperationGrid()
private void InitializeOperationGrid(ObservableCollection<OperationCell>operationGrid)
{
var operationGrid = new List<OperationCell>();
for (int i = 0; i < 5; i++) // 5 operations
{
for (int j = 0; j < 4; j++) // 4 cells per operation
{
operationGrid.Add(new OperationCell(i, j));
operationGrid.Add(new OperationCell(j, i));
}
}
return operationGrid;
}
public bool CheckOperationPossible(int x)
{
return OperationGrid[x * 4 + 3].IsChecked;
}
public bool IsCellInZones(Cell cell)
{
return Zones.Any(zone => zone.Contains(cell));
}
public bool IsCellInRopePath(Cell cell)
{
return RopePaths.Any(path => path.Contains(cell));
}
}
}

@ -16,5 +16,7 @@ namespace Models.Game
SUBTRACTION,
ADDITION,
MULTIPLICATION
}
}

@ -1,14 +1,30 @@
using System.ComponentModel;
using System.Runtime.Serialization;
namespace Models.Game
{
/// <summary>
/// Represents a cell in the operation grid of the game.
/// </summary>
public class OperationCell : Position
[DataContract]
public class OperationCell : Position, INotifyPropertyChanged
{
/// <summary>
/// It tells if the operation is checked or not in the operation grid of the game.
/// </summary>
public bool IsChecked { get; private set; }
[DataMember]
private bool isChecked;
public bool IsChecked
{
get => isChecked;
set
{
if (isChecked == value)
return;
isChecked = value;
OnPropertyChanged(nameof(IsChecked));
}
}
/// <summary>
/// Constructor of the OperationCell class.
@ -17,6 +33,17 @@ namespace Models.Game
/// <param name="y"></param>
public OperationCell(int x, int y) : base(x, y)
{
IsChecked = false;
}
public event PropertyChangedEventHandler? PropertyChanged;
void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
/// <summary>

@ -1,59 +1,77 @@
namespace Models.Game
using SQLite;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations.Schema;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using Models.Interfaces;
namespace Models.Game
{
/// <summary>
/// Represents a player in the game.
/// </summary>
public class Player
[DataContract, SQLite.Table("Players")]
public class Player : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
void OnPropertyChanged([CallerMemberName] string? propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
/// <summary>
/// It is he pseudo of the player.
/// </summary>
public string Pseudo { get; private set; }
private string _pseudo;
[DataMember, PrimaryKey]
public string Pseudo
{
get => _pseudo;
set
{
if (_pseudo == value)
return;
_pseudo = value;
OnPropertyChanged();
}
}
/// <summary>
/// It is the profile picture of the player.
/// </summary>
[DataMember]
public string ProfilePicture { get; private set; }
/// <summary>
/// It is the creation date of the player.
/// </summary>
public string? CreationDate { get; private set; }
[DataMember]
public string CreationDate { get; private set; }
/// <summary>
/// It tells when was the last time the player played.
/// </summary>
[DataMember]
public string? LastPlayed { get; private set; }
/// <summary>
/// Constructor with default values.
/// </summary>
public Player()
{
Pseudo = "Player";
ProfilePicture = "DefaultProfilePicture";
}
/// <summary>
/// Construct a new instance of Player with specified pseudo and profile picture.
/// </summary>
/// <param name="pseudo">The pseudo of the player.</param>
/// <param name="profilePicture">The profile picture of the player.</param>
public Player(string pseudo, string profilePicture = "DefaultProfilePicture")
public Player(string pseudo, string profilePicture)
{
Pseudo = pseudo;
ProfilePicture = profilePicture;
CreationDate = DateTime.Now.ToString("dd/MM/yyyy");
}
/// <summary>
/// Chooses the operation for the player.
/// SQLite constructor
/// </summary>
/// <returns>The chosen operation.</returns>
public Operation ChooseOperation()
{
return Operation.LOWER;
}
public Player() { }
/// <summary>
/// Redefine the equal operation between player.
@ -78,5 +96,13 @@
{
return Pseudo.GetHashCode();
}
/// <summary>
/// Actualize the last time the player played.
/// </summary>
public void UpdateLastPlayed()
{
LastPlayed = DateTime.Now.ToString("dd/MM/yyyy");
}
}
}

@ -1,19 +1,26 @@
namespace Models.Game
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
namespace Models.Game
{
/// <summary>
/// The Position (x,y) of a cell in the game.
/// </summary>
[DataContract]
public class Position
{
/// <summary>
/// The X coordinate.
/// </summary>
[DataMember]
public int X { get; set; }
/// <summary>
/// The Y coordinate.
/// </summary>
[DataMember]
public int Y { get; set; }
/// <summary>

@ -0,0 +1,24 @@
using Microsoft.Maui.Controls;
namespace Models.Interfaces
{
/// <summary>
/// Interface for image converters
/// </summary>
public interface IImageConverter
{
/// <summary>
/// Converter to any type that can be converted to a string (ex: Base64)
/// </summary>
/// <param name="imagePath"></param>
/// <returns></returns>
string ConvertImage(string imagePath);
/// <summary>
/// Retrieve an image from a string encoded
/// </summary>
/// <param name="imageString"></param>
/// <returns></returns>
ImageSource RetrieveImage(string imageString);
}
}

@ -0,0 +1,12 @@
using System.Collections.ObjectModel;
namespace Models.Interfaces
{
using Models.Game;
public interface IPersistence
{
(ObservableCollection<Player>, ObservableCollection<Game>, ObservableCollection<Map>, ObservableCollection<BestScore>) LoadData();
void SaveData(ObservableCollection<Player> players, ObservableCollection<Game> games, ObservableCollection<Map> maps, ObservableCollection<BestScore> bestScores);
}
}

@ -8,8 +8,6 @@ namespace Models.Interfaces
/// </summary>
public interface IRules
{
//public bool NearCellIsValid(Position playerChoicePosition, SortedDictionary<Position, Cell> cells);
/// <summary>
/// Return true if the cell is empty, otherwise false.
/// </summary>
@ -25,15 +23,6 @@ namespace Models.Interfaces
/// <returns>true if the cell is valid; otherwise, false</returns>
public bool IsCellValid(Cell playerChoicePosition, List<Cell> cells);
//public bool IsRopePath(Cell playerChoice, HashSet<RopePath> ropePaths);
//public bool IsAdjacent(Cell cell1, Cell cell2, List<Position, Cell> cells);
//public int HowMany(Cell playerChoice, List<Position, Cell> cells);
//public void SetValueAndPenalty(int valueChoice, Cell playerChoice, List<Position, Cell> cells);
/// <summary>
/// Check if the given cell is adjacent to the target cell.
/// </summary>

@ -7,9 +7,14 @@
</PropertyGroup>
<ItemGroup>
<Folder Include="Game\" />
<Folder Include="Interfaces\" />
<Folder Include="Rules\" />
<Folder Include="Game\" />
<Folder Include="Interfaces\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls.Core" Version="8.0.3" />
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
</ItemGroup>
</Project>

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -24,40 +25,32 @@ namespace Models.Rules
{
if (!IsCellEmpty(playerChoicePosition)) return false;
if (!playerChoicePosition.Valid) return false;
if (EveryAdjacentCells(playerChoicePosition, cells).Count == 1) return false;
return true;
}
public bool IsCellAdjacent(Cell choosenCell, Cell targetCell)
{
if (Math.Abs(choosenCell.X - targetCell.X) > 1 || Math.Abs(choosenCell.Y - targetCell.Y) > 1)
return false;
if (Math.Abs(choosenCell.X - targetCell.X) > 1 && Math.Abs(choosenCell.Y - targetCell.Y) > 1)
return false;
if (choosenCell.X == 0 && targetCell.X == 4)
return false;
if (choosenCell.Y == 0 && targetCell.Y == 4)
return false;
if (choosenCell.X == 4 && targetCell.X == 0)
return false;
if (choosenCell.Y == 4 && targetCell.Y == 0)
return false;
if (choosenCell.X == targetCell.X && choosenCell.Y == targetCell.Y)
return false;
if (Math.Abs(choosenCell.X - targetCell.X) <= 1 && Math.Abs(choosenCell.Y - targetCell.Y) <= 1)
{
return true;
}
return false;
}
return true;
}
public bool IsInRopePaths (Cell adjacente,List<List<Cell>> ropePaths,int index)
public bool IsInRopePaths (Cell adjacente,List<List<Cell>> ropePaths, int index)
{
foreach (List<Cell> path in ropePaths)
{
if (path.Contains(adjacente))
{
index=ropePaths.IndexOf(path);
return true;
}
if (!path.Contains(adjacente)) continue;
index=ropePaths.IndexOf(path);
return true;
}
return false;
}
@ -70,32 +63,36 @@ namespace Models.Rules
}
return false;
}
public bool NearCellIsValid(Cell choosenCell, List<Cell> cells)
{
if (choosenCell == null || cells == null) return false;
IEnumerable<Cell> PlayedCellsQuery =
from cell in cells
where cell.Value != null
where cell.Valid
select cell;
foreach (var cell in PlayedCellsQuery)
{
if(!IsCellAdjacent(choosenCell, cell)) continue;
return true;
if (choosenCell.X == cell.X && choosenCell.Y == cell.Y && cell.Value == null)
return true;
if (IsCellAdjacent(choosenCell, cell))
return true;
}
return false;
}
public void IsZoneValidAndAddToZones(Cell chosenCell, Map map)
{
if (chosenCell == null ||chosenCell.Value == null) return;
List<Cell> adjacentCells = new List<Cell>();
List<Cell> adjacentCells;
adjacentCells = EveryAdjacentCells(chosenCell, map.Boards);
adjacentCells = EveryAdjacentCells(chosenCell, map.Boards.ToList());
foreach(var cells in adjacentCells)
{
@ -109,12 +106,8 @@ namespace Models.Rules
{
NewZoneIsCreated(chosenCell, cells, map);
}
//return true; // Il y a une cellule adjacente avec la même valeur donc une zone est créée si elle n'est pas déjà existante
// Si il return true, tout c'est bien passer
}
}
return;
}
public bool IsValueInZones(Cell chosenCell, List<List<Cell>> zones)
@ -159,7 +152,6 @@ namespace Models.Rules
return;
}
}
return;
}
public void NewZoneIsCreated(Cell firstCell, Cell secondCell, Map map)
@ -171,9 +163,11 @@ namespace Models.Rules
map.Zones.Add(newZone);
}
public List<Cell> EveryAdjacentCells(Cell choosenCell, List<Cell> cells)
public List<Cell> EveryAdjacentCells(Cell? choosenCell, List<Cell> cells)
{
List<Cell> adjacentCells = new List<Cell>();
if (choosenCell == null || cells == null) return adjacentCells;
foreach (var cell in cells)
{
@ -195,11 +189,42 @@ 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;
}
public int? ScoreRopePaths(List<Cell> paths)
{
int? score = 0;
IEnumerable<Cell> sortPaths =
from cell in paths
orderby cell.Value descending
select cell;
foreach (var item in sortPaths)
{
if (score == 0)
score += item.Value;
else
score++;
}
return score;
}
public bool OperationAvailable(Operation operation,List<OperationCell> operationGrid)
{
IEnumerable<OperationCell> row =
from cell in operationGrid
where cell.Y == (int)operation
select cell;
foreach (var item in row)
{
if (!item.IsChecked)
return true;
}
return false;
}
}
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Models\Models.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Models.Game;
using Models.Interfaces;
using Moq;
namespace Tests
{
public class Base64ConverterTests
{
/*
public interface IImageConverter
{
/// <summary>
/// Converter to any type that can be converted to a string (ex: Base64)
/// </summary>
/// <param name="imagePath"></param>
/// <returns></returns>
string ConvertImage(string imagePath);
/// <summary>
/// Retrieve an image from a string encoded
/// </summary>
/// <param name="imageString"></param>
/// <returns></returns>
ImageSource RetrieveImage(string imageString);
}
*/
/*
public class Base64Converter : IImageConverter
{
/// <summary>
/// Converts an image to a base64 string
/// </summary>
/// <param name="imagePath">The path to access the image</param>
/// <returns>The base64 string representation of the image</returns>
/// <exception cref="FileNotFoundException">Native .NET exception</exception>
public string ConvertImage(string imagePath)
{
if (!File.Exists(imagePath))
{
// native .NET exception
throw new FileNotFoundException("Image file not found", imagePath);
}
byte[] imageBytes = File.ReadAllBytes(imagePath);
return Convert.ToBase64String(imageBytes);
}
/// <summary>
/// Retrieve an image from a string encoded in base64
/// </summary>
/// <param name="imageString"></param>
/// <returns></returns>
public ImageSource RetrieveImage(string imageString)
{
byte[] imageBytes = Convert.FromBase64String(imageString);
return ImageSource.FromStream(() => new MemoryStream(imageBytes));
}
}
*/
// Create a mock
private Mock<IImageConverter> _imageConverterMock;
public Base64ConverterTests()
{
_imageConverterMock = new Mock<IImageConverter>();
}
[Fact]
public void ConvertImage_WithNonExistentPath_ThrowsFileNotFoundException()
{
var base64Converter = new Base64Converter();
Assert.Throws<FileNotFoundException>(() => base64Converter.ConvertImage("nonexistent.jpg"));
}
[Fact]
public void RetrieveImage_WithInvalidBase64_ThrowsFormatException()
{
var base64Converter = new Base64Converter();
Assert.Throws<FormatException>(() => base64Converter.RetrieveImage("////invalidBase64//!!///"));
}
}
}

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using Models.Game;
@ -12,7 +13,11 @@ namespace Tests
[Fact]
public void Constructor_WithNegativeInputs_SetsPropertiesToZero()
{
var bestScore = new BestScore(-5, -10);
var myMap = new Map("Dunai", "Dunai.png");
var myPlayer = new Player("John", "pp.png");
var bestScore = new BestScore(myMap.Name, myPlayer, -5, -10);
Assert.Equal(0, bestScore.GamesPlayed);
Assert.Equal(0, bestScore.Score);
}
@ -20,7 +25,11 @@ namespace Tests
[Fact]
public void Constructor_WithPositiveInputs_SetsPropertiesCorrectly()
{
var bestScore = new BestScore(5, 10);
var myMap = new Map("Dunai", "Dunai.png");
var myPlayer = new Player("John", "pp.png");
var bestScore = new BestScore(myMap.Name, myPlayer, 5, 10);
Assert.Equal(5, bestScore.GamesPlayed);
Assert.Equal(10, bestScore.Score);
}
@ -28,25 +37,99 @@ namespace Tests
[Fact]
public void IncrGamesPlayed_IncrementsGamesPlayed()
{
var bestScore = new BestScore(0, 0);
var myMap = new Map("Dunai", "Dunai.png");
var myPlayer = new Player("John", "pp.png");
var bestScore = new BestScore(myMap.Name, myPlayer, 0, 0);
bestScore.IncrGamesPlayed();
Assert.Equal(1, bestScore.GamesPlayed);
}
[Fact]
public void ScoreSetter_WithLowerValue_DoesNotChangeScore()
{
var bestScore = new BestScore(0, 10);
var myMap = new Map("Dunai", "Dunai.png");
var myPlayer = new Player("John", "pp.png");
var bestScore = new BestScore(myMap.Name, myPlayer, 0, 10);
bestScore.UpdateScore(5);
Assert.Equal(10, bestScore.Score);
Assert.Equal(15, bestScore.Score);
}
[Fact]
public void ScoreSetter_WithHigherValue_ChangesScore()
{
var bestScore = new BestScore(0, 5);
var myMap = new Map("Dunai", "Dunai.png");
var myPlayer = new Player("John", "pp.png");
var bestScore = new BestScore(myMap.Name, myPlayer, 0, 5);
bestScore.UpdateScore(10);
Assert.Equal(10, bestScore.Score);
Assert.Equal(15, bestScore.Score);
}
[Fact]
public void IdGetter_ReturnsCorrectId()
{
var myMap = new Map("Dunai", "Dunai.png");
var myPlayer = new Player("John", "pp.png");
var bestScore = new BestScore(myMap.Name, myPlayer, 5, 10);
bestScore.Id = 1;
Assert.Equal(1, bestScore.Id);
}
[Fact]
public void Equals_WithEqualBestScores_ReturnsTrue()
{
var myMap = new Map("Dunai", "Dunai.png");
var myPlayer = new Player("John", "pp.png");
var bestScore1 = new BestScore(myMap.Name, myPlayer, 5, 10);
var bestScore2 = new BestScore(myMap.Name, myPlayer, 5, 10);
Assert.True(bestScore1.Equals(bestScore2));
}
[Fact]
public void Equals_WithDifferentBestScores_ReturnsFalse()
{
var myMap1 = new Map("Dunai", "Dunai.png");
var myMap2 = new Map("Amazon", "Amazon.png");
var myPlayer = new Player("John", "pp.png");
var bestScore1 = new BestScore(myMap1.Name, myPlayer, 5, 10);
var bestScore2 = new BestScore(myMap2.Name, myPlayer, 5, 10);
Assert.False(bestScore1.Equals(bestScore2));
}
[Fact]
public void GetHashCode_WithEqualBestScores_ReturnsSameHashCode()
{
var myMap = new Map("Dunai", "Dunai.png");
var myPlayer = new Player("John", "pp.png");
var bestScore1 = new BestScore(myMap.Name, myPlayer, 5, 10);
var bestScore2 = new BestScore(myMap.Name, myPlayer, 5, 10);
Assert.Equal(bestScore1.GetHashCode(), bestScore2.GetHashCode());
}
[Fact]
public void GetHashCode_WithDifferentBestScores_ReturnsDifferentHashCodes()
{
var myMap1 = new Map("Dunai", "Dunai.png");
var myMap2 = new Map("Amazon", "Amazon.png");
var myPlayer = new Player("John", "pp.png");
var bestScore1 = new BestScore(myMap1.Name, myPlayer, 5, 10);
var bestScore2 = new BestScore(myMap2.Name, myPlayer, 5, 10);
Assert.NotEqual(bestScore1.GetHashCode(), bestScore2.GetHashCode());
}
}
}

@ -13,30 +13,37 @@ 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_WithValueMoreThan5_SetsValueTo0()
{
Dice dice = new Dice(6);
Assert.Equal(0, dice.Value);
}
[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 +51,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);
}
}

@ -0,0 +1,694 @@
using System.Collections.ObjectModel;
using Moq;
using Models.Game;
using Models.Interfaces;
using System.Reflection;
using static System.Type;
using Xunit.Abstractions;
namespace Tests;
public class GameTests
{
private readonly Mock<IPersistence> _mockPersistence; // Mocking (faking) the persistence layer to allow for testing without a persistent connection
private readonly Game _game;
public GameTests()
{
_mockPersistence = new Mock<IPersistence>();
_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 DefaultConstructor_ShouldInitializeLists()
{
Assert.NotNull(_game.Players);
Assert.NotNull(_game.Games);
Assert.NotNull(_game.Maps);
Assert.NotNull(_game.BestScores);
Assert.NotNull(_game.GameRules);
Assert.False(_game.IsRunning);
}
[Fact]
public void AddPlayer_ShouldAddPlayerToList()
{
var player = new Player("test_player", "DefaultProfilePicture");
_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_name", "test_background.png");
_game.AddMap(map);
Assert.Contains(map, _game.Maps);
}
[Fact]
public void AddBestScore_ShouldAddBestScoreToList()
{
var bestScore = new BestScore("test", new Player("John", "Picture.png"), 3, 127);
_game.BestScores.Add(bestScore);
Assert.Contains(bestScore, _game.BestScores);
}
[Fact]
public void LoadData_ShouldLoadDataFromPersistence()
{
var myGame = new Game(_mockPersistence.Object);
var myPlayer = new Player("test", "DefaultProfilePicture");
var myMap = new Map("test_name", "test_background.png");
var players = new ObservableCollection<Player> { myPlayer };
var games = new ObservableCollection<Game> { myGame };
var maps = new ObservableCollection<Map> { myMap };
var bestScores = new ObservableCollection<BestScore> { new BestScore(myMap.Name, myPlayer, 1, 45) };
_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("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
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);
}
[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("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_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", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
Assert.Equal(map, _game.UsedMap);
}
[Fact]
public void Game_Initialization_SetsPlayer()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
Assert.Equal(player, _game.CurrentPlayer);
}
[Fact]
public void Game_Initialization_SetsDice()
{
Player player = new Player("test_player", "DefaultProfilePicture");
Map map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
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("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
// 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);
}
[Fact]
public void IsPlaceOperationCorrect()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
Assert.NotNull(_game.GameRules);
_game.UsedMap.Boards[0].Value = 1;
_game.UsedMap.Boards[1].Value = 2;
_game.UsedMap.Boards[0].Valid = true;
_game.UsedMap.Boards[1].Valid = true;
_game.UsedMap.Boards[2].Valid = true;
var methodInfo = typeof(Game).GetMethod("PlaceResult", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(methodInfo);
_game.PlayerCell = new Cell(2, 0);
_game.PlayerCell.Value = 3;
methodInfo.Invoke(_game, new object[] { _game.PlayerCell, 3 });
//_game.UsedMap.Boards[2].Value = _game.PlayerCell.Value;
Assert.Equal(3, _game.UsedMap.Boards[2].Value);
}
[Fact]
public void IsHandlePlayerChoice_Handling()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
var cell = new Cell(1, 0);
cell.Valid = true;
_game.UsedMap.Boards[0].Valid = true;
_game.UsedMap.Boards[0].Value = 1;
_game.UsedMap.Boards[1].Valid = true;
bool result = _game.HandlePlayerChoice(cell, 1);
Assert.True(result);
}
[Fact]
public void IsHandlePlayerChoice_InvalidCell()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
var cell = new Cell(0, 7);
cell.Value = 1;
bool result = _game.HandlePlayerChoice(cell, 1);
Assert.False(result);
}
[Fact]
public void IsHandlePlayerChoice_InvalidPlace()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
var cell = new Cell(0, 0);
cell.Value = 1;
var othercell = new Cell(3, 3);
bool result = _game.HandlePlayerChoice(othercell, 1);
Assert.False(result);
}
[Fact]
public void RemovePlayerTest_ShouldRemovePlayer()
{
Game game = new Game();
Player player = new Player("test", "DefaultProfilePicture");
game.AddPlayer(player);
Assert.True(game.RemovePlayer("test"));
}
[Fact]
public void RemovePlayerTest_ShouldNotRemovePlayer()
{
Game game = new Game();
Player player = new Player("test", "DefaultProfilePicture");
game.AddPlayer(player);
Assert.False(game.RemovePlayer("otherName"));
}
[Fact]
public void PutPenalty_ShouldPutPenalty()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
_game.UsedMap.Boards[1].Valid = true;
_game.UsedMap.Boards[2].Valid = true;
_game.UsedMap.Boards[1].Value = 5;
var methodInfo = typeof(Game).GetMethod("PlaceResult", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(methodInfo);
_game.PlayerCell = new Cell(2, 0);
_game.PlayerCell.Value = 14;
methodInfo.Invoke(_game, new object[] { _game.PlayerCell, 14 });
Assert.True(_game.UsedMap.Boards[2].Penalty);
}
[Fact]
public void PutPenalty_ShouldPutPenalty_ForDangerous()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
_game.UsedMap.Boards[1].Valid = true;
_game.UsedMap.Boards[2].Valid = true;
_game.UsedMap.Boards[1].Value = 5;
_game.UsedMap.Boards[2].IsDangerous = true;
var methodInfo = typeof(Game).GetMethod("PlaceResult", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(methodInfo);
_game.PlayerCell = new Cell(2, 0);
_game.PlayerCell.Value = 7;
methodInfo.Invoke(_game, new object[] { _game.PlayerCell, 7 });
Assert.True(_game.UsedMap.Boards[2].Penalty);
}
[Fact]
public void PutPenaltyForLostCells_ReallyPutPenalty_ForZones()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
_game.UsedMap.Boards[0].Valid = true;
_game.UsedMap.Boards[3].Valid = true;
_game.UsedMap.Boards[2].Valid = true;
_game.UsedMap.Boards[1].Valid = true;
_game.UsedMap.Boards[1].Value = 5;
_game.UsedMap.Boards[2].Value = 5;
_game.UsedMap.Boards[3].Value = 5;
_game.UsedMap.Boards[0].Value = 0;
foreach (var cells in _game.UsedMap.Boards.ToList())
{
_game.GameRules.IsZoneValidAndAddToZones(cells, _game.UsedMap);
}
_game.PutPenaltyForLostCells(_game.UsedMap.Boards);
Assert.True(_game.UsedMap.Boards[0].Penalty);
}
[Fact]
public void PutPenaltyForLostCells_ReallyPutPenalty_ForRopePathes()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
_game.UsedMap.Boards[1].Value = 1;
_game.UsedMap.Boards[2].Value = 2;
_game.UsedMap.Boards[3].Value = 5;
_game.UsedMap.Boards[0].Value = 0;
var methodInfo = typeof(Game).GetMethod("AddToRopePath", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(methodInfo);
foreach (var cells in _game.UsedMap.Boards.ToList())
{
methodInfo.Invoke(_game, new object[] { cells });
}
_game.PutPenaltyForLostCells(_game.UsedMap.Boards);
Assert.True(_game.UsedMap.Boards[3].Penalty);
}
[Fact]
public void CalculusOfPenalty_ReallyCalculusPenalty_ForZonesAndDangerousCellsAndOverTwelve()
{
var player = new Player("test_player", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
_game.InitializeGame(map, player, false);
_game.UsedMap.Boards[7].Valid = true;
_game.UsedMap.Boards[8].Valid = true;
_game.UsedMap.Boards[9].Valid = true;
_game.UsedMap.Boards[10].Valid = true;
_game.UsedMap.Boards[11].Valid = true;
_game.UsedMap.Boards[12].Valid = true;
_game.UsedMap.Boards[10].Value = 2; // 1,3 // penalty
_game.UsedMap.Boards[7].Value = 5; // 1,0
_game.UsedMap.Boards[8].Value = 5; // 1,1
_game.UsedMap.Boards[9].Value = 5; // 1,2
var place = typeof(Game).GetMethod("PlaceResult", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(place);
_game.PlayerCell = new Cell(4, 1);
_game.PlayerCell.Value = 7;
_game.PlayerCell.Valid = true;
_game.PlayerCell.IsDangerous = true;
place.Invoke(_game, new object[] { _game.PlayerCell, 7 }); //One penalty
_game.PlayerCell = new Cell(5, 1);
_game.PlayerCell.Value = 14;
_game.PlayerCell.Valid = true;
place.Invoke(_game, new object[] { _game.PlayerCell, 14 });
foreach (var cells in _game.UsedMap.Boards.ToList())
{
_game.GameRules.IsZoneValidAndAddToZones(cells, _game.UsedMap);
}
_game.PutPenaltyForLostCells(_game.UsedMap.Boards);
Assert.True(_game.UsedMap.Boards[11].Penalty);
Assert.Equal(9, _game.CalculusOfPenalty(_game.UsedMap.Boards));
}
[Fact]
public void CanIModifyAPlayer()
{
Game game = new Game();
Player player = new Player("test", "DefaultProfilePicture");
game.AddPlayer(player);
game.ModifyPlayer("test", "newName");
Assert.Equal("newName", game.Players[0].Pseudo);
}
[Fact]
public void CanIModifyANonExistentPlayer()
{
Game game = new Game();
var res = game.ModifyPlayer("nope", "newName");
Assert.False(res);
}
[Fact]
public void CanIRollDice()
{
_game.InitializeGame(new Map("test", "test.png"), new Player("test", "test.png"), false);
_game.RollAllDice();
Assert.NotNull(_game.Dice1);
Assert.NotNull(_game.Dice2);
Assert.True(_game.Dice1.Value >= 0 && _game.Dice1.Value <= 5 );
Assert.True(_game.Dice2.Value >= 1 && _game.Dice2.Value <= 6 );
}
[Fact]
public void CanIStartGame()
{
_game.InitializeGame(new Map("test", "test.png"), new Player("test", "test.png"), false);
var start = typeof(Game).GetMethod("StartGame", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(start);
start.Invoke(_game, null);
Assert.True(_game.IsRunning);
}
[Fact]
public void CanIEndGame()
{
_game.InitializeGame(new Map("test", "test.png"), new Player("test", "test.png"), false);
var start = typeof(Game).GetMethod("StartGame", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(start);
start.Invoke(_game, null);
var end = typeof(Game).GetMethod("EndGame", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(end);
end.Invoke(_game, new object[] { 14 } );
Assert.False(_game.IsRunning);
}
[Fact]
public void CalculusOfPointsWorksWellOrNot()
{
_game.InitializeGame(new Map("test", "test.png"), new Player("test", "test.png"), false);
_game.UsedMap.Boards[7].Valid = true;
_game.UsedMap.Boards[8].Valid = true;
_game.UsedMap.Boards[9].Valid = true;
_game.UsedMap.Boards[10].Valid = true;
_game.UsedMap.Boards[11].Valid = true;
_game.UsedMap.Boards[12].Valid = true;
_game.UsedMap.Boards[10].Value = 2; // penalty (- 3)
_game.UsedMap.Boards[7].Value = 5; //5 + 2 = 7
_game.UsedMap.Boards[8].Value = 5;
_game.UsedMap.Boards[9].Value = 5;
var place = typeof(Game).GetMethod("PlaceResult", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(place);
_game.PlayerCell = new Cell(4, 1);
_game.PlayerCell.Value = 7;
_game.PlayerCell.Valid = true;
_game.PlayerCell.IsDangerous = true;
place.Invoke(_game, new object[] { _game.PlayerCell, 7 }); //One penalty
foreach (var cells in _game.UsedMap.Boards.ToList())
{
_game.GameRules.IsZoneValidAndAddToZones(cells, _game.UsedMap);
}
_game.PutPenaltyForLostCells(_game.UsedMap.Boards);
Assert.True(_game.UsedMap.Boards[11].Penalty);
Assert.Equal(1, _game.FinalCalculusOfPoints());
}
[Fact]
public void CalculusOfPointsWorksWellOrNotForRopePathes()
{
_game.InitializeGame(new Map("test", "test.png"), new Player("test", "test.png"), false);
var methodInfo = typeof(Game).GetMethod("AddToRopePath", BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(methodInfo);
_game.Turn = 2;
_game.UsedMap.Boards[7].Valid = true;
_game.UsedMap.Boards[8].Valid = true;
_game.UsedMap.Boards[9].Valid = true;
_game.UsedMap.Boards[10].Valid = true;
_game.UsedMap.Boards[7].Value = 5; //7 + 2 = 9
methodInfo.Invoke(_game, new object[] { _game.UsedMap.Boards[7] });
_game.UsedMap.Boards[8].Value = 6;
methodInfo.Invoke(_game, new object[] { _game.UsedMap.Boards[8] });
_game.UsedMap.Boards[9].Value = 7;
methodInfo.Invoke(_game, new object[] { _game.UsedMap.Boards[9] });
_game.UsedMap.Boards[10].Value = 2; // penalty (- 3)
methodInfo.Invoke(_game, new object[] { _game.UsedMap.Boards[10] });
_game.PutPenaltyForLostCells(_game.UsedMap.Boards);
Assert.True(_game.UsedMap.Boards[10].Penalty);
Assert.Equal(6, _game.FinalCalculusOfPoints());
}
[Fact]
public void AddBestScore_AddsNewBestScoreToList()
{
var game = new Game(_mockPersistence.Object);
var player = new Player("John Doe", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
game.InitializeGame(map, player, false);
game.AddBestScore(100);
Assert.Single(game.BestScores);
Assert.Equal(100, game.BestScores[0].Score);
Assert.Equal(player, game.BestScores[0].ThePlayer);
Assert.Equal(map.Name, game.BestScores[0].MapName);
}
[Fact]
public void AddBestScore_UpdatesExistingBestScoreAndIncrementsGamesPlayed()
{
var game = new Game(_mockPersistence.Object);
var player = new Player("John Doe", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
game.InitializeGame(map, player, false);
game.AddBestScore(100);
game.AddBestScore(200);
Assert.Single(game.BestScores);
Assert.Equal(300, game.BestScores[0].Score);
Assert.Equal(2, game.BestScores[0].GamesPlayed);
Assert.Equal(player, game.BestScores[0].ThePlayer);
Assert.Equal(map.Name, game.BestScores[0].MapName);
}
[Fact]
public void AddBestScore_SortsBestScoresCorrectly()
{
var game = new Game(_mockPersistence.Object);
var player1 = new Player("John Doe", "DefaultProfilePicture");
var player2 = new Player("John Does", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
game.InitializeGame(map, player1, false);
game.AddBestScore(100);
game.InitializeGame(map, player2, false);
game.AddBestScore(200);
Assert.Equal(2, game.BestScores.Count);
Assert.Equal(200, game.BestScores[0].Score);
Assert.Equal(player2, game.BestScores[0].ThePlayer);
Assert.Equal(100, game.BestScores[1].Score);
Assert.Equal(player1, game.BestScores[1].ThePlayer);
}
[Fact]
public void CheckAndRemoveBestScoresDependencies_RemovesDependenciesCorrectly()
{
var game = new Game(_mockPersistence.Object);
var player = new Player("John Doe", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
game.InitializeGame(map, player, false);
game.AddBestScore(100);
game.CheckAndRemoveBestScoresDependencies(player.Pseudo);
Assert.DoesNotContain(game.BestScores, bs => bs.ThePlayer.Pseudo == player.Pseudo);
}
[Fact]
public void CheckAndChangeBestScoresDependencies_ChangesDependenciesCorrectly()
{
var game = new Game(_mockPersistence.Object);
var player = new Player("John Doe", "DefaultProfilePicture");
var map = new Map("test_name", "test_background.png");
game.InitializeGame(map, player, false);
game.AddBestScore(100);
game.CheckAndChangeBestScoresDependencies(player.Pseudo, "John Does");
Assert.All(game.BestScores, bs => Assert.NotEqual("John Doe", bs.ThePlayer.Pseudo));
Assert.Contains(game.BestScores, bs => bs.ThePlayer.Pseudo == "John Does");
}
}

@ -8,37 +8,101 @@ public class MapTests
[Fact]
public void Map_Initialization_SetsBackground()
{
string name = "test_name";
string background = "test_background";
var map = new Map(background);
var map = new Map(name, background);
Assert.Equal(background, map.Background);
}
[Fact]
public void Map_Initialization_InitializesBoards()
{
string name = "test_name";
string background = "test_background";
var map = new Map(background);
Assert.Equal(36, map.Boards.Count);
var map = new Map(name, background);
Assert.Equal(49, map.Boards.Count);
for (int i = 0; i < 36; i++)
{
Assert.Equal(new Cell(i / 6, i % 6), map.Boards[i]);
Assert.Equal(new Cell(i % 7, i / 7), map.Boards[i]);
}
}
[Fact]
public void Map_Initialization_InitializesRopePathsAndZones()
{
string name = "test_name";
string background = "test_background";
var map = new Map(background);
var map = new Map(name, background);
Assert.NotNull(map.RopePaths);
Assert.NotNull(map.Zones);
Assert.Empty(map.RopePaths);
Assert.Empty(map.Zones);
}
[Fact]
public void IsCellInZones_FoundTheCell()
{
var map = new Map("test_name", "test_background.png");
var cell = new Cell(0, 0);
var zone = new List<Cell> { cell };
map.Zones.Add(zone);
Assert.True(map.IsCellInZones(cell));
}
[Fact]
public void IsCellInZones_DoNotFoundTheCell()
{
var map = new Map("test_name", "test_background.png");
var cell = new Cell(0, 0);
var othercell = new Cell(1, 1);
var zone = new List<Cell> { cell };
map.Zones.Add(zone);
Assert.False(map.IsCellInZones(othercell));
}
[Fact]
public void IsCellInRopePath_FoundTheCell()
{
var map = new Map("test_name", "test_background.png");
var cell = new Cell(0, 0);
var paths = new List<Cell> { cell };
map.RopePaths.Add(paths);
Assert.True(map.IsCellInRopePath(cell));
}
[Fact]
public void IsCellInRopePath_DoNotFoundTheCell()
{
var map = new Map("test_name", "test_background.png");
var cell = new Cell(0, 0);
var othercell = new Cell(1, 1);
var paths = new List<Cell> { cell };
map.RopePaths.Add(paths);
Assert.False(map.IsCellInRopePath(othercell));
}
[Fact]
public void Clone_ReturnsNewMap()
{
var map = new Map("test_name", "test_background.png");
var clone = map.Clone();
Assert.NotEqual(map, clone);
Assert.Equal(map.Name, clone.Name);
Assert.Equal(map.Background, clone.Background);
}
}

@ -11,14 +11,7 @@ public class PlayerTests
var player = new Player(pseudo, profilePicture);
Assert.Equal(pseudo, player.Pseudo);
Assert.Equal(profilePicture, player.ProfilePicture);
}
[Fact]
public void Constructor_WithPseudoAndWithoutProfilePicture_SetsPseudoAndDefaultProfilePicture()
{
var player = new Player("MyPlayer");
Assert.Equal("MyPlayer", player.Pseudo);
Assert.Equal("DefaultProfilePicture", player.ProfilePicture);
Assert.Null(player.LastPlayed);
}
[Theory]
@ -26,8 +19,8 @@ public class PlayerTests
[InlineData("John Doe", "Jane Doe", false)]
public void Equals_WithSameOrDifferentPseudo_ReturnsExpectedResult(string pseudo1, string pseudo2, bool expectedResult)
{
var player1 = new Player(pseudo1);
var player2 = new Player(pseudo2);
var player1 = new Player(pseudo1, "DefaultProfilePicture");
var player2 = new Player(pseudo2, "DefaultProfilePicture");
Assert.Equal(expectedResult, player1.Equals(player2));
}
@ -36,8 +29,33 @@ public class PlayerTests
[InlineData("John Doe", "Jane Doe", false)]
public void GetHashCode_ReturnsSameOrDifferentHashCodeForPseudo(string pseudo1, string pseudo2, bool expectedResult)
{
var player1 = new Player(pseudo1);
var player2 = new Player(pseudo2);
var player1 = new Player(pseudo1, "DefaultProfilePicture");
var player2 = new Player(pseudo2, "DefaultProfilePicture");
Assert.Equal(expectedResult, player1.GetHashCode() == player2.GetHashCode());
}
[Fact]
public void Equals_WithNull_ReturnsFalse()
{
Player player = new Player(null, null);
Assert.False(player.Equals(null));
}
[Fact]
public void Equals_WithDifferentType_ReturnsFalse()
{
Player player = new Player("test_pseudo", "DefaultProfilePicture");
Assert.False(player.Equals(new Cell(0, 0)));
}
[Fact]
public void UpdateLastPlayed_UpdatesLastPlayedDate()
{
var player = new Player("John Doe", "DefaultProfilePicture");
player.UpdateLastPlayed();
Assert.NotNull(player.LastPlayed);
Assert.Equal(DateTime.Now.ToString("dd/MM/yyyy"), player.LastPlayed);
}
}

@ -0,0 +1,571 @@
namespace Tests;
using Models.Game;
using Models.Rules;
using System.Diagnostics;
public class RulesTests
{
[Fact]
public void IsCellEmpty_ReturnsTrue_WhenCellIsNull()
{
Rules rules = new Rules();
Assert.True(rules.IsCellEmpty(null));
}
[Fact]
public void IsCellEmpty_ReturnsTrue_WhenCellValueIsNull()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
Assert.True(rules.IsCellEmpty(cell));
}
[Fact]
public void IsCellEmpty_ReturnsFalse_WhenCellValueIsNotNull()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
cell.Value = 1;
Assert.False(rules.IsCellEmpty(cell));
}
[Fact]
public void IsCellValid_ReturnsFalse_WhenCellIsNotEmptyAndHasAdjacentCells()
{
Rules rules = new Rules();
Map map = new Map("test", "background");
Cell selectedCell = map.Boards[0];
selectedCell.Value = 5;
Assert.False(rules.IsCellValid(selectedCell, map.Boards.ToList()));
}
[Fact]
public void IsCellValid_ReturnsTrue_WhenCellIsEmptyAndHasAdjacentCells()
{
Rules rules = new Rules();
Map map = new Map("test", "background");
map.Boards[0].Valid = true;
Cell selectedCell = map.Boards[0];
Assert.True(rules.IsCellValid(selectedCell, map.Boards.ToList()));
}
[Fact]
public void IsCellAdjacent_ReturnsFalse_WhenCellsAreNotAdjacent()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(2, 2);
Assert.False(rules.IsCellAdjacent(cell1, cell2));
}
[Fact]
public void IsCellAdjacent_ReturnsTrue_WhenCellsAreSideBySide()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(0, 1);
Assert.True(rules.IsCellAdjacent(cell1, cell2));
}
[Fact]
public void IsCellAdjacent_ReturnsTrue_WhenCellsAreDiagonal()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(1, 1);
Assert.True(rules.IsCellAdjacent(cell1, cell2));
}
[Fact]
public void IsInRopePaths_ReturnsTrue_WhenCellsAreInRopePaths()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(0, 1);
Map map = new Map("test", "background");
map.RopePaths.Add(new List<Cell> { cell1, cell2 });
Assert.True(rules.IsInRopePaths(cell2, map.RopePaths, 0));
}
[Fact]
public void IsInRopePaths_ReturnsFalse_WhenCellsAreNotInRopePaths()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(0, 1);
Map map = new Map("test", "background");
map.RopePaths.Add(new List<Cell> { cell1 });
Assert.False(rules.IsInRopePaths(cell2, map.RopePaths, 0));
}
[Fact]
public void IsInRopePaths_ReturnsTrue_WhenCellsAreInRopePathsButNotInTheSamePath()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(0, 1);
Map map = new Map("test", "background");
map.RopePaths.Add(new List<Cell> { cell1 });
map.RopePaths.Add(new List<Cell> { cell2 });
Assert.True(rules.IsInRopePaths(cell2, map.RopePaths, 0));
}
[Fact]
public void AsValue_ReturnsTrue_WhenCellHasTheSameValue()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(0, 1);
cell1.Value = 5;
cell2.Value = 5;
Map map = new Map("test", "background");
map.RopePaths.Add(new List<Cell> { cell1, cell2 });
Assert.True(rules.AsValue(cell2, map.RopePaths, 0));
}
[Fact]
public void AsValue_ReturnsTrue_WhenCellValueIsAlreadyInRopePaths()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(0, 1);
cell1.Value = 5;
cell2.Value = 5;
Map map = new Map("test", "background");
map.RopePaths.Add(new List<Cell> { cell1 });
Assert.True(rules.AsValue(cell2, map.RopePaths, 0));
}
[Fact]
public void AsValue_ReturnsFalse_WhenCellHasDifferentValue()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(0, 1);
cell1.Value = 5;
cell2.Value = 6;
Map map = new Map("test", "background");
map.RopePaths.Add(new List<Cell> { cell1 });
Assert.False(rules.AsValue(cell2, map.RopePaths, 0));
}
[Fact]
public void NearCellIsValid_ReturnsFalse_WhenChosenCellIsNull()
{
Rules rules = new Rules();
Assert.False(rules.NearCellIsValid(null, new List<Cell>()));
}
[Fact]
public void NearCellIsValid_ReturnsFalse_WhenCellsIsNull()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
Assert.False(rules.NearCellIsValid(cell, null));
}
[Fact]
public void NearCellIsValid_ReturnsFalse_WhenNoAdjacentCells()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
List<Cell> cells = new List<Cell> { new Cell(2, 0) };
Assert.False(rules.NearCellIsValid(cell, cells));
}
[Fact]
public void NearCellIsValid_ReturnsTrue_WhenAdjacentCellExists()
{
Rules rules = new Rules();
Cell cell1 = new Cell(0, 0);
Cell cell2 = new Cell(0, 1);
cell2.Value = 12;
cell2.Valid = true;
List<Cell> cells = new List<Cell> { cell2 };
Assert.True(rules.NearCellIsValid(cell1, cells));
}
[Fact]
public void IsZoneValidAndAddToZones_DoesNothing_WhenCellIsNull()
{
Rules rules = new Rules();
Map map = new Map("test", "background");
rules.IsZoneValidAndAddToZones(null, map);
Assert.Empty(map.Zones);
}
[Fact]
public void IsZoneValidAndAddToZones_DoesNothing_WhenCellValueIsNull()
{
Rules rules = new Rules();
Map map = new Map("test", "background");
Cell cell = new Cell(0, 0);
rules.IsZoneValidAndAddToZones(cell, map);
Assert.Empty(map.Zones);
}
[Fact]
public void IsZoneValidAndAddToZones_AddsToExistingZone_WhenAdjacentCellWithSameValueExists()
{
Rules rules = new Rules();
Map map = new Map("Dunai", "background");
Cell cell = new Cell(0, 0);
cell.Value = 1;
Cell adjacentCell = new Cell(0, 1);
adjacentCell.Value = 1;
Cell finalCell = new Cell(0, 2);
map.Boards.ToList().Add(cell);
map.Boards.ToList().Add(adjacentCell);
rules.NewZoneIsCreated(cell, adjacentCell, map);
rules.IsZoneValidAndAddToZones(finalCell, map);
Assert.Contains(cell, map.Zones[0]);
}
[Fact]
public void IsZoneValidAndAddToZones_CreatesNewZone_WhenAdjacentCellWithSameValueExistsButNoExistingZone()
{
Rules rules = new Rules();
Map map = new Map("Dunai", "background");
Cell cell = new Cell(0, 0);
cell.Value = 1;
Cell adjacentCell = new Cell(0, 1);
adjacentCell.Value = 1;
map.Boards[0].Value = 1;
map.Boards[1].Value = 1;
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<List<Cell>>()));
}
[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<List<Cell>>()));
}
[Fact]
public void IsValueInZones_ReturnsFalse_WhenNoZoneContainsCellWithSameValue()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
cell.Value = 1;
List<List<Cell>> zones = new List<List<Cell>>
{
new List<Cell> { new Cell(0, 1) { Value = 2 } },
new List<Cell> { 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<List<Cell>> zones = new List<List<Cell>>
{
new List<Cell> { new Cell(0, 1) { Value = 1 } },
new List<Cell> { 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<List<Cell>>()));
}
[Fact]
public void IsCellInZone_ReturnsFalse_WhenZonesAreEmpty()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
Assert.False(rules.IsCellInZone(cell, new List<List<Cell>>()));
}
[Fact]
public void IsCellInZone_ReturnsFalse_WhenNoZoneContainsCell()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
List<List<Cell>> zones = new List<List<Cell>>
{
new List<Cell> { new Cell(0, 1) },
new List<Cell> { 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<List<Cell>> zones = new List<List<Cell>>
{
new List<Cell> { new Cell(0, 1) },
new List<Cell> { cell }
};
Assert.True(rules.IsCellInZone(cell, zones));
}
[Fact]
public void AddToZone_DoesNothing_WhenCellIsNull()
{
Rules rules = new Rules();
List<List<Cell>> zones = new List<List<Cell>>();
rules.AddToZone(null, zones);
Assert.Empty(zones);
}
[Fact]
public void AddToZone_DoesNothing_WhenCellValueIsNull()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
List<List<Cell>> zones = new List<List<Cell>>();
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<List<Cell>> zones = new List<List<Cell>>
{
new List<Cell> { 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<List<Cell>> zones = new List<List<Cell>>
{
new List<Cell> { 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("test", "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("test", "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("test", "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("test", "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("test", "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<Cell> cells = new List<Cell> { 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<Cell>()));
}
[Fact]
public void EveryAdjacentCells_ReturnsEmptyList_WhenNoAdjacentCells()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
List<Cell> cells = new List<Cell> { 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<Cell> cells = new List<Cell> { new Cell(0, 1), new Cell(1, 0), new Cell(2, 2) };
List<Cell> 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<List<Cell>>()));
}
[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<List<Cell>> zones = new List<List<Cell>>
{
new List<Cell> { cell, cell1, cell2 },
new List<Cell> { 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<List<Cell>> zones = new List<List<Cell>>
{
new List<Cell> { cell, cell1, cell2, cell3, cell4, cell5, cell6, cell7, cell8, cell9, cell10 }
};
Assert.Equal(23, rules.FinalCalculusOfZones(zones));
}
[Fact]
public void ScoreRopePaths_ReturnsZero_WhenPathsAreEmpty()
{
Rules rules = new Rules();
Assert.Equal(0, rules.ScoreRopePaths(new List<Cell>()));
}
[Fact]
public void ScoreRopePaths_ReturnsCorrectValue_WhenPathsAreNotEmpty()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
cell.Value = 5;
Cell cell1 = new Cell(0, 1);
cell1.Value = 6;
Cell cell2 = new Cell(0, 2);
cell2.Value = 4;
List<Cell> ropePath = new List<Cell> { cell, cell1, cell2 };
Assert.Equal(8, rules.ScoreRopePaths(ropePath));
}
[Fact]
public void ScoreRopePaths_ReturnsCorrectValue_WhenPathsAreNotEmptyAndSorted()
{
Rules rules = new Rules();
Cell cell = new Cell(0, 0);
cell.Value = 5;
Cell cell1 = new Cell(0, 1);
cell1.Value = 6;
Cell cell2 = new Cell(0, 2);
cell2.Value = 4;
List<Cell> ropePath = new List<Cell> { cell1, cell, cell2 };
Assert.Equal(8, rules.ScoreRopePaths(ropePath));
}
}

@ -10,8 +10,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0"/>
<PackageReference Include="xunit" Version="2.4.2"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>

@ -5,11 +5,15 @@ VisualStudioVersion = 17.8.34408.163
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Trek-12", "Trek-12\Trek-12.csproj", "{41EE7BF8-DDE6-4B00-9434-076589C0B419}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Models", "Models\Models.csproj", "{807AB723-7AD3-42DD-9DA6-7AA5B0A9AAB4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Models", "Models\Models.csproj", "{807AB723-7AD3-42DD-9DA6-7AA5B0A9AAB4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp", "ConsoleApp\ConsoleApp.csproj", "{795F2C88-3C43-4795-9764-E52F7330888D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp", "ConsoleApp\ConsoleApp.csproj", "{795F2C88-3C43-4795-9764-E52F7330888D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{383C4215-C680-4C2E-BC7E-B62F0B164370}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{383C4215-C680-4C2E-BC7E-B62F0B164370}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataContractPersistence", "DataContractPersistence\DataContractPersistence.csproj", "{FC6A23C3-A1E3-4BF4-85B0-404D8574E190}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stub", "Stub\Stub.csproj", "{49360F7D-C59D-4B4F-AF5A-73FF61D9EF9B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -35,6 +39,14 @@ Global
{383C4215-C680-4C2E-BC7E-B62F0B164370}.Debug|Any CPU.Build.0 = Debug|Any CPU
{383C4215-C680-4C2E-BC7E-B62F0B164370}.Release|Any CPU.ActiveCfg = Release|Any CPU
{383C4215-C680-4C2E-BC7E-B62F0B164370}.Release|Any CPU.Build.0 = Release|Any CPU
{FC6A23C3-A1E3-4BF4-85B0-404D8574E190}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FC6A23C3-A1E3-4BF4-85B0-404D8574E190}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FC6A23C3-A1E3-4BF4-85B0-404D8574E190}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC6A23C3-A1E3-4BF4-85B0-404D8574E190}.Release|Any CPU.Build.0 = Release|Any CPU
{49360F7D-C59D-4B4F-AF5A-73FF61D9EF9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{49360F7D-C59D-4B4F-AF5A-73FF61D9EF9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{49360F7D-C59D-4B4F-AF5A-73FF61D9EF9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{49360F7D-C59D-4B4F-AF5A-73FF61D9EF9B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

@ -1,12 +1,69 @@
namespace Trek_12
using System.Diagnostics;
using System.Runtime.Serialization.DataContracts;
namespace Trek_12
{
using Stub;
using Models.Game;
using Models.Interfaces;
using DataContractPersistence;
public partial class App : Application
{
public string FileName { get; set; } = "data.json";
public string FilePath { get; set; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Trek_12");
public Game Manager { get; private set; }
public App()
{
InitializeComponent();
Manager = new Game(new DataContractJson());
//Manager = new Game(new Stub());
if (!Directory.Exists(FilePath))
{
Directory.CreateDirectory(FilePath);
}
//File.Delete(Path.Combine(FilePath, FileName));
string fullPath = Path.Combine(FilePath, FileName);
if (File.Exists(fullPath))
{
Debug.WriteLine("Data loaded from " + fullPath);
Manager.LoadData();
}
/* Add the permanent maps if they are not already in the game */
if (Manager.Maps.Count == 0)
{
Manager.AddMap(new Map("Dunai","montagne2.png"));
Manager.AddMap(new Map("Kagkot", "montagne3.png"));
Manager.AddMap(new Map("Dhaulagiri", "montagne4.png"));
}
MainPage = new AppShell();
// If the MainPage is closed, we save the data
MainPage.Disappearing += (sender, e) =>
{
Debug.WriteLine("Saving data...");
Manager.SaveData();
};
}
/// <summary>
/// Save the data when the app is in background
/// </summary>
protected override void OnSleep()
{
Debug.WriteLine("Zzz Secure save...");
Manager.SaveData();
}
}
}

@ -5,38 +5,11 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Trek_12"
xmlns:views="clr-namespace:Trek_12.Views"
Shell.FlyoutBehavior="Flyout"
Shell.FlyoutBehavior="Disabled"
Title="Trek_12"
Shell.NavBarIsVisible="False">
<ShellContent
Title="Home"
ContentTemplate="{DataTemplate views:PageMenuPrincipal}"
Route="MenuPrincipal" />
<ShellContent
Title="LeaderBoard"
ContentTemplate="{DataTemplate views:PageLeaderBoard}"
Route="LeaderBoard"/>
<ShellContent
Title="Profils"
ContentTemplate="{DataTemplate views:PageProfils}"
Route="Profils"/>
<ShellContent
Title="Règles"
ContentTemplate="{DataTemplate views:PageRegles}"
Route="Regles"/>
<!--<ShellContent
Title="Map Select"
ContentTemplate="{DataTemplate views:PageMapSelect}"
Route="MapSelect"/>-->
<!--<ShellContent
Title="Plateau"
ContentTemplate="{DataTemplate views:PagePlateau}"
Route="Plateau"/>-->
Shell.NavBarIsVisible="False"
Shell.FlyoutItemIsVisible="False"
Shell.TabBarIsVisible="False">
<ShellContent ContentTemplate="{DataTemplate views:PageMenuPrincipal}" />
</Shell>

@ -1,10 +1,22 @@
namespace Trek_12
using Trek_12.Views;
namespace Trek_12
{
/// <summary>
/// Class for the route of the views and the navigation of the app.
/// </summary>
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute(nameof(PageMenuPrincipal), typeof(PageMenuPrincipal));
Routing.RegisterRoute(nameof(PageProfiles), typeof(PageProfiles));
Routing.RegisterRoute(nameof(PageSelectMap), typeof(PageSelectMap));
Routing.RegisterRoute(nameof(PageRegles), typeof(PageRegles));
Routing.RegisterRoute(nameof(PageLeaderBoard), typeof(PageLeaderBoard));
Routing.RegisterRoute(nameof(PageBoard), typeof(PageBoard));
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="456" height="456" fill="#512BD4" />
</svg>

Before

Width:  |  Height:  |  Size: 228 B

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="m 105.50037,281.60863 c -2.70293,0 -5.00091,-0.90042 -6.893127,-2.70209 -1.892214,-1.84778 -2.837901,-4.04181 -2.837901,-6.58209 0,-2.58722 0.945687,-4.80389 2.837901,-6.65167 1.892217,-1.84778 4.190197,-2.77167 6.893127,-2.77167 2.74819,0 5.06798,0.92389 6.96019,2.77167 1.93749,1.84778 2.90581,4.06445 2.90581,6.65167 0,2.54028 -0.96832,4.73431 -2.90581,6.58209 -1.89221,1.80167 -4.212,2.70209 -6.96019,2.70209 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 213.56111,280.08446 H 195.99044 L 149.69953,207.0544 c -1.17121,-1.84778 -2.14037,-3.76515 -2.90581,-5.75126 h -0.40578 c 0.36051,2.12528 0.54076,6.67515 0.54076,13.6496 v 65.13172 h -15.54349 v -99.36009 h 18.71925 l 44.7374,71.29798 c 1.89222,2.95695 3.1087,4.98917 3.64945,6.09751 h 0.26996 c -0.45021,-2.6325 -0.67573,-7.09015 -0.67573,-13.37293 v -64.02256 h 15.47557 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="m 289.25134,280.08446 h -54.40052 v -99.36009 h 52.23835 v 13.99669 h -36.15411 v 28.13085 h 33.31621 v 13.9271 h -33.31621 v 29.37835 h 38.31628 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 366.56466,194.72106 H 338.7222 v 85.3634 h -16.08423 v -85.3634 h -27.77455 v -13.99669 h 71.70124 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 972 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net8.0-windows10.0.19041.0</TargetFrameworks>
<!--<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net8.0-windows10.0.19041.0</TargetFrameworks>-->
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
<!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->
@ -38,16 +38,23 @@
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('windows'))">
<TargetFrameworks>$(TargetFrameworks);net8.0-windows10.0.19041.0</TargetFrameworks>
<WindowsPackageType>MSIX</WindowsPackageType>
<PackageCertificateThumbprint>404032fa5d4dc4c8bbf036505d2409963f355ebd</PackageCertificateThumbprint>
</PropertyGroup>
<ItemGroup>
<!-- App Icon -->
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
<MauiIcon Include="Resources\AppIcon\app_icon.png" />
<!-- Splash Screen -->
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />
<!-- Images -->
<MauiImage Include="Resources\Images\*" />
<MauiImage Update="Resources\Images\dotnet_bot.png" Resize="True" BaseSize="300,185" />
<!-- Custom Fonts -->
<MauiFont Include="Resources\Fonts\*" />
@ -56,6 +63,17 @@
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>
<ItemGroup>
<None Remove="Resources\Images\app_icon.png" />
<None Remove="Resources\Images\back_arrow.png" />
<None Remove="Resources\Images\checked.png" />
<None Remove="Resources\Images\maptest.png" />
<None Remove="Resources\Images\montagne2.png" />
<None Remove="Resources\Images\montagne3.png" />
<None Remove="Resources\Images\montagne4.png" />
<None Remove="Resources\Images\user.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Maui" Version="7.0.1" />
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
@ -63,26 +81,32 @@
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<Compile Update="Views\PageLeaderBoard.xaml.cs">
<DependentUpon>PageLeaderBoard.xaml</DependentUpon>
</Compile>
<Compile Update="Views\pageProfils.xaml.cs">
<DependentUpon>PageProfils.xaml</DependentUpon>
</Compile>
<Compile Update="Views\pageRegles.xaml.cs">
<DependentUpon>PageRegles.xaml</DependentUpon>
</Compile>
<ItemGroup>
<ProjectReference Include="..\DataContractPersistence\DataContractPersistence.csproj" />
<ProjectReference Include="..\Models\Models.csproj" />
<ProjectReference Include="..\Stub\Stub.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Views\PageLeaderBoard.xaml.cs">
<DependentUpon>PageLeaderBoard.xaml</DependentUpon>
</Compile>
<Compile Update="Views\pageProfiles.xaml.cs">
<DependentUpon>pageProfiles.xaml</DependentUpon>
</Compile>
<Compile Update="Views\pageRegles.xaml.cs">
<DependentUpon>PageRegles.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<MauiXaml Update="Views\Components\ContentLeaderBoard.xaml">
<Generator>MSBuild:Compile</Generator>
<MauiXaml Update="Views\Components\ContentLeaderBoard.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="Views\PageLeaderBoard.xaml">
<Generator>MSBuild:Compile</Generator>
<MauiXaml Update="Views\PageBoard.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="Views\PageConnexion.xaml">
<MauiXaml Update="Views\PageLeaderBoard.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
</ItemGroup>

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Trek_12.Views.Components.ContentLeaderBoard">
x:Class="Trek_12.Views.Components.ContentLeaderBoard"
x:Name="this">
<Grid ColumnDefinitions="auto,*,*,*,*"
RowDefinitions="*,*"
Margin="0,20">
@ -12,31 +14,31 @@
WidthRequest="60"
IsClippedToBounds="True"
HasShadow="True">
<Image Source="profile.jpg"
<Image Source="{Binding ProfilePicture, Source={x:Reference this}}"
Aspect="AspectFill"
Margin="-20"/>
</Frame>
<Label Text="Kiwi6026"
Grid.Column="1"
VerticalOptions="Center"
FontSize="Title"
TextColor="DarkSalmon"
Margin="10,0"/>
<Label Text="nbParties"
Grid.Column="2"
VerticalOptions="Center"
FontSize="Title"
TextColor="DarkSalmon"/>
<Label Text="map"
Grid.Column="3"
VerticalOptions="Center"
FontSize="Title"
TextColor="DarkSalmon"/>
<Label Text="bestScore"
Grid.Column="4"
VerticalOptions="Center"
FontSize="Title"
TextColor="DarkSalmon"/>
<Label Text="{Binding Pseudo, Source={x:Reference this}}"
Grid.Column="1"
VerticalOptions="Center"
FontSize="Title"
TextColor="DarkSalmon"
Margin="10,0"/>
<Label Text="{Binding NbGames, Source={x:Reference this}}"
Grid.Column="2"
VerticalOptions="Center"
FontSize="Title"
TextColor="DarkSalmon"/>
<Label Text="{Binding BestScore, Source={x:Reference this}}"
Grid.Column="3"
VerticalOptions="Center"
FontSize="Title"
TextColor="DarkSalmon"/>
<Label Text="{Binding Map, Source={x:Reference this}}"
Grid.Column="4"
VerticalOptions="Center"
FontSize="Title"
TextColor="DarkSalmon"/>
<BoxView Color="DarkSalmon"
Grid.Row="1"
HeightRequest="1"

File diff suppressed because one or more lines are too long

@ -1,11 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Trek_12.Views.Components.viewsProfils">
x:Class="Trek_12.Views.Components.viewsProfils"
x:Name="this">
<VerticalStackLayout>
<Grid ColumnDefinitions="*,*,*,*" >
<Grid ColumnDefinitions="*,*,*">
<Frame Margin="10"
Grid.Column="1"
Grid.Column="0"
BorderColor="Black"
CornerRadius="50"
HeightRequest="60"
@ -13,11 +15,12 @@
IsClippedToBounds="True"
HasShadow="True"
HorizontalOptions="CenterAndExpand">
<Image Source="profile.jpg"
<Image Source="{Binding ProfilePicture, Source={x:Reference this}}"
Aspect="AspectFill"
Margin="-20"/>
</Frame>
<Label Text="Profile n°*" Grid.Column="2" TextColor="Black" FontSize="Large" HorizontalTextAlignment="Center" Margin="100"/>
<Label Text="{Binding Pseudo, Source={x:Reference this}}" Grid.Column="1" TextColor="Black" FontSize="Large" HorizontalTextAlignment="Center"/>
<Label Text="{Binding CreationDate, Source={x:Reference this}}" Grid.Column="2" TextColor="Black" FontSize="Large" HorizontalTextAlignment="Center"/>
</Grid>
</VerticalStackLayout>
</ContentView>

File diff suppressed because one or more lines are too long

@ -0,0 +1,173 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Trek_12.Views.PageBoard"
Title="Board"
BackgroundColor="Bisque">
<Grid RowDefinitions="*,auto" ColumnDefinitions="auto,*,auto">
<Grid RowDefinitions="auto,auto"
Grid.Column="0"
HorizontalOptions="Center"
VerticalOptions="Center"
Margin="50,0,0,0">
<HorizontalStackLayout HorizontalOptions="Center"
Margin="0,20,0,0">
<Frame BorderColor="DarkGray"
HeightRequest="50"
WidthRequest="50"
Grid.Column="0"
HorizontalOptions="Center"
VerticalOptions="Center"
Padding="0"
BackgroundColor="Yellow"
IsVisible="Hidden"
x:Name="YellowDice">
<Label Text="{Binding Dice1.Value}"
FontSize="Large"
VerticalOptions="Center"
HorizontalOptions="Center"
TextColor="Black"
x:Name="Dice1"
FontAttributes="Bold"/>
</Frame>
<Frame BorderColor="DarkGray"
HeightRequest="50"
WidthRequest="50"
Grid.Column="0"
HorizontalOptions="Center"
VerticalOptions="Center"
Padding="0"
BackgroundColor="Red"
IsVisible="Hidden"
x:Name="RedDice">
<Label Text="{Binding Dice2.Value}"
FontSize="Large"
VerticalOptions="Center"
HorizontalOptions="Center"
TextColor="Black"
x:Name="Dice2"
FontAttributes="Bold"/>
</Frame>
</HorizontalStackLayout>
<Button Text="Roll"
HeightRequest="25"
WidthRequest="100"
Clicked="DiceButton_Clicked"
Grid.Row="1"
Margin="0,50,0,0"
x:Name="RollButton"/>
</Grid>
<CollectionView ItemsSource="{Binding UsedMap.Boards}"
Grid.Column="1"
SelectionMode="Single"
WidthRequest="350"
HeightRequest="350"
ItemsLayout="VerticalGrid,7"
x:Name="Board"
SelectionChanged="OnCellSelected">
<CollectionView.ItemTemplate>
<DataTemplate x:Name="Cellule">
<Grid VerticalOptions="Center" HorizontalOptions="Center" HeightRequest="50" WidthRequest="50">
<Frame
IsVisible="{Binding Valid}"
BorderColor="DarkGray"
CornerRadius="25"
HeightRequest="50"
WidthRequest="50"
BackgroundColor="White"
Opacity="0.7"
VerticalOptions="Center"
HorizontalOptions="Center"
Padding="0"
x:Name="CellValid">
<Label Text="{Binding Value}"
x:Name="CellValue"
FontSize="Large"
VerticalOptions="Center"
HorizontalOptions="Center"
TextColor="Black"
FontAttributes="Bold"/>
<Frame.Triggers>
<DataTrigger TargetType="Frame" Binding="{Binding IsDangerous}" Value="True">
<Setter Property="BorderColor" Value="Black"/>
</DataTrigger>
<DataTrigger TargetType="Frame" Binding="{Binding IsDangerous}" Value="False">
<Setter Property="BorderColor" Value="Transparent"/>
</DataTrigger>
</Frame.Triggers>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<!-- Operation Grid -->
<Grid Grid.Row="0" Grid.Column="2"
ColumnDefinitions="auto,*"
RowDefinitions="auto,auto">
<!--Images des operations -->
<!--Grille de la partie-->
<CollectionView Grid.Column="1"
ItemsSource="{Binding UsedMap.OperationGrid}"
WidthRequest="200"
HeightRequest="250"
BackgroundColor="Transparent"
SelectionChanged="OnOperationCellSelected"
Margin="50"
ItemsLayout="VerticalGrid,4">
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame HasShadow="True"
BorderColor="Black"
BackgroundColor="Transparent"
CornerRadius="0"
HorizontalOptions="Center"
VerticalOptions="Center"
HeightRequest="50"
WidthRequest="50"
Padding="0">
<Image Source="checked.png"
IsVisible="{Binding IsChecked}"
HorizontalOptions="Center"
VerticalOptions="Center"/>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<!--Les bouttons de selection d'operation-->
<HorizontalStackLayout Grid.Row="1"
Grid.ColumnSpan="2"
VerticalOptions="Center">
<Button HeightRequest="50"
WidthRequest="100"
x:Name="Lower"
Clicked="LowerClicked"
IsVisible="False"/>
<Button HeightRequest="50"
WidthRequest="100"
x:Name="Higher"
Clicked="HigherClicked"
IsVisible="False"/>
<Button HeightRequest="50"
WidthRequest="100"
x:Name="Substraction"
Clicked="SubstractionClicked"
IsVisible="False"/>
<Button HeightRequest="50"
WidthRequest="100"
x:Name="Addition"
Clicked="AdditionClicked"
IsVisible="False"/>
<Button HeightRequest="50"
WidthRequest="100"
x:Name="Multiplication"
Clicked="MultiplicationClicked"
IsVisible="False"/>
</HorizontalStackLayout>
</Grid>
</Grid>
</ContentPage>

@ -0,0 +1,216 @@
using System.ComponentModel;
using System.Diagnostics;
using Microsoft.VisualBasic;
namespace Trek_12.Views;
using Models.Events;
using Models.Game;
public partial class PageBoard : ContentPage
{
public Game GameManager => (App.Current as App).Manager;
public int Result { get; set; }
public Cell ChoosenCell { get; set; }
public PageBoard()
{
InitializeComponent();
BindingContext = GameManager;
GameManager.CurrentPlayer.UpdateLastPlayed();
GameManager.DiceRolled += TheGame_DiceRolled;
GameManager.DiceRolled += ResultAddition;
GameManager.DiceRolled += ResultLower;
GameManager.DiceRolled += ResultHigher;
GameManager.DiceRolled += ResultSubstraction;
GameManager.DiceRolled += ResultMultiplication;
GameManager.PlayerOption += GameManager_PlayerOption;
GameManager.CellChosen += HandleCellChosen;
GameManager.AddGame(GameManager);
GameManager.OnPropertyChanged(nameof(GameManager.Games));
GameManager.SaveData();
}
private void HandleCellChosen(object sender, CellChosenEventArgs e)
{
YellowDice.IsVisible = false;
RedDice.IsVisible = false;
RollButton.IsEnabled = true;
}
private void ResetOperationButtonsAndDice()
{
Lower.IsVisible = false;
Higher.IsVisible = false;
Substraction.IsVisible = false;
Addition.IsVisible = false;
Multiplication.IsVisible = false;
RollButton.IsEnabled = true;
YellowDice.IsVisible = false;
RedDice.IsVisible = false;
}
private void SetOperationButtonState(Button selectedButton)
{
// Deselect all buttons
Lower.BackgroundColor = Colors.DarkSalmon;
Higher.BackgroundColor = Colors.DarkSalmon;
Substraction.BackgroundColor = Colors.DarkSalmon;
Addition.BackgroundColor = Colors.DarkSalmon;
Multiplication.BackgroundColor = Colors.DarkSalmon;
// Select the clicked button
selectedButton.BackgroundColor = Colors.LightCoral;
}
private void GameManager_PlayerOption(object? sender, PlayerOptionEventArgs e)
{
/* IEnumerable<Cell> PlayedCellsQuery =
from cell in e.Board
where cell.Valid == true
where cell.Value != null
select cell;*/
// prévisualisation des zone disponible, Je ne sais pas comment ca marche... 😵
}
private void ResultMultiplication(object? sender, DiceRolledEventArgs e)
{
Multiplication.IsVisible = true;
Multiplication.Text = $"Mult {e.Dice1Value*e.Dice2Value}";
}
private void ResultSubstraction(object? sender, DiceRolledEventArgs e)
{
Substraction.IsVisible = true;
if (GameManager.Dice1.IsLower(GameManager.Dice2))
Substraction.Text = $"Sub {e.Dice2Value - e.Dice1Value}";
else Substraction.Text = $"Sub {e.Dice1Value - e.Dice2Value}";
}
private void ResultHigher(object? sender, DiceRolledEventArgs e)
{
Higher.IsVisible = true;
if (GameManager.Dice1.IsLower(GameManager.Dice2))
Higher.Text = $"Higher {e.Dice2Value}";
else Higher.Text = $"Higher {e.Dice1Value}";
}
private void ResultLower(object? sender, DiceRolledEventArgs e)
{
Lower.IsVisible = true;
if(GameManager.Dice1.IsLower(GameManager.Dice2))
Lower.Text = $"Lower {e.Dice1Value}";
else Lower.Text = $"Lower {e.Dice2Value}";
}
private void ResultAddition(object? sender, DiceRolledEventArgs e)
{
Addition.IsVisible = true;
Addition.Text = $"Add {e.Dice1Value+e.Dice2Value}";
}
private void TheGame_DiceRolled(object? sender, Models.Events.DiceRolledEventArgs e)
{
YellowDice.IsVisible = true;
RedDice.IsVisible = true;
Dice1.Text = $"{e.Dice1Value}";
Dice2.Text = $"{e.Dice2Value}";
RollButton.IsEnabled = false;
}
private void OnOperationCellSelected(object sender, SelectionChangedEventArgs e)
{
if (e.CurrentSelection.Count > 0) // Si un élément est sélectionné
{
var selectedCell = (OperationCell)e.CurrentSelection[0];
if (selectedCell != null && !selectedCell.IsChecked)
{
selectedCell.Check();
Debug.WriteLine("OperationCell at ({0}, {1}) is checked", selectedCell.X, selectedCell.Y); // Debug
}
((CollectionView)sender).SelectedItem = null; // Déselectionne l'élément pour la CollectionView
}
}
private void HigherClicked(object sender, EventArgs e)
{
GameManager.PlayerOperation = Operation.HIGHER;
SetOperationButtonState((Button)sender);
Result = GameManager.ResultOperation(Operation.HIGHER);
GameManager.HandlePlayerOperation(Operation.HIGHER);
}
private void LowerClicked(object sender, EventArgs e)
{
GameManager.PlayerOperation = Operation.LOWER;
SetOperationButtonState((Button)sender);
Result = GameManager.ResultOperation(Operation.LOWER);
GameManager.HandlePlayerOperation(Operation.LOWER);
}
private void AdditionClicked(object sender, EventArgs e)
{
GameManager.PlayerOperation = Operation.ADDITION;
SetOperationButtonState((Button)sender);
Result = GameManager.ResultOperation(Operation.ADDITION);
GameManager.HandlePlayerOperation(Operation.ADDITION);
}
private void SubstractionClicked(object sender, EventArgs e)
{
GameManager.PlayerOperation = Operation.SUBTRACTION;
SetOperationButtonState((Button)sender);
Result = GameManager.ResultOperation(Operation.SUBTRACTION);
GameManager.HandlePlayerOperation(Operation.SUBTRACTION);
}
private void MultiplicationClicked(object sender, EventArgs e)
{
GameManager.PlayerOperation = Operation.MULTIPLICATION;
SetOperationButtonState((Button)sender);
Result = GameManager.ResultOperation(Operation.MULTIPLICATION);
GameManager.HandlePlayerOperation(Operation.MULTIPLICATION);
}
private void DiceButton_Clicked(object sender, EventArgs e)
{
GameManager.RollAllDice();
}
private async void OnCellSelected(object sender, SelectionChangedEventArgs e)
{
if (!GameManager.DiceRolledFlag)
{
await DisplayAlert("Action Required", "You must roll the dice first.", "OK");
return;
}
if (!GameManager.OperationChosenFlag)
{
await DisplayAlert("Action Required", "You must choose an operation first.", "OK");
return;
}
if (e.CurrentSelection.Count > 0)
{
ChoosenCell = (Cell)e.CurrentSelection[0];
GameManager.PlayerCell = ChoosenCell;
GameManager.Resultat = Result;
OnPropertyChanged(nameof(GameManager.PlayerCell));
OnPropertyChanged(nameof(GameManager.Resultat));
GameManager.PlayerSelectionCell();
((CollectionView)sender).SelectedItem = null;
ResetOperationButtonsAndDice();
}
}
}

@ -1,43 +1,60 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Trek_12.Views.Components"
x:Class="Trek_12.Views.PageLeaderBoard"
Title="PageLeaderBoard">
<Grid BackgroundColor="BlanchedAlmond"
RowDefinitions="auto,6*,*">
<VerticalStackLayout>
<Label
Text="Leader board"
VerticalOptions="Center"
HorizontalOptions="Center"
FontSize="Title"/>
<BoxView
Color="DarkSalmon"
HeightRequest="1"
WidthRequest="125"/>
</VerticalStackLayout>
<ScrollView Grid.Row="1"
VerticalOptions="FillAndExpand"
VerticalScrollBarVisibility="Never"
Margin="0,10">
<VerticalStackLayout>
<views:ContentLeaderBoard/>
<views:ContentLeaderBoard/>
<views:ContentLeaderBoard/>
<views:ContentLeaderBoard/>
<views:ContentLeaderBoard/>
<views:ContentLeaderBoard/>
</VerticalStackLayout>
</ScrollView>
<Button Text="Back"
BackgroundColor="OliveDrab"
FontSize="Title"
Grid.Row="2"
HorizontalOptions="Start"
CornerRadius="20"
WidthRequest="150"
HeightRequest="75"
Margin="10"/>
</Grid>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Trek_12.Views.Components"
x:Class="Trek_12.Views.PageLeaderBoard"
Title="PageLeaderBoard">
<ContentPage.Content>
<Grid BackgroundColor="BlanchedAlmond"
RowDefinitions="auto,6*,*">
<Frame Grid.Row="0" BackgroundColor="Transparent" BorderColor="Transparent" Padding="0" Margin="15">
<Image Source="back_arrow.png"
Margin="0"
HeightRequest="50"
WidthRequest="50"
VerticalOptions="Center"
HorizontalOptions="Start"/>
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="OnBackArrow_Tapped"/>
</Frame.GestureRecognizers>
</Frame>
<VerticalStackLayout VerticalOptions="Center" HorizontalOptions="Center">
<Label
Text="Leaderboard"
VerticalOptions="Center"
HorizontalOptions="Center"
FontSize="Title"/>
<BoxView
Color="DarkSalmon"
HeightRequest="1"
WidthRequest="125"/>
</VerticalStackLayout>
<ScrollView Grid.Row="1"
VerticalOptions="FillAndExpand"
VerticalScrollBarVisibility="Never"
Margin="0,10">
<VerticalStackLayout HorizontalOptions="Center">
<CollectionView ItemsSource="{Binding BestScores}"
ItemsLayout="VerticalList"
VerticalOptions="Center">
<CollectionView.ItemTemplate>
<DataTemplate>
<views:ContentLeaderBoard
Pseudo="{Binding ThePlayer.Pseudo}"
ProfilePicture="{Binding ThePlayer.ProfilePicture}"
NbGames="{Binding GamesPlayed}"
BestScore="{Binding Score}"
Map="{Binding MapName}"
/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ScrollView>
</Grid>
</ContentPage.Content>
</ContentPage>

@ -1,9 +1,20 @@
namespace Trek_12.Views;
public partial class PageLeaderBoard : ContentPage
{
public PageLeaderBoard()
{
InitializeComponent();
}
using Models.Game;
using System.Diagnostics;
namespace Trek_12.Views;
public partial class PageLeaderBoard : ContentPage
{
public Game LeaderboardManager => (App.Current as App).Manager;
public PageLeaderBoard()
{
InitializeComponent();
BindingContext = LeaderboardManager;
}
private async void OnBackArrow_Tapped(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("..");
}
}

@ -17,11 +17,19 @@
HorizontalOptions="Center"
MinimumHeightRequest="50"
MaximumHeightRequest="150"/>
<Frame Grid.Row="0" BackgroundColor="Transparent" BorderColor="Transparent" Padding="0" Margin="20" HorizontalOptions="End" VerticalOptions="Start">
<Image Source="user.png" HeightRequest="50" WidthRequest="50" Aspect="AspectFit" VerticalOptions="Center" HorizontalOptions="Center" Margin="0" />
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="OnProfilesButton_Tapped"/>
</Frame.GestureRecognizers>
</Frame>
<Grid Grid.Row="1" ColumnDefinitions="*,2*,*" RowDefinitions="*,*" HorizontalOptions="Fill" VerticalOptions="Center" ColumnSpacing="50" RowSpacing="25" Margin="50,0">
<Button Grid.Column="0" Grid.Row="1" Text="Leaderboard" BackgroundColor="#936f49" Opacity="0.9" CornerRadius="6"/>
<Button Grid.Column="1" Grid.RowSpan="2" Text="JOUER" BackgroundColor="#936f49" Opacity="0.9" CornerRadius="6"/>
<Button Grid.Column="2" Grid.Row="1" Text="Règles" BackgroundColor="#936f49" Opacity="0.9" CornerRadius="6"/>
<Button Grid.Column="0" Grid.Row="1" Text="Leaderboard" BackgroundColor="#936f49" Opacity="0.9" CornerRadius="6" Clicked="OnLeaderBoardButton_Clicked"/>
<Button Grid.Column="1" Grid.RowSpan="2" Text="JOUER" BackgroundColor="#936f49" Opacity="0.9" CornerRadius="6" Clicked="OnPlayButton_Clicked"/>
<Button Grid.Column="2" Grid.Row="1" Text="Règles" BackgroundColor="#936f49" Opacity="0.9" CornerRadius="6" Clicked="OnRulesButton_Clicked"/>
</Grid>
</Grid>
</ContentPage.Content>

@ -1,8 +1,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Maui.Alerts;
using CommunityToolkit.Maui.Core;
using Font = Microsoft.Maui.Font;
namespace Trek_12.Views;
@ -12,4 +17,24 @@ public partial class PageMenuPrincipal : ContentPage
{
InitializeComponent();
}
private async void OnRulesButton_Clicked(object sender, EventArgs e)
{
await Shell.Current.GoToAsync(nameof(PageRegles));
}
private async void OnLeaderBoardButton_Clicked(object sender, EventArgs e)
{
await Shell.Current.GoToAsync(nameof(PageLeaderBoard));
}
private async void OnPlayButton_Clicked(object sender, EventArgs e)
{
await Shell.Current.GoToAsync(nameof(PageSelectMap));
}
private async void OnProfilesButton_Tapped(object sender, EventArgs e)
{
await Shell.Current.GoToAsync(nameof(PageProfiles));
}
}

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Trek_12.Views.PageSelectMap"
Title="PageSelectMap">
<Grid RowDefinitions="auto,auto,auto">
<Label Grid.Row="0" Text="Sélection de la Carte" HorizontalOptions="Center" FontSize="Header"/>
<CollectionView Grid.Row="1"
ItemsSource="{Binding Maps}"
ItemsLayout="HorizontalList"
HorizontalOptions="Center"
SelectionMode="Single"
SelectionChanged="OnSelectionChanged">
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame HasShadow="True"
BorderColor="DarkGray"
CornerRadius="5"
Margin="20"
HeightRequest="300"
WidthRequest="200"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
x:Name="Frame">
<Grid RowDefinitions="auto,*">
<Label Text="{Binding Name}"
FontAttributes="Bold"
FontSize="18"
HorizontalOptions="Center"
VerticalOptions="Center"/>
<Image Source="{Binding Background}"
Aspect="AspectFill"
HeightRequest="150"
WidthRequest="150"
HorizontalOptions="Center"
Grid.Row="1"/>
</Grid>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Button Text="Reprendre"
TextColor="White"
BackgroundColor="DarkRed"
FontAttributes="Bold"
FontSize="Large"
Grid.Row="2"
Margin="80"
HorizontalOptions="Center"
Clicked="ResumeButton_Clicked"
IsVisible="{Binding IsPreviousGameNotFinished}"/>
<Button Text="Retour"
FontAttributes="Bold"
FontSize="Large"
Grid.Row="2"
HorizontalOptions="Start"
Margin="100"
Clicked="BackButton_Clicked"/>
<Button Text="Jouer"
FontAttributes="Bold"
FontSize="Large"
Grid.Row="2"
HorizontalOptions="End"
Margin="100"
Clicked="PlayButton_Clicked"/>
</Grid>
</ContentPage>

@ -0,0 +1,114 @@
using System.Diagnostics;
namespace Trek_12.Views;
using Stub;
using Models.Game;
public partial class PageSelectMap : ContentPage
{
public Game SelectMapManager => (App.Current as App).Manager;
private Map? _selectedMap;
private bool isVisibleContinueButton = false;
protected override async void OnAppearing()
{
base.OnAppearing();
if (SelectMapManager.Games.Any(g => g.IsRunning))
{
isVisibleContinueButton = true;
await DisplayAlert("Warning", "You've previously quit in the middle of a game.\nIf you start a new game, this one will be permanently lost.", "I understand");
}
}
public PageSelectMap()
{
InitializeComponent();
BindingContext = SelectMapManager;
}
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
_selectedMap = e.CurrentSelection.FirstOrDefault() as Map;
}
private async void BackButton_Clicked(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("..");
}
private async void PlayButton_Clicked(object sender, EventArgs e)
{
if (_selectedMap == null)
{
await DisplayAlert("Selection Required", "Please select a map you want to play to continue.", "OK");
return;
}
if (SelectMapManager.Players.Count == 0)
{
await DisplayAlert("No player found", "Please add a player in the profile page.", "OK");
return;
}
string[] profiles = GetProfiles().ToArray();
string choosenPlayerName = await DisplayActionSheet("Choose a player", "Cancel", null, profiles);
if (choosenPlayerName == null || choosenPlayerName == "Cancel") return;
Player chosenPlayer = GetProfileByName(choosenPlayerName);
var runningGames = SelectMapManager.Games.Where(g => g.IsRunning).ToList();
bool delete = false;
foreach (var game in runningGames)
{
SelectMapManager.Games.Remove(game);
delete = true;
}
if (delete)
{
await DisplayAlert("Game deleted", "The previous game has been deleted because you started a new one.", "OK");
SelectMapManager.OnPropertyChanged(nameof(SelectMapManager.Games));
SelectMapManager.SaveData();
}
SelectMapManager.InitializeGame(_selectedMap.Clone(), chosenPlayer);
if (SelectMapManager.UsedMap != null && Equals(SelectMapManager.CurrentPlayer, chosenPlayer))
{
await Shell.Current.GoToAsync(nameof(PageBoard));
}
else
{
await DisplayAlert("Error", "An error occured while initializing the game. Please try again.", "OK");
}
}
private List<string> GetProfiles()
{
return SelectMapManager.Players.Select(p => p.Pseudo).ToList();
}
private Player GetProfileByName(string pseudo)
{
return SelectMapManager.Players.FirstOrDefault(p => p.Pseudo == pseudo);
}
private async void ResumeButton_Clicked(object sender, EventArgs e)
{
Game game = SelectMapManager.Games.FirstOrDefault(g => g.IsRunning);
if (game == null)
{
await DisplayAlert("No game found", "No game found to resume. Please start a new game.", "OK");
return;
}
SelectMapManager.InitializeGame(game.UsedMap, game.CurrentPlayer, false);
await Shell.Current.GoToAsync(nameof(PageBoard));
}
}

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Trek_12.Views.PageProfiles"
xmlns:views="clr-namespace:Trek_12.Views.Components"
Title="PageProfiles">
<ContentPage.Content>
<Grid RowDefinitions="auto,3*,*">
<Image Source="bg_profils.jpg" Grid.RowSpan="3" Aspect="AspectFill"/>
<Frame Grid.Row="0" BackgroundColor="Transparent" BorderColor="Transparent" Padding="0" Margin="15">
<Image Source="back_arrow.png"
Margin="0"
HeightRequest="50"
WidthRequest="50"
VerticalOptions="Center"
HorizontalOptions="Start"/>
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="OnBackArrow_Tapped"/>
</Frame.GestureRecognizers>
</Frame>
<Frame BackgroundColor="WhiteSmoke" Opacity="0.5" Grid.Row="1" />
<Label Grid.Row="0" Text="Profils" TextColor="black" HorizontalTextAlignment="Center" FontSize="Header" Margin="30"/>
<Grid Grid.Row="1" ColumnDefinitions="*,*,*">
<Label Text="Pseudo" TextColor="black" FontSize="Large" HorizontalTextAlignment="Center" Grid.Column="1"/>
<Label Text="Date de Création" TextColor="black" FontSize="Large" HorizontalTextAlignment="Center" Grid.Column="2"/>
</Grid>
<CollectionView Grid.Row="1" ItemsSource="{Binding Players}"
ItemsLayout="VerticalList"
VerticalOptions="Center">
<CollectionView.ItemTemplate>
<DataTemplate>
<views:viewsProfils
Pseudo="{Binding Pseudo}"
ProfilePicture="{Binding ProfilePicture}"
CreationDate="{Binding CreationDate}"/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<HorizontalStackLayout Grid.Row="2" HorizontalOptions="Center" Spacing="50">
<Button Text="Modifier" WidthRequest="300" HeightRequest="60" CornerRadius="4" Clicked="Button_ClickedModify"/>
<Button Text="Créer" WidthRequest="300" HeightRequest="60" CornerRadius="4" Clicked="Button_ClickedAdd"/>
<Button Text="Supprimer" WidthRequest="300" HeightRequest="60" CornerRadius="4" Clicked="Button_ClickedPop"/>
</HorizontalStackLayout>
</Grid>
</ContentPage.Content>
</ContentPage>

@ -0,0 +1,113 @@
using Models.Game;
using Stub;
using System.Diagnostics;
using CommunityToolkit.Maui.Alerts;
using CommunityToolkit.Maui.Core;
using Models.Interfaces;
namespace Trek_12.Views;
public partial class PageProfiles : ContentPage
{
public Game ProfileManager => (App.Current as App).Manager;
public PageProfiles()
{
InitializeComponent();
BindingContext = ProfileManager;
}
private async void OnBackArrow_Tapped(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("..");
}
async void Button_ClickedAdd(System.Object sender, System.EventArgs e)
{
string pseudo = await DisplayPromptAsync("Info", $"Choose a name : ", "Ok");
char[] trim = { ' ', '\n', '\t' };
pseudo = pseudo.TrimEnd(trim);
pseudo = pseudo.TrimStart(trim);
if (pseudo == null) return;
if (ProfileManager.Players.Any(p => p.Pseudo == pseudo))
{
await DisplayAlert("Info", "This name is already taken", "Ok");
return;
}
var profilePicture = await MediaPicker.PickPhotoAsync();
if (profilePicture == null) return;
IImageConverter converter = new Base64Converter();
string convertedProfilePicture = converter.ConvertImage(profilePicture.FullPath);
Player player = new Player(pseudo, convertedProfilePicture);
ProfileManager.AddPlayer(player);
Debug.WriteLine("Player " + pseudo + " added with profile picture " + convertedProfilePicture);
Debug.WriteLine("It's the number" + ProfileManager.Players.Count + " player");
ProfileManager.OnPropertyChanged(nameof(ProfileManager.Players));
ProfileManager.SaveData();
}
async void Button_ClickedPop(System.Object sender, System.EventArgs e)
{
if (ProfileManager.Players.Count == 0)
{
await DisplayAlert("Info", "There is no player actually\nPlease add one", "Ok");
return;
}
string result = await DisplayPromptAsync("Info", $"Choose a pseudo to delete : ", "Ok");
if (result == null) return;
Debug.WriteLine("Answer: " + result);
if (ProfileManager.RemovePlayer(result))
{
Debug.WriteLine("bam, deleted");
OnPropertyChanged(nameof(ProfileManager));
}
else
{
await DisplayAlert("Info", "This name do not exist", "Ok");
Debug.WriteLine("Player not found");
return;
}
}
async void Button_ClickedModify(System.Object sender, System.EventArgs e)
{
if (ProfileManager.Players.Count == 0)
{
await DisplayAlert("Info", "There is no player actually\nPlease add one", "Ok");
return;
}
string result = await DisplayPromptAsync("Info", $"Choose a name to modify : ", "Ok");
Debug.WriteLine("Answer: " + result);
if (result == null)
{
Debug.WriteLine("Did not found");
return;
}
string tomodify = await DisplayPromptAsync("Info", $"How will you rename it ?: ", "Ok");
Debug.WriteLine("Answer: " + tomodify);
if (tomodify == null)
{
Debug.WriteLine("Did not found");
return;
}
Debug.WriteLine("bam, modified");
bool ismodified = ProfileManager.ModifyPlayer(result, tomodify);
if (ismodified)
{
Debug.WriteLine("Modified");
ProfileManager.SaveData();
}
else Debug.WriteLine("Player not found");
}
}

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Trek_12.Views.PageProfils"
xmlns:views="clr-namespace:Trek_12.Views.Components"
Title="pageProfils">
<ContentPage.Content>
<Grid RowDefinitions="2*,3*,*">
<Image Source="bg_profils.jpg" Grid.RowSpan="3" Aspect="AspectFill"/>
<Label Text="Profils" TextColor="black" HorizontalTextAlignment="Center" FontSize="Header" Margin="30"/>
<ScrollView Grid.Row="1">
<Grid RowDefinitions="*,*,*,*,*,*">
<views:viewsProfils />
<views:viewsProfils Grid.Row="1" />
<views:viewsProfils Grid.Row="2"/>
<views:viewsProfils Grid.Row="3"/>
<views:viewsProfils Grid.Row="4"/>
<views:viewsProfils Grid.Row="5"/>
</Grid>
</ScrollView>
<HorizontalStackLayout Grid.Row="2" HorizontalOptions="Center" Spacing="50">
<Button Text="Modifier" WidthRequest="300" HeightRequest="60" CornerRadius="4"/>
<Button Text="Créer" WidthRequest="300" HeightRequest="60" CornerRadius="4"/>
<Button Text="Supprimer" WidthRequest="300" HeightRequest="60" CornerRadius="4"/>
</HorizontalStackLayout>
</Grid>
</ContentPage.Content>
</ContentPage>

@ -1,9 +0,0 @@
namespace Trek_12.Views;
public partial class PageProfils : ContentPage
{
public PageProfils()
{
InitializeComponent();
}
}

@ -12,7 +12,7 @@
<Image Source="bg_regles.jpg" Grid.RowSpan="3" Aspect="AspectFill"/>
<Label Text="Règles" TextColor="black" HorizontalTextAlignment="Center" FontSize="Header" Margin="30"/>
<Label Text="Règles" TextColor="white" HorizontalTextAlignment="Center" FontSize="Header" Margin="30"/>
<ScrollView Grid.Row="1">
<Grid RowDefinitions="*,*,*,*,*,*,*,*,*">
@ -25,11 +25,11 @@
<Label Text="But du jeu &#10;&#10;
Obtenir le score le plus élevé en remplissant astucieusement les cases circulaires afin de créer des chemins de corde et de cartographier des zones, au fur à mesure de votre ascension.
&#10;
Sélectionner votre carte pour débuter votre ascension.Pour une toute première partie, nous vous conseillons d'utiliser la carte DUNAI &#10;" Grid.Row="0"/>
Sélectionner votre carte pour débuter votre ascension.Pour une toute première partie, nous vous conseillons d'utiliser la carte DUNAI &#10;" TextColor="white" Grid.Row="0"/>
<Label Text=" &#10;Comment Jouer :&#10;
1. Lancer les deux dés.&#10;
Ici 1 et 4&#10;" Grid.Row="1" />
Ici 1 et 4&#10;" Grid.Row="1" TextColor="white"/>
<Label Text="2. Choisissez votre résultat&#10;
a. Pour choisir votre résultat, vous devez choisir une seule opération suivante :&#10;
@ -37,30 +37,30 @@
⬆Le résultat du plus grand dé (ici 4)&#10;
- La différence entre les deux dés (ici 3 - Une différence peut être nulle mais jamais négative)&#10;
+ La somme des deux dés (ici 5)&#10;
x Le produit des deux dés (ici 4)&#10;" Grid.Row="2" />
x Le produit des deux dés (ici 4)&#10;" Grid.Row="2" TextColor="white"/>
<Label Text=" &#10;b. Sélectionnez une case libre dans la ligne correspondant à votre choix&#10;
Lorsque tous les emplacements d'une même opération sont cochés,ce choix ne vous est plus accessible.&#10;" Grid.Row="3" />
Lorsque tous les emplacements d'une même opération sont cochés,ce choix ne vous est plus accessible.&#10;" Grid.Row="3" TextColor="white" />
<Label Text=" &#10;c. Sélectionnez la case circulaire de votre choix, pour y placer votre résultat.En respectant les règles suivantes&#10;
Placez votre 1er résultat dans la case de votre choix&#10;
Tous les résultats suivants doivent être placés adjacents a une case déjà remplie.&#10;
Les conséquences de votre choix sont appliquez immédiatement : Chemin de corde et/ou Zone&#10;" Grid.Row="4" />
Les conséquences de votre choix sont appliquez immédiatement : Chemin de corde et/ou Zone&#10;" Grid.Row="4" TextColor="white" />
<Label Text=" &#10;Les chemins de corde&#10;
Un chemin de corde est une suite de nombres qui se suivent exactement.&#10;
Au moment ou vous placez un nombre, s'il crée ou continue une suite avec un nombre voisin (croissant ou décroissant), un chemin de corde sera alors crée représenté par un trait reliant deux nombres &#10;
Si votre résultat peut être relié à plusieurs nombres voisins, vous devrez donc en choisir un seul. &#10;" Grid.Row="5" />
Si votre résultat peut être relié à plusieurs nombres voisins, vous devrez donc en choisir un seul. &#10;" Grid.Row="5" TextColor="white" />
<Label Text="&#10;Deux types de cases &#10;
Cases de Base : 12 MAX Dans ces cases, vous ne pouvez avoir un nombre supérieur a 12. Si vous choisissez, ou êtes oblige, d'y mettre un résultat supérieur a 12, un symbole y sera mit a la place&#10;
Cases Dangereuses : 6 MAX Dans ces cases, il est INTERDIT d'y avoir un nombre strictement supérieur a 6. Si vous choisissez, ou êtes oblige, d'inscrire un résultat supérieur a 6 sur une case dangereuse un symbole y sera mit a la place&#10;
Un symbole donne un Malus en fin de partie. &#10;" Grid.Row="6" />
Un symbole donne un Malus en fin de partie. &#10;" Grid.Row="6" TextColor="white" />
<Label Text=" &#10;Cartographier des zones&#10;
Une zone cartographiée est un groupe d'au moins 2 nombres contigus de même valeur.&#10;
Au moment ou vous placez un nombre, s'il est identique à un nombre voisin, il crée ou agrandit une zone.&#10;
Un motif sera alors représentez dans chaque case constituant la zone.&#10;" Grid.Row="7" />
Un motif sera alors représentez dans chaque case constituant la zone.&#10;" Grid.Row="7" TextColor="white" />
<Label Text=" &#10;Fin de partie&#10;
Une fois que toutes les cases de la surface de jeu sont remplies la partie est alors terminé.La valeur des chemins de corde, des zones, des bonus et malus sont additionné.&#10;
@ -68,14 +68,14 @@
Zones : Chaque Zone cartographiée rapporte autant de points que le nombre de l'une de ses cases,+1 point par autre case qui compose cette zone.&#10;
Bonus : Votre plus long Chemin de corde ET votre plus grande Zone rapportent un bonus dépendant de leur taille.&#10;
Malus : Un symbole est placé dans chaque case orpheline (contenant un nombre qui n'appartient ni à une zone ni à un Chemin de corde)&#10;
Chaque symbole donne un malus de 3 points." Grid.Row="8"/>
Chaque symbole donne un malus de 3 points." Grid.Row="8" TextColor="white" />
</Grid>
</ScrollView>
<HorizontalStackLayout Grid.Row="2" HorizontalOptions="Center" Spacing="50">
<Button Text="Retour" WidthRequest="300" HeightRequest="60" CornerRadius="4"/>
<Button Text="Acheter" WidthRequest="300" HeightRequest="60" CornerRadius="4"/>
<Button Text="Retour" WidthRequest="300" HeightRequest="60" CornerRadius="4" Clicked="BackButton_Clicked"/>
<Button Text="Acheter" WidthRequest="300" HeightRequest="60" CornerRadius="4" Clicked="BrowserOpen_Clicked"/>
</HorizontalStackLayout>
</Grid>

@ -1,3 +1,5 @@
using System.ComponentModel;
namespace Trek_12.Views;
public partial class PageRegles : ContentPage
@ -6,4 +8,23 @@ public partial class PageRegles : ContentPage
{
InitializeComponent();
}
}
private async void BackButton_Clicked(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("..");
}
private async void BrowserOpen_Clicked(System.Object sender, System.EventArgs e)
{
try
{
Uri uri = new Uri("https://www.philibertnet.com/fr/lumberjacks-studio/89702-trek-12-3760268310293.html");
await Browser.Default.OpenAsync(uri, BrowserLaunchMode.SystemPreferred);
}
catch (Exception)
{
// An unexpected error occurred. No browser may be installed on the device.
throw new NotImplementedException();
}
}
}

Loading…
Cancel
Save