From bc271afa7e22defc6a15fb71e6270009771d2aaa Mon Sep 17 00:00:00 2001 From: BlazingTwist <39350649+BlazingTwist@users.noreply.github.com> Date: Mon, 29 Jan 2024 00:57:35 +0100 Subject: [PATCH 1/3] create tests for castling --- src/test/java/helper/TestHelper.java | 40 +++++ src/test/java/jchess/RegressionTests.java | 16 +- .../common/moveset/special/CastlingTest.java | 155 ++++++++++++++++++ 3 files changed, 198 insertions(+), 13 deletions(-) create mode 100644 src/test/java/helper/TestHelper.java create mode 100644 src/test/java/jchess/common/moveset/special/CastlingTest.java diff --git a/src/test/java/helper/TestHelper.java b/src/test/java/helper/TestHelper.java new file mode 100644 index 0000000..9cbde9f --- /dev/null +++ b/src/test/java/helper/TestHelper.java @@ -0,0 +1,40 @@ +package helper; + +import dx.schema.types.PieceType; +import jchess.common.IChessGame; +import jchess.common.moveset.MoveIntention; +import jchess.common.moveset.NormalMove; +import jchess.ecs.Entity; + +import java.awt.Point; + +public class TestHelper { + public static Entity getTileAtPosition(IChessGame game, int x, int y) { + return game.getEntityManager().getEntities().stream() + .filter(entity -> entity.tile != null + && entity.tile.position.x == x + && entity.tile.position.y == y) + .findFirst().orElse(null); + } + + public static void movePiece(IChessGame game, Entity from, Entity to) { + NormalMove.getMove(game, from, to).onClick().run(); + } + + public static boolean hasPiece(Entity entity, PieceType pieceType, int owner) { + return entity.piece != null + && entity.piece.identifier.pieceType() == pieceType + && entity.piece.identifier.ownerId() == owner; + } + + public static MoveIntention findMoveToTile(IChessGame game, Entity piece, Point displayTile) { + return piece.findValidMoves(game, false) + .filter(move -> { + Entity displayEntity = move.displayTile(); + if (displayEntity == null || displayEntity.tile == null) return false; + + return displayEntity.tile.position.equals(displayTile); + }) + .findFirst().orElse(null); + } +} diff --git a/src/test/java/jchess/RegressionTests.java b/src/test/java/jchess/RegressionTests.java index 9f76862..a4afb82 100644 --- a/src/test/java/jchess/RegressionTests.java +++ b/src/test/java/jchess/RegressionTests.java @@ -1,7 +1,6 @@ package jchess; import dx.schema.types.PieceType; -import jchess.common.IChessGame; import jchess.common.events.BoardClickedEvent; import jchess.common.moveset.NormalMove; import jchess.ecs.Entity; @@ -15,19 +14,10 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -public class RegressionTests { - private static Entity getTileAtPosition(IChessGame game, int x, int y) { - return game.getEntityManager().getEntities().stream() - .filter(entity -> entity.tile != null - && entity.tile.position.x == x - && entity.tile.position.y == y) - .findFirst().orElse(null); - } - - private static void movePiece(IChessGame game, Entity from, Entity to) { - NormalMove.getMove(game, from, to).onClick().run(); - } +import static helper.TestHelper.getTileAtPosition; +import static helper.TestHelper.movePiece; +public class RegressionTests { /** * Simulates an error-condition caused by incorrect MoveSimulator#revert logic, as described in Issue #27 */ diff --git a/src/test/java/jchess/common/moveset/special/CastlingTest.java b/src/test/java/jchess/common/moveset/special/CastlingTest.java new file mode 100644 index 0000000..c8c551f --- /dev/null +++ b/src/test/java/jchess/common/moveset/special/CastlingTest.java @@ -0,0 +1,155 @@ +package jchess.common.moveset.special; + +import dx.schema.types.PieceType; +import helper.TestHelper; +import jchess.common.components.TileComponent; +import jchess.common.moveset.MoveIntention; +import jchess.ecs.Entity; +import jchess.gamemode.PieceStore; +import jchess.gamemode.square2p.Square2PlayerGame; +import jchess.gamemode.square2p.Square2pPieceLayouts; +import jchess.gamemode.square2p.Square2pPieces; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.awt.Point; + +import static helper.TestHelper.getTileAtPosition; +import static helper.TestHelper.hasPiece; + +@SuppressWarnings("FieldCanBeLocal") +public class CastlingTest { + private Square2PlayerGame game; + private Entity x0y0; + private Entity x1y0; + private Entity x2y0; + private Entity x3y0; + private Entity x4y0; + private Entity x7y7; + private Entity x6y7; + private Entity x5y7; + private Entity x4y7; + + @BeforeEach + public void init() { + game = new Square2PlayerGame(new PieceStore(Square2pPieces.values()), Square2pPieceLayouts.Standard); + game.start(); + + x0y0 = getTileAtPosition(game, 0, 0); + x1y0 = getTileAtPosition(game, 1, 0); + x2y0 = getTileAtPosition(game, 2, 0); + x3y0 = getTileAtPosition(game, 3, 0); + x4y0 = getTileAtPosition(game, 4, 0); + + x7y7 = getTileAtPosition(game, 7, 7); + x6y7 = getTileAtPosition(game, 6, 7); + x5y7 = getTileAtPosition(game, 5, 7); + x4y7 = getTileAtPosition(game, 4, 7); + + Assertions.assertTrue(hasPiece(x4y0, PieceType.KING, 1)); + Assertions.assertTrue(hasPiece(x4y7, PieceType.KING, 0)); + Assertions.assertTrue(hasPiece(x0y0, PieceType.ROOK, 1)); + Assertions.assertTrue(hasPiece(x7y7, PieceType.ROOK, 0)); + } + + @Test + public void test_castlingAllowed() { + x1y0.piece = null; + x2y0.piece = null; + x3y0.piece = null; + x6y7.piece = null; + x5y7.piece = null; + TileComponent.updateAttackInfo(game); + + MoveIntention blackCastleMove = TestHelper.findMoveToTile(game, x4y0, new Point(2, 0)); + MoveIntention whiteCastleMove = TestHelper.findMoveToTile(game, x4y7, new Point(6, 7)); + Assertions.assertNotNull(blackCastleMove); + Assertions.assertNotNull(whiteCastleMove); + + blackCastleMove.onClick().run(); + whiteCastleMove.onClick().run(); + Assertions.assertTrue(hasPiece(x2y0, PieceType.KING, 1)); + Assertions.assertTrue(hasPiece(x6y7, PieceType.KING, 0)); + Assertions.assertTrue(hasPiece(x3y0, PieceType.ROOK, 1)); + Assertions.assertTrue(hasPiece(x5y7, PieceType.ROOK, 0)); + } + + @Test + public void test_castlingBlocked() { + // x1y0.piece = null; + x2y0.piece = null; + x3y0.piece = null; + x6y7.piece = null; + // x5y7.piece = null; + TileComponent.updateAttackInfo(game); + + MoveIntention blackCastleMove = TestHelper.findMoveToTile(game, x4y0, new Point(2, 0)); + MoveIntention whiteCastleMove = TestHelper.findMoveToTile(game, x4y7, new Point(6, 7)); + Assertions.assertNull(blackCastleMove); + Assertions.assertNull(whiteCastleMove); + } + + @Test + public void test_castlingPieceMoved() { + x1y0.piece = null; + x2y0.piece = null; + x3y0.piece = null; + x6y7.piece = null; + x5y7.piece = null; + TileComponent.updateAttackInfo(game); + + // move black king and back + TestHelper.movePiece(game, x4y0, x3y0); + TestHelper.movePiece(game, x3y0, x4y0); + + // move white rook and back + TestHelper.movePiece(game, x7y7, x6y7); + TestHelper.movePiece(game, x6y7, x7y7); + + MoveIntention blackCastleMove = TestHelper.findMoveToTile(game, x4y0, new Point(2, 0)); + MoveIntention whiteCastleMove = TestHelper.findMoveToTile(game, x4y7, new Point(6, 7)); + Assertions.assertNull(blackCastleMove); + Assertions.assertNull(whiteCastleMove); + } + + @Test + public void test_castlingKingChecked() { + x1y0.piece = null; + x2y0.piece = null; + x3y0.piece = null; + x6y7.piece = null; + x5y7.piece = null; + game.createPiece(getTileAtPosition(game, 4, 6), PieceType.ROOK, 1); // create black rook checking white king + game.createPiece(getTileAtPosition(game, 4, 1), PieceType.ROOK, 0); // create white rook checking black king + TileComponent.updateAttackInfo(game); + + Assertions.assertTrue(x4y0.isAttacked()); // black king is attacked + Assertions.assertTrue(x4y7.isAttacked()); // white king is attacked + + MoveIntention blackCastleMove = TestHelper.findMoveToTile(game, x4y0, new Point(2, 0)); + MoveIntention whiteCastleMove = TestHelper.findMoveToTile(game, x4y7, new Point(6, 7)); + Assertions.assertNull(blackCastleMove); + Assertions.assertNull(whiteCastleMove); + } + + @Test + public void test_castlingKingPathChecked() { + x1y0.piece = null; + x2y0.piece = null; + x3y0.piece = null; + x6y7.piece = null; + x5y7.piece = null; + game.createPiece(getTileAtPosition(game, 5, 6), PieceType.ROOK, 1); // create black rook checking white kings path + game.createPiece(getTileAtPosition(game, 2, 1), PieceType.ROOK, 0); // create white rook checking black kings destination + TileComponent.updateAttackInfo(game); + + Assertions.assertTrue(x2y0.isAttacked(1)); // black king destination is attacked + Assertions.assertTrue(x5y7.isAttacked(0)); // white king path is attacked + + MoveIntention blackCastleMove = TestHelper.findMoveToTile(game, x4y0, new Point(2, 0)); + MoveIntention whiteCastleMove = TestHelper.findMoveToTile(game, x4y7, new Point(6, 7)); + Assertions.assertNull(blackCastleMove); + Assertions.assertNull(whiteCastleMove); + } +} From ab17ae8f083d20470cf7b9ce31e7e6b7bec61ab4 Mon Sep 17 00:00:00 2001 From: BlazingTwist <39350649+BlazingTwist@users.noreply.github.com> Date: Mon, 29 Jan 2024 01:25:24 +0100 Subject: [PATCH 2/3] create tests for enPassant --- src/test/java/helper/TestHelper.java | 11 ++ .../common/moveset/special/EnPassantTest.java | 129 ++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 src/test/java/jchess/common/moveset/special/EnPassantTest.java diff --git a/src/test/java/helper/TestHelper.java b/src/test/java/helper/TestHelper.java index 9cbde9f..ffa3a0b 100644 --- a/src/test/java/helper/TestHelper.java +++ b/src/test/java/helper/TestHelper.java @@ -37,4 +37,15 @@ public static MoveIntention findMoveToTile(IChessGame game, Entity piece, Point }) .findFirst().orElse(null); } + + public static MoveIntention findMoveToTile(IChessGame game, Entity piece, Entity displayTile) { + return piece.findValidMoves(game, false) + .filter(move -> { + Entity displayEntity = move.displayTile(); + if (displayEntity == null || displayEntity.tile == null) return false; + + return displayEntity == displayTile; + }) + .findFirst().orElse(null); + } } diff --git a/src/test/java/jchess/common/moveset/special/EnPassantTest.java b/src/test/java/jchess/common/moveset/special/EnPassantTest.java new file mode 100644 index 0000000..ec3d441 --- /dev/null +++ b/src/test/java/jchess/common/moveset/special/EnPassantTest.java @@ -0,0 +1,129 @@ +package jchess.common.moveset.special; + +import dx.schema.types.PieceType; +import helper.TestHelper; +import jchess.common.moveset.MoveIntention; +import jchess.ecs.Entity; +import jchess.gamemode.PieceStore; +import jchess.gamemode.square2p.Square2PlayerGame; +import jchess.gamemode.square2p.Square2pPieceLayouts; +import jchess.gamemode.square2p.Square2pPieces; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static helper.TestHelper.getTileAtPosition; +import static helper.TestHelper.hasPiece; + +@SuppressWarnings("FieldCanBeLocal") +public class EnPassantTest { + private Square2PlayerGame game; + + // enPassant pawn + private Entity x1y4; + + // unrelated pawn / movement + private Entity x0y1; + private Entity x0y2; + + // white pawn movement + private Entity x2y6; + private Entity x2y5; + private Entity x2y4; + + + @BeforeEach + public void init() { + game = new Square2PlayerGame(new PieceStore(Square2pPieces.values()), Square2pPieceLayouts.Standard); + game.start(); + + x1y4 = getTileAtPosition(game, 1, 4); + + x0y1 = getTileAtPosition(game, 0, 1); + x0y2 = getTileAtPosition(game, 0, 2); + + x2y6 = getTileAtPosition(game, 2, 6); + x2y5 = getTileAtPosition(game, 2, 5); + x2y4 = getTileAtPosition(game, 2, 4); + + game.createPiece(x1y4, PieceType.PAWN, 1); + + Assertions.assertTrue(hasPiece(x1y4, PieceType.PAWN, 1)); + Assertions.assertTrue(hasPiece(x0y1, PieceType.PAWN, 1)); + Assertions.assertTrue(hasPiece(x2y6, PieceType.PAWN, 0)); + } + + @Test + public void test_enPassantAllowed() { + MoveIntention whiteDoubleMove = TestHelper.findMoveToTile(game, x2y6, x2y4); + Assertions.assertNotNull(whiteDoubleMove); + whiteDoubleMove.onClick().run(); + + MoveIntention enPassantMove = TestHelper.findMoveToTile(game, x1y4, x2y5); + Assertions.assertNull(x2y5.piece); + Assertions.assertNotNull(enPassantMove); + + enPassantMove.onClick().run(); + Assertions.assertNull(x2y4.piece); + Assertions.assertTrue(hasPiece(x2y5, PieceType.PAWN, 1)); + } + + @Test + public void test_enPassantTooLate() { + MoveIntention whiteDoubleMove = TestHelper.findMoveToTile(game, x2y6, x2y4); + Assertions.assertNotNull(whiteDoubleMove); + whiteDoubleMove.onClick().run(); + + // black misses the enPassant opportunity by moving an unrelated piece + TestHelper.movePiece(game, x0y1, x0y2); + + MoveIntention enPassantMove = TestHelper.findMoveToTile(game, x1y4, x2y5); + Assertions.assertNull(enPassantMove); + } + + @Test + public void test_enPassantAlreadyCaptured() { + MoveIntention whiteDoubleMove = TestHelper.findMoveToTile(game, x2y6, x2y4); + Assertions.assertNotNull(whiteDoubleMove); + whiteDoubleMove.onClick().run(); + + // pretend another player (relevant for 3-Player chess) has already captured the pawn + x2y4.piece = null; + + MoveIntention enPassantMove = TestHelper.findMoveToTile(game, x1y4, x2y5); + Assertions.assertNull(enPassantMove); + } + + @Test + public void test_enPassantBlocked() { + MoveIntention whiteDoubleMove = TestHelper.findMoveToTile(game, x2y6, x2y4); + Assertions.assertNotNull(whiteDoubleMove); + whiteDoubleMove.onClick().run(); + + // suppose the active player is occupying the tile that was skipped over (relevant for SpecialFirstMove that isn't the default pawn behaviour) + game.createPiece(x2y5, PieceType.BISHOP, 1); + + // then enPassant is blocked by a friendly piece + MoveIntention enPassantMove = TestHelper.findMoveToTile(game, x1y4, x2y5); + Assertions.assertNull(enPassantMove); + } + + @Test + public void test_enPassantBlockedCapture() { + MoveIntention whiteDoubleMove = TestHelper.findMoveToTile(game, x2y6, x2y4); + Assertions.assertNotNull(whiteDoubleMove); + whiteDoubleMove.onClick().run(); + + // suppose another opponent moved onto the tile skipped by the white pawn + game.createPiece(x2y5, PieceType.BISHOP, 0); // 0 in this case, because 2 player game + + // then the move should be a normal capture move + MoveIntention enPassantMove = TestHelper.findMoveToTile(game, x1y4, x2y5); + Assertions.assertTrue(hasPiece(x2y5, PieceType.BISHOP, 0)); + Assertions.assertNotNull(enPassantMove); + + enPassantMove.onClick().run(); + Assertions.assertTrue(hasPiece(x2y5, PieceType.PAWN, 1)); + Assertions.assertTrue(hasPiece(x2y4, PieceType.PAWN, 0)); // white pawn should not be captured in this edge-case + } +} From 109a200b8e7b84ea0992290ce7bd483de1890ce3 Mon Sep 17 00:00:00 2001 From: BlazingTwist <39350649+BlazingTwist@users.noreply.github.com> Date: Mon, 29 Jan 2024 01:38:09 +0100 Subject: [PATCH 3/3] create tests for SpecialFirstMove --- src/test/java/helper/TestHelper.java | 4 + .../moveset/special/SpecialFirstMoveTest.java | 79 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 src/test/java/jchess/common/moveset/special/SpecialFirstMoveTest.java diff --git a/src/test/java/helper/TestHelper.java b/src/test/java/helper/TestHelper.java index ffa3a0b..c463abb 100644 --- a/src/test/java/helper/TestHelper.java +++ b/src/test/java/helper/TestHelper.java @@ -27,6 +27,10 @@ public static boolean hasPiece(Entity entity, PieceType pieceType, int owner) { && entity.piece.identifier.ownerId() == owner; } + public static boolean hasNoPiece(Entity entity) { + return entity.piece == null; + } + public static MoveIntention findMoveToTile(IChessGame game, Entity piece, Point displayTile) { return piece.findValidMoves(game, false) .filter(move -> { diff --git a/src/test/java/jchess/common/moveset/special/SpecialFirstMoveTest.java b/src/test/java/jchess/common/moveset/special/SpecialFirstMoveTest.java new file mode 100644 index 0000000..bc13cbd --- /dev/null +++ b/src/test/java/jchess/common/moveset/special/SpecialFirstMoveTest.java @@ -0,0 +1,79 @@ +package jchess.common.moveset.special; + +import dx.schema.types.PieceType; +import jchess.common.moveset.MoveIntention; +import jchess.ecs.Entity; +import jchess.gamemode.PieceStore; +import jchess.gamemode.square2p.Square2PlayerGame; +import jchess.gamemode.square2p.Square2pPieceLayouts; +import jchess.gamemode.square2p.Square2pPieces; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static helper.TestHelper.findMoveToTile; +import static helper.TestHelper.getTileAtPosition; +import static helper.TestHelper.hasNoPiece; +import static helper.TestHelper.hasPiece; + +@SuppressWarnings("FieldCanBeLocal") +public class SpecialFirstMoveTest { + private Square2PlayerGame game; + + private Entity x0y6; + private Entity x0y5; + private Entity x0y4; + private Entity x0y3; + + @BeforeEach + public void init() { + game = new Square2PlayerGame(new PieceStore(Square2pPieces.values()), Square2pPieceLayouts.Standard); + game.start(); + + x0y6 = getTileAtPosition(game, 0, 6); + x0y5 = getTileAtPosition(game, 0, 5); + x0y4 = getTileAtPosition(game, 0, 4); + x0y3 = getTileAtPosition(game, 0, 3); + + Assertions.assertTrue(hasPiece(x0y6, PieceType.PAWN, 0)); + } + + @Test + public void test_specialMoveAllowed() { + MoveIntention doubleMove = findMoveToTile(game, x0y6, x0y4); + Assertions.assertNotNull(doubleMove); + doubleMove.onClick().run(); + + Assertions.assertTrue(hasPiece(x0y4, PieceType.PAWN, 0)); + Assertions.assertTrue(hasNoPiece(x0y5)); + Assertions.assertTrue(hasNoPiece(x0y6)); + } + + @Test + public void test_specialMoveTooLate() { + MoveIntention singleMove = findMoveToTile(game, x0y6, x0y5); + Assertions.assertNotNull(singleMove); + singleMove.onClick().run(); + + MoveIntention doubleMove = findMoveToTile(game, x0y5, x0y3); + MoveIntention singleMove2 = findMoveToTile(game, x0y5, x0y4); + Assertions.assertNull(doubleMove); + Assertions.assertNotNull(singleMove2); + } + + @Test + public void test_specialMoveBlockedNear() { + game.createPiece(x0y5, PieceType.PAWN, 1); + + MoveIntention doubleMove = findMoveToTile(game, x0y6, x0y4); + Assertions.assertNull(doubleMove); + } + + @Test + public void test_specialMoveBlockedFar() { + game.createPiece(x0y4, PieceType.PAWN, 1); + + MoveIntention doubleMove = findMoveToTile(game, x0y6, x0y4); + Assertions.assertNull(doubleMove); + } +}