Compare commits

..

No commits in common. 'master' and 'test_old_branch' have entirely different histories.

File diff suppressed because one or more lines are too long

@ -53,16 +53,16 @@ namespace QwirkleClassLibrary.Boards
{ {
for (int b = 0; b < Columns; b++) for (int b = 0; b < Columns; b++)
{ {
Cell localcell = new(b, a); Cell localcell = new(a, b);
cells.Add(localcell); cells.Add(localcell);
} }
} }
} }
/// <summary> /// <summary>
/// This method is used to check if a cell in the board whether already contains a tile or not. /// This method is used to check if a cell in the board whether it already contains a tile or not.
/// </summary> /// </summary>
/// <returns>Returns a boolean : true if the cell doesn't contain any tile, false if already contains a tile.</returns> /// <returns>Returns a boolean : true if the cell doesn't contain any tile, false if it already contains a tile.</returns>
public bool HasOccupiedCase() public bool HasOccupiedCase()
{ {
foreach (var cell in cells) foreach (var cell in cells)

@ -4,94 +4,89 @@ using System.Runtime.CompilerServices;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using QwirkleClassLibrary.Tiles; using QwirkleClassLibrary.Tiles;
namespace QwirkleClassLibrary.Boards namespace QwirkleClassLibrary.Boards;
{
/// <summary> [DataContract]
/// Our board is made with a list of this class. It can stock infos such as its position on the board and the tile it contains. public class Cell : INotifyPropertyChanged
/// </summary> {
[DataContract] public event PropertyChangedEventHandler? PropertyChanged;
public class Cell : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
[DataMember] [DataMember]
private readonly int x; private readonly int x;
[DataMember] [DataMember]
private readonly int y; private readonly int y;
[DataMember] [DataMember]
public Tile? Tile { get; private set; } public Tile? Tile { get; private set;}
/// <summary> /// <summary>
/// This is the constructor for a Cell. /// This is the constructor for a Cell.
/// </summary> /// </summary>
/// <param name="x">The x attribute of the cell.</param> /// <param name="x">The x attribute of the cell.</param>
/// <param name="y">The y attribute of the cell.</param> /// <param name="y">The y attribute of the cell.</param>
/// <exception cref="ArgumentException">Throw an exception if the x or y attribute is negative.</exception> /// <exception cref="ArgumentException">Throw an exception if the x or y attribute is negative.</exception>
public Cell(int x, int y) public Cell(int x, int y)
{
if (x < 0 || y < 0)
{ {
if (x < 0 || y < 0) throw new ArgumentException(x.ToString() + y.ToString());
{
throw new ArgumentException(x.ToString() + y.ToString());
}
this.x = x;
this.y = y;
} }
/// <summary> this.x = x;
/// A getter for the position of the cell on the x-axis. this.y = y;
/// </summary> }
/// <returns>The position of the cell on the x-axis.</returns>
public int GetX
{
get { return x; }
}
/// <summary> /// <summary>
/// A getter for the position of the cell on the y-axis. /// A getter for the position of the cell on the x-axis.
/// </summary> /// </summary>
/// <returns>The position of the cell on the y-axis.</returns> /// <returns>The position of the cell on the x-axis.</returns>
public int GetY public int GetX
{ {
get { return y; } get { return x; }
} }
/// <summary> /// <summary>
/// Checks if the Cell whether is empty or contains a tile. /// A getter for the position of the cell on the y-axis.
/// </summary> /// </summary>
/// <returns>True if the cell is empty, false if the cell contains a tile.</returns> /// <returns>The position of the cell on the y-axis.</returns>
public bool IsFree public int GetY
{ {
get { return Tile! == null!; } get { return y; }
} }
/// <summary>
/// Check if the Cell whether is empty or contains a tile.
/// </summary>
/// <returns>True if the cell is empty, false if the cell contains a tile.</returns>
public bool IsFree
{
get { return Tile == null; }
}
/// <summary> /// <summary>
/// A setter for the tile in the cell. /// A setter for the tile in the cell.
/// </summary> /// </summary>
/// <param name="addedTile">The tile the player want to add in the cell.</param> /// <param name="addedTile">The tile the player want to add in the cell.</param>
/// <returns>True if added succefully (if the cell didn't already contain a tile), false if there already was a tile in this cell.</returns> /// <returns>True if added succefully (if the cell didn't already contain a tile), false if there already was a tile in this cell.</returns>
public bool SetTile(Tile addedTile) public bool SetTile(Tile addedTile)
{
if (Tile == null)
{ {
if (Tile! == null!)
{
Tile = addedTile; Tile = addedTile;
OnPropertyChanged(nameof(Tile)); OnPropertyChanged(nameof(Tile));
return true; return true;
}
else
{
return false;
}
} }
else
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); return false;
} }
} }
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
} }

@ -7,37 +7,43 @@ using QwirkleClassLibrary.Events;
using QwirkleClassLibrary.Players; using QwirkleClassLibrary.Players;
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using static System.Formats.Asn1.AsnWriter;
namespace QwirkleClassLibrary.Games namespace QwirkleClassLibrary.Games
{ {
/// <summary>
/// This is our main class for the Qwirkle application, taking care of the good efficiency of the game.
/// </summary>
[DataContract] [DataContract]
public class Game : IPlayer, IRules, INotifyPropertyChanged public class Game : IPlayer, IRules, INotifyPropertyChanged
{ {
[DataMember] private TileBag? bag = null; [DataMember]
private TileBag? bag = null;
[DataMember] public bool GameRunning { get; set; } [DataMember]
public bool GameRunning { get; set; }
[DataMember] private Board board = new(17, 14); [DataMember]
private Board board = new(17, 14);
public bool PlayerSwapping { get; set; } public bool PlayerSwapping { get; set; }
public Board Board => board; public Board Board => board;
public ReadOnlyCollection<Player> PlayerList => players.AsReadOnly(); public ReadOnlyCollection<Player> PlayerList => players.AsReadOnly();
[DataMember] private readonly List<Player> players = []; [DataMember]
private readonly List<Player> players = [];
[DataMember]
private readonly Dictionary<string, int> scoreBoard = new Dictionary<string, int>();
[DataMember] private readonly Dictionary<string, int> scoreBoard = new Dictionary<string, int>();
public ReadOnlyDictionary<string, int> ScoreBoard => scoreBoard.AsReadOnly(); public ReadOnlyDictionary<string, int> ScoreBoard => scoreBoard.AsReadOnly();
[DataMember] private readonly ObservableCollection<KeyValuePair<string, int>> observableScoreBoard = []; private readonly ObservableCollection<KeyValuePair<string, int>> observableScoreBoard = [];
public ReadOnlyObservableCollection<KeyValuePair<string, int>> ObservableScoreBoard => public ReadOnlyObservableCollection<KeyValuePair<string, int>> ObservableScoreBoard =>
new(observableScoreBoard); new ReadOnlyObservableCollection<KeyValuePair<string, int>>(observableScoreBoard);
[DataMember] private readonly List<Cell> cellUsed = []; [DataMember]
private readonly List<Cell> cellUsed = [];
public ReadOnlyCollection<Cell> CellsUsed => cellUsed.AsReadOnly(); public ReadOnlyCollection<Cell> CellsUsed => cellUsed.AsReadOnly();
@ -98,11 +104,9 @@ namespace QwirkleClassLibrary.Games
if (playersTag.Count <= 1 || playersTag.Count > 4) if (playersTag.Count <= 1 || playersTag.Count > 4)
{ {
playersTag.Clear(); playersTag.Clear();
OnPlayerNotified(new AddPlayerNotifiedEventArgs( OnPlayerNotified(new AddPlayerNotifiedEventArgs("ERROR : It takes a minimum of 2 players and a maximum of 4 players to start a game."));
"ERROR : It takes a minimum of 2 players and a maximum of 4 players to start a game."));
return false; return false;
} }
for (int i = playersTag.Count - 1; i >= 0; i--) for (int i = playersTag.Count - 1; i >= 0; i--)
{ {
if (!CheckPlayerTag(playersTag, i)) if (!CheckPlayerTag(playersTag, i))
@ -122,18 +126,12 @@ namespace QwirkleClassLibrary.Games
OnPlayerNotified(new AddPlayerNotifiedEventArgs("Players were correctly added.")); OnPlayerNotified(new AddPlayerNotifiedEventArgs("Players were correctly added."));
return true; return true;
} }
/// <summary>
/// This function is used to check if the player name that the user has entered meets the criteria set by the application.
/// </summary>
/// <param name="playersTag">A list that contains all the names entered when the game was started.</param>
/// <param name="pos">The position of the name we want to check in this list.</param>
/// <returns>boolean true if everything is okay, false if there was a problem in the player name.</returns>
public bool CheckPlayerTag(List<string> playersTag, int pos) public bool CheckPlayerTag(List<string> playersTag, int pos)
{ {
if (string.IsNullOrWhiteSpace(playersTag[pos])) if (string.IsNullOrWhiteSpace(playersTag[pos]))
{ {
OnPlayerNotified(new AddPlayerNotifiedEventArgs("ERROR with " + (pos + 1) + OnPlayerNotified(new AddPlayerNotifiedEventArgs("ERROR with " + (pos + 1) + " entry : The name is null or white space."));
" entry : The name is null or white space."));
return false; return false;
} }
@ -150,6 +148,7 @@ namespace QwirkleClassLibrary.Games
new AddPlayerNotifiedEventArgs("ERROR with " + (pos + 1) + " entry : Name alreay taken")); new AddPlayerNotifiedEventArgs("ERROR with " + (pos + 1) + " entry : Name alreay taken"));
return false; return false;
} }
} }
return true; return true;
@ -171,19 +170,13 @@ namespace QwirkleClassLibrary.Games
/// Returns the Board of the game /// Returns the Board of the game
/// </summary> /// </summary>
/// <returns>Board</returns> /// <returns>Board</returns>
public Board GetBoard() public Board? GetBoard() { return board; }
{
return board;
}
/// <summary> /// <summary>
/// Returns the tile bag of the game /// Returns the tile bag of the game
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public TileBag? GetTileBag() public TileBag? GetTileBag() { return bag; }
{
return bag;
}
/// <summary> /// <summary>
/// Creates a Board with a number of columns and rows /// Creates a Board with a number of columns and rows
@ -245,7 +238,6 @@ namespace QwirkleClassLibrary.Games
{ {
throw new ArgumentException("No player currently playing !"); throw new ArgumentException("No player currently playing !");
} }
return players[GetPlayingPlayerPosition()]; return players[GetPlayingPlayerPosition()];
} }
@ -262,7 +254,6 @@ namespace QwirkleClassLibrary.Games
return i; return i;
} }
} }
return -1; return -1;
} }
@ -321,10 +312,11 @@ namespace QwirkleClassLibrary.Games
startingPlayer = player; startingPlayer = player;
} }
} }
startingPlayer!.IsPlaying = true; startingPlayer!.IsPlaying = true;
OnNextPlayer(new NextPlayerNotifiedEventArgs(players[0])); OnNextPlayer(new NextPlayerNotifiedEventArgs(players[0]));
return startingPlayer.NameTag; return startingPlayer.NameTag;
} }
/// <summary> /// <summary>
@ -362,15 +354,13 @@ namespace QwirkleClassLibrary.Games
OnPlaceTile(new PlaceTileNotifiedEventArgs(tile, "you are swapping, you can't place tile !")); OnPlaceTile(new PlaceTileNotifiedEventArgs(tile, "you are swapping, you can't place tile !"));
return false; return false;
} }
if (!TileInbag(player, tile)) if (!TileInbag(player, tile))
{ {
OnPlaceTile(new PlaceTileNotifiedEventArgs(tile, "you can't play")); OnPlaceTile(new PlaceTileNotifiedEventArgs(tile, "you can't play"));
return false; return false;
} }
if (!IsMoveCorrect(tile, x, y, board!)) return false;
if (!IsMoveCorrect(tile, x, y, board)) return false; if (board!.AddTileInCell(x, y, tile))
if (board.AddTileInCell(x, y, tile))
{ {
AddCellUsed(board.GetCell(x, y)); AddCellUsed(board.GetCell(x, y));
return player.RemoveTileToPlayer(tile); return player.RemoveTileToPlayer(tile);
@ -419,21 +409,18 @@ namespace QwirkleClassLibrary.Games
if (cellUsed.Count != 0) if (cellUsed.Count != 0)
{ {
OnSwapTiles(new SwapTilesNotifiedEventArgs("You can't swap tiles after placing some !")); OnSwapTiles(new SwapTilesNotifiedEventArgs("You can't swap tiles after placing some !"));
ReSwap(tilesToSwap);
return false; return false;
} }
if (tilesToSwap.Count == 0) if (tilesToSwap.Count == 0)
{ {
OnSwapTiles(new SwapTilesNotifiedEventArgs("You must select at least one tile to swap !")); OnSwapTiles(new SwapTilesNotifiedEventArgs("You must select at least one tile to swap !"));
ReSwap(tilesToSwap);
return false; return false;
} }
if (bag!.TilesBag!.Count < tilesToSwap.Count) if (bag!.TilesBag!.Count < tilesToSwap.Count)
{ {
OnSwapTiles(new SwapTilesNotifiedEventArgs("Not enough tiles in the bag to swap !")); OnSwapTiles(new SwapTilesNotifiedEventArgs("Not enough tiles in the bag to swap !"));
ReSwap(tilesToSwap);
return false; return false;
} }
@ -450,18 +437,9 @@ namespace QwirkleClassLibrary.Games
return true; return true;
} }
private void ReSwap(List<Tile> tilesToSwap)
{
foreach (var t in tilesToSwap)
{
players[GetPlayingPlayerPosition()].AddTileToPlayer(t);
}
}
/// <summary> /// <summary>
/// Extension of IsMoveCorrect to check beyond the surrounding cells of the cell where the tile is placed /// Extension of IsMoveCorrect to check beyond the surrounding cells of the cell where the tile is placed
/// </summary> /// </summary>
/// <param name="previousTilesFound"></param>
/// <param name="tile"></param> /// <param name="tile"></param>
/// <param name="x"></param> /// <param name="x"></param>
/// <param name="y"></param> /// <param name="y"></param>
@ -469,39 +447,26 @@ namespace QwirkleClassLibrary.Games
/// <param name="dy">used to get the direction on the y axis</param> /// <param name="dy">used to get the direction on the y axis</param>
/// <param name="b"></param> /// <param name="b"></param>
/// <returns>bool</returns> /// <returns>bool</returns>
public bool CheckExtendedSurroundingCells(ref bool previousTilesFound, Tile tile, int x, int y, int dx, int dy, public bool CheckExtendedSurroundingCells(Tile tile, int x, int y, int dx, int dy, Board b)
Board b)
{ {
for (int i = 1; i < 7; i++) for (int i = 1; i < 7; i++)
{ {
var extendedCell = b.GetCell(x + i * dx, y + i * dy); var extendedCell = b.GetCell(x + i * dx, y + i * dy);
if (cellUsed.Count == 0) if (extendedCell?.Tile == null)
{
previousTilesFound = true;
}
if (cellUsed.Contains(extendedCell!))
{
previousTilesFound = true;
}
if (extendedCell?.Tile! == null!)
{ {
break; break;
} }
if (extendedCell.Tile.GetColor != tile.GetColor && extendedCell.Tile.GetShape != tile.GetShape) if (extendedCell.Tile.GetColor != tile.GetColor && extendedCell.Tile.GetShape != tile.GetShape)
{ {
OnPlaceTile(new PlaceTileNotifiedEventArgs(tile, OnPlaceTile(new PlaceTileNotifiedEventArgs(tile, " : Color / Shape does not match with the surrounding tiles !"));
" : Color / Shape does not match with the surrounding tiles !"));
return false; return false;
} }
if (extendedCell.Tile.GetColor == tile.GetColor && extendedCell.Tile.GetShape == tile.GetShape) if (extendedCell.Tile.GetColor == tile.GetColor && extendedCell.Tile.GetShape == tile.GetShape)
{ {
OnPlaceTile(new PlaceTileNotifiedEventArgs(tile, OnPlaceTile(new PlaceTileNotifiedEventArgs(tile, " : Tile already placed on the same line / column !"));
" : Tile already placed on the same line / column !"));
return false; return false;
} }
@ -550,7 +515,6 @@ namespace QwirkleClassLibrary.Games
{ {
return x == x1; return x == x1;
} }
if (y1 == y2) if (y1 == y2)
{ {
return y == y1; return y == y1;
@ -560,62 +524,13 @@ namespace QwirkleClassLibrary.Games
} }
/// <summary> /// <summary>
/// Check that there isn't any same tile on a said line when a tile is forming a line /// Main method to check if the move the player is trying to make is correct
/// </summary>
/// <param name="t1"></param>
/// <param name="nbTiles"></param>
/// <param name="checkdoubles"></param>
/// <returns></returns>
public static bool CheckTileInCompletedLines(Tile? t1, ref int nbTiles, ref List<Tile> checkdoubles)
{
if (t1! != null!)
{
nbTiles++;
if (checkdoubles.Any(t => t.CompareTo(t1) == 0))
{
return false;
}
checkdoubles.Add(t1);
}
return true;
}
/// <summary>
/// Check if the line is completed with the tile placed
/// </summary> /// </summary>
/// <param name="tile"></param> /// <param name="t"></param>
/// <param name="x"></param> /// <param name="x"></param>
/// <param name="y"></param> /// <param name="y"></param>
/// <param name="dx"></param>
/// <param name="dy"></param>
/// <param name="b"></param> /// <param name="b"></param>
/// <param name="checkdoubles"></param> /// <returns>bool</returns>
/// <returns></returns>
public static bool CheckWrongCompletedLines(int x, int y, int dx, int dy, Board b, ref List<Tile> checkdoubles)
{
int nbTiles = 1;
for (int i = 1; i < 7; i++)
{
var extendedCell = b.GetCell(x + i * dx, y + i * dy);
var extendedCell2 = b.GetCell(x - i * dx, y - i * dy);
if (extendedCell?.Tile! == null! && extendedCell2?.Tile! == null!)
{
break;
}
if(!CheckTileInCompletedLines(extendedCell?.Tile, ref nbTiles, ref checkdoubles)) return false;
if(!CheckTileInCompletedLines(extendedCell2?.Tile, ref nbTiles, ref checkdoubles)) return false;
}
return nbTiles <= 6;
}
public bool IsMoveCorrect(Tile t, int x, int y, Board b) public bool IsMoveCorrect(Tile t, int x, int y, Board b)
{ {
if (!b.HasOccupiedCase()) if (!b.HasOccupiedCase())
@ -623,10 +538,9 @@ namespace QwirkleClassLibrary.Games
return true; return true;
} }
if (b.GetCell(x, y)!.Tile! != null!) if (b.GetCell(x, y)!.Tile != null)
{ {
OnPlaceTile(new PlaceTileNotifiedEventArgs(t, " : Cell already used !")); OnPlaceTile(new PlaceTileNotifiedEventArgs(t, " : Cell already used !"));
return false;
} }
var surroundingCells = new List<Cell?> var surroundingCells = new List<Cell?>
@ -636,68 +550,50 @@ namespace QwirkleClassLibrary.Games
b.GetCell(x, y + 1), b.GetCell(x, y + 1),
b.GetCell(x, y - 1) b.GetCell(x, y - 1)
}; };
if (surroundingCells.All(cell => cell?.Tile! == null!))
{
OnPlaceTile(new PlaceTileNotifiedEventArgs(t,
" : You can't place a tile that isn't adjacent to another one !"));
return false;
}
return IsTilePlacementCorrect(t, x, y, b, surroundingCells);
}
public bool IsTilePlacementCorrect(Tile t, int x, int y, Board b, List<Cell?> surroundingCells)
{
bool previousTilesFound = false;
var checkDoubles = new List<Tile>();
foreach (var cell in surroundingCells) foreach (var cell in surroundingCells)
{ {
if (cell?.Tile! == null!) if (cell?.Tile == null)
{ {
continue; continue;
} }
if (cell.Tile.GetColor != t.GetColor && cell.Tile.GetShape != t.GetShape) if (cell.Tile.GetColor != t.GetColor && cell.Tile.GetShape != t.GetShape)
{ {
OnPlaceTile(new PlaceTileNotifiedEventArgs(t, OnPlaceTile(new PlaceTileNotifiedEventArgs(t, " : Colors / Shapes do not match with the surrounding tiles !"));
" : Colors / Shapes do not match with the surrounding tiles !"));
return false; return false;
} }
if (cell.Tile.GetColor == t.GetColor && cell.Tile.GetShape == t.GetShape) if (cell.Tile.GetColor == t.GetColor && cell.Tile.GetShape == t.GetShape)
{ {
OnPlaceTile(new PlaceTileNotifiedEventArgs(t, " is already placed on the same line / column !")); OnPlaceTile(new PlaceTileNotifiedEventArgs(t, " is already placed on the same line / column !"));
return false; return false;
} }
var dx = cell.GetX - x; var dx = cell.GetX - x;
var dy = cell.GetY - y; var dy = cell.GetY - y;
if (!CheckExtendedSurroundingCells(ref previousTilesFound, t, x, y, dx, dy, b)) if (!CheckExtendedSurroundingCells(t, x, y, dx, dy, b))
{ {
return false; return false;
} }
}
if (CheckWrongCompletedLines(x, y, dx, dy, b, ref checkDoubles)) continue;
OnPlaceTile(new PlaceTileNotifiedEventArgs(t, if (!CheckTilesInLine(cellUsed, b, x, y))
" : You can't complete this line ! (More than 6 tiles / same tiles on the line)")); {
OnPlaceTile(new PlaceTileNotifiedEventArgs(t, "isn't on the same line as the ones previously placed !"));
return false; return false;
} }
if (!CheckTilesInLine(cellUsed, b, x, y)) if (surroundingCells.All(cell => cell?.Tile == null))
{ {
OnPlaceTile(new PlaceTileNotifiedEventArgs(t, OnPlaceTile(new PlaceTileNotifiedEventArgs(t, " : You can't place a tile that isn't adjacent to another one !"));
"isn't on the same line as the ones previously placed !"));
return false; return false;
} }
if (previousTilesFound) return true; return true;
OnPlaceTile(new PlaceTileNotifiedEventArgs(t,
" : You must place your tile next / on the same line as the ones previously placed !"));
return false;
} }
@ -717,9 +613,6 @@ namespace QwirkleClassLibrary.Games
int score = cellsPlayed.Count; int score = cellsPlayed.Count;
int nbCellsInLine = cellsPlayed.Count; int nbCellsInLine = cellsPlayed.Count;
int nbCellsInPerpLine = 1;
var checkedCells = new List<Cell>();
if (cellsPlayed.Count == 6) if (cellsPlayed.Count == 6)
{ {
@ -749,16 +642,16 @@ namespace QwirkleClassLibrary.Games
cellsX = cellsY = -1; cellsX = cellsY = -1;
} }
score += cellsPlayed.Sum(cell => score += cellsPlayed.Sum(cell => CalculateAdjacentScore(cell, b, cellsPlayed, cellsX, cellsY, ref nbCellsInLine));
CalculateAdjacentScore(cell, b, cellsPlayed, new Tuple<int, int>(cellsX, cellsY), ref nbCellsInLine, ref nbCellsInPerpLine, ref checkedCells));
if (nbCellsInLine == 6 || nbCellsInPerpLine == 6) if (nbCellsInLine == 6)
{ {
score += 6; score += 6;
} }
if (!scoreBoard.TryAdd(player.NameTag, score)) if (!scoreBoard.TryAdd(player.NameTag, score))
{ {
scoreBoard.TryGetValue(player.NameTag, out int scoreold); scoreBoard.TryGetValue(player.NameTag, out int scoreold);
SetScoreBoard(player.NameTag, score + scoreold); SetScoreBoard(player.NameTag, score + scoreold);
} }
@ -772,12 +665,11 @@ namespace QwirkleClassLibrary.Games
/// <param name="cell"></param> /// <param name="cell"></param>
/// <param name="b"></param> /// <param name="b"></param>
/// <param name="cellsPlayed"></param> /// <param name="cellsPlayed"></param>
/// <param name="orientation"></param> /// <param name="cellsX"></param>
/// <param name="nbCellsInPerpLine"></param> /// <param name="cellsY"></param>
/// <param name="checkedCells"></param>
/// <param name="nbCellsInLine"></param> /// <param name="nbCellsInLine"></param>
/// <returns>int</returns> /// <returns>int</returns>
public int CalculateAdjacentScore(Cell cell, Board b, ReadOnlyCollection<Cell> cellsPlayed, Tuple<int, int> orientation, ref int nbCellsInLine, ref int nbCellsInPerpLine, ref List<Cell> checkedCells) public int CalculateAdjacentScore(Cell cell, Board b, ReadOnlyCollection<Cell> cellsPlayed, int cellsX, int cellsY, ref int nbCellsInLine)
{ {
int score = 0; int score = 0;
@ -789,10 +681,11 @@ namespace QwirkleClassLibrary.Games
b.GetCell(cell.GetX, cell.GetY - 1) b.GetCell(cell.GetX, cell.GetY - 1)
}; };
var checkedSurroundingCells = new List<Cell>();
foreach (var adjacentCell in surroundingCells) foreach (var adjacentCell in surroundingCells)
{ {
if (adjacentCell?.Tile! == null! || cellsPlayed.Contains(adjacentCell) || if (adjacentCell?.Tile == null || cellsPlayed.Contains(adjacentCell) || checkedSurroundingCells.Contains(adjacentCell))
checkedCells.Contains(adjacentCell))
{ {
continue; continue;
} }
@ -800,10 +693,9 @@ namespace QwirkleClassLibrary.Games
int dx = adjacentCell.GetX - cell.GetX; int dx = adjacentCell.GetX - cell.GetX;
int dy = adjacentCell.GetY - cell.GetY; int dy = adjacentCell.GetY - cell.GetY;
score += CalculateLineScore(cellsPlayed, cell, new Tuple<int, int>(dx, dy), score += CalculateLineScore(cellsPlayed, cell, new Tuple<int, int>(dx, dy), b, new Tuple<int, int>(cellsX, cellsY), ref nbCellsInLine);
new Tuple<int, int>(orientation.Item1, orientation.Item2), ref nbCellsInLine, ref nbCellsInPerpLine, ref checkedCells);
checkedCells.Add(adjacentCell); checkedSurroundingCells.Add(adjacentCell);
} }
return score; return score;
@ -815,20 +707,19 @@ namespace QwirkleClassLibrary.Games
/// <param name="cellsPlayed"></param> /// <param name="cellsPlayed"></param>
/// <param name="cell"></param> /// <param name="cell"></param>
/// <param name="direction"></param> /// <param name="direction"></param>
/// <param name="b"></param>
/// <param name="orientation"></param> /// <param name="orientation"></param>
/// <param name="nbCellsInLine"></param> /// <param name="nbCellsInLine"></param>
/// <param name="nbCellsInPerpLine"></param>
/// <param name="checkedCells"></param>
/// <returns>int</returns> /// <returns>int</returns>
public int CalculateLineScore(ReadOnlyCollection<Cell> cellsPlayed, Cell cell, Tuple<int, int> direction, Tuple<int, int> orientation, ref int nbCellsInLine, ref int nbCellsInPerpLine, ref List<Cell> checkedCells) public int CalculateLineScore(ReadOnlyCollection<Cell> cellsPlayed, Cell cell, Tuple<int, int> direction, Board b, Tuple<int, int> orientation, ref int nbCellsInLine)
{ {
int score = 0; int score = 0;
for (int i = 1; i < 6; i++) for (int i = 1; i < 6; i++)
{ {
var extendedCell = board.GetCell(cell.GetX + i * direction.Item1, cell.GetY + i * direction.Item2); var extendedCell = b.GetCell(cell.GetX + i * direction.Item1, cell.GetY + i * direction.Item2);
if (extendedCell?.Tile! == null! || cellsPlayed.Contains(extendedCell) || checkedCells.Contains(extendedCell)) if (extendedCell?.Tile == null || cellsPlayed.Contains(extendedCell))
{ {
break; break;
} }
@ -837,17 +728,11 @@ namespace QwirkleClassLibrary.Games
{ {
nbCellsInLine++; nbCellsInLine++;
} }
if (direction.Item1 != 0 && orientation.Item1 != -1 || direction.Item2 != 0 && orientation.Item2 != -1)
{
nbCellsInPerpLine++;
}
checkedCells.Add(extendedCell);
score++; score++;
} }
if (ShouldIncreaseScore(direction, orientation)) if (direction.Item1 == 0 && orientation.Item1 == -1 && orientation.Item2 != -1 || direction.Item2 == 0 && orientation.Item2 == -1 && orientation.Item1 != -1)
{ {
score += 1; score += 1;
} }
@ -855,12 +740,7 @@ namespace QwirkleClassLibrary.Games
return score; return score;
} }
public static bool ShouldIncreaseScore(Tuple<int, int> direction, Tuple<int, int> orientation)
{
return direction.Item1 == 0 && orientation.Item1 == -1 && orientation.Item2 != -1 ||
direction.Item2 == 0 && orientation.Item2 == -1 && orientation.Item1 != -1;
}
/// <summary> /// <summary>
/// Returns the list of the positions of the players who still have tiles in their bag /// Returns the list of the positions of the players who still have tiles in their bag
/// </summary> /// </summary>
@ -869,7 +749,7 @@ namespace QwirkleClassLibrary.Games
{ {
List<int> playerTilesBagPos = []; List<int> playerTilesBagPos = [];
if (bag!.TilesBag!.Count <= 12) if (bag!.TilesBag!.Count == 0)
{ {
for (int i = 0; i < players.Count; i++) for (int i = 0; i < players.Count; i++)
{ {
@ -878,6 +758,7 @@ namespace QwirkleClassLibrary.Games
playerTilesBagPos.Add(i); playerTilesBagPos.Add(i);
} }
} }
} }
return playerTilesBagPos; return playerTilesBagPos;
@ -894,7 +775,7 @@ namespace QwirkleClassLibrary.Games
{ {
foreach (var t in players[t1].Tiles) foreach (var t in players[t1].Tiles)
{ {
for (int b = 0; b < board.ReadCells.Count; b++) for (int b = 0; b < board!.ReadCells.Count; b++)
{ {
int x = board.ReadCells[b].GetX; int x = board.ReadCells[b].GetX;
int y = board.ReadCells[b].GetY; int y = board.ReadCells[b].GetY;
@ -920,8 +801,7 @@ namespace QwirkleClassLibrary.Games
{ {
List<int> playerTilesBagPos = CheckTilesBag(); List<int> playerTilesBagPos = CheckTilesBag();
if (playerTilesBagPos.Count != 0 && !CheckPlacementPossibilities(playerTilesBagPos) || if (playerTilesBagPos.Count != 0 && !CheckPlacementPossibilities(playerTilesBagPos) || bag!.TilesBag!.Count == 0 && players[GetPlayingPlayerPosition()].Tiles.Count == 0)
bag!.TilesBag!.Count == 0 && players[GetPlayingPlayerPosition()].Tiles.Count == 0)
{ {
OnEndOfGame(new EndOfGameNotifiedEventArgs(player)); OnEndOfGame(new EndOfGameNotifiedEventArgs(player));
GameRunning = false; GameRunning = false;
@ -938,7 +818,6 @@ namespace QwirkleClassLibrary.Games
players.Clear(); players.Clear();
scoreBoard.Clear(); scoreBoard.Clear();
cellUsed.Clear(); cellUsed.Clear();
observableScoreBoard.Clear();
bag = null; bag = null;
board = CreateBoard(); board = CreateBoard();
GameRunning = false; GameRunning = false;
@ -946,6 +825,7 @@ namespace QwirkleClassLibrary.Games
public void SetScoreBoard(string name, int score) public void SetScoreBoard(string name, int score)
{ {
if (!scoreBoard.TryAdd(name, score)) if (!scoreBoard.TryAdd(name, score))
{ {
scoreBoard[name] = score; scoreBoard[name] = score;
@ -956,7 +836,6 @@ namespace QwirkleClassLibrary.Games
{ {
observableScoreBoard.Add(item); observableScoreBoard.Add(item);
} }
OnPropertyChanged(nameof(ObservableScoreBoard)); OnPropertyChanged(nameof(ObservableScoreBoard));
} }
} }

@ -3,32 +3,25 @@ using QwirkleClassLibrary.Boards;
using QwirkleClassLibrary.Players; using QwirkleClassLibrary.Players;
using QwirkleClassLibrary.Tiles; using QwirkleClassLibrary.Tiles;
namespace QwirkleClassLibrary.Games namespace QwirkleClassLibrary.Games;
{
/// <summary> public interface IPlayer
/// This interface is used for all methods related to the player, such as the moves he can make. {
/// </summary> public Player CreatePlayer(string playerTag);
public interface IPlayer
{
public Player CreatePlayer(string playerTag);
public string SetNextPlayer(); public string SetNextPlayer();
public string SetFirstPlayer(ReadOnlyCollection<Player> playingPlayers); public string SetFirstPlayer(ReadOnlyCollection<Player> playingPlayers);
public bool PlaceTile(Player player, Tile tile, int x, int y); public bool PlaceTile(Player player, Tile tile, int x, int y);
public bool DrawTiles(Player player); public bool DrawTiles(Player player);
public bool SwapTiles(Player player, List<Tile> tilesToSwap); public bool SwapTiles(Player player, List<Tile> tilesToSwap);
public int GetPlayerScore(Player player, ReadOnlyCollection<Cell> cellsPlayed, Board b); public int GetPlayerScore(Player player, ReadOnlyCollection<Cell> cellsPlayed, Board b);
int CalculateAdjacentScore(Cell cell, Board b, ReadOnlyCollection<Cell> cellsPlayed, Tuple<int, int> orientation, int CalculateAdjacentScore(Cell cell, Board b, ReadOnlyCollection<Cell> cellsPlayed, int cellsX, int cellsY, ref int nbCellsInLine);
ref int nbCellsInLine, ref int nbCellsInPerpLine, ref List<Cell> checkedCells);
int CalculateLineScore(ReadOnlyCollection<Cell> cellsPlayed, Cell cell, Tuple<int, int> direction, int CalculateLineScore(ReadOnlyCollection<Cell> cellsPlayed, Cell cell, Tuple<int, int> direction, Board b, Tuple<int, int> orientation, ref int nbCellsInLine);
Tuple<int, int> orientation, ref int nbCellsInLine, ref int nbCellsInPerpLine, ref List<Cell> checkedCells);
}
} }

@ -9,9 +9,6 @@ using QwirkleClassLibrary.Tiles;
namespace QwirkleClassLibrary.Games namespace QwirkleClassLibrary.Games
{ {
/// <summary>
/// This interface is used to define the functions used in the game. It is mainly about all the rules of the games, checking if the player moves are correct.
/// </summary>
public interface IRules public interface IRules
{ {
Board CreateBoard(); Board CreateBoard();
@ -20,7 +17,7 @@ namespace QwirkleClassLibrary.Games
bool IsMoveCorrect(Tile t, int x, int y, Board b); bool IsMoveCorrect(Tile t, int x, int y, Board b);
bool CheckExtendedSurroundingCells(ref bool previousTilesFound, Tile tile, int x, int y, int dx, int dy, Board b); bool CheckExtendedSurroundingCells(Tile tile, int x, int y, int dx, int dy, Board b);
bool CheckTilesInLine(List<Cell> cells, Board b, int x, int y); bool CheckTilesInLine(List<Cell> cells, Board b, int x, int y);

@ -1,47 +1,36 @@
using System.Runtime.Serialization; using System.Runtime.Serialization;
using QwirkleClassLibrary.Games; using QwirkleClassLibrary.Games;
namespace QwirkleClassLibrary.Persistences namespace QwirkleClassLibrary.Persistences;
public class GamePersistenceXml : IGamePersistence
{ {
/// <summary> public void SaveGame(Game game)
/// This class takes care of managing persistence with regard to the information of the current game, allowing the last game played to be resumed even when returning to the menu or exiting the application.
/// </summary>
public class GamePersistenceXml : IGamePersistence
{ {
/// <summary> var serializer = new DataContractSerializer(typeof(Game),
/// The main purpose of this method is to save the data from the game when the user quits the app, so players can continue it later. new DataContractSerializerSettings() { PreserveObjectReferences = true });
/// </summary>
/// <param name="game"></param> using (Stream writer = File.Create("Game.xml"))
public void SaveGame(Game game)
{ {
var serializer = new DataContractSerializer(typeof(Game), serializer.WriteObject(writer, game);
new DataContractSerializerSettings() { PreserveObjectReferences = true }); }
}
using (Stream writer = File.Create("Game.xml")) public Game LoadGame()
{
var serializer = new DataContractSerializer(typeof(Game));
try
{
using (Stream reader = File.OpenRead("Game.xml"))
{ {
serializer.WriteObject(writer, game); var newGame = serializer.ReadObject(reader) as Game;
return newGame!;
} }
} }
/// <summary> catch
/// This method is used to retrieve the information needed to resume the last game launched on the application.
/// </summary>
/// <returns>A Game.</returns>
public Game LoadGame()
{ {
var serializer = new DataContractSerializer(typeof(Game)); return new Game();
try
{
using (Stream reader = File.OpenRead("Game.xml"))
{
var newGame = serializer.ReadObject(reader) as Game;
return newGame!;
}
}
catch
{
return new Game();
}
} }
} }
} }

@ -1,39 +1,27 @@
using System.Runtime.Serialization.Json; using System.Runtime.Serialization.Json;
using QwirkleClassLibrary.Players; using QwirkleClassLibrary.Players;
namespace QwirkleClassLibrary.Persistences namespace QwirkleClassLibrary.Persistences;
public class LeaderboardPersistenceJson : ILeaderboardPersistence
{ {
/// <summary> public void SaveLeaderboard(Leaderboard leaderboard)
/// This is the persistence class for the leaderboard : it is in charge of managing all the parameters necessary for the backup and recovery of data concerning the leaderboard.
/// </summary>
public class LeaderboardPersistenceJson : ILeaderboardPersistence
{ {
/// <summary> var serializer = new DataContractJsonSerializer(typeof(Leaderboard));
/// As the name suggest, this class is used to save the data from the leaderboard.
/// </summary> using (Stream writer = File.Create("Leaderboard.json"))
/// <param name="leaderboard">The current leaderboard we want to save data from.</param>
public void SaveLeaderboard(Leaderboard leaderboard)
{ {
var serializer = new DataContractJsonSerializer(typeof(Leaderboard)); serializer.WriteObject(writer, leaderboard);
using (Stream writer = File.Create("Leaderboard.json"))
{
serializer.WriteObject(writer, leaderboard);
}
} }
/// <summary> }
/// This method is used to load the leaderboard into the app when the application starts.
/// </summary>
/// <returns>Leaderboard</returns>
/// <exception cref="InvalidOperationException"></exception>
public Leaderboard LoadLeaderboard()
{
var serializer = new DataContractJsonSerializer(typeof(Leaderboard));
using (Stream reader = File.OpenRead("Leaderboard.json")) public Leaderboard LoadLeaderboard()
{ {
return serializer.ReadObject(reader) as Leaderboard ?? throw new InvalidOperationException(); var serializer = new DataContractJsonSerializer(typeof(Leaderboard));
}
using (Stream reader = File.OpenRead("Leaderboard.json"))
{
return serializer.ReadObject(reader) as Leaderboard ?? throw new InvalidOperationException();
} }
} }
} }

@ -1,10 +1,7 @@
using QwirkleClassLibrary.Tiles; using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices; using System.Runtime.ExceptionServices;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Text; using System.Text;
@ -12,23 +9,14 @@ using System.Threading.Tasks;
namespace QwirkleClassLibrary.Players namespace QwirkleClassLibrary.Players
{ {
/// <summary>
/// The purpose of this class is to save data at the end of a game so players can consult it later, comparing their best scores along their games.
/// </summary>
[DataContract] [DataContract]
public class Leaderboard : INotifyPropertyChanged public class Leaderboard
{ {
public ReadOnlyCollection<Score> Lb => leaderboard.AsReadOnly();
[DataMember] [DataMember]
private ObservableCollection<Score> leaderboard = new(); private readonly List<Score> leaderboard = new();
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ReadOnlyObservableCollection<Score> Lb => new(leaderboard);
/// <summary> /// <summary>
/// Returns the index of the player in the leaderboard, -1 if the player is not in the leaderboard /// Returns the index of the player in the leaderboard, -1 if the player is not in the leaderboard
@ -69,13 +57,11 @@ namespace QwirkleClassLibrary.Players
if (first) if (first)
{ {
leaderboard[i].Victories++; leaderboard[i].Victories++;
OnPropertyChanged(nameof(leaderboard));
} }
if (pair.Value > leaderboard[i].Points) if (pair.Value > leaderboard[i].Points)
{ {
leaderboard[i].Points = pair.Value; leaderboard[i].Points = pair.Value;
OnPropertyChanged(nameof(leaderboard));
} }
} }
@ -88,13 +74,11 @@ namespace QwirkleClassLibrary.Players
} }
Score score = new Score(pair.Key, now, pair.Value, v); Score score = new Score(pair.Key, now, pair.Value, v);
leaderboard.Add(score); leaderboard.Add(score);
OnPropertyChanged(nameof(leaderboard));
} }
first = false; first = false;
} }
leaderboard = new ObservableCollection<Score>(leaderboard.OrderByDescending(x => x.Points).ThenBy(x => x.Victories));
} }
} }
} }

@ -12,9 +12,6 @@ using QwirkleClassLibrary.Tiles;
namespace QwirkleClassLibrary.Players namespace QwirkleClassLibrary.Players
{ {
/// <summary>
/// This class is mainly used to manage the sets of tiles of players during the game.
/// </summary>
[DataContract] [DataContract]
public class Player : INotifyPropertyChanged public class Player : INotifyPropertyChanged
{ {

@ -9,9 +9,6 @@ using System.Threading.Tasks;
namespace QwirkleClassLibrary.Players namespace QwirkleClassLibrary.Players
{ {
/// <summary>
/// The main purpose of this class is to save the data of the scores during the games, allowing the app to back up data.
/// </summary>
[DataContract] [DataContract]
public class Score public class Score
{ {

@ -8,11 +8,8 @@ using System.Threading.Tasks;
namespace QwirkleClassLibrary.Tiles namespace QwirkleClassLibrary.Tiles
{ {
/// <summary>
/// This is the class for the Tile, it defines what it is and what can be done with it.
/// </summary>
[DataContract] [DataContract]
public class Tile : IComparable public class Tile
{ {
[DataMember] [DataMember]
private readonly Shape shape; private readonly Shape shape;
@ -44,13 +41,19 @@ namespace QwirkleClassLibrary.Tiles
/// A getter for the shape of the Tile. /// A getter for the shape of the Tile.
/// </summary> /// </summary>
/// <returns>The shape attribute of the Tile.</returns> /// <returns>The shape attribute of the Tile.</returns>
public Shape GetShape => shape; public Shape GetShape
{
get { return shape; }
}
/// <summary> /// <summary>
/// A getter for the color of the Tile. /// A getter for the color of the Tile.
/// </summary> /// </summary>
/// <returns>The color attribute of the Tile.</returns> /// <returns>The color attribute of the Tile.</returns>
public Color GetColor => color; public Color GetColor
{
get { return color; }
}
/// <summary> /// <summary>
/// This method is used to override the ToString() method. It is simply a tool to facilitate the development. /// This method is used to override the ToString() method. It is simply a tool to facilitate the development.
@ -60,69 +63,5 @@ namespace QwirkleClassLibrary.Tiles
{ {
return color.ToString() + " " + shape.ToString(); return color.ToString() + " " + shape.ToString();
} }
public int CompareTo(object? obj)
{
if (obj == null)
{
return 1;
}
var otherTile = obj as Tile;
if (color == otherTile!.color)
{
return shape.CompareTo(otherTile.shape);
}
return color.CompareTo(otherTile.color);
}
public override bool Equals(object? obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
var otherTile = obj as Tile;
return color == otherTile!.color && shape == otherTile.shape;
}
public override int GetHashCode()
{
return HashCode.Combine(color, shape);
}
public static bool operator ==(Tile tile1, Tile tile2)
{
return EqualityComparer<Tile>.Default.Equals(tile1, tile2);
}
public static bool operator !=(Tile tile1, Tile tile2)
{
return !(tile1 == tile2);
}
public static bool operator <(Tile tile1, Tile tile2)
{
return tile1.CompareTo(tile2) < 0;
}
public static bool operator >(Tile tile1, Tile tile2)
{
return tile1.CompareTo(tile2) > 0;
}
public static bool operator <=(Tile tile1, Tile tile2)
{
return tile1.CompareTo(tile2) <= 0;
}
public static bool operator >=(Tile tile1, Tile tile2)
{
return tile1.CompareTo(tile2) >= 0;
}
} }
} }

@ -8,17 +8,14 @@ using System.Threading.Tasks;
namespace QwirkleClassLibrary.Tiles namespace QwirkleClassLibrary.Tiles
{ {
/// <summary>
/// This class is used during the game for the tile redistribution system.
/// </summary>
[DataContract] [DataContract]
public class TileBag public class TileBag
{ {
[DataMember] [DataMember]
private readonly List<Tile> tiles = []; private readonly List<Tile> tiles = [];
public ReadOnlyCollection<Tile>? TilesBag { get; private set; } public ReadOnlyCollection<Tile>? TilesBag { get; private set; }
/// <summary> /// <summary>
/// This is the constructor for the TileBag. It will create a tile of each of the possibilities among the Color and Shape Enums. /// This is the constructor for the TileBag. It will create a tile of each of the possibilities among the Color and Shape Enums.
@ -43,7 +40,7 @@ namespace QwirkleClassLibrary.Tiles
} }
} }
} }
Init(); Init();
} }
@ -73,7 +70,7 @@ namespace QwirkleClassLibrary.Tiles
} }
return false; return false;
} }
[OnDeserialized] [OnDeserialized]
private void Init(StreamingContext sc = new()) private void Init(StreamingContext sc = new())
{ {

@ -2,9 +2,6 @@
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Qwirkle.Pages; using Qwirkle.Pages;
using QwirkleClassLibrary.Games; using QwirkleClassLibrary.Games;
using QwirkleClassLibrary.Persistences;
using QwirkleClassLibrary.Players;
using Leaderboard = QwirkleClassLibrary.Players.Leaderboard;
namespace Qwirkle namespace Qwirkle
{ {
@ -12,36 +9,17 @@ namespace Qwirkle
{ {
public App() public App()
{ {
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string targetPath = Path.Combine(appDataPath, "Programs", "Files");
Directory.CreateDirectory(targetPath);
Directory.SetCurrentDirectory(targetPath);
InitializeComponent(); InitializeComponent();
//MainPage = new NavigationPage(new MainPage());
MainPage = new AppShell(); MainPage = new AppShell();
Routing.RegisterRoute(nameof(SetPlayers), typeof(SetPlayers)); Routing.RegisterRoute(nameof(SetPlayers), typeof(SetPlayers));
Routing.RegisterRoute(nameof(Gameboard), typeof(Gameboard)); Routing.RegisterRoute(nameof(Gameboard), typeof(Gameboard));
Routing.RegisterRoute(nameof(Rules), typeof(Rules)); Routing.RegisterRoute(nameof(Rules), typeof(Rules));
Routing.RegisterRoute(nameof(MainPage), typeof(MainPage)); Routing.RegisterRoute(nameof(MainPage), typeof(MainPage));
Routing.RegisterRoute(nameof(Qwirkle.Pages.Leaderboard), typeof(Qwirkle.Pages.Leaderboard));
ILeaderboardPersistence leaderboardLoad = new LeaderboardPersistenceJson();
try
{
Ld = leaderboardLoad.LoadLeaderboard();
}
catch
{
Ld = new Leaderboard();
}
} }
public Game Game { get; set; } = new(); public Game Game { get; set; } = new();
public Leaderboard Ld { get; set; }
} }
} }

@ -17,7 +17,7 @@ namespace Qwirkle.Converters
if (colorstring == "Red") return Colors.Red; if (colorstring == "Red") return Colors.Red;
if (colorstring == "Blue") return Colors.Blue; if (colorstring == "Blue") return Colors.Blue;
if (colorstring == "Green") return Colors.Green; if (colorstring == "Green") return Colors.Green;
if (colorstring == "Orange") return Colors.Orange; if (colorstring == "Orange") return Colors.OrangeRed;
if (colorstring == "Purple") return Colors.Purple; if (colorstring == "Purple") return Colors.Purple;
if (colorstring == "Transparent") return Colors.Transparent; if (colorstring == "Transparent") return Colors.Transparent;

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Qwirkle.MainPage" x:Class="Qwirkle.MainPage"
@ -7,41 +6,40 @@
<ScrollView> <ScrollView>
<VerticalStackLayout <VerticalStackLayout
Padding="30, 0, 30, 30" Padding="30,0"
Spacing="25"> Spacing="25">
<Label <Label
Text="QWIRKLE" Text="QWIRKLE"
Style="{StaticResource SuperTitle}" /> Style="{StaticResource SuperTitle}"
/>
<Image <Image
Source="qwirklelogo.png" Source="qwirklelogo.png"
HeightRequest="300" HeightRequest="300"
Aspect="AspectFit" /> Aspect="AspectFit"
/>
<controls:ButtonShadow <controls:ButtonShadow
Text="Play" Text="Play"
InfoClicked="OnInfoClicked" /> InfoClicked="OnInfoClicked"
/>
<controls:ButtonShadow Text="Continue"
InfoClicked="OnContinueClicked" />
<controls:ButtonShadow Text="Leaderboard" <controls:ButtonShadow Text="Leaderboard"/>
InfoClicked="OnCLeaderboardClicked" />
<controls:ButtonShadow Text="Rules" <controls:ButtonShadow Text="Rules"
InfoClicked="OnRulesClicked" /> InfoClicked="OnRulesClicked"/>
<controls:ButtonShadow Text="Settings" <controls:ButtonShadow Text="Settings"
InfoClicked="OnSettingsClicked" /> InfoClicked="OnSettingsClicked"/>
<controls:ButtonShadow Text="Credits" <controls:ButtonShadow Text="Credits"
InfoClicked="OnCreditsClicked" /> InfoClicked="OnCreditsClicked"/>
</VerticalStackLayout> </VerticalStackLayout>
</ScrollView> </ScrollView>
</ContentPage> </ContentPage>

@ -3,7 +3,6 @@ using QwirkleClassLibrary;
using QwirkleClassLibrary.Games; using QwirkleClassLibrary.Games;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Qwirkle.Pages; using Qwirkle.Pages;
using QwirkleClassLibrary.Persistences;
namespace Qwirkle namespace Qwirkle
{ {
@ -20,32 +19,10 @@ namespace Qwirkle
{ {
DisplayAlert("Game notification", "Enter minimun 2 player and max 4 player !", "Ok ! Lets's go !"); DisplayAlert("Game notification", "Enter minimun 2 player and max 4 player !", "Ok ! Lets's go !");
Shell.Current.GoToAsync("SetPlayers"); Navigation.PushAsync(new SetPlayers());
//Navigation.PushAsync(new SetPlayers());
} }
public async void OnContinueClicked(object sender, EventArgs e)
{
IGamePersistence gameLoad = new GamePersistenceXml();
try
{
((App)Application.Current!).Game = gameLoad.LoadGame();
await Shell.Current.GoToAsync("Gameboard");
//await Navigation.PushAsync(new Gameboard());
}
catch
{
await DisplayAlert("Error", "No game found", "Got it !");
await Navigation.PopAsync();
}
if (!((App)Application.Current!).Game.GameRunning)
{
await DisplayAlert("Error", "No game found", "Got it !");
await Navigation.PopAsync();
}
}
public void OnRulesClicked(object sender, EventArgs e) public void OnRulesClicked(object sender, EventArgs e)
{ {
Navigation.PushAsync(new Rules()); Navigation.PushAsync(new Rules());
@ -61,11 +38,6 @@ namespace Qwirkle
Navigation.PushAsync(new Credits()); Navigation.PushAsync(new Credits());
} }
public void OnCLeaderboardClicked(object sender, EventArgs e)
{
Navigation.PushAsync(new Leaderboard());
}
} }
} }

@ -1,7 +1,5 @@
using CommunityToolkit.Maui; using CommunityToolkit.Maui;
using CommunityToolkit.Maui.Views;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Maui;
namespace Qwirkle namespace Qwirkle
{ {
@ -13,7 +11,6 @@ namespace Qwirkle
builder builder
.UseMauiApp<App>() .UseMauiApp<App>()
.UseMauiCommunityToolkit() .UseMauiCommunityToolkit()
.UseMauiCommunityToolkitMediaElement()
.ConfigureFonts(fonts => .ConfigureFonts(fonts =>
{ {
fonts.AddFont("DiloWorld.ttf", "DiloWorld"); fonts.AddFont("DiloWorld.ttf", "DiloWorld");
@ -21,7 +18,7 @@ namespace Qwirkle
}); });
#if DEBUG #if DEBUG
builder.Logging.AddDebug(); builder.Logging.AddDebug();
#endif #endif

@ -9,9 +9,6 @@ using System.ComponentModel;
using Cell = QwirkleClassLibrary.Boards.Cell; using Cell = QwirkleClassLibrary.Boards.Cell;
using Color = Microsoft.Maui.Graphics.Color; using Color = Microsoft.Maui.Graphics.Color;
using System.Drawing; using System.Drawing;
using QwirkleClassLibrary.Persistences;
using CommunityToolkit.Maui.Views;
using System.Diagnostics;
namespace Qwirkle.Pages; namespace Qwirkle.Pages;
@ -31,8 +28,10 @@ public partial class Gameboard : ContentPage
public Color ColorBC3 { get; set; } = Colors.Transparent; public Color ColorBC3 { get; set; } = Colors.Transparent;
public Color ColorBC4 { get; set; } = Colors.Transparent; public Color ColorBC4 { get; set; } = Colors.Transparent;
public Gameboard() public Gameboard()
{ {
InitializeComponent(); InitializeComponent();
BindingContext = game; BindingContext = game;
@ -40,16 +39,12 @@ public partial class Gameboard : ContentPage
} }
private async void Game_EndOfGameNotified(object? sender, EndOfGameNotifiedEventArgs e)
{
await PopUpEnd();
await Navigation.PushAsync(new MainPage());
}
private Task PopUpEnd() private void Game_EndOfGameNotified(object? sender, EndOfGameNotifiedEventArgs e)
{ {
return this.ShowPopupAsync(new PopUpEndGame()); DisplayAlert("THE END.", "FAUT QU'ON PARLE DE CE QUE QU'ON VEUT FAIRE ICI !!!" ,"<3");
Navigation.PushAsync(new MainPage());
game.ClearGame();
} }
private void OnDragStarting(object sender, DragStartingEventArgs e) private void OnDragStarting(object sender, DragStartingEventArgs e)
@ -99,45 +94,23 @@ public partial class Gameboard : ContentPage
if (game.PlayerSwapping) if (game.PlayerSwapping)
{ {
game.SwapTilesNotified += Game_SwapTilesNotified;
game.SwapTiles(game.GetPlayingPlayer(), tilesSwap); game.SwapTiles(game.GetPlayingPlayer(), tilesSwap);
tilesSwap.Clear(); tilesSwap.Clear();
game.PlayerSwapping = false; game.PlayerSwapping = false;
game.SwapTilesNotified -= Game_SwapTilesNotified;
} }
else else
{ {
var x = game.GetPlayerScore(game.GetPlayingPlayer(), game.CellsUsed, game.GetBoard()!); game.GetPlayerScore(game.GetPlayingPlayer(), game.CellsUsed, game.GetBoard()!);
game.EmptyCellUsed(); game.EmptyCellUsed();
game.DrawTiles(game.GetPlayingPlayer()); game.DrawTiles(game.GetPlayingPlayer());
} }
game.CheckGameOver(game.GetPlayingPlayer());
IGamePersistence gameIntermediateSave = new GamePersistenceXml();
gameIntermediateSave.SaveGame(game);
if (!game.CheckGameOver(game.GetPlayingPlayer())) game.CheckGameOver(game.GetPlayingPlayer());
{ game.SetNextPlayer();
game.SetNextPlayer();
ChangeColorBC();
}
else
{
((App)Application.Current!).Ld.AddScoreInLead(game.ScoreBoard);
IGamePersistence gameEndSave = new GamePersistenceXml();
gameEndSave.SaveGame(game);
ILeaderboardPersistence leaderboardSave = new LeaderboardPersistenceJson();
leaderboardSave.SaveLeaderboard(((App)Application.Current!).Ld);
game.ClearGame();
}
game.NextPlayerNotified -= Game_NextPlayerNotified; game.NextPlayerNotified -= Game_NextPlayerNotified;
game.EndOfGameNotified -= Game_EndOfGameNotified; game.EndOfGameNotified -= Game_EndOfGameNotified;
ChangeColorBC();
} }
private void ChangeColorBC() private void ChangeColorBC()
@ -190,28 +163,30 @@ public partial class Gameboard : ContentPage
private void Game_NextPlayerNotified(object? sender, NextPlayerNotifiedEventArgs args) private void Game_NextPlayerNotified(object? sender, NextPlayerNotifiedEventArgs args)
{ {
Debug.WriteLine(args.Player.NameTag); DisplayAlert("Player switch !", "It's your turn : " + args.Player.NameTag, "<3");
} }
private void OnButtonSwapClicked(object sender, EventArgs e) private void OnButtonSwapClicked(object sender, EventArgs e)
{ {
game.PlaceTileNotified += Game_PlaceTileNotified; game.PlaceTileNotified += Game_PlaceTileNotified;
game.SwapTilesNotified += Game_SwapTilesNotified;
if (game.CellsUsed.Count == 0 && !game.PlayerSwapping) if (game.CellsUsed.Count == 0 && !game.PlayerSwapping)
{ {
_ = AnswerSwap(); //DisplayAlert("Swap system", "\r\nWelcome to the swap system! To use the system, take the tiles you wish to swap and place them in the bag (you will not see the tiles disappear). Then, click on the skip button. /!\\ Attention, during the swap phase, you cannot play /!\\", "Copy !");
} AnswerSwap();
else
{
DisplayAlert("Swap system", "You cannot place tiles and swap in the same turn.", "Ok !");
} }
game.SwapTilesNotified -= Game_SwapTilesNotified;
game.PlaceTileNotified -= Game_PlaceTileNotified; game.PlaceTileNotified -= Game_PlaceTileNotified;
} }
private async Task AnswerSwap() private async void AnswerSwap()
{ {
bool answer = await DisplayAlert("Swap System", "Are you sure you want to swap your tiles? Attention, if you swap you can not play", "Yes", "No"); bool answer = await DisplayAlert("Swap System", "Etes vous sur de vouloir swap vos tuiles ? Attention, si vous swapez vous ne pouvez pu jouer", "Yes", "No");
if (answer) if (answer)
{ {
@ -232,32 +207,4 @@ public partial class Gameboard : ContentPage
tilesSwap.Add(tiledrag!); tilesSwap.Add(tiledrag!);
} }
} }
}
private async void OnButtonExitClicked(object sender, EventArgs e)
{
bool answer = await DisplayAlert("System information", "Are you sure you want to quit? You can resume your game at any time from the home screen.", "Yes", "No");
if (answer)
{
IGamePersistence gameIntermediateSave = new GamePersistenceXml();
gameIntermediateSave.SaveGame(game);
game.ClearGame();
await Navigation.PopToRootAsync();
}
}
private void OnButtonBookClicked(object? sender, EventArgs e)
{
Navigation.PushAsync(new Rules());
}
private void OnButtonSettingsClicked(object? sender, EventArgs e)
{
Navigation.PushAsync(new Settings());
}
}

@ -4,21 +4,13 @@
xmlns:controls="clr-namespace:Qwirkle.Views" xmlns:controls="clr-namespace:Qwirkle.Views"
Title="Gameboard" Title="Gameboard"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit" xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:Name="root" x:Name="root">
>
<Shell.BackButtonBehavior>
<BackButtonBehavior IsVisible="False" IsEnabled="False"></BackButtonBehavior>
</Shell.BackButtonBehavior>
<ContentPage.Resources> <ContentPage.Resources>
<x:Double x:Key="CellWidth">75</x:Double> <x:Double x:Key="CellWidth">75</x:Double>
<x:Double x:Key="CellHeight">75</x:Double> <x:Double x:Key="CellHeight">75</x:Double>
<x:Double x:Key="VerticalSpacing">1</x:Double> <x:Double x:Key="VerticalSpacing">1</x:Double>
<x:Double x:Key="HorizontalSpacing">1</x:Double> <x:Double x:Key="HorizontalSpacing">1</x:Double>
<toolkit:MultiMathExpressionConverter x:Key="multiMathExpressionConverter" /> <toolkit:MultiMathExpressionConverter x:Key="multiMathExpressionConverter" />
</ContentPage.Resources> </ContentPage.Resources>
<Grid BackgroundColor="#efca85"> <Grid BackgroundColor="#efca85">
@ -34,27 +26,25 @@
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Border WidthRequest="80" HeightRequest="80" <Border WidthRequest="80" HeightRequest="80"
BackgroundColor="Transparent" BackgroundColor="WhiteSmoke"
Margin="0"> Margin="0" >
<Border.GestureRecognizers> <Border.GestureRecognizers >
<DropGestureRecognizer DragOver="OnDragOverBag" <DropGestureRecognizer DragOver="OnDragOverBag"
DropCommand="{Binding OnDropB, Source={x:Reference root}}" /> DropCommand="{Binding OnDropB, Source={x:Reference root}}"
/>
</Border.GestureRecognizers> </Border.GestureRecognizers>
<Image Source="bag.png" <Image Source="bag.png" ToolTipProperties.Text="Pour m'utiliser il faut cliquer sur le boutton swap avant ^^"></Image>
ToolTipProperties.Text="To use me, you need to click the swap button first ^^">
</Image>
</Border> </Border>
<ImageButton Grid.Row="0" Grid.Column="2" Source="bookicon.png" Clicked="OnButtonBookClicked" HeightRequest="70"></ImageButton> <Button
<Button
HorizontalOptions="Start" HorizontalOptions="Start"
Grid.Row="0" Grid.Column="1" Grid.Row="0" Grid.Column="1"
Text="Swap" Text="Swap"
Clicked="OnButtonSwapClicked" Clicked="OnButtonSwapClicked"
ToolTipProperties.Text="Click to swap your tiles !" ToolTipProperties.Text="Click to swap your tiles !"
Style="{StaticResource GameButton}" /> Style="{StaticResource GameButton}"/>
<Button <Button
HorizontalOptions="End" HorizontalOptions="End"
@ -62,40 +52,30 @@
Text="Skip / End Turn" WidthRequest="200" Text="Skip / End Turn" WidthRequest="200"
Clicked="OnButtonSkipClicked" Clicked="OnButtonSkipClicked"
ToolTipProperties.Text="Click for skip / end your turn ;)" ToolTipProperties.Text="Click for skip / end your turn ;)"
Style="{StaticResource GameButton}" /> Style="{StaticResource GameButton}"/>
<Button <Button
HorizontalOptions="Start" HorizontalOptions="Start"
Grid.Row="2" Grid.Column="1" Grid.Row="2" Grid.Column="1"
Text="Settings" Text="Settings"
Clicked="OnButtonSettingsClicked"
ToolTipProperties.Text="Click to check your settings -_-" ToolTipProperties.Text="Click to check your settings -_-"
Style="{StaticResource GameButton}" /> Style="{StaticResource GameButton}"/>
<Button
HorizontalOptions="End"
Grid.Row="0" Grid.Column="1"
Text="Exit" WidthRequest="200"
Clicked="OnButtonExitClicked"
ToolTipProperties.Text="Click here to exit ;)"
Style="{StaticResource GameButton}" />
<Label HorizontalOptions="Center" Grid.Row="0" Grid.Column="1" FontSize="Medium" Text="{Binding PlayerList[0].NameTag}"></Label>
<CollectionView Grid.Row="0" Grid.Column="1" ItemsSource="{Binding PlayerList[0].Tiles}" <CollectionView Grid.Row="0" Grid.Column="1" ItemsSource="{Binding PlayerList[0].Tiles}"
HorizontalOptions="Center" HorizontalOptions="Center"
VerticalOptions="Center"> VerticalOptions="Center" >
<CollectionView.ItemsLayout> <CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Horizontal" /> <GridItemsLayout Orientation="Horizontal"/>
</CollectionView.ItemsLayout> </CollectionView.ItemsLayout>
<CollectionView.ItemTemplate> <CollectionView.ItemTemplate>
<DataTemplate> <DataTemplate>
<Border WidthRequest="70" HeightRequest="70" <Border WidthRequest="70" HeightRequest="70"
BackgroundColor="{Binding ColorBC1, Source={x:Reference root}}" BackgroundColor="{Binding ColorBC1, Source={x:Reference root}}"
Margin="0"> Margin="0"
>
<Border.GestureRecognizers> <Border.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" DragStarting="OnDragStarting" /> <DragGestureRecognizer CanDrag="True" DragStarting="OnDragStarting"/>
</Border.GestureRecognizers> </Border.GestureRecognizers>
<controls:TileView Shape="{Binding GetShape}" Color="{Binding GetColor}"></controls:TileView> <controls:TileView Shape="{Binding GetShape}" Color="{Binding GetColor}"></controls:TileView>
</Border> </Border>
@ -103,21 +83,19 @@
</CollectionView.ItemTemplate> </CollectionView.ItemTemplate>
</CollectionView> </CollectionView>
<Label HorizontalOptions="Center" Grid.Row="1" Grid.Column="0" FontSize="Medium" Text="{Binding PlayerList[2].NameTag}"></Label>
<CollectionView Grid.Row="1" Grid.Column="0" ItemsSource="{Binding PlayerList[2].Tiles}" <CollectionView Grid.Row="1" Grid.Column="0" ItemsSource="{Binding PlayerList[2].Tiles}"
HorizontalOptions="Center" HorizontalOptions="Center"
VerticalOptions="Center"> VerticalOptions="Center" >
<CollectionView.ItemsLayout> <CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" /> <GridItemsLayout Orientation="Vertical"/>
</CollectionView.ItemsLayout> </CollectionView.ItemsLayout>
<CollectionView.ItemTemplate> <CollectionView.ItemTemplate>
<DataTemplate> <DataTemplate>
<Border WidthRequest="70" HeightRequest="70" <Border WidthRequest="70" HeightRequest="70"
BackgroundColor="{Binding ColorBC3, Source={x:Reference root}}" BackgroundColor="{Binding ColorBC3, Source={x:Reference root}}"
Margin="0"> Margin="0">
<Border.GestureRecognizers> <Border.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" DragStarting="OnDragStarting" /> <DragGestureRecognizer CanDrag="True" DragStarting="OnDragStarting"/>
</Border.GestureRecognizers> </Border.GestureRecognizers>
<controls:TileView Shape="{Binding GetShape}" Color="{Binding GetColor}"></controls:TileView> <controls:TileView Shape="{Binding GetShape}" Color="{Binding GetColor}"></controls:TileView>
</Border> </Border>
@ -125,20 +103,19 @@
</CollectionView.ItemTemplate> </CollectionView.ItemTemplate>
</CollectionView> </CollectionView>
<Label HorizontalOptions="Center" Grid.Row="1" Grid.Column="2" FontSize="Medium" Text="{Binding PlayerList[3].NameTag}"></Label>
<CollectionView Grid.Row="1" Grid.Column="2" ItemsSource="{Binding PlayerList[3].Tiles}" <CollectionView Grid.Row="1" Grid.Column="2" ItemsSource="{Binding PlayerList[3].Tiles}"
HorizontalOptions="Center" HorizontalOptions="Center"
VerticalOptions="Center"> VerticalOptions="Center" >
<CollectionView.ItemsLayout> <CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" /> <GridItemsLayout Orientation="Vertical"/>
</CollectionView.ItemsLayout> </CollectionView.ItemsLayout>
<CollectionView.ItemTemplate> <CollectionView.ItemTemplate>
<DataTemplate> <DataTemplate>
<Border WidthRequest="70" HeightRequest="70" <Border WidthRequest="70" HeightRequest="70"
BackgroundColor="{Binding ColorBC4, Source={x:Reference root}}" BackgroundColor="{Binding ColorBC4, Source={x:Reference root}}"
Margin="0"> Margin="0">
<Border.GestureRecognizers> <Border.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" DragStarting="OnDragStarting" /> <DragGestureRecognizer CanDrag="True" DragStarting="OnDragStarting"/>
</Border.GestureRecognizers> </Border.GestureRecognizers>
<controls:TileView Shape="{Binding GetShape}" Color="{Binding GetColor}"></controls:TileView> <controls:TileView Shape="{Binding GetShape}" Color="{Binding GetColor}"></controls:TileView>
</Border> </Border>
@ -146,20 +123,19 @@
</CollectionView.ItemTemplate> </CollectionView.ItemTemplate>
</CollectionView> </CollectionView>
<Label HorizontalOptions="Center" VerticalOptions="End" Grid.Row="2" Grid.Column="1" FontSize="Medium" Text="{Binding PlayerList[1].NameTag}"></Label>
<CollectionView Grid.Row="2" Grid.Column="1" ItemsSource="{Binding PlayerList[1].Tiles}" <CollectionView Grid.Row="2" Grid.Column="1" ItemsSource="{Binding PlayerList[1].Tiles}"
HorizontalOptions="Center" HorizontalOptions="Center"
VerticalOptions="Start"> VerticalOptions="Start" >
<CollectionView.ItemsLayout> <CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Horizontal" /> <GridItemsLayout Orientation="Horizontal"/>
</CollectionView.ItemsLayout> </CollectionView.ItemsLayout>
<CollectionView.ItemTemplate> <CollectionView.ItemTemplate>
<DataTemplate> <DataTemplate>
<Border WidthRequest="70" HeightRequest="70" <Border WidthRequest="70" HeightRequest="70"
BackgroundColor="{Binding ColorBC2, Source={x:Reference root}}" BackgroundColor="{Binding ColorBC2, Source={x:Reference root}}"
Margin="0"> Margin="0">
<Border.GestureRecognizers> <Border.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" DragStarting="OnDragStarting" /> <DragGestureRecognizer CanDrag="True" DragStarting="OnDragStarting"/>
</Border.GestureRecognizers> </Border.GestureRecognizers>
<controls:TileView Shape="{Binding GetShape}" Color="{Binding GetColor}"></controls:TileView> <controls:TileView Shape="{Binding GetShape}" Color="{Binding GetColor}"></controls:TileView>
</Border> </Border>
@ -170,59 +146,56 @@
<ScrollView Grid.Row="1" Grid.Column="1" VerticalOptions="FillAndExpand"> <ScrollView Grid.Row="1" Grid.Column="1" VerticalOptions="FillAndExpand">
<CollectionView ItemsSource="{Binding Board.ReadCells}" <CollectionView ItemsSource="{Binding Board.ReadCells}"
HorizontalOptions="Center" HorizontalOptions="Center"
VerticalOptions="Center" HorizontalScrollBarVisibility="Never" VerticalOptions="Center" HorizontalScrollBarVisibility="Never" VerticalScrollBarVisibility="Never"
VerticalScrollBarVisibility="Never"> >
<CollectionView.HeightRequest> <CollectionView.HeightRequest>
<MultiBinding Converter="{StaticResource multiMathExpressionConverter}" <MultiBinding Converter="{StaticResource multiMathExpressionConverter}" ConverterParameter="x0 * x1 +(x0 - 1) * x2 ">
ConverterParameter="x0 * x1 +(x0 - 1) * x2 ">
<Binding Path="Board.Rows" /> <Binding Path="Board.Rows" />
<Binding Source="{StaticResource CellHeight}" /> <Binding Source="{StaticResource CellHeight}" />
<Binding Source="{StaticResource VerticalSpacing}" /> <Binding Source="{StaticResource VerticalSpacing}" />
</MultiBinding> </MultiBinding>
</CollectionView.HeightRequest> </CollectionView.HeightRequest>
<CollectionView.WidthRequest> <CollectionView.WidthRequest>
<MultiBinding Converter="{StaticResource multiMathExpressionConverter}" <MultiBinding Converter="{StaticResource multiMathExpressionConverter}" ConverterParameter="x0 * x1 +(x0 - 1) * x2 ">
ConverterParameter="x0 * x1 +(x0 - 1) * x2 ">
<Binding Path="Board.Columns" /> <Binding Path="Board.Columns" />
<Binding Source="{StaticResource CellWidth}" /> <Binding Source="{StaticResource CellWidth}" />
<Binding Source="{StaticResource HorizontalSpacing}" /> <Binding Source="{StaticResource HorizontalSpacing}" />
</MultiBinding> </MultiBinding>
</CollectionView.WidthRequest> </CollectionView.WidthRequest>
<CollectionView.ItemsLayout> <CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" <GridItemsLayout Orientation="Vertical"
Span="{Binding Board.Columns}" Span="{Binding Board.Columns}"
HorizontalItemSpacing="{StaticResource HorizontalSpacing}" HorizontalItemSpacing="{StaticResource HorizontalSpacing}"
VerticalItemSpacing="{StaticResource VerticalSpacing}" /> VerticalItemSpacing="{StaticResource VerticalSpacing}"/>
</CollectionView.ItemsLayout> </CollectionView.ItemsLayout>
<CollectionView.ItemTemplate> <CollectionView.ItemTemplate>
<DataTemplate> <DataTemplate>
<Border WidthRequest="{StaticResource CellWidth}" <Border WidthRequest="{StaticResource CellWidth}"
HeightRequest="{StaticResource CellHeight}" HeightRequest="{StaticResource CellHeight}"
BackgroundColor="WhiteSmoke"> BackgroundColor="WhiteSmoke">
<Border.GestureRecognizers> <Border.GestureRecognizers >
<DropGestureRecognizer DragOver="OnDragOver" <DropGestureRecognizer DragOver="OnDragOver"
DropCommand="{Binding OnDrop, Source={x:Reference root}}" DropCommand="{Binding OnDrop, Source={x:Reference root}}"
DropCommandParameter="{Binding .}" /> DropCommandParameter="{Binding .}"/>
</Border.GestureRecognizers> </Border.GestureRecognizers>
<controls:TileView HorizontalOptions="Center" Shape="{Binding Tile.GetShape}" <controls:TileView HorizontalOptions="Center" Shape="{Binding Tile.GetShape}" Color="{Binding Tile.GetColor}"></controls:TileView>
Color="{Binding Tile.GetColor}">
</controls:TileView>
</Border> </Border>
</DataTemplate> </DataTemplate>
</CollectionView.ItemTemplate> </CollectionView.ItemTemplate>
</CollectionView> </CollectionView>
</ScrollView> </ScrollView>
<controls:Scoreboard InputTransparent="True" HorizontalOptions="End" Grid.Row="1" Grid.Column="1"></controls:Scoreboard> <controls:Scoreboard InputTransparent="True" HorizontalOptions="End" Grid.Row="1" Grid.Column="1" ></controls:Scoreboard>
</Grid> </Grid>
</ContentPage> </ContentPage>

@ -1,73 +1,77 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Qwirkle.Pages.Leaderboard" x:Class="Qwirkle.Pages.Leaderboard"
xmlns:controls="clr-namespace:Qwirkle.Views" xmlns:controls="clr-namespace:Qwirkle.Views"
Title="Leaderboard" Title="Leaderboard">
x:Name="root">
<ScrollView> <ScrollView>
<VerticalStackLayout Spacing="25" Padding="5, 5, 5, 10"> <VerticalStackLayout Spacing="25" Padding="5, 5, 5, 10">
<Grid Style="{StaticResource GridMain}"> <Grid Style="{StaticResource GridMain}">
<controls:GoBack></controls:GoBack> <controls:GoBack></controls:GoBack>
<Label Text="Leaderboard" <Label Text="Leaderboard"
Style="{StaticResource Title}" /> Style="{StaticResource Title}"/>
</Grid> </Grid>
<Border Style="{StaticResource TabBorder}"> <Border Style="{StaticResource TabBorder}">
<Border.Shadow> <Border.Shadow>
<Shadow /> <Shadow/>
</Border.Shadow> </Border.Shadow>
<Border.StrokeShape> <Border.StrokeShape>
<RoundRectangle CornerRadius="3" /> <RoundRectangle CornerRadius="3"/>
</Border.StrokeShape> </Border.StrokeShape>
<StackLayout> <VerticalStackLayout>
<Grid ColumnDefinitions="4*, auto, 2*, auto, 2*, auto, 2*" <controls:LeaderboardLine></controls:LeaderboardLine>
RowDefinitions="50"> <Rectangle/>
<Label
Text="Player tag" <controls:LeaderboardLine></controls:LeaderboardLine>
Style="{StaticResource ContentTab}" /> <Rectangle/>
<Rectangle
Style="{StaticResource RectangleTab}" <controls:LeaderboardLine></controls:LeaderboardLine>
Grid.Column="1" /> <Rectangle/>
<Label
Grid.Column="2" <controls:LeaderboardLine></controls:LeaderboardLine>
Text="Date" <Rectangle/>
Style="{StaticResource ContentTab}" />
<Rectangle <controls:LeaderboardLine></controls:LeaderboardLine>
Style="{StaticResource RectangleTab}" <Rectangle/>
Grid.Column="3" />
<Label <controls:LeaderboardLine></controls:LeaderboardLine>
Grid.Column="4" <Rectangle/>
Text="Points"
Style="{StaticResource ContentTab}" /> <controls:LeaderboardLine></controls:LeaderboardLine>
<Rectangle <Rectangle/>
Style="{StaticResource RectangleTab}"
Grid.Column="5" /> <controls:LeaderboardLine></controls:LeaderboardLine>
<Label <Rectangle/>
Grid.Column="6"
Style="{StaticResource ContentTab}" <controls:LeaderboardLine></controls:LeaderboardLine>
Text="Victories" /> <Rectangle/>
</Grid>
<controls:LeaderboardLine></controls:LeaderboardLine>
<CollectionView ItemsSource="{Binding Lb}"> <Rectangle/>
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" /> <controls:LeaderboardLine></controls:LeaderboardLine>
</CollectionView.ItemsLayout> <Rectangle/>
<CollectionView.ItemTemplate>
<DataTemplate> <controls:LeaderboardLine></controls:LeaderboardLine>
<controls:LeaderboardLine PlayerName="{Binding PlayerName}" Date="{Binding Date}" <Rectangle/>
Points="{Binding Points}" Victories="{Binding Victories}" />
</DataTemplate> <controls:LeaderboardLine></controls:LeaderboardLine>
</CollectionView.ItemTemplate> <Rectangle/>
</CollectionView>
<controls:LeaderboardLine></controls:LeaderboardLine>
</StackLayout> <Rectangle/>
<controls:LeaderboardLine></controls:LeaderboardLine>
</VerticalStackLayout>
</Border> </Border>
</VerticalStackLayout> </VerticalStackLayout>
</ScrollView> </ScrollView>
</ContentPage> </ContentPage>

@ -5,6 +5,5 @@ public partial class Leaderboard : ContentPage
public Leaderboard() public Leaderboard()
{ {
InitializeComponent(); InitializeComponent();
BindingContext = ((App)Application.Current!).Ld; }
}
} }

@ -10,8 +10,7 @@
<Grid Style="{StaticResource GridMain}"> <Grid Style="{StaticResource GridMain}">
<controls:GoBack></controls:GoBack> <controls:GoBack></controls:GoBack>
<Label Text="Qwirkle Rules" <Label Text="Qwirkle Rules"
Style="{StaticResource Title}" Style="{StaticResource Title}" />
/>
</Grid> </Grid>
<Label <Label
@ -46,34 +45,6 @@
<Label <Label
Text="2. On a line assigned to a certain color/shape, the tiles must have the same color/shape." Text="2. On a line assigned to a certain color/shape, the tiles must have the same color/shape."
Style="{StaticResource RulesContent}" /> Style="{StaticResource RulesContent}" />
<Label
Text="3. For a line assigned to a certain color/shape, if a tile of a specific shape/color has already been placed, they can't place it again."
Style="{StaticResource RulesContent}" />
<Label
Text="Example: If a player has already placed a blue square on a line assigned to the color blue, they can't place another blue square on the same line."
Style="{StaticResource RulesSubContent}" />
<Label
Text="Swapping your tiles"
Style="{StaticResource RulesSubTitle2}" />
<Label
Text="If a player can't place any tiles on the board, they can swap between 1 and 6 of their tiles with the remaining ones in the bag."
Style="{StaticResource RulesContent}" />
<Label
Text="Swapping can be only done if the player didn't place any tile on this turn."
Style="{StaticResource RulesContent}" />
<Label
Text="This action makes the player skip their turn."
Style="{StaticResource RulesContent}" />
<Label
Text="End of the game"
Style="{StaticResource RulesSubTitle}" />
<Label
Text="The game stops either right after a player has placed their last tile or when no player can place any more tiles."
Style="{StaticResource RulesContent}" />
</VerticalStackLayout> </VerticalStackLayout>

@ -53,9 +53,8 @@ public partial class SetPlayers : ContentPage
game.StartGame(); game.StartGame();
game.GiveTilesToPlayers(); game.GiveTilesToPlayers();
game.SetNextPlayer(); game.SetNextPlayer();
Shell.Current.GoToAsync("Gameboard"); Navigation.PushAsync(new Gameboard());
} }
game.PlayerAddNotified -= Game_PlayerAddNotified; game.PlayerAddNotified -= Game_PlayerAddNotified;

@ -6,4 +6,9 @@ public partial class Settings : ContentPage
{ {
InitializeComponent(); InitializeComponent();
} }
public void OnGoBackClicked(object sender, EventArgs e)
{
Navigation.PopAsync();
}
} }

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Qwirkle.PopUpEndGame"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:Name="root"
CanBeDismissedByTappingOutsideOfPopup="False"
xmlns:controls="clr-namespace:Qwirkle.Views"
>
<VerticalStackLayout HeightRequest="400" WidthRequest="500">
<Label
Text="THE END :("
Style="{StaticResource SuperTitle}"
FontSize="Medium"
/>
<Label Text="THE WINNER IS :"></Label>
<Label Text="{Binding ScoreboardList[0].Key, Source={x:Reference root}}" Style="{StaticResource SuperTitle}" TextColor="HotPink" FontSize="Medium"></Label>
<controls:Scoreboard></controls:Scoreboard>
<Button Text="Skip" Style="{StaticResource GameButton}" Clicked="OnButtonNextClick"></Button>
</VerticalStackLayout>
</toolkit:Popup>

@ -1,40 +0,0 @@
using CommunityToolkit.Maui.Views;
using Microsoft.Maui.Controls;
using QwirkleClassLibrary.Games;
using System.Collections.ObjectModel;
namespace Qwirkle;
public partial class PopUpEndGame : Popup
{
public PopUpEndGame()
{
InitializeComponent();
var scoreboard = game.ObservableScoreBoard.OrderByDescending(x => x.Value).ThenBy(x => x.Key);
ScoreboardList = new ObservableCollection<KeyValuePair<string, int>>(scoreboard);
}
private Game game = ((App)Application.Current!).Game;
private ObservableCollection<KeyValuePair<string, int>>? scoreboardList;
public ObservableCollection<KeyValuePair<string, int>>? ScoreboardList
{
get => scoreboardList;
set
{
if (scoreboardList != value)
{
scoreboardList = value;
OnPropertyChanged(nameof(ScoreboardList));
}
}
}
public async void OnButtonNextClick(object sender, EventArgs e)
{
Close();
await Shell.Current.GoToAsync("MainPage");
}
}

@ -1,7 +1,7 @@
{ {
"profiles": { "profiles": {
"Windows Machine": { "Windows Machine": {
"commandName": "Project", "commandName": "MsixPackage",
"nativeDebugging": false "nativeDebugging": false
} }
} }

@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0-windows10.0.19041.0</TargetFrameworks> <TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</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 --> <!-- 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> --> <!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->
@ -29,9 +30,6 @@
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion> <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion> <ApplicationVersion>1</ApplicationVersion>
<WindowsPackageType Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">None</WindowsPackageType>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">11.0</SupportedOSPlatformVersion> <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">11.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">13.1</SupportedOSPlatformVersion> <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">13.1</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion> <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion>
@ -42,8 +40,7 @@
<ItemGroup> <ItemGroup>
<!-- App Icon --> <!-- App Icon -->
<!-- <MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />--> <MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
<MauiIcon Include="Resources\AppIcon\appicon.svg" />
<!-- Splash Screen --> <!-- Splash Screen -->
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" /> <MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />
@ -63,15 +60,14 @@
<None Remove="Resources\Fonts\DiloWorld.ttf" /> <None Remove="Resources\Fonts\DiloWorld.ttf" />
<None Remove="Resources\Fonts\Lexend-Medium.ttf" /> <None Remove="Resources\Fonts\Lexend-Medium.ttf" />
<None Remove="Resources\Images\qwirklelogo.png" /> <None Remove="Resources\Images\qwirklelogo.png" />
<None Remove="Resources\Images\uca.png" /> <None Remove="Resources\Images\uca.png" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommunityToolkit.Maui" Version="9.0.1" /> <PackageReference Include="CommunityToolkit.Maui" Version="9.0.0" />
<PackageReference Include="CommunityToolkit.Maui.MediaElement" Version="3.1.1" />
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.40" /> <PackageReference Include="Microsoft.Maui.Controls" Version="8.0.40" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.40" /> <PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.40" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.0-preview.4.24266.19" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 591 KiB

After

Width:  |  Height:  |  Size: 228 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 586 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

@ -262,14 +262,6 @@
<Setter Property="FontAttributes" Value="Bold" /> <Setter Property="FontAttributes" Value="Bold" />
</Style> </Style>
<Style TargetType="Label" x:Key="RulesSubTitle2">
<Setter Property="TextColor" Value="Black" />
<Setter Property="FontSize" Value="22" />
<Setter Property="Padding" Value="15" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="FontAttributes" Value="Bold, Italic" />
</Style>
<Style TargetType="Label" x:Key="ContentCenter"> <Style TargetType="Label" x:Key="ContentCenter">
<Setter Property="TextColor" Value="Black" /> <Setter Property="TextColor" Value="Black" />
<Setter Property="FontSize" Value="20" /> <Setter Property="FontSize" Value="20" />
@ -282,13 +274,6 @@
<Setter Property="HorizontalOptions" Value="Center" /> <Setter Property="HorizontalOptions" Value="Center" />
</Style> </Style>
<Style TargetType="Label" x:Key="RulesSubContent">
<Setter Property="TextColor" Value="Black" />
<Setter Property="FontSize" Value="14" />
<Setter Property="FontAttributes" Value="Italic"></Setter>
<Setter Property="HorizontalOptions" Value="Center" />
</Style>
<Style TargetType="Label" x:Key="ContentStart"> <Style TargetType="Label" x:Key="ContentStart">
<Setter Property="TextColor" Value="Black" /> <Setter Property="TextColor" Value="Black" />
<Setter Property="FontSize" Value="20" /> <Setter Property="FontSize" Value="20" />

@ -1,5 +1,3 @@
using Qwirkle.Pages;
namespace Qwirkle.Views; namespace Qwirkle.Views;
public partial class GoBack : ContentView public partial class GoBack : ContentView

@ -1,42 +1,42 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui" <ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Qwirkle.Views.LeaderboardLine" x:Class="Qwirkle.Views.LeaderboardLine">
x:Name="root">
<Grid ColumnDefinitions="4*, auto, 2*, auto, 2*, auto, 2*" <Grid ColumnDefinitions="4*, auto, 2*, auto, 2*, auto, 2*"
RowDefinitions="50"> RowDefinitions="50">
<Label <Label
Text="{Binding PlayerName}" Text="Player Tag"
Style="{StaticResource ContentTab}"/> Style="{StaticResource ContentTab}"
/>
<Rectangle <Rectangle
Style="{StaticResource RectangleTab}" Style="{StaticResource RectangleTab}"
Grid.Column="1"/> Grid.Column="1"/>
<Label <Label
Grid.Column="2" Grid.Column="2"
Text="{Binding Date, StringFormat='{0:MM/dd/yyyy}'}" Text="Date"
Style="{StaticResource ContentTab}"/> Style="{StaticResource ContentTab}"/>
<Rectangle <Rectangle
Style="{StaticResource RectangleTab}" Style="{StaticResource RectangleTab}"
Grid.Column="3"/> Grid.Column="3"/>
<Label <Label
Grid.Column="4" Grid.Column="4"
Text="{Binding Points}" Text="Points"
Style="{StaticResource ContentTab}"/> Style="{StaticResource ContentTab}"/>
<Rectangle <Rectangle
Style="{StaticResource RectangleTab}" Style="{StaticResource RectangleTab}"
Grid.Column="5"/> Grid.Column="5"/>
<Label <Label
Grid.Column="6" Grid.Column="6"
Style="{StaticResource ContentTab}" Style="{StaticResource ContentTab}"
Text="{Binding Victories}"/> Text="Victories"
/>
</Grid> </Grid>
</ContentView> </ContentView>

@ -3,51 +3,13 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Maui.Controls;
namespace Qwirkle.Views namespace Qwirkle.Views;
public partial class LeaderboardLine : ContentView
{ {
public partial class LeaderboardLine : ContentView public LeaderboardLine()
{ {
public LeaderboardLine() InitializeComponent();
{
InitializeComponent();
}
public static readonly BindableProperty PlayerNameProperty =
BindableProperty.Create(nameof(PlayerName), typeof(string), typeof(LeaderboardLine), "");
public static readonly BindableProperty DateProperty =
BindableProperty.Create(nameof(Date), typeof(DateTime), typeof(LeaderboardLine), default(DateTime));
public static readonly BindableProperty PointsProperty =
BindableProperty.Create(nameof(Points), typeof(int), typeof(LeaderboardLine), 0);
public static readonly BindableProperty VictoriesProperty =
BindableProperty.Create(nameof(Victories), typeof(int), typeof(LeaderboardLine), 0);
public string PlayerName
{
get => (string)GetValue(PlayerNameProperty);
set => SetValue(PlayerNameProperty, value);
}
public DateTime Date
{
get => (DateTime)GetValue(DateProperty);
set => SetValue(DateProperty, value);
}
public int Points
{
get => (int)GetValue(PointsProperty);
set => SetValue(PointsProperty, value);
}
public int Victories
{
get => (int)GetValue(VictoriesProperty);
set => SetValue(VictoriesProperty, value);
}
} }
} }

@ -0,0 +1,27 @@
<?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="Qwirkle.Views.ScoreboardLine"
x:Name="root">
<StackLayout>
<Grid ColumnDefinitions="4*, auto, 2*"
RowDefinitions="50">
<Label
Grid.Column="0"
Text="{Binding Nameplayer, Source={x:Reference root}}"
Style="{StaticResource ContentTab}"/>
<Rectangle
Style="{StaticResource RectangleTab}"
Grid.Column="1"/>
<Label
Grid.Column="2"
Text="{Binding Score, Source={x:Reference root}}"
Style="{StaticResource ContentTab}"/>
</Grid>
<Rectangle/>
</StackLayout>
</ContentView>

@ -0,0 +1,43 @@
namespace Qwirkle.Views
{
public partial class ScoreboardLine : ContentView
{
public static readonly BindableProperty NameplayerProperty =
BindableProperty.Create(nameof(Nameplayer), typeof(string), typeof(ScoreboardLine), default(string), propertyChanged: OnPlayerChanged);
public string Nameplayer
{
get => (string)GetValue(NameplayerProperty);
set => SetValue(NameplayerProperty, value);
}
private static void OnPlayerChanged(BindableObject bindable, object oldValue, object newValue)
{
var bin = (ScoreboardLine)bindable;
bin.OnPropertyChanged(nameof(Nameplayer));
}
public static readonly BindableProperty ScoreProperty =
BindableProperty.Create(nameof(Score), typeof(int), typeof(ScoreboardLine), default(int), propertyChanged: OnScoreChanged);
public int Score
{
get => (int)GetValue(ScoreProperty);
set => SetValue(ScoreProperty, value);
}
private static void OnScoreChanged(BindableObject bindable, object oldValue, object newValue)
{
var bin = (ScoreboardLine)bindable;
bin.OnPropertyChanged(nameof(Score));
}
public ScoreboardLine()
{
InitializeComponent();
BindingContext = this;
}
}
}

@ -287,6 +287,25 @@ public class TestGame
Assert.False(game.PlaceTile(game.GetPlayingPlayer(), game.PlayerList[game.GetPlayingPlayerPosition()].Tiles[0], -5, 1)); Assert.False(game.PlaceTile(game.GetPlayingPlayer(), game.PlayerList[game.GetPlayingPlayerPosition()].Tiles[0], -5, 1));
} }
// [Fact]
// public void Test_DrawTile()
// {
// Game game = new Game();
// game.AddPlayerInGame("Test1");
// game.AddPlayerInGame(playerstest);
//
// game.StartGame();
// game.SetNextPlayer();
//
// Assert.True(game.DrawTiles(game.GetPlayingPlayer()));
//
// /*test*/
// TileBag bag = new TileBag(0);
// Assert.False(game.DrawTiles(game.GetPlayingPlayer()));
// return;
//
// }
[Theory] [Theory]
[InlineData(true)] [InlineData(true)]
[InlineData(false)] [InlineData(false)]
@ -323,48 +342,6 @@ public class TestGame
Assert.Equal(p, events.Player); Assert.Equal(p, events.Player);
} }
[Fact]
public void Test_IsTilePlacementCorrect()
{
var game = new Game();
var board = new Board(17, 14);
var tile = new Tile(Shape.Club, Color.Red);
board.AddTileInCell(0, 1, new Tile(Shape.Club, Color.Blue));
var x = 1;
var y = 1;
var surroundingCells = new List<Cell?>
{
board.GetCell(x + 1, y),
board.GetCell(x - 1, y),
board.GetCell(x, y + 1),
board.GetCell(x, y - 1)
};
bool result = game.IsTilePlacementCorrect(tile, x, y, board, surroundingCells);
Assert.True(result);
}
[Fact]
public void Test_IsMoveCorrect()
{
var game = new Game();
var board = new Board(17, 14);
var tile = new Tile(Shape.Club, Color.Red);
board.AddTileInCell(0, 1, new Tile(Shape.Club, Color.Blue));
var x = 2;
var y = 1;
bool result = game.IsMoveCorrect(tile, x, y, board);
Assert.False(result);
}
[Fact] [Fact]
public void Test_IsMoveCorrectSixLine() public void Test_IsMoveCorrectSixLine()
{ {
@ -404,41 +381,6 @@ public class TestGame
} }
[Fact]
public void Test_CheckTileInCompletedLines()
{
int nbTiles = 0;
var checkdoubles = new List<Tile>()
{
new(Shape.Club, Color.Blue),
new(Shape.Club, Color.Red),
new(Shape.Club, Color.Green),
};
var t1 = new Tile(Shape.Club, Color.Green);
Assert.False(Game.CheckTileInCompletedLines(t1, ref nbTiles, ref checkdoubles));
}
[Fact]
public void Test_CheckWrongCompletedLines()
{
var board = new Board(17, 14);
var checkDoubles = new List<Tile>();
int x = 4, y = 1, dx = 1, dy = 0;
board.AddTileInCell(1, 1, new Tile(Shape.Club, Color.Red));
board.AddTileInCell(2, 1, new Tile(Shape.Square, Color.Red));
board.AddTileInCell(3, 1, new Tile(Shape.Star, Color.Red));
board.AddTileInCell(5, 1, new Tile(Shape.Round, Color.Red));
board.AddTileInCell(6, 1, new Tile(Shape.Shuriken, Color.Red));
board.AddTileInCell(7, 1, new Tile(Shape.Rhombus, Color.Red));
bool result = Game.CheckWrongCompletedLines(x, y, dx, dy, board, ref checkDoubles);
Assert.False(result);
}
[Theory] [Theory]
[InlineData(3, 1, 4, 1, 5, 1, 5)] [InlineData(3, 1, 4, 1, 5, 1, 5)]
[InlineData(2, 2, 3, 2, 4, 2, 5)] [InlineData(2, 2, 3, 2, 4, 2, 5)]
@ -447,7 +389,7 @@ public class TestGame
{ {
var game = new Game(); var game = new Game();
var player = new Player("TestPlayer"); var player = new Player("TestPlayer");
var board = game.GetBoard(); var board = new Board(8, 8);
board.AddTileInCell(1, 1, new Tile(Shape.Club, Color.Red)); board.AddTileInCell(1, 1, new Tile(Shape.Club, Color.Red));
board.AddTileInCell(2, 1, new Tile(Shape.Square, Color.Red)); board.AddTileInCell(2, 1, new Tile(Shape.Square, Color.Red));
@ -471,14 +413,6 @@ public class TestGame
Assert.Equal(expectedScore, score); Assert.Equal(expectedScore, score);
} }
[Theory]
[InlineData(0, -1, -1, 6)]
[InlineData(1, 0, 4, -1)]
public void Test_ShouldIncreaseScore(int dx, int dy, int cellsX, int cellsY)
{
Assert.True(Game.ShouldIncreaseScore(new Tuple<int, int>(dx, dy), new Tuple<int, int>(cellsX, cellsY)));
}
[Fact] [Fact]
public void Test_EndOfGame() public void Test_EndOfGame()

@ -50,6 +50,8 @@ public class TestLeaderboard
leaderboard.AddScoreInLead(game.ScoreBoard); leaderboard.AddScoreInLead(game.ScoreBoard);
var game2 = new Game();
game.AddPlayerInGame(playerstest); game.AddPlayerInGame(playerstest);
game.StartGame(); game.StartGame();
@ -59,7 +61,7 @@ public class TestLeaderboard
leaderboard.AddScoreInLead(game.ScoreBoard); leaderboard.AddScoreInLead(game.ScoreBoard);
Assert.Equal(2, leaderboard.Lb[1].Victories); Assert.Equal(2, leaderboard.Lb[0].Victories);
} }

Binary file not shown.

@ -1,3 +1,4 @@
[![Build Status](https://codefirst.iut.uca.fr/api/badges/jeremy.mouyon/sae201_qwirkle/status.svg)](https://codefirst.iut.uca.fr/jeremy.mouyon/sae201_qwirkle) [![Build Status](https://codefirst.iut.uca.fr/api/badges/jeremy.mouyon/sae201_qwirkle/status.svg)](https://codefirst.iut.uca.fr/jeremy.mouyon/sae201_qwirkle)
# Notre QWIRKLE # Notre QWIRKLE
@ -8,11 +9,7 @@ Projet .NET MAUI portant sur la conception d'un jeu vidéo en reprenant un jeu d
## Présentation de notre jeu ## Présentation de notre jeu
Notre présentation est consutable sur ce [lien](https://codefirst.iut.uca.fr/git/jeremy.mouyon/sae201_qwirkle/wiki/Pr%C3%A9sentation-du-jeu). Notré présentation est consutable sur ce [lien](https://codefirst.iut.uca.fr/git/jeremy.mouyon/sae201_qwirkle/wiki/Pr%C3%A9sentation-du-jeu).
### Vidéo de présentation
Voici le [lien](https://opencast.dsi.uca.fr/paella/ui/watch.html?id=4c3f5f66-69e8-4700-878e-3ec700e14640) de notre vidéo de présentation du jeu
## Composition du groupe ## Composition du groupe
@ -23,17 +20,6 @@ Voici le [lien](https://opencast.dsi.uca.fr/paella/ui/watch.html?id=4c3f5f66-69e
## *Comment utiliser notre application ?* ## *Comment utiliser notre application ?*
### Executable
Semé d'embûches, le chemin pour vous fournir un exécutable a finalement été trouvé. Il se trouve à la racine de notre projet.
Vous pourrez via celui-ci, utiliser notre application de A à Z !
*Nous ne sommes malheureusement pas en mesure de vous fournir un fichier .msix!*
### Via un IDE
Tout d'abord, récupérez notre projet en tapant dans votre terminal : Tout d'abord, récupérez notre projet en tapant dans votre terminal :
```sh ```sh
git clone https://codefirst.iut.uca.fr/git/jeremy.mouyon/sae201_qwirkle.git git clone https://codefirst.iut.uca.fr/git/jeremy.mouyon/sae201_qwirkle.git
@ -67,10 +53,9 @@ Après cela, vous pourrez continuer à placer d'autres tuiles, si vous le souhai
Si vous souhaitez échanger des des tuiles de votre main, vous pouvez choisir de les échanger avec des tuiles de la pioche. Entrez simplement les numéros correspondants des tuiles que vous voulez échanger. Si vous souhaitez échanger des des tuiles de votre main, vous pouvez choisir de les échanger avec des tuiles de la pioche. Entrez simplement les numéros correspondants des tuiles que vous voulez échanger.
Attention, gardez à l'esprit que si vous choisissez cette option, vous ne pourrez pas jouer durant ce tour. Attention, gardez à l'esprit que si vous choisissez cette option, vous ne pourrez pas jouer durant ce tour.
## Where is the sound ? ### Et voilà !
Vous vous demandez probablement où est passé le son ? Il n'y en a finalement pas eu. Le temps d'implémenter un système sonore avec un réglage était impossible dans les délais fixés. Nous avons quand même fait la page, conformément à notre conception !
## Et voilà ! Votre partie est lancée ! Amusez-vous bien avec ce jeu de Qwirkle !
Votre partie est lancée ! Amusez-vous bien avec ce jeu de Qwirkle ! :) *PS pour Mr Chargueraud : Promesse faite, promesse tenue ! Mon problème de score est en grande partie réglé (non sans peine) !*
*Le seul problème restant étant que sur certains cas, le +6 de la complétion de ligne peut s'effectuer 2 fois.*
Loading…
Cancel
Save