diff --git a/Qwirkle/QwirkleClassLibrary/Boards/Board.cs b/Qwirkle/QwirkleClassLibrary/Boards/Board.cs index 7dfc7e5..1a03e1e 100644 --- a/Qwirkle/QwirkleClassLibrary/Boards/Board.cs +++ b/Qwirkle/QwirkleClassLibrary/Boards/Board.cs @@ -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); } } diff --git a/Qwirkle/QwirkleClassLibrary/Games/Game.cs b/Qwirkle/QwirkleClassLibrary/Games/Game.cs index a7d7385..d7766a6 100644 --- a/Qwirkle/QwirkleClassLibrary/Games/Game.cs +++ b/Qwirkle/QwirkleClassLibrary/Games/Game.cs @@ -11,39 +11,31 @@ 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 PlayerList => players.AsReadOnly(); - [DataMember] - private readonly List players = []; + [DataMember] private readonly List players = []; - [DataMember] - private readonly Dictionary scoreBoard = new Dictionary(); + [DataMember] private readonly Dictionary scoreBoard = new Dictionary(); public ReadOnlyDictionary ScoreBoard => scoreBoard.AsReadOnly(); - [DataMember] - private readonly ObservableCollection> observableScoreBoard = []; - + [DataMember] private readonly ObservableCollection> observableScoreBoard = []; + public ReadOnlyObservableCollection> ObservableScoreBoard => new(observableScoreBoard); - [DataMember] - private readonly List cellUsed = []; + [DataMember] private readonly List cellUsed = []; public ReadOnlyCollection CellsUsed => cellUsed.AsReadOnly(); @@ -104,9 +96,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 +125,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 +143,6 @@ namespace QwirkleClassLibrary.Games new AddPlayerNotifiedEventArgs("ERROR with " + (pos + 1) + " entry : Name alreay taken")); return false; } - } return true; @@ -170,13 +164,19 @@ namespace QwirkleClassLibrary.Games /// Returns the Board of the game /// /// Board - public Board? GetBoard() { return board; } + public Board? GetBoard() + { + return board; + } /// /// Returns the tile bag of the game /// /// - public TileBag? GetTileBag() { return bag; } + public TileBag? GetTileBag() + { + return bag; + } /// /// Creates a Board with a number of columns and rows @@ -238,6 +238,7 @@ namespace QwirkleClassLibrary.Games { throw new ArgumentException("No player currently playing !"); } + return players[GetPlayingPlayerPosition()]; } @@ -254,6 +255,7 @@ namespace QwirkleClassLibrary.Games return i; } } + return -1; } @@ -312,11 +314,10 @@ namespace QwirkleClassLibrary.Games startingPlayer = player; } } - + startingPlayer!.IsPlaying = true; OnNextPlayer(new NextPlayerNotifiedEventArgs(players[0])); return startingPlayer.NameTag; - } /// @@ -354,11 +355,13 @@ 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)) { @@ -446,12 +449,12 @@ namespace QwirkleClassLibrary.Games { players[GetPlayingPlayerPosition()].AddTileToPlayer(t); } - - } + } /// /// Extension of IsMoveCorrect to check beyond the surrounding cells of the cell where the tile is placed /// + /// /// /// /// @@ -459,12 +462,23 @@ namespace QwirkleClassLibrary.Games /// used to get the direction on the y axis /// /// bool - 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 (cellUsed.Count == 0) + { + previousTilesFound = true; + } + + if (cellUsed.Contains(extendedCell)) + { + previousTilesFound = true; + } + if (extendedCell?.Tile == null) { break; @@ -472,13 +486,15 @@ namespace QwirkleClassLibrary.Games 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 +543,7 @@ namespace QwirkleClassLibrary.Games { return x == x1; } + if (y1 == y2) { return y == y1; @@ -534,6 +551,54 @@ namespace QwirkleClassLibrary.Games return false; } + + public bool CheckWrongCompletedLines(Tile tile, int x, int y, int dx, int dy, Board b, ref List 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 (extendedCell?.Tile != null) + { + nbTiles++; + + foreach (var t in checkdoubles) + { + if (t.CompareTo(extendedCell.Tile) == 0) + { + return false; + } + } + + checkdoubles.Add(extendedCell.Tile); + } + + if (extendedCell2?.Tile != null) + { + nbTiles++; + + foreach (var t in checkdoubles) + { + if (t.CompareTo(extendedCell2.Tile) == 0) + { + return false; + } + } + + checkdoubles.Add(extendedCell2.Tile); + } + } + + return nbTiles <= 6; + } /// /// Main method to check if the move the player is trying to make is correct @@ -545,6 +610,10 @@ namespace QwirkleClassLibrary.Games /// bool public bool IsMoveCorrect(Tile t, int x, int y, Board b) { + bool previousTilesFound = false; + + var checkDoubles = new List(); + if (!b.HasOccupiedCase()) { return true; @@ -572,7 +641,8 @@ namespace QwirkleClassLibrary.Games 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; } @@ -586,26 +656,38 @@ namespace QwirkleClassLibrary.Games 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 (!CheckWrongCompletedLines(t, x, y, dx, dy, b, ref checkDoubles)) + { + OnPlaceTile(new PlaceTileNotifiedEventArgs(t, " : You can't complete this line ! (More than 6 tiles / same tiles on the line)")); + return false; + } } if (!CheckTilesInLine(cellUsed, b, x, y)) { - OnPlaceTile(new PlaceTileNotifiedEventArgs(t, "isn't on the same line as the ones previously placed !")); + OnPlaceTile(new PlaceTileNotifiedEventArgs(t, + "isn't on the same line as the ones previously placed !")); return false; } if (surroundingCells.All(cell => cell?.Tile == null)) { - OnPlaceTile(new PlaceTileNotifiedEventArgs(t, " : You can't place a tile that isn't adjacent to another one !")); + OnPlaceTile(new PlaceTileNotifiedEventArgs(t, + " : You can't place a tile that isn't adjacent to another one !")); 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 +736,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 +746,6 @@ namespace QwirkleClassLibrary.Games if (!scoreBoard.TryAdd(player.NameTag, score)) { - scoreBoard.TryGetValue(player.NameTag, out int scoreold); SetScoreBoard(player.NameTag, score + scoreold); } @@ -681,7 +763,8 @@ namespace QwirkleClassLibrary.Games /// /// /// int - public int CalculateAdjacentScore(Cell cell, Board b, ReadOnlyCollection cellsPlayed, int cellsX, int cellsY, ref int nbCellsInLine) + public int CalculateAdjacentScore(Cell cell, Board b, ReadOnlyCollection cellsPlayed, int cellsX, + int cellsY, ref int nbCellsInLine) { int score = 0; @@ -697,7 +780,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 +789,8 @@ namespace QwirkleClassLibrary.Games int dx = adjacentCell.GetX - cell.GetX; int dy = adjacentCell.GetY - cell.GetY; - score += CalculateLineScore(cellsPlayed, cell, new Tuple(dx, dy), b, new Tuple(cellsX, cellsY), ref nbCellsInLine); + score += CalculateLineScore(cellsPlayed, cell, new Tuple(dx, dy), b, + new Tuple(cellsX, cellsY), ref nbCellsInLine); checkedSurroundingCells.Add(adjacentCell); } @@ -723,7 +808,8 @@ namespace QwirkleClassLibrary.Games /// /// /// int - public int CalculateLineScore(ReadOnlyCollection cellsPlayed, Cell cell, Tuple direction, Board b, Tuple orientation, ref int nbCellsInLine) + public int CalculateLineScore(ReadOnlyCollection cellsPlayed, Cell cell, Tuple direction, + Board b, Tuple orientation, ref int nbCellsInLine) { int score = 0; @@ -744,7 +830,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 +857,6 @@ namespace QwirkleClassLibrary.Games playerTilesBagPos.Add(i); } } - } return playerTilesBagPos; @@ -813,7 +899,8 @@ namespace QwirkleClassLibrary.Games { List 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 +925,6 @@ namespace QwirkleClassLibrary.Games public void SetScoreBoard(string name, int score) { - if (!scoreBoard.TryAdd(name, score)) { scoreBoard[name] = score; @@ -849,6 +935,7 @@ namespace QwirkleClassLibrary.Games { observableScoreBoard.Add(item); } + OnPropertyChanged(nameof(ObservableScoreBoard)); } } diff --git a/Qwirkle/QwirkleClassLibrary/Games/IRules.cs b/Qwirkle/QwirkleClassLibrary/Games/IRules.cs index 064fde2..7e5b709 100644 --- a/Qwirkle/QwirkleClassLibrary/Games/IRules.cs +++ b/Qwirkle/QwirkleClassLibrary/Games/IRules.cs @@ -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 cells, Board b, int x, int y); diff --git a/Qwirkle/QwirkleClassLibrary/Tiles/Tile.cs b/Qwirkle/QwirkleClassLibrary/Tiles/Tile.cs index 2a2efb9..de68ec4 100644 --- a/Qwirkle/QwirkleClassLibrary/Tiles/Tile.cs +++ b/Qwirkle/QwirkleClassLibrary/Tiles/Tile.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; namespace QwirkleClassLibrary.Tiles { [DataContract] - public class Tile + public class Tile : IComparable { [DataMember] private readonly Shape shape; @@ -63,5 +63,23 @@ namespace QwirkleClassLibrary.Tiles { return color.ToString() + " " + shape.ToString(); } + + public int CompareTo(object? obj) + { + if (obj == null) return 1; + + var otherTile = obj as Tile; + if (otherTile != null) + { + if (color == otherTile.color) + { + return shape.CompareTo(otherTile.shape); + } + + return color.CompareTo(otherTile.color); + } + + throw new ArgumentException("Object is not a Tile"); + } } } \ No newline at end of file