Skip to content

Commit

Permalink
new methods to create sequences from a specific condition of cells
Browse files Browse the repository at this point in the history
  • Loading branch information
ninetailsrabbit committed Jun 15, 2024
1 parent 3cbc45c commit 2756a71
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 4 deletions.
81 changes: 81 additions & 0 deletions Match3Maker.Tests/tests/src/components/BoardTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -776,9 +776,90 @@ void listener(Sequence sequence) {
Assert.True(receivedEvents[0].Pieces().All(piece => piece.Type.GetType().Equals(typeof(NormalPieceType)) && piece.Type.Shape.Equals("circle")));

board.ConsumedSequence -= listener;
}

[Fact]
public void Should_Be_Able_Create_Sequences_From_Rows_In_Board() {
Piece square = new(_pieceFactory.CreateNormalPiece("square"));
Piece circle = new(_pieceFactory.CreateNormalPiece("circle"));
Piece triangle = new(_pieceFactory.CreateNormalPiece("triangle"));
Piece prism = new(_pieceFactory.CreateNormalPiece("prism"));
Piece special = new(_pieceFactory.CreateSpecialPiece("special"));


List<Piece> pieces = [square, circle, triangle, prism, special];

var board = new Board(8, 7, 10);

board.AddAvailablePieces(pieces).PrepareGridCells().FillInitialBoard(true);

Assert.True(board.CreateSequenceFromRow(1).Cells.All(cell => cell.Row.Equals(1)));
Assert.True(board.CreateSequenceFromRowOfPieceType(1, typeof(NormalPieceType)).Cells.All(cell => cell.Piece.Type is NormalPieceType));
Assert.True(board.CreateSequenceFromRowOfShape(1, "triangle").Cells.All(cell => cell.Piece.Type.Shape.Equals("triangle")));
}

[Fact]
public void Should_Be_Able_Create_Sequences_From_Columns_In_Board() {
Piece square = new(_pieceFactory.CreateNormalPiece("square"));
Piece circle = new(_pieceFactory.CreateNormalPiece("circle"));
Piece triangle = new(_pieceFactory.CreateNormalPiece("triangle"));
Piece prism = new(_pieceFactory.CreateNormalPiece("prism"));
Piece special = new(_pieceFactory.CreateSpecialPiece("special"));


List<Piece> pieces = [square, circle, triangle, prism, special];

var board = new Board(8, 7, 10);

board.AddAvailablePieces(pieces).PrepareGridCells().FillInitialBoard(true);

Assert.True(board.CreateSequenceFromColumn(1).Cells.All(cell => cell.Column.Equals(1)));
Assert.True(board.CreateSequenceFromColumnOfPieceType(1, typeof(NormalPieceType)).Cells.All(cell => cell.Piece.Type is NormalPieceType));
Assert.True(board.CreateSequenceFromColumnOfShape(1, "triangle").Cells.All(cell => cell.Piece.Type.Shape.Equals("triangle")));
}

[Fact]
public void Should_Be_Able_Create_Sequences_Of_Cells_With_Selected_Type() {
Piece square = new(_pieceFactory.CreateNormalPiece("square"));
Piece circle = new(_pieceFactory.CreateNormalPiece("circle"));
Piece triangle = new(_pieceFactory.CreateNormalPiece("triangle"));
Piece prism = new(_pieceFactory.CreateNormalPiece("prism"));
Piece special = new(_pieceFactory.CreateSpecialPiece("special"));


List<Piece> pieces = [square, circle, triangle, prism, special];

var board = new Board(8, 7, 10);

board.AddAvailablePieces(pieces).PrepareGridCells().FillInitialBoard(true);

Assert.True(board.CreateSequenceOfCellsWithPieceType(typeof(NormalPieceType)).Cells.All(cell => cell.Piece.Type is NormalPieceType));
Assert.True(board.CreateSequenceOfCellsWithShape("circle").Cells.All(cell => cell.Piece.Type.Shape.Equals("circle")));
}


[Fact]
public void Should_Be_Able_To_Calculate_The_Pending_Fall_Moves_When_Sequence_Is_Consumed() {
Piece square = new(_pieceFactory.CreateNormalPiece("square"));
Piece circle = new(_pieceFactory.CreateNormalPiece("circle"));
Piece triangle = new(_pieceFactory.CreateNormalPiece("triangle"));
Piece prism = new(_pieceFactory.CreateNormalPiece("prism"));
Piece special = new(_pieceFactory.CreateSpecialPiece("special"));


List<Piece> pieces = [square, circle, triangle, prism, special];

var board = new Board(8, 7, 10);

board.AddAvailablePieces(pieces).PrepareGridCells().FillInitialBoard(true);

Assert.Empty(board.PendingFallMoves());

board.CreateSequenceFromRow(2).Consume();

Assert.Equal(board.GridWidth, board.EmptyCells().Count);
Assert.Equal(board.GridWidth, board.PendingFallMoves().Count);
}
}

}
15 changes: 15 additions & 0 deletions Match3Maker.Tests/tests/src/components/GridCellTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,21 @@ public void Should_Detect_Piece() {
Assert.Equal(piece, cell.Piece);
}

[Fact]
public void Should_Replace_Piece_Only_When_Has_One_Assigned() {
var cell = new GridCell(1, 1);
var piece = new Piece(_pieceFactory.CreateNormalPiece("square"));
var newPiece = new Piece(_pieceFactory.CreateNormalPiece("square"));

Assert.True(cell.IsEmpty());
Assert.Null(cell.ReplacePiece(piece));

cell.AssignPiece(piece);

Assert.Equal(piece, cell.ReplacePiece(newPiece));
Assert.Equal(newPiece, cell.Piece);
}

[Fact]
public void Should_Not_Swap_Piece_When_Conditions_Are_Not_Met() {
var squarePiece = new Piece(_pieceFactory.CreateNormalPiece("square"));
Expand Down
2 changes: 1 addition & 1 deletion Match3Maker/Match3Maker.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

<PropertyGroup>
<Title>Ninetailsrabbit.Match3Maker</Title>
<Version>1.1.3</Version>
<Version>1.1.4</Version>
<Description>This lightweight library provides the core logic and functionality you need to build engaging match-3 games. Focus on game design and mechanics while leaving the complex logic to this library</Description>
<Copyright>© 2024 Ninetailsrabbit</Copyright>
<Authors>Ninetailsrabbit</Authors>
Expand Down
100 changes: 98 additions & 2 deletions Match3Maker/src/components/Board.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,36 @@
using System.Numerics;
using SystemExtensions;
using static Match3Maker.BoardCellUpdate;

namespace Match3Maker {

public sealed class VirtualBoard(List<GridCell> gridCells) {
public List<GridCell> GridCells = gridCells;
public List<BoardCellUpdate> Updates = [];

public void AddUpdate(BoardCellUpdate update) {
Updates.Add(update);
}

}
public sealed class BoardCellUpdate(UPDATE_TYPE currentUpdateType, CellPieceMovement? cellPieceMovement = null, CellPieceFill? cellPieceFill = null) {
public enum UPDATE_TYPE {
MOVEMENT,
FILL
}

public UPDATE_TYPE CurrentUpdateType = currentUpdateType;
public CellPieceMovement? CellPieceMovement = cellPieceMovement;
public CellPieceFill? CellPieceFill = cellPieceFill;
public bool IsMovement() => CurrentUpdateType.Equals(UPDATE_TYPE.MOVEMENT);
public bool IsFill() => CurrentUpdateType.Equals(UPDATE_TYPE.FILL);
}


public record CellPieceMovement(GridCell PreviousCell, GridCell NewCell, Piece Piece);
public record CellPieceFill(GridCell Cell, Piece Piece);


public class Board {
public static readonly int MIN_GRID_WIDTH = 3;
public static readonly int MIN_GRID_HEIGHT = 3;
Expand Down Expand Up @@ -162,6 +190,9 @@ public Board DecreaseMoves(int amount) {
return this;
}

public bool IsLocked() => Locked;
public bool IsFree() => !Locked;

public void Lock() {
Locked = true;
}
Expand Down Expand Up @@ -327,6 +358,14 @@ public List<GridCell> LeftCellsFrom(GridCell cell, int distance) {
public List<GridCell> CellsThatCannotContainPiecesFromRow(int row) => CellsFromRow(row).Where(cell => !cell.CanContainPiece).ToList();
public List<GridCell> CellsThatCanContainPiecesFromColumn(int column) => CellsFromColumn(column).Where(cell => cell.CanContainPiece).ToList();
public List<GridCell> CellsThatCannotContainPiecesFromColumn(int column) => CellsFromColumn(column).Where(cell => !cell.CanContainPiece).ToList();
public Sequence CreateSequenceFromRow(int row) => new(CellsThatCanContainPiecesFromRow(row));
public Sequence CreateSequenceFromColumn(int column) => new(CellsThatCanContainPiecesFromColumn(column));
public Sequence CreateSequenceFromRowOfPieceType(int row, Type type) => new(CellsFromRowOfPieceType(row, type));
public Sequence CreateSequenceFromColumnOfPieceType(int column, Type type) => new(CellsFromColumnOfPieceType(column, type));
public Sequence CreateSequenceOfCellsWithPieceType(Type type) => new(CellsWithPieceType(type));
public Sequence CreateSequenceFromRowOfShape(int row, string shape) => new(CellsFromRowOfShape(row, shape));
public Sequence CreateSequenceFromColumnOfShape(int column, string shape) => new(CellsFromColumnOfShape(column, shape));
public Sequence CreateSequenceOfCellsWithShape(string shape) => new(CellsWithShape(shape));

public List<GridCell> CellsFromColumn(int column) {
List<GridCell> result = [];
Expand Down Expand Up @@ -363,12 +402,32 @@ public List<GridCell> CellsFromColumnOfPieceType(int column, Type type) {
.ToList();
}


public List<GridCell> CellsWithPieceType(Type type) {
return GridCells.SelectMany(cells => cells)
.Where(cell => cell.HasPiece() && cell.Piece.Type.GetType().Equals(type))
.ToList();
}

public List<GridCell> CellsFromRowOfShape(int row, string shape) {
return CellsFromRow(row)
.Where(cell => cell.HasPiece() && cell.Piece.Type.Shape.Equals(shape, StringComparison.OrdinalIgnoreCase))
.ToList();
}

public List<GridCell> CellsFromColumnOfShape(int column, string shape) {
return CellsFromColumn(column)
.Where(cell => cell.HasPiece() && cell.Piece.Type.Shape.Equals(shape, StringComparison.OrdinalIgnoreCase))
.ToList();
}

public List<GridCell> CellsWithShape(string shape) {
return GridCells.SelectMany(cells => cells)
.Where(cell => cell.HasPiece() && cell.Piece.Type.Shape.Equals(shape, StringComparison.OrdinalIgnoreCase))
.ToList();
}


public void UpdateGridCellsNeighbours() {
GridCells.SelectMany(cells => cells).ToList().ForEach(cell => {
cell.NeighbourUp = Cell(cell.Column, cell.Row - 1);
Expand All @@ -381,8 +440,6 @@ public void UpdateGridCellsNeighbours() {
cell.DiagonalNeighbourBottomLeft = Cell(cell.Column - 1, cell.Row + 1);
});
}


public GridCell? FindGridCellWithPiece(Piece piece) => FindGridCellWithPiece(piece.Id);
public GridCell? FindGridCellWithPiece(Guid id) => FindGridCellWithPiece(id.ToString());
public GridCell? FindGridCellWithPiece(string id) {
Expand Down Expand Up @@ -458,6 +515,45 @@ public void RemoveMatchesFromBoard() {
}
}

public VirtualBoard MovePiecesAndFillEmptyCells() {
var gridCellsCopy = GridCells.SelectMany(cells => cells).Select(cell => new GridCell(cell.Column, cell.Row, cell.Piece, cell.CanContainPiece)).ToList();

VirtualBoard virtualBoard = new(gridCellsCopy);

if (GridCells.Count > 0 && IsFree()) {

var pendingMoves = PendingFallMoves(virtualBoard.GridCells);

while (pendingMoves.Count > 0) {
virtualBoard.Updates.Clear();

foreach (var currentCell in pendingMoves) {
GridCell? bottomCell = currentCell.NeighbourBottom;

if (bottomCell is not null) {
bottomCell.AssignPiece(currentCell.Piece);
currentCell.RemovePiece();

virtualBoard.AddUpdate(new BoardCellUpdate(UPDATE_TYPE.MOVEMENT, new(currentCell, bottomCell, bottomCell.Piece)));
}
}

pendingMoves = PendingFallMoves(virtualBoard.GridCells);
}
}

return virtualBoard;
}

public List<GridCell> PendingFallMoves(IEnumerable<GridCell>? cells = null) {
cells ??= GridCells.SelectMany(cells => cells).Select(cell => cell).ToList();

return cells.Where(
cell => cell.HasPiece() &&
cell.Piece.Type.CanBeMoved() &&
cell.NeighbourBottom is GridCell bottomCell && bottomCell.IsEmpty())
.ToList();
}
#endregion

#region Pieces
Expand Down
11 changes: 10 additions & 1 deletion Match3Maker/src/components/GridCell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,23 @@ public GridCell(int column, int row, Piece? piece = null, bool canContainPiece =
public void AssignPiece(Piece piece) {
if (CanContainPiece && IsEmpty())
Piece = piece;

}
public Piece? RemovePiece() {
Piece? previousPiece = Piece;
Piece = null;

return previousPiece;
}

public Piece? ReplacePiece(Piece newPiece) {
Piece previousPiece = Piece;

if (HasPiece())
Piece = newPiece;

return previousPiece;
}

public bool SwapPieceWith(GridCell otherCell) {
if (CanSwapPieceWith(otherCell)) {

Expand Down

0 comments on commit 2756a71

Please sign in to comment.