Merge branch 'master' of https://codefirst.iut.uca.fr/git/jeremy.mouyon/sae201_qwirkle
continuous-integration/drone/push Build is passing Details

master
Jérémy Mouyon 1 year ago
commit 019307e893

File diff suppressed because one or more lines are too long

@ -53,7 +53,7 @@ namespace QwirkleClassLibrary.Boards
{
for (int b = 0; b < Columns; b++)
{
Cell localcell = new(a, b);
Cell localcell = new(b, a);
cells.Add(localcell);
}
}

@ -61,7 +61,7 @@ public class Cell : INotifyPropertyChanged
/// <returns>True if the cell is empty, false if the cell contains a tile.</returns>
public bool IsFree
{
get { return Tile == null; }
get { return Tile! == null!; }
}
@ -72,7 +72,7 @@ public class Cell : INotifyPropertyChanged
/// <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)
{
if (Tile == null)
if (Tile! == null!)
{
Tile = addedTile;

@ -7,43 +7,34 @@ using QwirkleClassLibrary.Events;
using QwirkleClassLibrary.Players;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using static System.Formats.Asn1.AsnWriter;
namespace QwirkleClassLibrary.Games
{
[DataContract]
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 Board Board => board;
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();
[DataMember]
private readonly ObservableCollection<KeyValuePair<string, int>> observableScoreBoard = [];
[DataMember] private readonly ObservableCollection<KeyValuePair<string, int>> observableScoreBoard = [];
public ReadOnlyObservableCollection<KeyValuePair<string, int>> ObservableScoreBoard =>
new(observableScoreBoard);
[DataMember]
private readonly List<Cell> cellUsed = [];
[DataMember] private readonly List<Cell> cellUsed = [];
public ReadOnlyCollection<Cell> CellsUsed => cellUsed.AsReadOnly();
@ -104,9 +95,11 @@ namespace QwirkleClassLibrary.Games
if (playersTag.Count <= 1 || playersTag.Count > 4)
{
playersTag.Clear();
OnPlayerNotified(new AddPlayerNotifiedEventArgs("ERROR : It takes a minimum of 2 players and a maximum of 4 players to start a game."));
OnPlayerNotified(new AddPlayerNotifiedEventArgs(
"ERROR : It takes a minimum of 2 players and a maximum of 4 players to start a game."));
return false;
}
for (int i = playersTag.Count - 1; i >= 0; i--)
{
if (!CheckPlayerTag(playersTag, i))
@ -131,7 +124,8 @@ namespace QwirkleClassLibrary.Games
{
if (string.IsNullOrWhiteSpace(playersTag[pos]))
{
OnPlayerNotified(new AddPlayerNotifiedEventArgs("ERROR with " + (pos + 1) + " entry : The name is null or white space."));
OnPlayerNotified(new AddPlayerNotifiedEventArgs("ERROR with " + (pos + 1) +
" entry : The name is null or white space."));
return false;
}
@ -148,7 +142,6 @@ namespace QwirkleClassLibrary.Games
new AddPlayerNotifiedEventArgs("ERROR with " + (pos + 1) + " entry : Name alreay taken"));
return false;
}
}
return true;
@ -170,13 +163,19 @@ namespace QwirkleClassLibrary.Games
/// Returns the Board of the game
/// </summary>
/// <returns>Board</returns>
public Board? GetBoard() { return board; }
public Board GetBoard()
{
return board;
}
/// <summary>
/// Returns the tile bag of the game
/// </summary>
/// <returns></returns>
public TileBag? GetTileBag() { return bag; }
public TileBag? GetTileBag()
{
return bag;
}
/// <summary>
/// Creates a Board with a number of columns and rows
@ -238,6 +237,7 @@ namespace QwirkleClassLibrary.Games
{
throw new ArgumentException("No player currently playing !");
}
return players[GetPlayingPlayerPosition()];
}
@ -254,6 +254,7 @@ namespace QwirkleClassLibrary.Games
return i;
}
}
return -1;
}
@ -312,11 +313,10 @@ namespace QwirkleClassLibrary.Games
startingPlayer = player;
}
}
startingPlayer!.IsPlaying = true;
OnNextPlayer(new NextPlayerNotifiedEventArgs(players[0]));
return startingPlayer.NameTag;
}
/// <summary>
@ -354,13 +354,15 @@ namespace QwirkleClassLibrary.Games
OnPlaceTile(new PlaceTileNotifiedEventArgs(tile, "you are swapping, you can't place tile !"));
return false;
}
if (!TileInbag(player, tile))
{
OnPlaceTile(new PlaceTileNotifiedEventArgs(tile, "you can't play"));
return false;
}
if (!IsMoveCorrect(tile, x, y, board!)) return false;
if (board!.AddTileInCell(x, y, tile))
if (!IsMoveCorrect(tile, x, y, board)) return false;
if (board.AddTileInCell(x, y, tile))
{
AddCellUsed(board.GetCell(x, y));
return player.RemoveTileToPlayer(tile);
@ -446,12 +448,12 @@ namespace QwirkleClassLibrary.Games
{
players[GetPlayingPlayerPosition()].AddTileToPlayer(t);
}
}
}
/// <summary>
/// Extension of IsMoveCorrect to check beyond the surrounding cells of the cell where the tile is placed
/// </summary>
/// <param name="previousTilesFound"></param>
/// <param name="tile"></param>
/// <param name="x"></param>
/// <param name="y"></param>
@ -459,26 +461,39 @@ namespace QwirkleClassLibrary.Games
/// <param name="dy">used to get the direction on the y axis</param>
/// <param name="b"></param>
/// <returns>bool</returns>
public bool CheckExtendedSurroundingCells(Tile tile, int x, int y, int dx, int dy, Board b)
public bool CheckExtendedSurroundingCells(ref bool previousTilesFound, Tile tile, int x, int y, int dx, int dy,
Board b)
{
for (int i = 1; i < 7; i++)
{
var extendedCell = b.GetCell(x + i * dx, y + i * dy);
if (extendedCell?.Tile == null)
if (cellUsed.Count == 0)
{
previousTilesFound = true;
}
if (cellUsed.Contains(extendedCell!))
{
previousTilesFound = true;
}
if (extendedCell?.Tile! == null!)
{
break;
}
if (extendedCell.Tile.GetColor != tile.GetColor && extendedCell.Tile.GetShape != tile.GetShape)
{
OnPlaceTile(new PlaceTileNotifiedEventArgs(tile, " : Color / Shape does not match with the surrounding tiles !"));
OnPlaceTile(new PlaceTileNotifiedEventArgs(tile,
" : Color / Shape does not match with the surrounding tiles !"));
return false;
}
if (extendedCell.Tile.GetColor == tile.GetColor && extendedCell.Tile.GetShape == tile.GetShape)
{
OnPlaceTile(new PlaceTileNotifiedEventArgs(tile, " : Tile already placed on the same line / column !"));
OnPlaceTile(new PlaceTileNotifiedEventArgs(tile,
" : Tile already placed on the same line / column !"));
return false;
}
@ -527,6 +542,7 @@ namespace QwirkleClassLibrary.Games
{
return x == x1;
}
if (y1 == y2)
{
return y == y1;
@ -536,13 +552,62 @@ namespace QwirkleClassLibrary.Games
}
/// <summary>
/// Main method to check if the move the player is trying to make is correct
/// Check that there isn't any same tile on a said line when a tile is forming a line
/// </summary>
/// <param name="t"></param>
/// <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>
/// <param name="tile"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="dx"></param>
/// <param name="dy"></param>
/// <param name="b"></param>
/// <returns>bool</returns>
/// <param name="checkdoubles"></param>
/// <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)
{
if (!b.HasOccupiedCase())
@ -550,9 +615,10 @@ namespace QwirkleClassLibrary.Games
return true;
}
if (b.GetCell(x, y)!.Tile != null)
if (b.GetCell(x, y)!.Tile! != null!)
{
OnPlaceTile(new PlaceTileNotifiedEventArgs(t, " : Cell already used !"));
return false;
}
var surroundingCells = new List<Cell?>
@ -562,50 +628,68 @@ namespace QwirkleClassLibrary.Games
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)
{
if (cell?.Tile == null)
if (cell?.Tile! == null!)
{
continue;
}
if (cell.Tile.GetColor != t.GetColor && cell.Tile.GetShape != t.GetShape)
{
OnPlaceTile(new PlaceTileNotifiedEventArgs(t, " : Colors / Shapes do not match with the surrounding tiles !"));
OnPlaceTile(new PlaceTileNotifiedEventArgs(t,
" : Colors / Shapes do not match with the surrounding tiles !"));
return false;
}
if (cell.Tile.GetColor == t.GetColor && cell.Tile.GetShape == t.GetShape)
{
OnPlaceTile(new PlaceTileNotifiedEventArgs(t, " is already placed on the same line / column !"));
return false;
}
var dx = cell.GetX - x;
var dy = cell.GetY - y;
if (!CheckExtendedSurroundingCells(t, x, y, dx, dy, b))
if (!CheckExtendedSurroundingCells(ref previousTilesFound, t, x, y, dx, dy, b))
{
return false;
}
}
if (!CheckTilesInLine(cellUsed, b, x, y))
{
OnPlaceTile(new PlaceTileNotifiedEventArgs(t, "isn't on the same line as the ones previously placed !"));
if (CheckWrongCompletedLines(x, y, dx, dy, b, ref checkDoubles)) continue;
OnPlaceTile(new PlaceTileNotifiedEventArgs(t,
" : You can't complete this line ! (More than 6 tiles / same tiles on the line)"));
return false;
}
if (surroundingCells.All(cell => cell?.Tile == null))
if (!CheckTilesInLine(cellUsed, b, x, y))
{
OnPlaceTile(new PlaceTileNotifiedEventArgs(t, " : You can't place a tile that isn't adjacent to another one !"));
OnPlaceTile(new PlaceTileNotifiedEventArgs(t,
"isn't on the same line as the ones previously placed !"));
return false;
}
return true;
if (previousTilesFound) return true;
OnPlaceTile(new PlaceTileNotifiedEventArgs(t,
" : You must place your tile next / on the same line as the ones previously placed !"));
return false;
}
@ -654,7 +738,8 @@ namespace QwirkleClassLibrary.Games
cellsX = cellsY = -1;
}
score += cellsPlayed.Sum(cell => CalculateAdjacentScore(cell, b, cellsPlayed, cellsX, cellsY, ref nbCellsInLine));
score += cellsPlayed.Sum(cell =>
CalculateAdjacentScore(cell, b, cellsPlayed, cellsX, cellsY, ref nbCellsInLine));
if (nbCellsInLine == 6)
{
@ -663,7 +748,6 @@ namespace QwirkleClassLibrary.Games
if (!scoreBoard.TryAdd(player.NameTag, score))
{
scoreBoard.TryGetValue(player.NameTag, out int scoreold);
SetScoreBoard(player.NameTag, score + scoreold);
}
@ -681,7 +765,8 @@ namespace QwirkleClassLibrary.Games
/// <param name="cellsY"></param>
/// <param name="nbCellsInLine"></param>
/// <returns>int</returns>
public int CalculateAdjacentScore(Cell cell, Board b, ReadOnlyCollection<Cell> cellsPlayed, int cellsX, int cellsY, ref int nbCellsInLine)
public int CalculateAdjacentScore(Cell cell, Board b, ReadOnlyCollection<Cell> cellsPlayed, int cellsX,
int cellsY, ref int nbCellsInLine)
{
int score = 0;
@ -697,7 +782,8 @@ namespace QwirkleClassLibrary.Games
foreach (var adjacentCell in surroundingCells)
{
if (adjacentCell?.Tile == null || cellsPlayed.Contains(adjacentCell) || checkedSurroundingCells.Contains(adjacentCell))
if (adjacentCell?.Tile! == null! || cellsPlayed.Contains(adjacentCell) ||
checkedSurroundingCells.Contains(adjacentCell))
{
continue;
}
@ -705,7 +791,8 @@ namespace QwirkleClassLibrary.Games
int dx = adjacentCell.GetX - cell.GetX;
int dy = adjacentCell.GetY - cell.GetY;
score += CalculateLineScore(cellsPlayed, cell, new Tuple<int, int>(dx, dy), b, new Tuple<int, int>(cellsX, cellsY), ref nbCellsInLine);
score += CalculateLineScore(cellsPlayed, cell, new Tuple<int, int>(dx, dy), b,
new Tuple<int, int>(cellsX, cellsY), ref nbCellsInLine);
checkedSurroundingCells.Add(adjacentCell);
}
@ -723,7 +810,8 @@ namespace QwirkleClassLibrary.Games
/// <param name="orientation"></param>
/// <param name="nbCellsInLine"></param>
/// <returns>int</returns>
public int CalculateLineScore(ReadOnlyCollection<Cell> cellsPlayed, Cell cell, Tuple<int, int> direction, Board b, Tuple<int, int> orientation, ref int nbCellsInLine)
public int CalculateLineScore(ReadOnlyCollection<Cell> cellsPlayed, Cell cell, Tuple<int, int> direction,
Board b, Tuple<int, int> orientation, ref int nbCellsInLine)
{
int score = 0;
@ -731,7 +819,7 @@ namespace QwirkleClassLibrary.Games
{
var extendedCell = b.GetCell(cell.GetX + i * direction.Item1, cell.GetY + i * direction.Item2);
if (extendedCell?.Tile == null || cellsPlayed.Contains(extendedCell))
if (extendedCell?.Tile! == null! || cellsPlayed.Contains(extendedCell))
{
break;
}
@ -744,7 +832,8 @@ namespace QwirkleClassLibrary.Games
score++;
}
if (direction.Item1 == 0 && orientation.Item1 == -1 && orientation.Item2 != -1 || direction.Item2 == 0 && orientation.Item2 == -1 && orientation.Item1 != -1)
if (direction.Item1 == 0 && orientation.Item1 == -1 && orientation.Item2 != -1 ||
direction.Item2 == 0 && orientation.Item2 == -1 && orientation.Item1 != -1)
{
score += 1;
}
@ -770,7 +859,6 @@ namespace QwirkleClassLibrary.Games
playerTilesBagPos.Add(i);
}
}
}
return playerTilesBagPos;
@ -787,7 +875,7 @@ namespace QwirkleClassLibrary.Games
{
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 y = board.ReadCells[b].GetY;
@ -813,7 +901,8 @@ namespace QwirkleClassLibrary.Games
{
List<int> playerTilesBagPos = CheckTilesBag();
if (playerTilesBagPos.Count != 0 && !CheckPlacementPossibilities(playerTilesBagPos) || bag!.TilesBag!.Count == 0 && players[GetPlayingPlayerPosition()].Tiles.Count == 0)
if (playerTilesBagPos.Count != 0 && !CheckPlacementPossibilities(playerTilesBagPos) ||
bag!.TilesBag!.Count == 0 && players[GetPlayingPlayerPosition()].Tiles.Count == 0)
{
OnEndOfGame(new EndOfGameNotifiedEventArgs(player));
GameRunning = false;
@ -838,7 +927,6 @@ namespace QwirkleClassLibrary.Games
public void SetScoreBoard(string name, int score)
{
if (!scoreBoard.TryAdd(name, score))
{
scoreBoard[name] = score;
@ -849,6 +937,7 @@ namespace QwirkleClassLibrary.Games
{
observableScoreBoard.Add(item);
}
OnPropertyChanged(nameof(ObservableScoreBoard));
}
}

@ -17,7 +17,7 @@ namespace QwirkleClassLibrary.Games
bool IsMoveCorrect(Tile t, int x, int y, Board b);
bool CheckExtendedSurroundingCells(Tile tile, int x, int y, int dx, int dy, Board b);
bool CheckExtendedSurroundingCells(ref bool previousTilesFound, Tile tile, int x, int y, int dx, int dy, Board b);
bool CheckTilesInLine(List<Cell> cells, Board b, int x, int y);

@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace QwirkleClassLibrary.Tiles
{
[DataContract]
public class Tile
public class Tile : IComparable
{
[DataMember]
private readonly Shape shape;
@ -41,19 +41,13 @@ namespace QwirkleClassLibrary.Tiles
/// A getter for the shape of the Tile.
/// </summary>
/// <returns>The shape attribute of the Tile.</returns>
public Shape GetShape
{
get { return shape; }
}
public Shape GetShape => shape;
/// <summary>
/// A getter for the color of the Tile.
/// </summary>
/// <returns>The color attribute of the Tile.</returns>
public Color GetColor
{
get { return color; }
}
public Color GetColor => color;
/// <summary>
/// This method is used to override the ToString() method. It is simply a tool to facilitate the development.
@ -63,5 +57,69 @@ namespace QwirkleClassLibrary.Tiles
{
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;
}
}
}

@ -287,25 +287,6 @@ public class TestGame
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]
[InlineData(true)]
[InlineData(false)]
@ -342,6 +323,48 @@ public class TestGame
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]
public void Test_IsMoveCorrectSixLine()
{
@ -381,6 +404,41 @@ 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]
[InlineData(3, 1, 4, 1, 5, 1, 5)]
[InlineData(2, 2, 3, 2, 4, 2, 5)]

Loading…
Cancel
Save