From eefb07660cf113830d0f1c965aa51c46081c40bd Mon Sep 17 00:00:00 2001 From: martinmarth Date: Sun, 28 Jan 2024 14:06:42 +0100 Subject: [PATCH 01/11] WIP started implementation of Hex2P --- .../java/jchess/gamemode/GameModeStore.java | 10 ++ .../jchess/gamemode/hex2p/Hex2PlayerGame.java | 134 ++++++++++++++++++ .../gamemode/hex2p/Hex2pPieceLayouts.java | 131 +++++++++++++++++ .../jchess/gamemode/hex2p/Hex2pPieces.java | 126 ++++++++++++++++ src/main/jchess-web/bun.lockb | Bin 176536 -> 177782 bytes src/main/jchess-web/package.json | 2 +- .../dx/schema/types/LayoutId.schema.json | 1 + .../jchess/themeV2/theme_default.json | 30 ++++ 8 files changed, 433 insertions(+), 1 deletion(-) create mode 100644 src/main/java/jchess/gamemode/hex2p/Hex2PlayerGame.java create mode 100644 src/main/java/jchess/gamemode/hex2p/Hex2pPieceLayouts.java create mode 100644 src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java diff --git a/src/main/java/jchess/gamemode/GameModeStore.java b/src/main/java/jchess/gamemode/GameModeStore.java index 7bd06ed..5e02936 100644 --- a/src/main/java/jchess/gamemode/GameModeStore.java +++ b/src/main/java/jchess/gamemode/GameModeStore.java @@ -2,6 +2,9 @@ import dx.schema.types.LayoutId; import jchess.common.IChessGame; +import jchess.gamemode.hex2p.Hex2pPieceLayouts; +import jchess.gamemode.hex2p.Hex2pPieces; +import jchess.gamemode.hex2p.Hex2PlayerGame; import jchess.gamemode.hex3p.Hex3PlayerGame; import jchess.gamemode.hex3p.Hex3pPieceLayouts; import jchess.gamemode.hex3p.Hex3pPieces; @@ -25,6 +28,13 @@ public class GameModeStore { )); } + for (Hex2pPieceLayouts pieceLayout : Hex2pPieceLayouts.values()) { + gameModeProviders.put(LayoutId.HEX_2_P.name() + "." + pieceLayout.name(), new GameModeProvider( + Hex2PlayerGame::new, LayoutId.HEX_2_P, "2 Player Hexagonal Chess - " + pieceLayout.name(), 2, + new PieceStore(Hex2pPieces.values()), pieceLayout + )); + } + for (Square2pPieceLayouts pieceLayout : Square2pPieceLayouts.values()) { gameModeProviders.put(LayoutId.SQUARE_2_P.name() + "." + pieceLayout.name(), new GameModeProvider( Square2PlayerGame::new, LayoutId.SQUARE_2_P, "2 Player Classic Chess - " + pieceLayout.name(), 2, diff --git a/src/main/java/jchess/gamemode/hex2p/Hex2PlayerGame.java b/src/main/java/jchess/gamemode/hex2p/Hex2PlayerGame.java new file mode 100644 index 0000000..0de22b1 --- /dev/null +++ b/src/main/java/jchess/gamemode/hex2p/Hex2PlayerGame.java @@ -0,0 +1,134 @@ +package jchess.gamemode.hex2p; + +import dx.schema.types.PieceType; +import dx.schema.types.Vector2I; +import jchess.common.BaseChessGame; +import jchess.common.components.PieceComponent; +import jchess.common.components.PieceIdentifier; +import jchess.common.components.TileComponent; +import jchess.ecs.Entity; +import jchess.gamemode.IPieceLayoutProvider; +import jchess.gamemode.PieceStore; +import jchess.gamemode.PieceStore.IPieceDefinitionProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.*; + +public class Hex2PlayerGame extends BaseChessGame { + + private static final Logger logger = LoggerFactory.getLogger(Hex2PlayerGame.class); + + private static final int numTilesHorizontal = 6 + 5; + + private static final int numTilesVertical = 21; + + private final Entity[][] tiles = new Entity[numTilesVertical][numTilesHorizontal]; + + public Hex2PlayerGame(PieceStore pieceStore, IPieceLayoutProvider layoutProvider) { + super(2, pieceStore, layoutProvider); + } + + @Override + public dx.schema.types.Entity applyPerspective(dx.schema.types.Entity tile, int playerIndex) { + if (playerIndex < 0 || playerIndex > 1) { + throw new IllegalArgumentException("playerIndex must be 0 or 1, but was " + playerIndex); + } + if (playerIndex == 0) return tile; + if (tile == null || tile.getTile() == null || tile.getTile().getDisplayPos() == null) return tile; + + final double strideX = 1.73205; + final double strideY = 3; + final double cosine = -0.5; + final double sine = 0.8660254; + + Vector2I displayPos = tile.getTile().getDisplayPos(); + double scaledX = (displayPos.getX() - 5) * strideX; + double scaledY = (displayPos.getY() - 10) * strideY; + double rotatedX = (scaledX * cosine) - (scaledY * sine); + double rotatedY = (scaledX * sine) + (scaledY * cosine); + displayPos.setX((int) Math.round(rotatedX / strideX) + 5); + displayPos.setY((int) Math.round(rotatedY / strideY) + 10); + return tile; + } + + @Override + protected Entity getEntityAtPosition(int x, int y) { + if (x < 0 || x >= numTilesHorizontal) return null; + if (y < 0 || y >= numTilesVertical) return null; + + return tiles[y][x]; + } + + @Override + public void createPiece(Entity targetTile, PieceType pieceType, int ownerId) { + IPieceDefinitionProvider pieceProvider = pieceStore.getPiece(pieceType); + if (pieceProvider == null) { + logger.error("unable to place piece with pieceType '" + pieceType + "'. PieceType does not exist."); + return; + } + + int direction = ((ownerId - 2) * (-120)) % 360; + placePiece(targetTile, ownerId, direction, pieceProvider); + } + + // Helper method to create TileComponent and set neighbors + private void createTileAndSetNeighbors(Entity entity, int x, int y) { + TileComponent tile = new TileComponent(new Point(x, y), y % 3); + tile.neighborsByDirection.put(0, getEntityAtPosition(x, y - 2)); + tile.neighborsByDirection.put(30, getEntityAtPosition(x + 1, y - 1)); + tile.neighborsByDirection.put(60, getEntityAtPosition(x + 3, y - 1)); + tile.neighborsByDirection.put(90, getEntityAtPosition(x + 2, y)); + tile.neighborsByDirection.put(120, getEntityAtPosition(x + 3, y + 1)); + tile.neighborsByDirection.put(150, getEntityAtPosition(x + 1, y + 1)); + tile.neighborsByDirection.put(180, getEntityAtPosition(x, y + 2)); + tile.neighborsByDirection.put(210, getEntityAtPosition(x - 1, y + 1)); + tile.neighborsByDirection.put(240, getEntityAtPosition(x - 3, y + 1)); + tile.neighborsByDirection.put(270, getEntityAtPosition(x - 2, y)); + tile.neighborsByDirection.put(300, getEntityAtPosition(x - 3, y - 1)); + tile.neighborsByDirection.put(330, getEntityAtPosition(x - 1, y - 1)); + + entity.tile = tile; + } + + @Override + protected void generateBoard() { + + for (int y = 0; y < numTilesVertical; y++) { + Entity[] tileRow = tiles[y]; + int x_start, x_end; + + if (y < 5) { // top part + x_start = 5 - y; + x_end = x_start + 2 * (y + 1); + } else if (y < 16) { // middle part + x_start = (y % 2 == 0) ? 1 : 0; + x_end = numTilesHorizontal; + } else { // bottom part + int tmp_y = y - 15; + x_start = tmp_y; + x_end = tmp_y + (6 - tmp_y) * 2; + } + + for (int x = x_start; x < x_end; x += 2) { + tileRow[x] = entityManager.createEntity(); + createTileAndSetNeighbors(tileRow[x], x, y); + } + } + } + + private void placePiece(Entity tile, int ownerId, int direction, IPieceDefinitionProvider pieceProvider) { + PieceStore.PieceDefinition pieceDefinition = pieceProvider.getPieceDefinition(); + PieceIdentifier pieceIdentifier = new PieceIdentifier( + pieceProvider.getPieceType(), + pieceDefinition.shortName(), + ownerId, + direction + ); + + PieceComponent pieceComp = new PieceComponent(this, pieceIdentifier, pieceDefinition.baseMoves()); + pieceComp.addSpecialMoves(pieceDefinition.specialRules()); + tile.piece = pieceComp; + } + +} diff --git a/src/main/java/jchess/gamemode/hex2p/Hex2pPieceLayouts.java b/src/main/java/jchess/gamemode/hex2p/Hex2pPieceLayouts.java new file mode 100644 index 0000000..b840cfa --- /dev/null +++ b/src/main/java/jchess/gamemode/hex2p/Hex2pPieceLayouts.java @@ -0,0 +1,131 @@ +package jchess.gamemode.hex2p; + +import jchess.common.IChessGame; +import jchess.ecs.Entity; +import jchess.gamemode.IPieceLayoutProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.function.BiFunction; + +public enum Hex2pPieceLayouts implements IPieceLayoutProvider { + Standard((game, tileProvider) -> populateStandardBoard(new BoardController(game, tileProvider))), + Custom((game, tileProvider) -> populateCustomBoard(new BoardController(game, tileProvider))), + ; + + private static final int PLAYER_LIGHT = 0; + private static final int PLAYER_DARK = 1; + + private static final Logger logger = LoggerFactory.getLogger(Hex2pPieceLayouts.class); + + public final IPieceLayoutProvider layoutProvider; + + Hex2pPieceLayouts(IPieceLayoutProvider layoutProvider) { + this.layoutProvider = layoutProvider; + } + + @Override + public void placePieces(IChessGame game, BiFunction tileProvider) { + layoutProvider.placePieces(game, tileProvider); + } + + private static void populateStandardBoard(BoardController boardController) { + // place kings + boardController.placeKing(6, 19, PLAYER_LIGHT); + boardController.placeKing(6, 1, PLAYER_DARK); + + // place queens + //boardController.placeQueen(4, 19, PLAYER_LIGHT); + //boardController.placeQueen(4, 1, PLAYER_DARK); + + + // place bishops + for (int i = 0; i <= 4; i+=2) { + boardController.placeBishop(5, i, PLAYER_DARK); + boardController.placeBishop(5, 20 - i, PLAYER_LIGHT); + } +/* + // place knights + for (int x : new int[]{3, 7}) { + boardController.placeKnight(x,18, PLAYER_LIGHT); + boardController.placeKnight(x,2, PLAYER_DARK); + } + + // place rooks + for (int x : new int[] {2, 8}) { + boardController.placeRook(x,17, PLAYER_LIGHT); + boardController.placeRook(x,3, PLAYER_DARK); + } + + // place pawns + for (int i = 0; i < 5; i++) { + for (int x : new int[] {1+i, 9-i}) { + boardController.placePawn(x, 16 - i, PLAYER_LIGHT); + boardController.placePawn(x, 4 + i, PLAYER_DARK); + } + } + + */ + + } + + private static void populateCustomBoard(BoardController boardController) { + populateStandardBoard(boardController); + } + + private record BoardController(IChessGame game, BiFunction tileProvider) { + + private Entity getEntityAtPosition(int x, int y) { + return tileProvider.apply(x, y); + } + + private void placeRook(int x, int y, int playerColor) { + placePiece(x, y, playerColor, Hex2pPieces.Rook); + } + + private void placeKnight(int x, int y, int playerColor) { + placePiece(x, y, playerColor, Hex2pPieces.Knight); + } + + private void placeBishop(int x, int y, int playerColor) { + placePiece(x, y, playerColor, Hex2pPieces.Bishop); + } + + private void placeQueen(int x, int y, int playerColor) { + placePiece(x, y, playerColor, Hex2pPieces.Queen); + } + + private void placeKing(int x, int y, int playerColor) { + placePiece(x, y, playerColor, Hex2pPieces.King); + } + + private void placePawn(int x, int y, int playerColor) { + placePiece(x, y, playerColor, Hex2pPieces.Pawn); + } + + private void placeArcher(int x, int y, int playerColor) { + placePiece(x, y, playerColor, Hex2pPieces.Archer); + } + + private void placePegasus(int x, int y, int playerColor) { + placePiece(x, y, playerColor, Hex2pPieces.Pegasus); + } + + private void placeSkrull(int x, int y, int playerColor) { + placePiece(x, y, playerColor, Hex2pPieces.Skrull); + } + + private void placeCatapult(int x, int y, int playerColor) { + placePiece(x, y, playerColor, Hex2pPieces.Catapult); + } + + private void placePiece(int x, int y, int playerColor, Hex2pPieces piece) { + Entity tile = getEntityAtPosition(x, y); + if (tile == null) { + logger.error("cannot place piece on tile ({}, {}). No tile found.", x, y); + return; + } + game.createPiece(tile, piece.getPieceType(), playerColor); + } + } +} diff --git a/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java b/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java new file mode 100644 index 0000000..200a9f0 --- /dev/null +++ b/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java @@ -0,0 +1,126 @@ +package jchess.gamemode.hex2p; + +import dx.schema.types.PieceType; +import jchess.common.components.PieceIdentifier; +import jchess.common.moveset.special.*; +import jchess.ecs.Entity; +import jchess.el.CompiledTileExpression; +import jchess.el.v2.ExpressionCompiler; +import jchess.el.v2.TileExpression; +import jchess.gamemode.PieceStore; + +import java.util.function.Predicate; +import java.util.stream.Stream; + +import static jchess.el.v2.TileExpression.*; + +public enum Hex2pPieces implements PieceStore.IPieceDefinitionProvider { + Rook(PieceType.ROOK, new PieceStore.PieceDefinition( + "R", + rotations(regex("0+", false), 6) + )), + Knight(PieceType.KNIGHT, new PieceStore.PieceDefinition( + "N", + rotations(regex("0.330 0.60", true), 6) + )), + Bishop(PieceType.BISHOP, new PieceStore.PieceDefinition( + "B", + regex("0+ 60+ 120+ 180+ 240+ 300+", false) + )), + Queen(PieceType.QUEEN, new PieceStore.PieceDefinition( + "Q", + TileExpression.or(Rook.pieceDefinition.baseMoves(), Bishop.pieceDefinition.baseMoves()) + )), + King(PieceType.KING, new PieceStore.PieceDefinition( + "K", + rotations(regex("0", false), 12), + (game, kingIdentifier) -> new Castling( + game, kingIdentifier, Rook.pieceType, 90, 270, + regex("270.270.270", true), regex("90.90", true) + ) + )), + Pawn(PieceType.PAWN, new PieceStore.PieceDefinition( + "", + TileExpression.or( + TileExpression.filter(neighbor(0), TileExpression.FILTER_EMPTY_TILE), + TileExpression.filter2(neighbor(330, 30), TileExpression.FILTER_CAPTURE) + ), + (game, pawnIdentifier) -> new SpecialFirstMove( + game, pawnIdentifier, + TileExpression.filter(regex("0.0", false), TileExpression.FILTER_EMPTY_TILE) + ), + (game, pawnId) -> new EnPassant(game, pawnId, PieceType.PAWN, new int[]{0}, new int[]{330, 30}), + (game, pawnId) -> { + int owner = pawnId.ownerId(); + return new PawnPromotion( + game, getPromotionTilePredicate(neighbor(330, 30), pawnId), + Stream.of(Rook, Knight, Bishop, Queen).map(type -> getPiece(type, owner)).toArray(dx.schema.message.Piece[]::new) + ); + } + )), Archer(PieceType.ARCHER, new PieceStore.PieceDefinition( + "A", + TileExpression.filter( + rotations(regex("0{1,2}", false), 12), + TileExpression.FILTER_EMPTY_TILE + ), + (game, archerIdentifier) -> new RangedAttack( + game, archerIdentifier, + rotations(regex("(0 30 60){1,2}", true), 6) + ) + )), + + Pegasus(PieceType.PEGASUS, new PieceStore.PieceDefinition( + "PE", + rotations(regex("(30 90){1,3}", true), 6) + )), + Catapult(PieceType.CATAPULT, new PieceStore.PieceDefinition( + "C", + TileExpression.filter(rotations(neighbor(0), 12), TileExpression.FILTER_EMPTY_TILE), + (game, catapultId) -> new RangedAttack( + game, catapultId, + rotations(regex("(30 90){4,6}", true), 6) + ) + )), + Skrull(PieceType.SKRULL, new PieceStore.PieceDefinition( + "S", + Pawn.pieceDefinition.baseMoves(), + (game, skrullId) -> new ShapeShifting(game, skrullId, 1, 2, + PieceType.ROOK, PieceType.KNIGHT, PieceType.BISHOP, PieceType.QUEEN, PieceType.ARCHER, PieceType.CATAPULT, PieceType.PAWN + ) + )); + + private final PieceType pieceType; + private final PieceStore.PieceDefinition pieceDefinition; + + Hex2pPieces(PieceType pieceType, PieceStore.PieceDefinition pieceDefinition) { + this.pieceType = pieceType; + this.pieceDefinition = pieceDefinition; + } + + private static Predicate getPromotionTilePredicate(ExpressionCompiler forwardTiles, PieceIdentifier pawnId) { + CompiledTileExpression forwardExpression = forwardTiles.toV1(pawnId); + return tile -> { + if (tile.tile == null) return false; + + // if both forward tiles exit the board bounds (= empty result) -> pawn can promote + return forwardExpression.findTiles(tile).findAny().isEmpty(); + }; + } + + private static dx.schema.message.Piece getPiece(Hex2pPieces pieceType, int ownerId) { + dx.schema.message.Piece result = new dx.schema.message.Piece(); + result.setPieceTypeId(pieceType.pieceType); + result.setPlayerIdx(ownerId); + return result; + } + + @Override + public PieceType getPieceType() { + return pieceType; + } + + @Override + public PieceStore.PieceDefinition getPieceDefinition() { + return pieceDefinition; + } +} diff --git a/src/main/jchess-web/bun.lockb b/src/main/jchess-web/bun.lockb index f16f516e1ee8883357db67ccae74e17c8bd7d0e3..22d3e10399f3192e3dcb37efe76f687dd87be4bf 100755 GIT binary patch delta 34153 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 ZbYS|R+`@_qEgGDnHd=QxMP1hRe*lS!;4lCH delta 33379 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 Date: Sun, 28 Jan 2024 17:44:03 +0100 Subject: [PATCH 02/11] WIP improved piece movements and completed initial board setup --- .../jchess/gamemode/hex2p/Hex2PlayerGame.java | 40 ++++++++++++++----- .../gamemode/hex2p/Hex2pPieceLayouts.java | 29 +++++++++++--- .../jchess/gamemode/hex2p/Hex2pPieces.java | 20 ++++------ 3 files changed, 59 insertions(+), 30 deletions(-) diff --git a/src/main/java/jchess/gamemode/hex2p/Hex2PlayerGame.java b/src/main/java/jchess/gamemode/hex2p/Hex2PlayerGame.java index 0de22b1..894cbf1 100644 --- a/src/main/java/jchess/gamemode/hex2p/Hex2PlayerGame.java +++ b/src/main/java/jchess/gamemode/hex2p/Hex2PlayerGame.java @@ -76,17 +76,17 @@ public void createPiece(Entity targetTile, PieceType pieceType, int ownerId) { private void createTileAndSetNeighbors(Entity entity, int x, int y) { TileComponent tile = new TileComponent(new Point(x, y), y % 3); tile.neighborsByDirection.put(0, getEntityAtPosition(x, y - 2)); - tile.neighborsByDirection.put(30, getEntityAtPosition(x + 1, y - 1)); - tile.neighborsByDirection.put(60, getEntityAtPosition(x + 3, y - 1)); + tile.neighborsByDirection.put(30, getEntityAtPosition(x + 1, y - 3)); + tile.neighborsByDirection.put(60, getEntityAtPosition(x + 1, y - 1)); tile.neighborsByDirection.put(90, getEntityAtPosition(x + 2, y)); - tile.neighborsByDirection.put(120, getEntityAtPosition(x + 3, y + 1)); - tile.neighborsByDirection.put(150, getEntityAtPosition(x + 1, y + 1)); + tile.neighborsByDirection.put(120, getEntityAtPosition(x + 1, y + 1)); + tile.neighborsByDirection.put(150, getEntityAtPosition(x + 1, y + 3)); tile.neighborsByDirection.put(180, getEntityAtPosition(x, y + 2)); - tile.neighborsByDirection.put(210, getEntityAtPosition(x - 1, y + 1)); - tile.neighborsByDirection.put(240, getEntityAtPosition(x - 3, y + 1)); + tile.neighborsByDirection.put(210, getEntityAtPosition(x - 1, y + 3)); + tile.neighborsByDirection.put(240, getEntityAtPosition(x - 1, y + 1)); tile.neighborsByDirection.put(270, getEntityAtPosition(x - 2, y)); - tile.neighborsByDirection.put(300, getEntityAtPosition(x - 3, y - 1)); - tile.neighborsByDirection.put(330, getEntityAtPosition(x - 1, y - 1)); + tile.neighborsByDirection.put(300, getEntityAtPosition(x - 1, y - 1)); + tile.neighborsByDirection.put(330, getEntityAtPosition(x - 1, y - 3)); entity.tile = tile; } @@ -105,13 +105,31 @@ protected void generateBoard() { x_start = (y % 2 == 0) ? 1 : 0; x_end = numTilesHorizontal; } else { // bottom part - int tmp_y = y - 15; - x_start = tmp_y; - x_end = tmp_y + (6 - tmp_y) * 2; + x_start = y - 15; + x_end = x_start + (6 - x_start) * 2; } for (int x = x_start; x < x_end; x += 2) { tileRow[x] = entityManager.createEntity(); + } + } + + for (int y = 0; y < numTilesVertical; y++) { + Entity[] tileRow = tiles[y]; + int x_start, x_end; + + if (y < 5) { // top part + x_start = 5 - y; + x_end = x_start + 2 * (y + 1); + } else if (y < 16) { // middle part + x_start = (y % 2 == 0) ? 1 : 0; + x_end = numTilesHorizontal; + } else { // bottom part + x_start = y - 15; + x_end = x_start + (6 - x_start) * 2; + } + + for (int x = x_start; x < x_end; x += 2) { createTileAndSetNeighbors(tileRow[x], x, y); } } diff --git a/src/main/java/jchess/gamemode/hex2p/Hex2pPieceLayouts.java b/src/main/java/jchess/gamemode/hex2p/Hex2pPieceLayouts.java index b840cfa..09a347a 100644 --- a/src/main/java/jchess/gamemode/hex2p/Hex2pPieceLayouts.java +++ b/src/main/java/jchess/gamemode/hex2p/Hex2pPieceLayouts.java @@ -35,8 +35,8 @@ private static void populateStandardBoard(BoardController boardController) { boardController.placeKing(6, 1, PLAYER_DARK); // place queens - //boardController.placeQueen(4, 19, PLAYER_LIGHT); - //boardController.placeQueen(4, 1, PLAYER_DARK); + boardController.placeQueen(4, 19, PLAYER_LIGHT); + boardController.placeQueen(4, 1, PLAYER_DARK); // place bishops @@ -44,7 +44,7 @@ private static void populateStandardBoard(BoardController boardController) { boardController.placeBishop(5, i, PLAYER_DARK); boardController.placeBishop(5, 20 - i, PLAYER_LIGHT); } -/* + // place knights for (int x : new int[]{3, 7}) { boardController.placeKnight(x,18, PLAYER_LIGHT); @@ -57,6 +57,7 @@ private static void populateStandardBoard(BoardController boardController) { boardController.placeRook(x,3, PLAYER_DARK); } + // place pawns for (int i = 0; i < 5; i++) { for (int x : new int[] {1+i, 9-i}) { @@ -64,13 +65,29 @@ private static void populateStandardBoard(BoardController boardController) { boardController.placePawn(x, 4 + i, PLAYER_DARK); } } - - */ - } private static void populateCustomBoard(BoardController boardController) { populateStandardBoard(boardController); + + boardController.placeArcher(4,15, PLAYER_LIGHT); + boardController.placeArcher(6,15, PLAYER_LIGHT); + boardController.placeArcher(4,5, PLAYER_DARK); + boardController.placeArcher(6,5, PLAYER_DARK); + + boardController.placePegasus(5,14, PLAYER_LIGHT); + boardController.placePegasus(5,6, PLAYER_DARK); + + boardController.placeSkrull(3,16, PLAYER_LIGHT); + boardController.placeSkrull(7,16, PLAYER_LIGHT); + boardController.placeSkrull(3,4, PLAYER_DARK); + boardController.placeSkrull(7,4, PLAYER_DARK); + + boardController.placeCatapult(4,17, PLAYER_LIGHT); + boardController.placeCatapult(6,17, PLAYER_LIGHT); + boardController.placeCatapult(4,3, PLAYER_DARK); + boardController.placeCatapult(6,3, PLAYER_DARK); + } private record BoardController(IChessGame game, BiFunction tileProvider) { diff --git a/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java b/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java index 200a9f0..539caec 100644 --- a/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java +++ b/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java @@ -21,11 +21,11 @@ public enum Hex2pPieces implements PieceStore.IPieceDefinitionProvider { )), Knight(PieceType.KNIGHT, new PieceStore.PieceDefinition( "N", - rotations(regex("0.330 0.60", true), 6) + rotations(regex("30.0 30.60", true), 6) )), Bishop(PieceType.BISHOP, new PieceStore.PieceDefinition( "B", - regex("0+ 60+ 120+ 180+ 240+ 300+", false) + rotations(regex("30+", false),6) )), Queen(PieceType.QUEEN, new PieceStore.PieceDefinition( "Q", @@ -33,21 +33,13 @@ public enum Hex2pPieces implements PieceStore.IPieceDefinitionProvider { )), King(PieceType.KING, new PieceStore.PieceDefinition( "K", - rotations(regex("0", false), 12), - (game, kingIdentifier) -> new Castling( - game, kingIdentifier, Rook.pieceType, 90, 270, - regex("270.270.270", true), regex("90.90", true) - ) + rotations(regex("0", false), 12) )), Pawn(PieceType.PAWN, new PieceStore.PieceDefinition( "", TileExpression.or( TileExpression.filter(neighbor(0), TileExpression.FILTER_EMPTY_TILE), - TileExpression.filter2(neighbor(330, 30), TileExpression.FILTER_CAPTURE) - ), - (game, pawnIdentifier) -> new SpecialFirstMove( - game, pawnIdentifier, - TileExpression.filter(regex("0.0", false), TileExpression.FILTER_EMPTY_TILE) + TileExpression.filter2(neighbor(300, 60), TileExpression.FILTER_CAPTURE) ), (game, pawnId) -> new EnPassant(game, pawnId, PieceType.PAWN, new int[]{0}, new int[]{330, 30}), (game, pawnId) -> { @@ -57,7 +49,9 @@ game, getPromotionTilePredicate(neighbor(330, 30), pawnId), Stream.of(Rook, Knight, Bishop, Queen).map(type -> getPiece(type, owner)).toArray(dx.schema.message.Piece[]::new) ); } - )), Archer(PieceType.ARCHER, new PieceStore.PieceDefinition( + )), + // TODO Movement of Custom Pieces should be adapted to the 2P hexagonal board + Archer(PieceType.ARCHER, new PieceStore.PieceDefinition( "A", TileExpression.filter( rotations(regex("0{1,2}", false), 12), From fad06012a10478976728d1cc86a44d80a1117c53 Mon Sep 17 00:00:00 2001 From: BlazingTwist <39350649+BlazingTwist@users.noreply.github.com> Date: Sun, 28 Jan 2024 23:24:27 +0100 Subject: [PATCH 03/11] fix Hex2P 'createPiece' and 'applyPerspective' | also deduplicate createPiece --- .../java/jchess/common/BaseChessGame.java | 30 +++++ .../jchess/gamemode/hex2p/Hex2PlayerGame.java | 111 +++++------------- .../gamemode/hex2p/Hex2pPieceLayouts.java | 1 + .../jchess/gamemode/hex3p/Hex3PlayerGame.java | 80 ++++--------- .../gamemode/square2p/Square2PlayerGame.java | 32 +---- 5 files changed, 88 insertions(+), 166 deletions(-) diff --git a/src/main/java/jchess/common/BaseChessGame.java b/src/main/java/jchess/common/BaseChessGame.java index 0f007d7..17cd9c9 100644 --- a/src/main/java/jchess/common/BaseChessGame.java +++ b/src/main/java/jchess/common/BaseChessGame.java @@ -3,6 +3,8 @@ import dx.schema.types.MarkerType; import dx.schema.types.PieceType; import jchess.common.components.MarkerComponent; +import jchess.common.components.PieceComponent; +import jchess.common.components.PieceIdentifier; import jchess.common.components.TileComponent; import jchess.common.events.BoardClickedEvent; import jchess.common.events.BoardInitializedEvent; @@ -61,6 +63,8 @@ public BaseChessGame(int numPlayers, PieceStore pieceStore, IPieceLayoutProvider protected abstract Entity getEntityAtPosition(int x, int y); + protected abstract int getDirectionFromOwnerId(int ownerId); + protected void onBoardClicked(int x, int y) { Entity clickedEntity = getEntityAtPosition(x, y); if (clickedEntity == null) { @@ -200,6 +204,20 @@ protected MateResult computeMateResult() { } } + protected void placePiece(Entity tile, int ownerId, int direction, PieceStore.IPieceDefinitionProvider pieceProvider) { + PieceStore.PieceDefinition pieceDefinition = pieceProvider.getPieceDefinition(); + PieceIdentifier pieceIdentifier = new PieceIdentifier( + pieceProvider.getPieceType(), + pieceDefinition.shortName(), + ownerId, + direction + ); + + PieceComponent pieceComp = new PieceComponent(this, pieceIdentifier, pieceDefinition.baseMoves()); + pieceComp.addSpecialMoves(pieceDefinition.specialRules()); + tile.piece = pieceComp; + } + @Override public void start() { generateBoard(); @@ -224,6 +242,18 @@ public int getActivePlayerId() { return activePlayerId; } + @Override + public void createPiece(Entity targetTile, PieceType pieceType, int ownerId) { + PieceStore.IPieceDefinitionProvider pieceProvider = pieceStore.getPiece(pieceType); + if (pieceProvider == null) { + logger.error("unable to place piece with pieceType '" + pieceType + "'. PieceType does not exist."); + return; + } + + int direction = getDirectionFromOwnerId(ownerId); + placePiece(targetTile, ownerId, direction, pieceProvider); + } + @Override public void movePiece(Entity fromTile, Entity toTile, Class moveType) { toTile.piece = fromTile.piece; diff --git a/src/main/java/jchess/gamemode/hex2p/Hex2PlayerGame.java b/src/main/java/jchess/gamemode/hex2p/Hex2PlayerGame.java index 894cbf1..d8adad8 100644 --- a/src/main/java/jchess/gamemode/hex2p/Hex2PlayerGame.java +++ b/src/main/java/jchess/gamemode/hex2p/Hex2PlayerGame.java @@ -1,24 +1,18 @@ package jchess.gamemode.hex2p; -import dx.schema.types.PieceType; import dx.schema.types.Vector2I; import jchess.common.BaseChessGame; -import jchess.common.components.PieceComponent; -import jchess.common.components.PieceIdentifier; import jchess.common.components.TileComponent; import jchess.ecs.Entity; import jchess.gamemode.IPieceLayoutProvider; import jchess.gamemode.PieceStore; -import jchess.gamemode.PieceStore.IPieceDefinitionProvider; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.awt.*; +import java.awt.Point; +import java.util.Arrays; +import java.util.Objects; public class Hex2PlayerGame extends BaseChessGame { - private static final Logger logger = LoggerFactory.getLogger(Hex2PlayerGame.class); - private static final int numTilesHorizontal = 6 + 5; private static final int numTilesVertical = 21; @@ -37,18 +31,9 @@ public dx.schema.types.Entity applyPerspective(dx.schema.types.Entity tile, int if (playerIndex == 0) return tile; if (tile == null || tile.getTile() == null || tile.getTile().getDisplayPos() == null) return tile; - final double strideX = 1.73205; - final double strideY = 3; - final double cosine = -0.5; - final double sine = 0.8660254; - Vector2I displayPos = tile.getTile().getDisplayPos(); - double scaledX = (displayPos.getX() - 5) * strideX; - double scaledY = (displayPos.getY() - 10) * strideY; - double rotatedX = (scaledX * cosine) - (scaledY * sine); - double rotatedY = (scaledX * sine) + (scaledY * cosine); - displayPos.setX((int) Math.round(rotatedX / strideX) + 5); - displayPos.setY((int) Math.round(rotatedY / strideY) + 10); + displayPos.setX(numTilesHorizontal - displayPos.getX()); + displayPos.setY(numTilesVertical - displayPos.getY()); return tile; } @@ -61,39 +46,12 @@ protected Entity getEntityAtPosition(int x, int y) { } @Override - public void createPiece(Entity targetTile, PieceType pieceType, int ownerId) { - IPieceDefinitionProvider pieceProvider = pieceStore.getPiece(pieceType); - if (pieceProvider == null) { - logger.error("unable to place piece with pieceType '" + pieceType + "'. PieceType does not exist."); - return; - } - - int direction = ((ownerId - 2) * (-120)) % 360; - placePiece(targetTile, ownerId, direction, pieceProvider); - } - - // Helper method to create TileComponent and set neighbors - private void createTileAndSetNeighbors(Entity entity, int x, int y) { - TileComponent tile = new TileComponent(new Point(x, y), y % 3); - tile.neighborsByDirection.put(0, getEntityAtPosition(x, y - 2)); - tile.neighborsByDirection.put(30, getEntityAtPosition(x + 1, y - 3)); - tile.neighborsByDirection.put(60, getEntityAtPosition(x + 1, y - 1)); - tile.neighborsByDirection.put(90, getEntityAtPosition(x + 2, y)); - tile.neighborsByDirection.put(120, getEntityAtPosition(x + 1, y + 1)); - tile.neighborsByDirection.put(150, getEntityAtPosition(x + 1, y + 3)); - tile.neighborsByDirection.put(180, getEntityAtPosition(x, y + 2)); - tile.neighborsByDirection.put(210, getEntityAtPosition(x - 1, y + 3)); - tile.neighborsByDirection.put(240, getEntityAtPosition(x - 1, y + 1)); - tile.neighborsByDirection.put(270, getEntityAtPosition(x - 2, y)); - tile.neighborsByDirection.put(300, getEntityAtPosition(x - 1, y - 1)); - tile.neighborsByDirection.put(330, getEntityAtPosition(x - 1, y - 3)); - - entity.tile = tile; + protected int getDirectionFromOwnerId(int ownerId) { + return ownerId == 0 ? 0 : 180; } @Override protected void generateBoard() { - for (int y = 0; y < numTilesVertical; y++) { Entity[] tileRow = tiles[y]; int x_start, x_end; @@ -111,42 +69,31 @@ protected void generateBoard() { for (int x = x_start; x < x_end; x += 2) { tileRow[x] = entityManager.createEntity(); + tileRow[x].tile = new TileComponent(new Point(x, y), y % 3); } } - for (int y = 0; y < numTilesVertical; y++) { - Entity[] tileRow = tiles[y]; - int x_start, x_end; - - if (y < 5) { // top part - x_start = 5 - y; - x_end = x_start + 2 * (y + 1); - } else if (y < 16) { // middle part - x_start = (y % 2 == 0) ? 1 : 0; - x_end = numTilesHorizontal; - } else { // bottom part - x_start = y - 15; - x_end = x_start + (6 - x_start) * 2; - } - - for (int x = x_start; x < x_end; x += 2) { - createTileAndSetNeighbors(tileRow[x], x, y); - } - } - } - - private void placePiece(Entity tile, int ownerId, int direction, IPieceDefinitionProvider pieceProvider) { - PieceStore.PieceDefinition pieceDefinition = pieceProvider.getPieceDefinition(); - PieceIdentifier pieceIdentifier = new PieceIdentifier( - pieceProvider.getPieceType(), - pieceDefinition.shortName(), - ownerId, - direction - ); - - PieceComponent pieceComp = new PieceComponent(this, pieceIdentifier, pieceDefinition.baseMoves()); - pieceComp.addSpecialMoves(pieceDefinition.specialRules()); - tile.piece = pieceComp; + // second pass: generate neighbors + Arrays.stream(tiles).flatMap(Arrays::stream) + .filter(Objects::nonNull) + .forEach(entity -> { + TileComponent tile = entity.tile; + assert tile != null; + int x = tile.position.x; + int y = tile.position.y; + tile.neighborsByDirection.put(0, getEntityAtPosition(x, y - 2)); + tile.neighborsByDirection.put(30, getEntityAtPosition(x + 1, y - 3)); + tile.neighborsByDirection.put(60, getEntityAtPosition(x + 1, y - 1)); + tile.neighborsByDirection.put(90, getEntityAtPosition(x + 2, y)); + tile.neighborsByDirection.put(120, getEntityAtPosition(x + 1, y + 1)); + tile.neighborsByDirection.put(150, getEntityAtPosition(x + 1, y + 3)); + tile.neighborsByDirection.put(180, getEntityAtPosition(x, y + 2)); + tile.neighborsByDirection.put(210, getEntityAtPosition(x - 1, y + 3)); + tile.neighborsByDirection.put(240, getEntityAtPosition(x - 1, y + 1)); + tile.neighborsByDirection.put(270, getEntityAtPosition(x - 2, y)); + tile.neighborsByDirection.put(300, getEntityAtPosition(x - 1, y - 1)); + tile.neighborsByDirection.put(330, getEntityAtPosition(x - 1, y - 3)); + }); } } diff --git a/src/main/java/jchess/gamemode/hex2p/Hex2pPieceLayouts.java b/src/main/java/jchess/gamemode/hex2p/Hex2pPieceLayouts.java index 09a347a..4e9abe2 100644 --- a/src/main/java/jchess/gamemode/hex2p/Hex2pPieceLayouts.java +++ b/src/main/java/jchess/gamemode/hex2p/Hex2pPieceLayouts.java @@ -90,6 +90,7 @@ private static void populateCustomBoard(BoardController boardController) { } + @SuppressWarnings("SameParameterValue") private record BoardController(IChessGame game, BiFunction tileProvider) { private Entity getEntityAtPosition(int x, int y) { diff --git a/src/main/java/jchess/gamemode/hex3p/Hex3PlayerGame.java b/src/main/java/jchess/gamemode/hex3p/Hex3PlayerGame.java index 3a2e7cc..47c32c4 100644 --- a/src/main/java/jchess/gamemode/hex3p/Hex3PlayerGame.java +++ b/src/main/java/jchess/gamemode/hex3p/Hex3PlayerGame.java @@ -1,22 +1,17 @@ package jchess.gamemode.hex3p; -import dx.schema.types.PieceType; import dx.schema.types.Vector2I; import jchess.common.BaseChessGame; -import jchess.common.components.PieceComponent; -import jchess.common.components.PieceIdentifier; import jchess.common.components.TileComponent; import jchess.ecs.Entity; import jchess.gamemode.IPieceLayoutProvider; import jchess.gamemode.PieceStore; -import jchess.gamemode.PieceStore.IPieceDefinitionProvider; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.awt.Point; +import java.util.Arrays; +import java.util.Objects; public class Hex3PlayerGame extends BaseChessGame { - private static final Logger logger = LoggerFactory.getLogger(Hex3PlayerGame.class); private static final int numTilesHorizontal = 17 + 16; private static final int numTilesVertical = 17; @@ -58,15 +53,8 @@ protected Entity getEntityAtPosition(int x, int y) { } @Override - public void createPiece(Entity targetTile, PieceType pieceType, int ownerId) { - IPieceDefinitionProvider pieceProvider = pieceStore.getPiece(pieceType); - if (pieceProvider == null) { - logger.error("unable to place piece with pieceType '" + pieceType + "'. PieceType does not exist."); - return; - } - - int direction = ((ownerId - 3) * (-120)) % 360; // [0, 240, 120] - placePiece(targetTile, ownerId, direction, pieceProvider); + protected int getDirectionFromOwnerId(int ownerId) { + return ((ownerId - 3) * (-120)) % 360; // [0, 240, 120] } @Override @@ -78,46 +66,30 @@ protected void generateBoard() { int x1 = 32 - x0; for (int x = x0; x <= x1; x += 2) { tileRow[x] = entityManager.createEntity(); + tileRow[x].tile = new TileComponent(new Point(x, y), x % 3); } } - // second pass: fill entities with components - for (int y = 0; y < numTilesVertical; y++) { - Entity[] tileRow = tiles[y]; - int x0 = Math.abs(8 - y); - int x1 = 32 - x0; - for (int x = x0; x <= x1; x += 2) { - TileComponent tile = new TileComponent(new Point(x, y), x % 3); - - tile.neighborsByDirection.put(0, getEntityAtPosition(x, y - 2)); - tile.neighborsByDirection.put(30, getEntityAtPosition(x + 1, y - 1)); - tile.neighborsByDirection.put(60, getEntityAtPosition(x + 3, y - 1)); - tile.neighborsByDirection.put(90, getEntityAtPosition(x + 2, y)); - tile.neighborsByDirection.put(120, getEntityAtPosition(x + 3, y + 1)); - tile.neighborsByDirection.put(150, getEntityAtPosition(x + 1, y + 1)); - tile.neighborsByDirection.put(180, getEntityAtPosition(x, y + 2)); - tile.neighborsByDirection.put(210, getEntityAtPosition(x - 1, y + 1)); - tile.neighborsByDirection.put(240, getEntityAtPosition(x - 3, y + 1)); - tile.neighborsByDirection.put(270, getEntityAtPosition(x - 2, y)); - tile.neighborsByDirection.put(300, getEntityAtPosition(x - 3, y - 1)); - tile.neighborsByDirection.put(330, getEntityAtPosition(x - 1, y - 1)); - - tileRow[x].tile = tile; - } - } - } - - private void placePiece(Entity tile, int ownerId, int direction, IPieceDefinitionProvider pieceProvider) { - PieceStore.PieceDefinition pieceDefinition = pieceProvider.getPieceDefinition(); - PieceIdentifier pieceIdentifier = new PieceIdentifier( - pieceProvider.getPieceType(), - pieceDefinition.shortName(), - ownerId, - direction - ); - - PieceComponent pieceComp = new PieceComponent(this, pieceIdentifier, pieceDefinition.baseMoves()); - pieceComp.addSpecialMoves(pieceDefinition.specialRules()); - tile.piece = pieceComp; + // second pass: generate neighbors + Arrays.stream(tiles).flatMap(Arrays::stream) + .filter(Objects::nonNull) + .forEach(entity -> { + TileComponent tile = entity.tile; + assert tile != null; + int x = tile.position.x; + int y = tile.position.y; + tile.neighborsByDirection.put(0, getEntityAtPosition(x, y - 2)); + tile.neighborsByDirection.put(30, getEntityAtPosition(x + 1, y - 1)); + tile.neighborsByDirection.put(60, getEntityAtPosition(x + 3, y - 1)); + tile.neighborsByDirection.put(90, getEntityAtPosition(x + 2, y)); + tile.neighborsByDirection.put(120, getEntityAtPosition(x + 3, y + 1)); + tile.neighborsByDirection.put(150, getEntityAtPosition(x + 1, y + 1)); + tile.neighborsByDirection.put(180, getEntityAtPosition(x, y + 2)); + tile.neighborsByDirection.put(210, getEntityAtPosition(x - 1, y + 1)); + tile.neighborsByDirection.put(240, getEntityAtPosition(x - 3, y + 1)); + tile.neighborsByDirection.put(270, getEntityAtPosition(x - 2, y)); + tile.neighborsByDirection.put(300, getEntityAtPosition(x - 3, y - 1)); + tile.neighborsByDirection.put(330, getEntityAtPosition(x - 1, y - 1)); + }); } } diff --git a/src/main/java/jchess/gamemode/square2p/Square2PlayerGame.java b/src/main/java/jchess/gamemode/square2p/Square2PlayerGame.java index 73849c3..dce1faa 100644 --- a/src/main/java/jchess/gamemode/square2p/Square2PlayerGame.java +++ b/src/main/java/jchess/gamemode/square2p/Square2PlayerGame.java @@ -2,19 +2,14 @@ import dx.schema.types.Vector2I; import jchess.common.BaseChessGame; -import jchess.common.components.PieceComponent; -import jchess.common.components.PieceIdentifier; import jchess.common.components.TileComponent; import jchess.ecs.Entity; import jchess.gamemode.IPieceLayoutProvider; import jchess.gamemode.PieceStore; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.awt.Point; public class Square2PlayerGame extends BaseChessGame { - private static final Logger logger = LoggerFactory.getLogger(Square2PlayerGame.class); private static final int numTiles = 8; private final Entity[][] tiles = new Entity[numTiles][numTiles]; @@ -47,18 +42,8 @@ protected Entity getEntityAtPosition(int x, int y) { } @Override - public void createPiece(Entity targetTile, dx.schema.types.PieceType pieceType, int ownerId) { - for (Square2pPieces piece : Square2pPieces.values()) { - if (piece.getPieceType() == pieceType) { - placePiece( - targetTile, ownerId, - ownerId == 0 ? 0 : 180, - piece - ); - return; - } - } - logger.error("unable to place piece with pieceType '" + pieceType + "'. PieceType does not exist."); + protected int getDirectionFromOwnerId(int ownerId) { + return ownerId == 0 ? 0 : 180; } @Override @@ -90,17 +75,4 @@ protected void generateBoard() { } } - private void placePiece(Entity tile, int ownerId, int direction, Square2pPieces pieceType) { - PieceIdentifier pieceIdentifier = new PieceIdentifier( - pieceType.getPieceType(), - pieceType.getPieceDefinition().shortName(), - ownerId, - direction - ); - - PieceComponent piece = new PieceComponent(this, pieceIdentifier, pieceType.getPieceDefinition().baseMoves()); - piece.addSpecialMoves(pieceType.getPieceDefinition().specialRules()); - tile.piece = piece; - } - } From f89367cd1261d6ac3d60e9fd1c746e0609dc696c Mon Sep 17 00:00:00 2001 From: BlazingTwist <39350649+BlazingTwist@users.noreply.github.com> Date: Sun, 28 Jan 2024 23:32:57 +0100 Subject: [PATCH 04/11] add hex2p tile markers --- .../default/board_hex2/hexMarker_noAction.png | Bin 0 -> 229 bytes .../default/board_hex2/hexMarker_selected.png | Bin 0 -> 224 bytes .../default/board_hex2/hexMarker_yesAction.png | Bin 0 -> 1459 bytes .../resources/jchess/themeV2/theme_default.json | 6 +++--- 4 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 src/main/resources/jchess/themeV2/default/board_hex2/hexMarker_noAction.png create mode 100644 src/main/resources/jchess/themeV2/default/board_hex2/hexMarker_selected.png create mode 100644 src/main/resources/jchess/themeV2/default/board_hex2/hexMarker_yesAction.png diff --git a/src/main/resources/jchess/themeV2/default/board_hex2/hexMarker_noAction.png b/src/main/resources/jchess/themeV2/default/board_hex2/hexMarker_noAction.png new file mode 100644 index 0000000000000000000000000000000000000000..f41e71d6a6c611c9af9c66ed8662d1c64b20efd6 GIT binary patch literal 229 zcmeAS@N?(olHy`uVBq!ia0vp^3P3E!!3HFKxnwkf6kC$Fy9>izhGK?d@wwk5fFhg) z9+AZi40_5S%viD1z6>bHUgGKN%Km~;Mod=VwM<|VP^ie$#WBR=cyfvY!yElqCI$fv ze6QjUuAV<}+BD(E9BOThY&~1P*)9x_n4{>Bd;c+a>p^j*EuU=H1tct4{`7{7Va1|` zxP9*Ytp^2}zI?MS(%`A#;FizhGK?d@wwk5fFhg) z9+AZi40_5S%viD1z6>bHUgGKN%Km~;MogCFIImQH z{_OVp$ZZ=_n0#~o^M2`M+ThT>`!>I#MgN%x@slL3>7?xL*C-H*c>AxHNxo6%yT3?) zX#mHTyX2J&%*f*)2r8oZz$(|As?W>AnW+74inLA<5v-W{>RQZt^N{Q R<^i;r!PC{xWt~$(695lCOSk|4 literal 0 HcmV?d00001 diff --git a/src/main/resources/jchess/themeV2/default/board_hex2/hexMarker_yesAction.png b/src/main/resources/jchess/themeV2/default/board_hex2/hexMarker_yesAction.png new file mode 100644 index 0000000000000000000000000000000000000000..3db09b9c8fc00503da091ed2759a43e911ba96a2 GIT binary patch literal 1459 zcmV;k1x)&hP)q5aW(;h)sZ zz4*ELoX4xrIrn)Uub3aWT+Z(~=XZYJ`<(ln=bYaO^!NAkG%+dO0gN^ig2y`(g2y|- zIP^IkcoygcT7k!-*}0#B1Hg;Ga$pIN3?u=QEp_^Vhd>MP7jOyq9q9HgC?DqGxj+sg zG%1Gm-H7N4pbGg>Ji|-D&jIfOZ{ppvp$;eo{_x13CxRyd`2(M+jNrCzEAtwjctbqj! zhU@Dg@EP!@n}FR2o&$UiyfEZSeLd{o4`gOHUK?dsRScd@vQMeuasDGAN zKLm0W%b`QCV~2^4*jTXw%FAK0^}U5=;C)2A7AEuMJm4+GQeF z0&knRF%kS8^13KAG(dhn^!1ta=Z?}+IDFV{U5mW*XrU|kWgtVbY~K#8t-AeqM?nGH zxns3V1vcn5*A@JZQmU+k3m0_z@RrU_d1|Xunc&&82ZzyEV9p%7#kJt}fsYUhL1w0L zT)we#rQPBVi zuqLW!%z$&}Z7;13C^K!=0ZgUBqtlXGN7P9HttkffkMD&V?35*+7zG!IlnQ;?c0(}yL#2s-|!?RiqQ4z z?N@q$kC8iUL@Y-Wx`9uDM#Y+z1~oO3Dot_RX&IS705)xcy1Hm$dy#~mu~=}I5FbAK z-`)--C2;a2+`6UjF&u_ft7HhLr$?*Nh79AFPXV|+@{j<0fIXvV<;D%!>AZYdQY6jI za>gSZmgQX}0*e-j(5$SH9)JzV$hzw$V6RT`ypAN>p7+9;jUM0}GDAFa>&(kzOF`s( z)&}I1fLopb<;eVh-vfVuKL^vE0p0*Ik&SWF9fL;T7ewqo-iGFnU@IXcH?;&gl{XXF z37vtw9kmR6T7lchnGMfHTJc-(nB%ECK0G0KyfYzqyfYzqyz|rv{uipLPW{I-#i{@R N002ovPDHLkV1jn7wafqj literal 0 HcmV?d00001 diff --git a/src/main/resources/jchess/themeV2/theme_default.json b/src/main/resources/jchess/themeV2/theme_default.json index 504cd4a..e19c2e0 100644 --- a/src/main/resources/jchess/themeV2/theme_default.json +++ b/src/main/resources/jchess/themeV2/theme_default.json @@ -50,15 +50,15 @@ "markers": [ { "markerType": "yesAction", - "icon": "./default/board_hex3/hexMarker_yesAction.png" + "icon": "./default/board_hex2/hexMarker_yesAction.png" }, { "markerType": "noAction", - "icon": "./default/board_hex3/hexMarker_noAction.png" + "icon": "./default/board_hex2/hexMarker_noAction.png" }, { "markerType": "selected", - "icon": "./default/board_hex3/hexMarker_selected.png" + "icon": "./default/board_hex2/hexMarker_selected.png" } ] }, From 77dae49818dad68ba6cd9031122e0c219d22e90e Mon Sep 17 00:00:00 2001 From: BlazingTwist <39350649+BlazingTwist@users.noreply.github.com> Date: Sun, 28 Jan 2024 23:40:55 +0100 Subject: [PATCH 05/11] update special piece logic --- src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java b/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java index 539caec..0722155 100644 --- a/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java +++ b/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java @@ -50,29 +50,28 @@ game, getPromotionTilePredicate(neighbor(330, 30), pawnId), ); } )), - // TODO Movement of Custom Pieces should be adapted to the 2P hexagonal board Archer(PieceType.ARCHER, new PieceStore.PieceDefinition( "A", TileExpression.filter( - rotations(regex("0{1,2}", false), 12), + rotations(regex("0{1,2}", false), 6), TileExpression.FILTER_EMPTY_TILE ), (game, archerIdentifier) -> new RangedAttack( game, archerIdentifier, - rotations(regex("(0 30 60){1,2}", true), 6) + rotations(regex("(30 60 90){1,2}", true), 6) ) )), Pegasus(PieceType.PEGASUS, new PieceStore.PieceDefinition( "PE", - rotations(regex("(30 90){1,3}", true), 6) + rotations(regex("(0 60){1,3}", true), 6) )), Catapult(PieceType.CATAPULT, new PieceStore.PieceDefinition( "C", - TileExpression.filter(rotations(neighbor(0), 12), TileExpression.FILTER_EMPTY_TILE), + TileExpression.filter(rotations(neighbor(0), 6), TileExpression.FILTER_EMPTY_TILE), (game, catapultId) -> new RangedAttack( game, catapultId, - rotations(regex("(30 90){4,6}", true), 6) + rotations(regex("(0 60){3,4}", true), 6) ) )), Skrull(PieceType.SKRULL, new PieceStore.PieceDefinition( From ed68fd33b05a856a0219a6b7f74fef3da0b68536 Mon Sep 17 00:00:00 2001 From: BlazingTwist <39350649+BlazingTwist@users.noreply.github.com> Date: Sun, 28 Jan 2024 23:54:30 +0100 Subject: [PATCH 06/11] register matlak and hunter themes for hex2p --- .../jchess/gamemode/hex2p/Hex2pPieces.java | 2 +- .../hunter/board_hex2/hexMarker_noAction.png | Bin 0 -> 407 bytes .../hunter/board_hex2/hexMarker_selected.png | Bin 0 -> 1644 bytes .../hunter/board_hex2/hexMarker_yesAction.png | Bin 0 -> 2337 bytes .../matlak/board_hex2/hexMarker_noAction.png | Bin 0 -> 403 bytes .../matlak/board_hex2/hexMarker_selected.png | Bin 0 -> 407 bytes .../matlak/board_hex2/hexMarker_yesAction.png | Bin 0 -> 408 bytes .../jchess/themeV2/theme_hunter.json | 30 ++++++++++++++++++ .../jchess/themeV2/theme_matlak.json | 30 ++++++++++++++++++ 9 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/jchess/themeV2/hunter/board_hex2/hexMarker_noAction.png create mode 100644 src/main/resources/jchess/themeV2/hunter/board_hex2/hexMarker_selected.png create mode 100644 src/main/resources/jchess/themeV2/hunter/board_hex2/hexMarker_yesAction.png create mode 100644 src/main/resources/jchess/themeV2/matlak/board_hex2/hexMarker_noAction.png create mode 100644 src/main/resources/jchess/themeV2/matlak/board_hex2/hexMarker_selected.png create mode 100644 src/main/resources/jchess/themeV2/matlak/board_hex2/hexMarker_yesAction.png diff --git a/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java b/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java index 0722155..5eb586d 100644 --- a/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java +++ b/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java @@ -64,7 +64,7 @@ game, getPromotionTilePredicate(neighbor(330, 30), pawnId), Pegasus(PieceType.PEGASUS, new PieceStore.PieceDefinition( "PE", - rotations(regex("(0 60){1,3}", true), 6) + rotations(regex("(0 60){3}", true), 6) )), Catapult(PieceType.CATAPULT, new PieceStore.PieceDefinition( "C", diff --git a/src/main/resources/jchess/themeV2/hunter/board_hex2/hexMarker_noAction.png b/src/main/resources/jchess/themeV2/hunter/board_hex2/hexMarker_noAction.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec62e3b8e2e5e3104bed63816c7d5cef371e525 GIT binary patch literal 407 zcmV;I0cie-P)61)oCt5)&?|oKc|ow6l3(8OCg4hSJjYCm7IaAs_PM> zo{mrH>^`9&oz_0v3-SZ^3kt%ILx6|=w|mDMc!+l72!xKs!&Fx$C7mh6nxa>wo)ls| z=Y5b*MLpfeqqrl5h5OIHVZ6Z_i3pneJ|`UosG?**WAw=teQ^*OE=7XAbSJcE$ZXC; zfB}W9`_j9hC52+HRRIkt6y0yV4X3Vc@i@(r{0Bl)jTh<}wblRt002ovPDHLkV1g^m Bq#XbN literal 0 HcmV?d00001 diff --git a/src/main/resources/jchess/themeV2/hunter/board_hex2/hexMarker_selected.png b/src/main/resources/jchess/themeV2/hunter/board_hex2/hexMarker_selected.png new file mode 100644 index 0000000000000000000000000000000000000000..bd380e5bc6b3e99230a515bcf3f33bea76e74687 GIT binary patch literal 1644 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 BqZ|ML literal 0 HcmV?d00001 diff --git a/src/main/resources/jchess/themeV2/matlak/board_hex2/hexMarker_yesAction.png b/src/main/resources/jchess/themeV2/matlak/board_hex2/hexMarker_yesAction.png new file mode 100644 index 0000000000000000000000000000000000000000..8fd6a6e79e944557e29c7dede5c83f99920984a9 GIT binary patch literal 408 zcmV;J0cZY+P)j!Y~X)UoAFt3D-d42y>0$a%|xm907@Q%o3KFjT^NjO`{SUOWis4|1U|C zbj-xh{m=nB-~?=07cbxf{N0X^kW1JU25+_b9_UsI=(WZfc;*tM;J(*yZF6*N5A<5I zPJp`q{Q!M+EirUoML~?+FV+-e_lp#y@wTI4SMA0kB$v?G{g^^xP>j(pPK6jQys8B$ zj_$J*#Mym9L7d$u6yyu;ClrJ)hX7YSx4q*9Tt(06ClDN)hoP?wN}MUgo}yPJPYSW$ z?f3}tuE^7U{1rP=n7DsG4dV^gNJP-umoe!mKounkjnOAp^vRFNa4Hh?r8}X;f}E|H z2r!^9>%R0ZXi1@1D=VNOg`)eVx8bmATRcu@N&W-zXpBPl+oFm90000 Date: Mon, 29 Jan 2024 00:03:00 +0100 Subject: [PATCH 07/11] update scrull for hex2p --- .../java/jchess/common/moveset/special/ShapeShifting.java | 1 + src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java | 4 ++-- src/main/java/jchess/gamemode/hex3p/Hex3pPieces.java | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/jchess/common/moveset/special/ShapeShifting.java b/src/main/java/jchess/common/moveset/special/ShapeShifting.java index 1dce915..619bf43 100644 --- a/src/main/java/jchess/common/moveset/special/ShapeShifting.java +++ b/src/main/java/jchess/common/moveset/special/ShapeShifting.java @@ -8,6 +8,7 @@ import jchess.ecs.Entity; import jchess.el.CompiledTileExpression; import jchess.el.v2.ExpressionCompiler; + import java.util.Set; import java.util.stream.Stream; diff --git a/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java b/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java index 5eb586d..f848c78 100644 --- a/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java +++ b/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java @@ -25,7 +25,7 @@ public enum Hex2pPieces implements PieceStore.IPieceDefinitionProvider { )), Bishop(PieceType.BISHOP, new PieceStore.PieceDefinition( "B", - rotations(regex("30+", false),6) + rotations(regex("30+", false), 6) )), Queen(PieceType.QUEEN, new PieceStore.PieceDefinition( "Q", @@ -77,7 +77,7 @@ game, getPromotionTilePredicate(neighbor(330, 30), pawnId), Skrull(PieceType.SKRULL, new PieceStore.PieceDefinition( "S", Pawn.pieceDefinition.baseMoves(), - (game, skrullId) -> new ShapeShifting(game, skrullId, 1, 2, + (game, skrullId) -> new ShapeShifting(game, skrullId, rotations(regex("(0 30 60){1,2}", true), 6), PieceType.ROOK, PieceType.KNIGHT, PieceType.BISHOP, PieceType.QUEEN, PieceType.ARCHER, PieceType.CATAPULT, PieceType.PAWN ) )); diff --git a/src/main/java/jchess/gamemode/hex3p/Hex3pPieces.java b/src/main/java/jchess/gamemode/hex3p/Hex3pPieces.java index 01b0299..b649fc6 100644 --- a/src/main/java/jchess/gamemode/hex3p/Hex3pPieces.java +++ b/src/main/java/jchess/gamemode/hex3p/Hex3pPieces.java @@ -91,7 +91,7 @@ game, getPromotionTilePredicate(neighbor(330, 30), pawnId), "S", Pawn.pieceDefinition.baseMoves(), (game, skrullId) -> new ShapeShifting(game, skrullId, - rotations(regex("(0 30 60){1,2}", true),6), + rotations(regex("(0 30 60){1,2}", true), 6), PieceType.ROOK, PieceType.KNIGHT, PieceType.BISHOP, PieceType.QUEEN, PieceType.ARCHER, PieceType.CATAPULT, PieceType.PAWN ) )); From 2eb7eeff05e7d1f6c329541a79c209c0b4a637ef Mon Sep 17 00:00:00 2001 From: BlazingTwist <39350649+BlazingTwist@users.noreply.github.com> Date: Mon, 29 Jan 2024 00:06:22 +0100 Subject: [PATCH 08/11] fix hex2p ui overflow --- .../GameComponent/GameComponent.tsx | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/main/jchess-web/src/components/GameComponent/GameComponent.tsx b/src/main/jchess-web/src/components/GameComponent/GameComponent.tsx index d852224..e36265e 100644 --- a/src/main/jchess-web/src/components/GameComponent/GameComponent.tsx +++ b/src/main/jchess-web/src/components/GameComponent/GameComponent.tsx @@ -1,14 +1,14 @@ "use client"; -import React, { ReactElement, useCallback, useEffect, useRef, useState } from "react"; -import { useGameContext } from "@/src/app/context/game_context"; -import { useBoardUpdateContext } from "@/src/app/context/board_update_context"; -import { Entity } from "@/models/BoardUpdate.schema"; +import React, {ReactElement, useCallback, useEffect, useRef, useState} from "react"; +import {useGameContext} from "@/src/app/context/game_context"; +import {useBoardUpdateContext} from "@/src/app/context/board_update_context"; +import {Entity} from "@/models/BoardUpdate.schema"; import Config from "@/src/utils/config"; -import { postClick } from "@/src/services/rest_api_service"; +import {postClick} from "@/src/services/rest_api_service"; import PlayerOverviewComponent from "./PlayerOverviewComponent"; import ChatComponent from "./ChatComponent"; import PieceSelectionComponent from "./PieceSelectionComponent"; -import { useThemeHelperContext } from "@/src/app/context/theme_helper_context"; +import {useThemeHelperContext} from "@/src/app/context/theme_helper_context"; import GameOverComponent from "@/src/components/GameComponent/GameOverComponent"; export default function GameComponent(): ReactElement { @@ -22,8 +22,8 @@ export default function GameComponent(): ReactElement { // Contexts const gameContext = useGameContext(); - const { boardUpdate } = useBoardUpdateContext(); // this is the current game state coming from the server - const { themeHelper } = useThemeHelperContext(); + const {boardUpdate} = useBoardUpdateContext(); // this is the current game state coming from the server + const {themeHelper} = useThemeHelperContext(); // State const canvasRef = useRef(null); @@ -51,7 +51,7 @@ export default function GameComponent(): ReactElement { minTilePos = [minX, minY]; maxTilePos = [maxX, maxY]; } - return { maxTilePos, minTilePos }; + return {maxTilePos, minTilePos}; } /** @@ -81,7 +81,11 @@ export default function GameComponent(): ReactElement { const tileStride = themeHelper.getTileStride(); const rawBoardWidth = tileSize!.x + tileStride.x * (maxTilePos[0] - minTilePos[0]); const rawBoardHeight = tileSize!.y + tileStride.y * (maxTilePos[1] - minTilePos[1]); + let scaleFactor = offsetWidthFromCanvasRef / rawBoardWidth; + if (rawBoardHeight * scaleFactor > offsetHeightFromCanvasRef) { + scaleFactor = offsetHeightFromCanvasRef / rawBoardHeight; + } const centerX = offsetWidthFromCanvasRef / 2; const centerY = offsetHeightFromCanvasRef / 2; @@ -226,7 +230,7 @@ export default function GameComponent(): ReactElement { } // calculate the min and max tile positions - const { maxTilePos, minTilePos } = calculateMinMaxTilePosition(boardUpdate.boardState); + const {maxTilePos, minTilePos} = calculateMinMaxTilePosition(boardUpdate.boardState); // calculate the piece size adjustment and translation offset to ensure clickability of the pieces const pieceSizeAdjustment = 0.7; // this is needed so the bounding box of the piece is not overlapping with other pieces @@ -272,14 +276,14 @@ export default function GameComponent(): ReactElement { ref={canvasRef} className="w-[80vw] h-[80vw] xl:w-[50vw] xl:h-[50vw] md:w-[65vw] md:h-[65vw] min-w-[20px] min-h-[200px] max-w-[80vh] max-h-[80vh] justify-self-center relative" > - - + +
{board}
- - + +
); From 50f732ebbd71554b866bd714ae276132ea105351 Mon Sep 17 00:00:00 2001 From: BlazingTwist <39350649+BlazingTwist@users.noreply.github.com> Date: Mon, 29 Jan 2024 00:14:18 +0100 Subject: [PATCH 09/11] show moveFrom marker even when clicking on a piece --- src/main/java/jchess/common/BaseChessGame.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/jchess/common/BaseChessGame.java b/src/main/java/jchess/common/BaseChessGame.java index df2aa75..487cc87 100644 --- a/src/main/java/jchess/common/BaseChessGame.java +++ b/src/main/java/jchess/common/BaseChessGame.java @@ -94,7 +94,11 @@ protected void onBoardClicked(int x, int y) { if (clickedMarker.onMarkerClicked != null) { clickedMarker.onMarkerClicked.run(); } - } else if (clickedEntity.piece != null) { + eventManager.getEvent(RenderEvent.class).fire(null); + return; + } + + if (clickedEntity.piece != null) { long startTime = System.currentTimeMillis(); // show the tiles this piece can move to @@ -104,7 +108,9 @@ protected void onBoardClicked(int x, int y) { long endTime = System.currentTimeMillis(); logger.info("Computing valid moves with kingCheck took {} ms", endTime - startTime); - } else if (clickedEntity.tile != null) { + } + + if (clickedEntity.tile != null) { // show which pieces can move to the selected tile for (Entity attacker : clickedEntity.tile.attackingPieces) { createMoveFromMarker(attacker); From 32f86748cb25291bdde5b4474c59c6046f77fb67 Mon Sep 17 00:00:00 2001 From: BlazingTwist <39350649+BlazingTwist@users.noreply.github.com> Date: Mon, 29 Jan 2024 00:17:34 +0100 Subject: [PATCH 10/11] fix failing test --- src/test/java/jchess/el/v2/RotationsTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/jchess/el/v2/RotationsTest.java b/src/test/java/jchess/el/v2/RotationsTest.java index a97ed18..4416dae 100644 --- a/src/test/java/jchess/el/v2/RotationsTest.java +++ b/src/test/java/jchess/el/v2/RotationsTest.java @@ -5,6 +5,7 @@ import jchess.gamemode.IPieceLayoutProvider; import jchess.gamemode.PieceStore; import jchess.gamemode.square2p.Square2PlayerGame; +import jchess.gamemode.square2p.Square2pPieces; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -25,7 +26,7 @@ void test_rotations() { TileExpression.repeat(TileExpression.neighbor(270), 1, 2, true) ); - Square2pBoardProvider square2pProvider = new Square2pBoardProvider(null, null); + Square2pBoardProvider square2pProvider = new Square2pBoardProvider(new PieceStore(Square2pPieces.values()), null); square2pProvider.generateBoard(); Entity originTile = square2pProvider.getEntityAtPosition(3, 3); square2pProvider.createPiece(originTile, PieceType.PAWN, 0); From f97ef539935caa1e69208c7c0d842ad57237176d Mon Sep 17 00:00:00 2001 From: BlazingTwist <39350649+BlazingTwist@users.noreply.github.com> Date: Mon, 29 Jan 2024 00:24:19 +0100 Subject: [PATCH 11/11] fix codacy warnings --- .../jchess/gamemode/hex2p/Hex2PlayerGame.java | 17 +++++++++-------- .../java/jchess/gamemode/hex2p/Hex2pPieces.java | 9 +++++++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/main/java/jchess/gamemode/hex2p/Hex2PlayerGame.java b/src/main/java/jchess/gamemode/hex2p/Hex2PlayerGame.java index d8adad8..14c773d 100644 --- a/src/main/java/jchess/gamemode/hex2p/Hex2PlayerGame.java +++ b/src/main/java/jchess/gamemode/hex2p/Hex2PlayerGame.java @@ -54,20 +54,21 @@ protected int getDirectionFromOwnerId(int ownerId) { protected void generateBoard() { for (int y = 0; y < numTilesVertical; y++) { Entity[] tileRow = tiles[y]; - int x_start, x_end; + final int x0; + final int x1; if (y < 5) { // top part - x_start = 5 - y; - x_end = x_start + 2 * (y + 1); + x0 = 5 - y; + x1 = x0 + 2 * (y + 1); } else if (y < 16) { // middle part - x_start = (y % 2 == 0) ? 1 : 0; - x_end = numTilesHorizontal; + x0 = (y % 2 == 0) ? 1 : 0; + x1 = numTilesHorizontal; } else { // bottom part - x_start = y - 15; - x_end = x_start + (6 - x_start) * 2; + x0 = y - 15; + x1 = x0 + (6 - x0) * 2; } - for (int x = x_start; x < x_end; x += 2) { + for (int x = x0; x < x1; x += 2) { tileRow[x] = entityManager.createEntity(); tileRow[x].tile = new TileComponent(new Point(x, y), y % 3); } diff --git a/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java b/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java index f848c78..55e1a74 100644 --- a/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java +++ b/src/main/java/jchess/gamemode/hex2p/Hex2pPieces.java @@ -2,7 +2,10 @@ import dx.schema.types.PieceType; import jchess.common.components.PieceIdentifier; -import jchess.common.moveset.special.*; +import jchess.common.moveset.special.EnPassant; +import jchess.common.moveset.special.PawnPromotion; +import jchess.common.moveset.special.RangedAttack; +import jchess.common.moveset.special.ShapeShifting; import jchess.ecs.Entity; import jchess.el.CompiledTileExpression; import jchess.el.v2.ExpressionCompiler; @@ -12,7 +15,9 @@ import java.util.function.Predicate; import java.util.stream.Stream; -import static jchess.el.v2.TileExpression.*; +import static jchess.el.v2.TileExpression.neighbor; +import static jchess.el.v2.TileExpression.regex; +import static jchess.el.v2.TileExpression.rotations; public enum Hex2pPieces implements PieceStore.IPieceDefinitionProvider { Rook(PieceType.ROOK, new PieceStore.PieceDefinition(