From 1ebb3c1a799649f1b3999ff48aef61f7787e3f16 Mon Sep 17 00:00:00 2001 From: Tim Speckhals Date: Sat, 15 Sep 2018 01:19:15 +0200 Subject: [PATCH] 2.6.2 --- src/ExampleEngineVSEngine.java | 8 +- src/Tests.java | 243 +++++---- src/sh/hell/compactchess/Main.java | 1 - src/sh/hell/compactchess/game/Game.java | 488 +++++++++---------- src/sh/hell/compactchess/game/Move.java | 167 +++---- src/sh/hell/compactchess/game/Piece.java | 103 ---- src/sh/hell/compactchess/game/PieceType.java | 20 +- src/sh/hell/compactchess/game/Square.java | 81 ++- 8 files changed, 497 insertions(+), 614 deletions(-) delete mode 100644 src/sh/hell/compactchess/game/Piece.java diff --git a/src/ExampleEngineVSEngine.java b/src/ExampleEngineVSEngine.java index e9755d0..e6ca117 100644 --- a/src/ExampleEngineVSEngine.java +++ b/src/ExampleEngineVSEngine.java @@ -18,12 +18,12 @@ public static void main(String[] args) throws IOException, InterruptedException final String whiteName = "Stockfish 9"; final Engine whiteEngine = new Engine("stockfish_9_multivariant.exe", null, uciOptions, true); //final String whiteName = "LCZero 594"; - //final Engine whiteEngine = new Engine("lc0.exe", null, uciOptions, true).debug(true); + //final Engine whiteEngine = new Engine("lc0.exe", null, uciOptions, true); //whiteEngine.evaluate(new Game().loadFEN("8/8/8/8/8/8/8/8 w - -").setTimed(1, 0).start()).awaitConclusion(); final String blackName = "Stockfish 9"; final Engine blackEngine = new Engine("stockfish_9_multivariant.exe", null, uciOptions, true); //final String blackName = "LCZero 594"; - //final Engine blackEngine = new Engine("lc0.exe", null, uciOptions, true).debug(true); + //final Engine blackEngine = new Engine("lc0.exe", null, uciOptions, true); //blackEngine.evaluate(new Game().loadFEN("8/8/8/8/8/8/8/8 w - -").setTimed(1, 0).start()).awaitConclusion(); System.out.println("Engines are ready."); try @@ -31,7 +31,7 @@ public static void main(String[] args) throws IOException, InterruptedException //noinspection InfiniteLoopStatement do { - final Game game = new Game().setTimed(15 * 1000, 0).setPlayerNames(whiteName, blackName).setTag("Event", "Engine VS Engine").start(); + final Game game = new Game().setTimed(15000, 0).setPlayerNames(whiteName, blackName).setTag("Event", "Engine VS Engine").start(); FileWriter fw = new FileWriter("board.svg", false); fw.write(game.toSVG()); fw.close(); @@ -43,7 +43,7 @@ public static void main(String[] args) throws IOException, InterruptedException Color mover = game.toMove; Engine engine = (mover == Color.WHITE ? whiteEngine : blackEngine); final Move move; - move = engine.evaluate(game.resetMoveTime()).awaitConclusion().getBestMove(); + move = engine.evaluate(game).awaitConclusion().getBestMove(); fw = new FileWriter("info.txt", false); fw.write(whiteName + " VS " + blackName + "\n"); if(engine.score == 0 && game.canDrawBeClaimed()) diff --git a/src/Tests.java b/src/Tests.java index 190291d..5f77342 100644 --- a/src/Tests.java +++ b/src/Tests.java @@ -10,7 +10,6 @@ import sh.hell.compactchess.game.GameStatus; import sh.hell.compactchess.game.Language; import sh.hell.compactchess.game.Move; -import sh.hell.compactchess.game.Piece; import sh.hell.compactchess.game.PieceType; import sh.hell.compactchess.game.Square; import sh.hell.compactchess.game.TimeControl; @@ -26,6 +25,7 @@ import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertNotNull; import static junit.framework.TestCase.assertTrue; +import static junit.framework.TestCase.fail; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; @@ -42,9 +42,9 @@ private static void evaluatePossibleMoves(int expectedSquares, Game game, ArrayL int uniqueSquares = 0; for(Square square : squares) { - if(!game.square(square).hasPiece() || game.square(square).getPiece().type != PieceType.KING) + if(square.pieceType != PieceType.KING) { - game.insertPiece(new Piece(PieceType.KING, Color.WHITE), square); + game.setPiece(square, Color.WHITE, PieceType.KING); uniqueSquares++; } } @@ -56,25 +56,24 @@ private static void evaluatePossibleMoves(int expectedSquares, Game game, ArrayL public void enPassant() throws ChessException { System.out.println("En Passant\n"); - Game game = new Game().loadFEN("8/8/8/8/1p6/8/P6P/8 w - -").start(); + Game game = new Game().loadFEN("8/2p5/8/8/1p6/8/P7/8 b - -").start(); visualize(game); - game.uciMove("h2h3").commit(); + game.uciMove("c7c6").commit(); visualize(game); assertNull(game.enPassantSquare); - game.opponentToMove(); game.uciMove("a2a4").commit(); visualize(game); assertEquals("a3", game.enPassantSquare.getAlgebraicNotation()); game.uciMove("b4a3").commit(); visualize(game); - synchronized(game.pieces) - { - assertEquals(2, game.pieces.size()); - Piece piece = game.pieces.get(0); - assertEquals(PieceType.PAWN, piece.type); - assertEquals(Color.BLACK, piece.color); - assertEquals("a3", piece.getSquare().getAlgebraicNotation()); - } + assertEquals(2, game.pieces.size()); + assertTrue(game.square("c6").hasPiece()); + game.unsetPiece(game.square("c6")); + assertEquals(1, game.pieces.size()); + Square square = game.pieces.get(0); + assertEquals(PieceType.PAWN, square.pieceType); + assertEquals(Color.BLACK, square.pieceColor); + assertEquals("a3", square.getAlgebraicNotation()); } @Test(timeout = 1000L) @@ -83,32 +82,108 @@ public void promotion() throws ChessException System.out.println("Promotion\n"); Game game = new Game().loadFEN("8/7P/8/8/8/8/7p/8 w").start(); visualize(game); - assertEquals(2, game.pieces.size()); - assertEquals(PieceType.PAWN, game.pieces.get(0).type); - assertEquals(Color.WHITE, game.pieces.get(0).color); - assertEquals(PieceType.PAWN, game.pieces.get(1).type); - assertEquals(Color.BLACK, game.pieces.get(1).color); + int whitePawns = 0; + int blackPawns = 0; + for(Square s : game.squares) + { + if(s.hasPiece()) + { + if(s.pieceType == PieceType.PAWN) + { + if(s.pieceColor == Color.WHITE) + { + whitePawns++; + } + else + { + blackPawns++; + } + } + else + { + fail(); + } + } + } + assertEquals(1, whitePawns); + assertEquals(1, blackPawns); Move move = game.uciMove("h7h8q"); move.toUCI(); move.commit(); visualize(game); - assertEquals(2, game.pieces.size()); - assertEquals(PieceType.QUEEN, game.pieces.get(0).type); - assertEquals(Color.WHITE, game.pieces.get(0).color); + blackPawns = 0; + int whiteQueens = 0; + for(Square s : game.pieces) + { + if(s.pieceType == PieceType.QUEEN) + { + assertEquals(Color.WHITE, s.pieceColor); + whiteQueens++; + } + else if(s.pieceType == PieceType.PAWN) + { + assertEquals(Color.BLACK, s.pieceColor); + blackPawns++; + } + else + { + fail(); + } + } + assertEquals(1, whiteQueens); + assertEquals(1, blackPawns); move = game.uciMove("h2h1q"); move.toUCI(); move.commit(); visualize(game); - assertEquals(2, game.pieces.size()); - assertEquals(PieceType.QUEEN, game.pieces.get(1).type); - assertEquals(Color.BLACK, game.pieces.get(1).color); + whiteQueens = 0; + int blackQueens = 0; + for(Square s : game.pieces) + { + if(s.pieceType == PieceType.QUEEN) + { + if(s.pieceColor == Color.WHITE) + { + whiteQueens++; + } + else + { + blackQueens++; + } + } + else + { + fail(); + } + } + assertEquals(1, whiteQueens); + assertEquals(1, blackQueens); move = game.uciMove("h8h1"); move.toUCI(); move.commit(); visualize(game); - assertEquals(1, game.pieces.size()); - assertEquals(PieceType.QUEEN, game.pieces.get(0).type); - assertEquals(Color.WHITE, game.pieces.get(0).color); + whiteQueens = 0; + blackQueens = 0; + for(Square s : game.pieces) + { + if(s.pieceType == PieceType.QUEEN) + { + if(s.pieceColor == Color.WHITE) + { + whiteQueens++; + } + else + { + blackQueens++; + } + } + else + { + fail(); + } + } + assertEquals(1, whiteQueens); + assertEquals(1, blackPawns); } @Test(timeout = 1000L) @@ -129,15 +204,12 @@ public void castling() throws ChessException visualize(game); assertEquals(CastlingType.QUEENSIDE, move.castlingType); assertNotNull(move.getIllegalReason()); - synchronized(game.pieces) + for(Square s : game.pieces) { - for(Piece p : game.pieces) + if(s.pieceType == PieceType.ROOK && s.pieceColor == Color.WHITE) { - if(p.type == PieceType.ROOK && p.color == Color.WHITE) - { - assertEquals(3, p.getSquare().file); - break; - } + assertEquals(3, s.file); + break; } } move = game.uciMove("e8g8"); @@ -145,15 +217,12 @@ public void castling() throws ChessException visualize(game); assertEquals(CastlingType.KINGSIDE, move.castlingType); assertNull(move.getIllegalReason()); - synchronized(game.pieces) + for(Square s : game.pieces) { - for(Piece p : game.pieces) + if(s.pieceType == PieceType.ROOK && s.pieceColor == Color.BLACK) { - if(p.type == PieceType.ROOK && p.color == Color.BLACK) - { - assertEquals(5, p.getSquare().file); - break; - } + assertEquals(5, s.file); + break; } } game = new Game().loadFEN("rn2k3/8/1R6/8/8/8/8/4K3 b q -").start(); @@ -173,11 +242,10 @@ public void controllers() throws ChessException { Game game = new Game().loadFEN("rnbqkb1r/pppppppp/5n2/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq -").start(); Square s = game.square("e4"); - Piece p = s.getPiece(); assertEquals(1, game.getControllers(s).size()); - assertEquals(1, game.getAttackers(p).size()); - assertEquals(0, game.getDefenders(p).size()); - assertTrue(game.isHanging(p)); + assertEquals(1, game.getAttackers(s).size()); + assertEquals(0, game.getDefenders(s).size()); + assertTrue(game.isHanging(s)); } @Test(timeout = 1000L) @@ -221,10 +289,7 @@ public void stalemate() throws ChessException System.out.println("Stalemate\n"); final Game game = new Game().loadFEN("k1K5/pp6/N7/8/8/8/8/7B b").start(); visualize(game); - assertFalse(game.isCheck()); assertEquals(EndReason.STALEMATE, game.endReason); - game.opponentToMove(); - assertFalse(game.isCheck()); } @Test(timeout = 1000L) @@ -460,7 +525,7 @@ public void antichess() throws ChessException visualize(game); assertFalse(game.uciMove("c5b5").isLegal()); final Move move = game.uciMove("c5d5"); - assertTrue(move.isLegal()); + assertNull(move.getIllegalReason()); move.commit(); visualize(game); assertFalse(game.isCheck()); @@ -536,21 +601,15 @@ public void chess960() throws ChessException assertFalse(game.whiteCanCastle); assertEquals(CastlingType.KINGSIDE, move.castlingType); assertNull(move.getIllegalReason()); - synchronized(game.pieces) + for(Square s : game.getPieces(Color.WHITE)) { - for(Piece p : game.pieces) + if(s.pieceType == PieceType.ROOK) { - if(p.color == Color.WHITE) - { - if(p.type == PieceType.ROOK) - { - assertEquals(5, p.getSquare().file); - } - else if(p.type == PieceType.KING) - { - assertEquals(6, p.getSquare().file); - } - } + assertEquals(5, s.file); + } + else if(s.pieceType == PieceType.KING) + { + assertEquals(6, s.file); } } assertTrue(game.blackCanCastleQueenside); @@ -560,15 +619,12 @@ else if(p.type == PieceType.KING) assertFalse(game.blackCanCastleQueenside); assertEquals(CastlingType.QUEENSIDE, move.castlingType); assertNull(move.getIllegalReason()); - synchronized(game.pieces) + for(Square s : game.getPieces(Color.BLACK)) { - for(Piece p : game.pieces) + if(s.pieceType == PieceType.ROOK) { - if(p.type == PieceType.ROOK && p.color == Color.BLACK) - { - assertEquals(3, p.getSquare().file); - break; - } + assertEquals(3, s.file); + break; } } for(String fen : new String[]{"rk3r2/8/8/8/4R3/8/8/8 b q -", "r2kr3/8/8/8/8/8/8/8 b q -"}) @@ -582,15 +638,12 @@ else if(p.type == PieceType.KING) assertFalse(game.blackCanCastleQueenside); assertEquals(CastlingType.QUEENSIDE, move.castlingType); assertNull(move.getIllegalReason()); - synchronized(game.pieces) + for(Square s : game.getPieces(Color.BLACK)) { - for(Piece p : game.pieces) + if(s.pieceType == PieceType.ROOK && s.file < 4) { - if(p.type == PieceType.ROOK && p.color == Color.BLACK) - { - assertEquals(3, p.getSquare().file); - break; - } + assertEquals(3, s.file); + break; } } } @@ -604,11 +657,11 @@ public void rook() throws ChessException System.out.println("Possible Moves: Rook\n"); final Game game = new Game().loadFEN("8/5p2/8/8/8/8/8/2B2RN1 w").start(); final ArrayList squares = new ArrayList<>(); - for(Piece piece : game.pieces) + for(Square s : game.pieces) { - if(piece.type == PieceType.ROOK) + if(s.pieceType == PieceType.ROOK) { - squares.addAll(game.getSquaresControlledBy(piece)); + squares.addAll(game.getSquaresControlledBy(s)); } } evaluatePossibleMoves(8, game, squares); @@ -620,11 +673,11 @@ public void pawn() throws ChessException System.out.println("Possible Moves: Pawn\n"); final Game game = new Game().loadFEN("8/8/8/4Pp2/8/8/P2P3P/8 w - f6 0 2").start(); final ArrayList squares = new ArrayList<>(); - for(Piece piece : game.pieces) + for(Square s : game.pieces) { - if(piece.color == Color.WHITE && piece.type == PieceType.PAWN) + if(s.pieceColor == Color.WHITE && s.pieceType == PieceType.PAWN) { - squares.addAll(game.getSquaresControlledBy(piece)); + squares.addAll(game.getSquaresControlledBy(s)); } } evaluatePossibleMoves(8, game, squares); @@ -636,11 +689,11 @@ public void king() throws ChessException System.out.println("Possible Moves: King\n"); final Game game = new Game().loadFEN("7k/8/3k2k1/8/8/1k6/8/k7 w - -").start(); final ArrayList squares = new ArrayList<>(); - for(Piece piece : game.pieces) + for(Square s : game.pieces) { - if(piece.type == PieceType.KING) + if(s.pieceType == PieceType.KING) { - squares.addAll(game.getSquaresControlledBy(piece)); + squares.addAll(game.getSquaresControlledBy(s)); } } evaluatePossibleMoves(26, game, squares); @@ -652,11 +705,11 @@ public void bishop() throws ChessException System.out.println("Possible Moves: Bishop\n"); final Game game = new Game().loadFEN("8/8/8/8/3B4/8/8/8 w").start(); final ArrayList squares = new ArrayList<>(); - for(Piece piece : game.pieces) + for(Square s : game.pieces) { - if(piece.type == PieceType.BISHOP) + if(s.pieceType == PieceType.BISHOP) { - squares.addAll(game.getSquaresControlledBy(piece)); + squares.addAll(game.getSquaresControlledBy(s)); } } evaluatePossibleMoves(13, game, squares); @@ -668,11 +721,11 @@ public void queen() throws ChessException System.out.println("Possible Moves: Queen\n"); final Game game = new Game().loadFEN("8/8/8/8/3Q4/8/8/8 w").start(); final ArrayList squares = new ArrayList<>(); - for(Piece piece : game.pieces) + for(Square s : game.pieces) { - if(piece.type == PieceType.QUEEN) + if(s.pieceType == PieceType.QUEEN) { - squares.addAll(game.getSquaresControlledBy(piece)); + squares.addAll(game.getSquaresControlledBy(s)); } } evaluatePossibleMoves(27, game, squares); @@ -684,11 +737,11 @@ public void knight() throws ChessException System.out.println("Possible Moves: Queen\n"); final Game game = new Game().loadFEN("8/1N4N1/8/8/3N4/8/1N4N1/8 w").start(); final ArrayList squares = new ArrayList<>(); - for(Piece piece : game.pieces) + for(Square s : game.pieces) { - if(piece.type == PieceType.KNIGHT) + if(s.pieceType == PieceType.KNIGHT) { - squares.addAll(game.getSquaresControlledBy(piece)); + squares.addAll(game.getSquaresControlledBy(s)); } } evaluatePossibleMoves(22, game, squares); diff --git a/src/sh/hell/compactchess/Main.java b/src/sh/hell/compactchess/Main.java index ab2c87a..547d8b2 100644 --- a/src/sh/hell/compactchess/Main.java +++ b/src/sh/hell/compactchess/Main.java @@ -25,7 +25,6 @@ public static void main(String[] args) throws IOException, ChessException, Inter main(); } - @SuppressWarnings("ResultOfMethodCallIgnored") private static void main() throws IOException, ChessException, InterruptedException { System.out.println("# CompactChess"); diff --git a/src/sh/hell/compactchess/game/Game.java b/src/sh/hell/compactchess/game/Game.java index c299323..458d10c 100644 --- a/src/sh/hell/compactchess/game/Game.java +++ b/src/sh/hell/compactchess/game/Game.java @@ -26,7 +26,7 @@ public class Game { public static final short MAX_SCORE = 12800; public final ArrayList moves = new ArrayList<>(); - public final ArrayList pieces = new ArrayList<>(); + public final ArrayList pieces = new ArrayList<>(); final public TreeMap tags = new TreeMap<>(new PGNTagComparator()); final HashMap repetitionPostitions = new HashMap<>(); public Game start; @@ -564,14 +564,13 @@ else if((whiteCanCastle && toMove == Color.WHITE && uci.equals("e1g1")) || (blac byte kingFile = 8; synchronized(this.pieces) { - for(Piece p : this.pieces) + for(Square s : this.pieces) { - if(p.color == toMove && p.type == PieceType.KING) + if(s.pieceColor == toMove && s.pieceType == PieceType.KING) { - Square s = p.getSquare(); if(s.rank == rank) { - kingFile = p.getSquare().file; + kingFile = s.file; } } } @@ -583,11 +582,10 @@ else if((whiteCanCastle && toMove == Color.WHITE && uci.equals("e1g1")) || (blac { synchronized(this.pieces) { - for(Piece p : this.pieces) + for(Square s : this.pieces) { - if(p.color == toMove && p.type == PieceType.ROOK) + if(s.pieceColor == toMove && s.pieceType == PieceType.ROOK) { - Square s = p.getSquare(); if(s.rank == rank && s.file < kingFile) { rookSquare = s.getAlgebraicNotation(); @@ -601,11 +599,10 @@ else if((whiteCanCastle && toMove == Color.WHITE && uci.equals("e1g1")) || (blac { synchronized(this.pieces) { - for(Piece p : this.pieces) + for(Square s : this.pieces) { - if(p.color == toMove && p.type == PieceType.ROOK) + if(s.pieceColor == toMove && s.pieceType == PieceType.ROOK) { - Square s = p.getSquare(); if(s.rank == rank && s.file > kingFile) { rookSquare = s.getAlgebraicNotation(); @@ -703,12 +700,11 @@ else if(move.equalsIgnoreCase("O-O") || move.equals("0-0") || move.equals("e1g1" byte rank = (byte) (Byte.valueOf(piece) - 1); synchronized(this.pieces) { - for(Piece p : this.pieces) + for(Square s : pieces) { - final Square pSquare = p.getSquare(); - if(p.color == this.toMove && p.type == pieceType && pSquare.rank == rank && this.getSquaresControlledBy(p).contains(toSquare) && new Move(this, p.getSquare(), toSquare, null, true).isLegal()) + if(s.pieceColor == this.toMove && s.pieceType == pieceType && s.rank == rank && this.getSquaresControlledBy(s).contains(toSquare) && new Move(this, s, toSquare, null, true).isLegal()) { - squares.add(pSquare); + squares.add(s); } } } @@ -718,12 +714,11 @@ else if(move.equalsIgnoreCase("O-O") || move.equals("0-0") || move.equals("e1g1" byte file = Square.file(piece); synchronized(this.pieces) { - for(Piece p : this.pieces) + for(Square s : this.pieces) { - final Square pSquare = p.getSquare(); - if(p.color == this.toMove && p.type == pieceType && pSquare.file == file && this.getSquaresControlledBy(p).contains(toSquare) && new Move(this, p.getSquare(), toSquare, null, true).isLegal()) + if(s.pieceColor == this.toMove && s.pieceType == pieceType && s.file == file && this.getSquaresControlledBy(s).contains(toSquare) && new Move(this, s, toSquare, null, true).isLegal()) { - squares.add(pSquare); + squares.add(s); } } } @@ -733,11 +728,11 @@ else if(move.equalsIgnoreCase("O-O") || move.equals("0-0") || move.equals("e1g1" { synchronized(this.pieces) { - for(Piece p : this.pieces) + for(Square s : this.pieces) { - if(p.color == this.toMove && p.type == pieceType && this.getSquaresControlledBy(p).contains(toSquare) && new Move(this, p.getSquare(), toSquare, null, true).isLegal()) + if(s.pieceColor == this.toMove && s.pieceType == pieceType && this.getSquaresControlledBy(s).contains(toSquare) && new Move(this, s, toSquare, null, true).isLegal()) { - squares.add(p.getSquare()); + squares.add(s); } } } @@ -769,6 +764,31 @@ public Game setVariant(Variant variant) return this; } + public Game setPiece(Square square, Color color, PieceType pieceType) + { + synchronized(this.pieces) + { + square.pieceColor = color; + square.pieceType = pieceType; + if(!this.pieces.contains(square)) + { + this.pieces.add(square); + } + } + return this; + } + + public Game unsetPiece(Square square) + { + synchronized(this.pieces) + { + square.pieceColor = null; + square.pieceType = null; + this.pieces.remove(square); + } + return this; + } + public Game loadFEN(String fen) throws ChessException { if(this.status != GameStatus.BUILDING) @@ -792,25 +812,32 @@ else if(fen.equalsIgnoreCase("random960")) throw new InvalidFENException("Not enough information in FEN: " + fen); } String pieceSequence = arr[0].replace("/", "").replace("8", " ").replace("7", " ").replace("6", " ").replace("5", " ").replace("4", " ").replace("3", " ").replace("2", " ").replace("1", " "); - this.squares = new Square[64]; - byte file = 0; - byte rank = 7; - for(char c : pieceSequence.toCharArray()) + synchronized(this.pieces) { - String s = String.valueOf(c); - Square square = new Square(file, rank); - if(!s.equals(" ")) - { - Piece piece = new Piece(Language.ENGLISH.pieceFromChar(s), (s.toUpperCase().equals(s) ? Color.WHITE : Color.BLACK), square); - this.pieces.add(piece); - square.setPiece(piece); - } - this.squares[Square.index(file, rank)] = square; - file++; - if(file == 8) + this.pieces.clear(); + this.squares = new Square[64]; + byte file = 0; + byte rank = 7; + for(char c : pieceSequence.toCharArray()) { - rank--; - file = 0; + String s = String.valueOf(c); + final Square square; + if(s.equals(" ")) + { + square = new Square(file, rank); + } + else + { + square = new Square(file, rank, (s.toUpperCase().equals(s) ? Color.WHITE : Color.BLACK), Language.ENGLISH.pieceFromChar(s)); + this.pieces.add(square); + } + this.squares[Square.index(file, rank)] = square; + file++; + if(file == 8) + { + rank--; + file = 0; + } } } this.toMove = ((arr[1].equals("w")) ? Color.WHITE : Color.BLACK); @@ -939,9 +966,9 @@ else if(variant == Variant.STANDARD || variant == Variant.CHESS960) boolean definitelySufficientMaterial = false; synchronized(pieces) { - for(Piece p : pieces) + for(Square s : pieces) { - if(p.type != PieceType.KING && p.type != PieceType.KNIGHT && p.type != PieceType.BISHOP) + if(s.pieceType != PieceType.KING && s.pieceType != PieceType.KNIGHT && s.pieceType != PieceType.BISHOP) { definitelySufficientMaterial = true; break; @@ -958,11 +985,11 @@ else if(variant == Variant.STANDARD || variant == Variant.CHESS960) boolean blackHasBlackBishop = false; synchronized(pieces) { - for(Piece p : pieces) + for(Square s : pieces) { - if(p.type == PieceType.KNIGHT) + if(s.pieceType == PieceType.KNIGHT) { - if(p.color == Color.WHITE) + if(s.pieceColor == Color.WHITE) { whiteKnights++; } @@ -971,11 +998,11 @@ else if(variant == Variant.STANDARD || variant == Variant.CHESS960) blackKnights++; } } - else if(p.type == PieceType.BISHOP) + else if(s.pieceType == PieceType.BISHOP) { - if(p.color == Color.WHITE) + if(s.pieceColor == Color.WHITE) { - if(p.getSquare().isWhite()) + if(s.isWhite()) { whiteHasWhiteBishop = true; } @@ -986,7 +1013,7 @@ else if(p.type == PieceType.BISHOP) } else { - if(p.getSquare().isWhite()) + if(s.isWhite()) { blackHasWhiteBishop = true; } @@ -1009,9 +1036,9 @@ else if(variant == Variant.THREE_CHECK) boolean sufficientMaterial = false; synchronized(pieces) { - for(Piece p : pieces) + for(Square s : pieces) { - if(p.type != PieceType.KING) + if(s.pieceType != PieceType.KING) { sufficientMaterial = true; break; @@ -1063,63 +1090,99 @@ public Square square(byte file, byte rank) return squares[Square.index(file, rank)]; } - public Game insertPiece(final Piece piece, final Square square) + public ArrayList getPieces(Color owner) { - if(this.status != GameStatus.BUILDING) + final ArrayList squares = new ArrayList<>(); + synchronized(this.pieces) { - this.exportable = false; + for(Square s : this.pieces) + { + if(s.pieceColor == owner) + { + squares.add(s); + } + } } - piece.setSquare(square); - square.setPiece(piece); - return this; + return squares; + } + + public ArrayList getPieces(Color owner, PieceType pieceType) + { + final ArrayList squares = new ArrayList<>(); + synchronized(this.pieces) + { + for(Square s : this.pieces) + { + if(s.pieceColor == owner && s.pieceType == pieceType) + { + squares.add(s); + } + } + } + return squares; + } + + public ArrayList getPieces(PieceType pieceType) + { + final ArrayList squares = new ArrayList<>(); + synchronized(this.pieces) + { + for(Square s : this.pieces) + { + if(s.pieceType == pieceType) + { + squares.add(s); + } + } + } + return squares; } - public ArrayList getSquaresControlledBy(Piece piece) + public ArrayList getSquaresControlledBy(Square square) { final ArrayList squares = new ArrayList<>(); - final Square square = piece.getSquare(); - if(piece.type == PieceType.PAWN) + if(square.pieceType == PieceType.PAWN) { final byte file = square.file; final byte rank = square.rank; Square square_; - if(piece.color == Color.WHITE ? rank < 7 : rank > 0) + if(square.pieceColor == Color.WHITE ? rank < 7 : rank > 0) { - square_ = this.square(file, (byte) (rank + (piece.color == Color.WHITE ? 1 : -1))); + square_ = this.square(file, (byte) (rank + (square.pieceColor == Color.WHITE ? 1 : -1))); if(!square_.hasPiece()) { - squares.add(this.square(file, (byte) (rank + (piece.color == Color.WHITE ? 1 : -1)))); - if((rank == 1 && piece.color == Color.WHITE) || (rank == 6 && piece.color == Color.BLACK)) + squares.add(this.square(file, (byte) (rank + (square.pieceColor == Color.WHITE ? 1 : -1)))); + if((rank == 1 && square.pieceColor == Color.WHITE) || (rank == 6 && square.pieceColor == Color.BLACK)) { - square_ = this.square(file, (byte) (rank + (piece.color == Color.WHITE ? 2 : -2))); + square_ = this.square(file, (byte) (rank + (square.pieceColor == Color.WHITE ? 2 : -2))); if(!square_.hasPiece()) { - squares.add(this.square(file, (byte) (rank + (piece.color == Color.WHITE ? 2 : -2)))); + squares.add(this.square(file, (byte) (rank + (square.pieceColor == Color.WHITE ? 2 : -2)))); } } } } - if(piece.color == Color.WHITE ? rank < 7 : rank > 0) + if(square.pieceColor == Color.WHITE ? rank < 7 : rank > 0) { if(file > 0) { - square_ = this.square((byte) (file - 1), (byte) (rank + (piece.color == Color.WHITE ? 1 : -1))); - if((square_.hasPiece() && square_.getPiece().color != piece.color) || square_.equals(this.enPassantSquare)) + square_ = this.square((byte) (file - 1), (byte) (rank + (square.pieceColor == Color.WHITE ? 1 : -1))); + if((square_.pieceColor == square.pieceColor.opposite()) || square_.equals(this.enPassantSquare)) { squares.add(square_); } } if(file < 7) { - square_ = this.square((byte) (file + 1), (byte) (rank + (piece.color == Color.WHITE ? 1 : -1))); - if((square_.hasPiece() && square_.getPiece().color != piece.color) || square_.equals(this.enPassantSquare)) + square_ = this.square((byte) (file + 1), (byte) (rank + (square.pieceColor == Color.WHITE ? 1 : -1))); + if(square_.pieceColor == square.pieceColor.opposite() || square_.equals(this.enPassantSquare)) { squares.add(square_); } } } } - else if(piece.type == PieceType.KING) + else if(square.pieceType == PieceType.KING) { final byte file = square.file; final byte rank = square.rank; @@ -1134,7 +1197,7 @@ else if(piece.type == PieceType.KING) if(rank_ >= 0 && rank_ < 8) { final Square square_ = this.square(file_, rank_); - if(!square_.hasPiece() || square_.getPiece().color != piece.color) + if(square_.pieceColor != square.pieceColor) { squares.add(square_); } @@ -1143,14 +1206,14 @@ else if(piece.type == PieceType.KING) } } } - else if(piece.type == PieceType.KNIGHT) + else if(square.pieceType == PieceType.KNIGHT) { if(square.rank > 1) { if(square.file > 0) { final Square square_ = this.square((byte) (square.file - 1), (byte) (square.rank - 2)); - if(!square_.hasPiece() || square_.getPiece().color != piece.color) + if(square_.pieceColor != square.pieceColor) { squares.add(square_); } @@ -1158,7 +1221,7 @@ else if(piece.type == PieceType.KNIGHT) if(square.file < 7) { final Square square_ = this.square((byte) (square.file + 1), (byte) (square.rank - 2)); - if(!square_.hasPiece() || square_.getPiece().color != piece.color) + if(square_.pieceColor != square.pieceColor) { squares.add(square_); } @@ -1169,7 +1232,7 @@ else if(piece.type == PieceType.KNIGHT) if(square.file > 0) { final Square square_ = this.square((byte) (square.file - 1), (byte) (square.rank + 2)); - if(!square_.hasPiece() || square_.getPiece().color != piece.color) + if(square_.pieceColor != square.pieceColor) { squares.add(square_); } @@ -1177,7 +1240,7 @@ else if(piece.type == PieceType.KNIGHT) if(square.file < 7) { final Square square_ = this.square((byte) (square.file + 1), (byte) (square.rank + 2)); - if(!square_.hasPiece() || square_.getPiece().color != piece.color) + if(square_.pieceColor != square.pieceColor) { squares.add(square_); } @@ -1188,7 +1251,7 @@ else if(piece.type == PieceType.KNIGHT) if(square.rank > 0) { final Square square_ = this.square((byte) (square.file - 2), (byte) (square.rank - 1)); - if(!square_.hasPiece() || square_.getPiece().color != piece.color) + if(square_.pieceColor != square.pieceColor) { squares.add(square_); } @@ -1196,7 +1259,7 @@ else if(piece.type == PieceType.KNIGHT) if(square.rank < 7) { final Square square_ = this.square((byte) (square.file - 2), (byte) (square.rank + 1)); - if(!square_.hasPiece() || square_.getPiece().color != piece.color) + if(square_.pieceColor != square.pieceColor) { squares.add(square_); } @@ -1207,7 +1270,7 @@ else if(piece.type == PieceType.KNIGHT) if(square.rank > 0) { final Square square_ = this.square((byte) (square.file + 2), (byte) (square.rank - 1)); - if(!square_.hasPiece() || square_.getPiece().color != piece.color) + if(square_.pieceColor != square.pieceColor) { squares.add(square_); } @@ -1215,7 +1278,7 @@ else if(piece.type == PieceType.KNIGHT) if(square.rank < 7) { final Square square_ = this.square((byte) (square.file + 2), (byte) (square.rank + 1)); - if(!square_.hasPiece() || square_.getPiece().color != piece.color) + if(square_.pieceColor != square.pieceColor) { squares.add(square_); } @@ -1224,14 +1287,14 @@ else if(piece.type == PieceType.KNIGHT) } else { - if(piece.type == PieceType.ROOK || piece.type == PieceType.QUEEN) + if(square.pieceType == PieceType.ROOK || square.pieceType == PieceType.QUEEN) { byte rank = square.rank; while(rank < 7) { rank++; final Square square_ = this.square(square.file, rank); - if(!square_.hasPiece() || square_.getPiece().color != piece.color) + if(square_.pieceColor != square.pieceColor) { squares.add(square_); } @@ -1245,7 +1308,7 @@ else if(piece.type == PieceType.KNIGHT) { rank--; final Square square_ = this.square(square.file, rank); - if(!square_.hasPiece() || square_.getPiece().color != piece.color) + if(square_.pieceColor != square.pieceColor) { squares.add(square_); } @@ -1259,7 +1322,7 @@ else if(piece.type == PieceType.KNIGHT) { file++; final Square square_ = this.square(file, square.rank); - if(!square_.hasPiece() || square_.getPiece().color != piece.color) + if(square_.pieceColor != square.pieceColor) { squares.add(square_); } @@ -1273,7 +1336,7 @@ else if(piece.type == PieceType.KNIGHT) { file--; final Square square_ = this.square(file, square.rank); - if(!square_.hasPiece() || square_.getPiece().color != piece.color) + if(square_.pieceColor != square.pieceColor) { squares.add(square_); } @@ -1283,7 +1346,7 @@ else if(piece.type == PieceType.KNIGHT) } } } - if(piece.type == PieceType.BISHOP || piece.type == PieceType.QUEEN) + if(square.pieceType == PieceType.BISHOP || square.pieceType == PieceType.QUEEN) { byte file = square.file; byte rank = square.rank; @@ -1292,7 +1355,7 @@ else if(piece.type == PieceType.KNIGHT) file++; rank++; final Square square_ = this.square(file, rank); - if(!square_.hasPiece() || square_.getPiece().color != piece.color) + if(square_.pieceColor != square.pieceColor) { squares.add(square_); } @@ -1308,7 +1371,7 @@ else if(piece.type == PieceType.KNIGHT) file++; rank--; final Square square_ = this.square(file, rank); - if(!square_.hasPiece() || square_.getPiece().color != piece.color) + if(square_.pieceColor != square.pieceColor) { squares.add(square_); } @@ -1324,7 +1387,7 @@ else if(piece.type == PieceType.KNIGHT) file--; rank++; final Square square_ = this.square(file, rank); - if(!square_.hasPiece() || square_.getPiece().color != piece.color) + if(square_.pieceColor != square.pieceColor) { squares.add(square_); } @@ -1340,7 +1403,7 @@ else if(piece.type == PieceType.KNIGHT) file--; rank--; final Square square_ = this.square(file, rank); - if(!square_.hasPiece() || square_.getPiece().color != piece.color) + if(square_.pieceColor != square.pieceColor) { squares.add(square_); } @@ -1357,46 +1420,37 @@ else if(piece.type == PieceType.KNIGHT) public ArrayList getSquaresControlledBy(Color color) { final ArrayList squares = new ArrayList<>(); - synchronized(this.pieces) + for(Square s : this.getPieces(color)) { - for(Piece p : this.pieces) - { - if(p.color == color) - { - squares.addAll(this.getSquaresControlledBy(p)); - } - } + squares.addAll(this.getSquaresControlledBy(s)); } return squares; } - public ArrayList getControllers(Square square) + public ArrayList getControllers(Square square) { - final ArrayList controllers = new ArrayList<>(); + final ArrayList controllers = new ArrayList<>(); synchronized(this.pieces) { - for(Piece p : pieces) + for(Square s : pieces) { - if(this.getSquaresControlledBy(p).contains(square)) + if(this.getSquaresControlledBy(s).contains(square)) { - controllers.add(p); + controllers.add(s); } } } return controllers; } - public ArrayList getControllers(Square square, Color color) + public ArrayList getControllers(Square square, Color by) { - final ArrayList controllers = new ArrayList<>(); - synchronized(this.pieces) + final ArrayList controllers = new ArrayList<>(); + for(Square s : this.getPieces(by)) { - for(Piece p : pieces) + if(s.pieceColor == by && this.getSquaresControlledBy(s).contains(square)) { - if(p.color == color && this.getSquaresControlledBy(p).contains(square)) - { - controllers.add(p); - } + controllers.add(s); } } return controllers; @@ -1461,20 +1515,13 @@ public boolean isStalemate(boolean isCheck) throws ChessException { return false; } - synchronized(pieces) + for(Square s : this.getPieces(this.toMove)) { - for(Piece p : pieces) + for(Square s_ : this.getSquaresControlledBy(s)) { - if(p.color == this.toMove) + if(new Move(this, s, s_, null, false).isLegal()) { - final Square s = p.getSquare(); - for(Square s_ : this.getSquaresControlledBy(p)) - { - if(new Move(this, s, s_, null, true).isLegal()) - { - return false; - } - } + return false; } } } @@ -1501,73 +1548,34 @@ public boolean isCheckmate(boolean isCheck) throws ChessException } else if(this.variant == Variant.KING_OF_THE_HILL) { - synchronized(this.pieces) + Square s = this.getPieces(this.toMove.opposite(), PieceType.KING).get(0); + if((s.file == 3 || s.file == 4) && (s.rank == 3 || s.rank == 4)) { - for(Piece p : this.pieces) - { - if(p.color != this.toMove && p.type == PieceType.KING) - { - byte file = p.getSquare().file; - byte rank = p.getSquare().rank; - if((file == 3 || file == 4) && (rank == 3 || rank == 4)) - { - return true; - } - } - } + return true; } } else if(this.variant == Variant.RACING_KINGS) { - synchronized(this.pieces) - { - for(Piece p : this.pieces) - { - if(p.color != this.toMove && p.type == PieceType.KING && p.getSquare().rank == 7) - { - return true; - } - } - } - return false; + return this.getPieces(this.toMove.opposite(), PieceType.KING).get(0).rank == 7; } else if(this.variant == Variant.HORDE && this.toMove == Color.WHITE) { - synchronized(this.pieces) + if(this.getPieces(Color.WHITE).size() == 0) { - boolean whiteHasPieces = false; - for(Piece p : this.pieces) - { - if(p.color == Color.WHITE) - { - whiteHasPieces = true; - break; - } - } - if(!whiteHasPieces) - { - return true; - } + return true; } } if(!isCheck) { return this.variant == Variant.ANTICHESS && isStalemate(false); } - synchronized(this.pieces) + for(Square s : this.getPieces(this.toMove)) { - for(Piece p : this.pieces) + for(Square s_ : this.getSquaresControlledBy(s)) { - if(p.color == this.toMove) + if(!new Move(this, s, s_, null, true).commitTo(this.copy(), true).opponentToMove().isCheck()) { - final Square s = p.getSquare(); - for(Square s_ : this.getSquaresControlledBy(p)) - { - if(!new Move(this, s, s_, null, true).commitTo(this.copy(), true).opponentToMove().isCheck()) - { - return false; - } - } + return false; } } } @@ -1622,22 +1630,12 @@ public Game setBlack(String name, String elo) public boolean isCheck() { - synchronized(this.pieces) + final ArrayList squaresControlledByOpponent = this.getSquaresControlledBy(this.toMove.opposite()); + for(Square s : this.getPieces(this.toMove, PieceType.KING)) { - final ArrayList squaresControlledByOpponent = this.getSquaresControlledBy(this.toMove.opposite()); - for(Piece p : pieces) + if(squaresControlledByOpponent.contains(s)) { - if(p.color == this.toMove && p.type == PieceType.KING) - { - Square kingSquare = p.getSquare(); - for(Square s : squaresControlledByOpponent) - { - if(s.equals(kingSquare)) - { - return true; - } - } - } + return true; } } return false; @@ -1667,18 +1665,18 @@ public String getPositionalFEN(final boolean compact) for(byte file = 0; file < 8; file++) { Square square = this.square(file, rank); - if(!square.hasPiece()) - { - emptySquares++; - } - else + if(square.hasPiece()) { if(emptySquares > 0) { sb.append(emptySquares); emptySquares = 0; } - sb.append(square.getPiece().getCharacter(Language.ENGLISH)); + sb.append(square.getCharacter(Language.ENGLISH)); + } + else + { + emptySquares++; } } if(emptySquares > 0) @@ -1741,40 +1739,40 @@ void determineCastlingAbilities() { return; } - Piece piece = square((byte) 4, (byte) 0).getPiece(); - if(piece == null || piece.type != PieceType.KING || piece.color != Color.WHITE) + Square square = square((byte) 4, (byte) 0); + if(square.pieceType != PieceType.KING || square.pieceColor != Color.WHITE) { whiteCanCastle = false; whiteCanCastleQueenside = false; } else { - piece = square((byte) 7, (byte) 0).getPiece(); - if(piece == null || piece.type != PieceType.ROOK || piece.color != Color.WHITE) + square = square((byte) 7, (byte) 0); + if(square.pieceType != PieceType.ROOK || square.pieceColor != Color.WHITE) { whiteCanCastle = false; } - piece = square((byte) 0, (byte) 0).getPiece(); - if(piece == null || piece.type != PieceType.ROOK || piece.color != Color.WHITE) + square = square((byte) 0, (byte) 0); + if(square.pieceType != PieceType.ROOK || square.pieceColor != Color.WHITE) { whiteCanCastleQueenside = false; } } - piece = square((byte) 4, (byte) 7).getPiece(); - if(piece == null || piece.type != PieceType.KING || piece.color != Color.BLACK) + square = square((byte) 4, (byte) 7); + if(square.pieceType != PieceType.KING || square.pieceColor != Color.BLACK) { blackCanCastle = false; blackCanCastleQueenside = false; } else { - piece = square((byte) 7, (byte) 7).getPiece(); - if(piece == null || piece.type != PieceType.ROOK || piece.color != Color.BLACK) + square = square((byte) 7, (byte) 7); + if(square.pieceType != PieceType.ROOK || square.pieceColor != Color.BLACK) { blackCanCastle = false; } - piece = square((byte) 0, (byte) 7).getPiece(); - if(piece == null || piece.type != PieceType.ROOK || piece.color != Color.BLACK) + square = square((byte) 0, (byte) 7); + if(square.pieceType != PieceType.ROOK || square.pieceColor != Color.BLACK) { blackCanCastleQueenside = false; } @@ -2154,31 +2152,24 @@ public ArrayList getPossibleMoves() throws ChessException public ArrayList getPossibleMoves(boolean includeIllegal) throws ChessException { final ArrayList moves = new ArrayList<>(); - synchronized(this.pieces) + for(Square _s : this.getPieces(this.toMove)) { - for(Piece p : this.pieces) + for(Square s : this.getSquaresControlledBy(_s)) { - if(p.color == this.toMove) + Move m = new Move(this, _s, s, null, true); + if(includeIllegal || m.isLegal()) { - Square _s = p.getSquare(); - for(Square s : this.getSquaresControlledBy(p)) + if(s.pieceType == PieceType.PAWN && (toMove == Color.WHITE ? s.rank == 7 : s.rank == 0)) { - Move m = new Move(this, _s, s, null, true); - if(includeIllegal || m.isLegal()) + for(PieceType pt : this.variant.getPossiblePromotions()) { - if(p.type == PieceType.PAWN && (m.toSquare.rank == 0 || m.toSquare.rank == 7)) - { - for(PieceType pt : this.variant.getPossiblePromotions()) - { - moves.add(new Move(this, _s, s, pt, true)); - } - } - else - { - moves.add(m); - } + moves.add(new Move(this, _s, s, pt, true)); } } + else + { + moves.add(m); + } } } } @@ -2252,31 +2243,39 @@ public ArrayList getPossibleMoves(boolean includeIllegal) throws ChessExce return moves; } - public ArrayList getAttackers(Piece piece) + public ArrayList getAttackers(Square square) { - return this.getControllers(piece.getSquare(), piece.color.opposite()); + if(square.hasPiece()) + { + return this.getControllers(square, square.pieceColor.opposite()); + } + return new ArrayList<>(); } - public ArrayList getDefenders(Piece piece) + public ArrayList getDefenders(Square square) { - return this.getControllers(piece.getSquare(), piece.color); + if(square.hasPiece()) + { + return this.getControllers(square, square.pieceColor); + } + return new ArrayList<>(); } - public boolean isHanging(Piece piece) + public boolean isHanging(Square square) { - byte attackers = 0; - for(Piece p : getControllers(piece.getSquare())) + boolean attacked = false; + for(Square s : this.getControllers(square)) { - if(p.color == piece.color) + if(s.pieceColor == square.pieceColor) { return false; } - else + else if(!attacked) { - attackers++; + attacked = true; } } - return attackers > 0; + return attacked; } public short getMaterialScore(Color perspective) @@ -2285,15 +2284,15 @@ public short getMaterialScore(Color perspective) short blackscore = 0; synchronized(this.pieces) { - for(Piece p : this.pieces) + for(Square s : this.pieces) { - if(p.color == Color.WHITE) + if(s.pieceColor == Color.WHITE) { - whitescore += p.getValue(); + whitescore += s.getMaterialValue(); } else { - blackscore += p.getValue(); + blackscore += s.getMaterialValue(); } } } @@ -2310,15 +2309,9 @@ public short getMaterialScore(Color perspective) public short getMaterialScoreOf(Color color) { short score = 0; - synchronized(this.pieces) + for(Square s : this.getPieces(color)) { - for(Piece p : this.pieces) - { - if(p.color == color) - { - score += p.getValue(); - } - } + score += s.getMaterialValue(); } return score; } @@ -2462,9 +2455,8 @@ public String toSVG(boolean indicateLastMove, boolean indicateCheck) Square square = this.square(file, rank); if(square.hasPiece()) { - Piece p = square.getPiece(); - String piece_svg = p.getSVG(); - if(isCheck && p.color == this.toMove && p.type == PieceType.KING) + String piece_svg = square.getSVG(); + if(isCheck && square.pieceColor == this.toMove && square.pieceType == PieceType.KING) { svg.append(""); } diff --git a/src/sh/hell/compactchess/game/Move.java b/src/sh/hell/compactchess/game/Move.java index 6538d6f..55ebd16 100644 --- a/src/sh/hell/compactchess/game/Move.java +++ b/src/sh/hell/compactchess/game/Move.java @@ -22,12 +22,11 @@ public class Move public Move(Game game, Square fromSquare, Square toSquare, PieceType promoteTo, boolean validate) throws ChessException { - final Piece piece = fromSquare.getPiece(); if(game.status == GameStatus.BUILDING) { throw new InvalidMoveException("The game has not started yet"); } - if(piece == null) + if(!fromSquare.hasPiece()) { throw new InvalidMoveException("There's no piece on " + fromSquare.getAlgebraicNotation()); } @@ -35,23 +34,11 @@ public Move(Game game, Square fromSquare, Square toSquare, PieceType promoteTo, { throw new InvalidMoveException("Can't move to the same square"); } - if(promoteTo != null) - { - if(piece.type != PieceType.PAWN) - { - throw new InvalidMoveException("Only pawns can be promoted"); - } - if(!game.variant.getPossiblePromotions().contains(promoteTo)) - { - throw new InvalidMoveException("You can't promote to " + promoteTo.name().toLowerCase() + " in " + game.variant.name); - } - } - if(piece.type == PieceType.KING && (piece.color == Color.WHITE ? (fromSquare.rank == 0 && (game.whiteCanCastle || game.whiteCanCastleQueenside)) : (fromSquare.rank == 7 && (game.blackCanCastle || game.blackCanCastleQueenside)))) + if(fromSquare.pieceType == PieceType.KING && (fromSquare.pieceColor == Color.WHITE ? (fromSquare.rank == 0 && (game.whiteCanCastle || game.whiteCanCastleQueenside)) : (fromSquare.rank == 7 && (game.blackCanCastle || game.blackCanCastleQueenside)))) { if(game.variant == Variant.CHESS960) { - final Piece toPiece = toSquare.getPiece(); - if(toPiece != null && toPiece.type == PieceType.ROOK && toPiece.color == piece.color) + if(toSquare.pieceType == PieceType.ROOK && toSquare.pieceColor == fromSquare.pieceColor) { if(toSquare.file > fromSquare.file) { @@ -91,9 +78,9 @@ else if(toSquare.file == 2) { this.castlingType = CastlingType.NONE; } - if(validate && this.castlingType == CastlingType.NONE && !game.getSquaresControlledBy(fromSquare.getPiece()).contains(toSquare)) + if(validate && this.castlingType == CastlingType.NONE && !game.getSquaresControlledBy(fromSquare).contains(toSquare)) { - throw new InvalidMoveException("Your " + fromSquare.getPiece().type.name().toLowerCase() + " on " + fromSquare.getAlgebraicNotation() + " can't move to " + toSquare.getAlgebraicNotation()); + throw new InvalidMoveException("Your " + fromSquare.pieceType.name().toLowerCase() + " on " + fromSquare.getAlgebraicNotation() + " can't move to " + toSquare.getAlgebraicNotation()); } this.game = new WeakReference<>(game); this._game = game.copy(); @@ -101,7 +88,7 @@ else if(toSquare.file == 2) this.fromSquare = fromSquare; this.toSquare = toSquare; this.promoteTo = promoteTo; - this.isEnPassant = fromSquare.getPiece().type == PieceType.PAWN && toSquare.equals(game.enPassantSquare); + this.isEnPassant = fromSquare.pieceType == PieceType.PAWN && toSquare.equals(game.enPassantSquare); } public Move annotate(String annotation) @@ -166,9 +153,10 @@ public String getAnnotation(boolean noTags) private void handle(Game game, boolean doCounting, boolean dontCalculate) throws ChessException { - Square fromSquare = game.square(this.fromSquare); - Piece fromPiece = fromSquare.getPiece(); - fromSquare.unsetPiece(); + final Square fromSquare = game.square(this.fromSquare); + Color fromPieceColor = fromSquare.pieceColor; + PieceType fromPieceType = fromSquare.pieceType; + game.unsetPiece(fromSquare); Square toSquare = null; if(doCounting && this.castlingType != CastlingType.NONE) { @@ -211,25 +199,21 @@ private void handle(Game game, boolean doCounting, boolean dontCalculate) throws if(toSquare.hasPiece()) { capture = true; - synchronized(game.pieces) + if(toSquare.pieceType == PieceType.PAWN) { - if(!dontCalculate && toSquare.getPiece().type == PieceType.PAWN) + synchronized(game.repetitionPostitions) { - synchronized(game.repetitionPostitions) - { - game.repetitionPostitions.clear(); - } + game.repetitionPostitions.clear(); } - game.pieces.remove(toSquare.getPiece()); } + game.unsetPiece(toSquare); } - toSquare.setPiece(fromPiece); - toSquare.getPiece().setSquare(toSquare); - if(fromPiece.type == PieceType.PAWN) + game.setPiece(toSquare, fromPieceColor, fromPieceType); + if(fromPieceType == PieceType.PAWN) { if(promoteTo != null) { - toSquare.getPiece().type = promoteTo; + toSquare.pieceType = promoteTo; } if(doCounting) { @@ -248,9 +232,9 @@ else if(doCounting) } if(!dontCalculate && game.variant == Variant.CHESS960) { - if(fromPiece.type == PieceType.KING) + if(fromPieceType == PieceType.KING) { - if(fromPiece.color == Color.WHITE) + if(fromPieceColor == Color.WHITE) { game.whiteCanCastle = false; game.whiteCanCastleQueenside = false; @@ -261,24 +245,14 @@ else if(doCounting) game.blackCanCastleQueenside = false; } } - else if(fromPiece.type == PieceType.ROOK) + else if(fromPieceType == PieceType.ROOK) { - byte kingFile = 8; - synchronized(game.pieces) - { - for(Piece p : game.pieces) - { - if(p.color == fromPiece.color && p.type == PieceType.KING) - { - kingFile = p.getSquare().file; - } - } - } + byte kingFile = game.getPieces(fromPieceColor, PieceType.KING).get(0).file; if(kingFile != 8) { if(fromSquare.file > kingFile) { - if(fromPiece.color == Color.WHITE) + if(fromPieceColor == Color.WHITE) { game.whiteCanCastle = false; } @@ -289,7 +263,7 @@ else if(fromPiece.type == PieceType.ROOK) } else { - if(fromPiece.color == Color.WHITE) + if(fromPieceColor == Color.WHITE) { game.whiteCanCastleQueenside = false; } @@ -308,7 +282,7 @@ public Game commitTo(Game game, boolean dontCalculate) throws ChessException { if(this.isEnPassant) { - Square epPieceSquare; + final Square epPieceSquare; if(game.enPassantSquare.rank == 2) { epPieceSquare = game.square(game.enPassantSquare.file, (byte) 3); @@ -319,11 +293,7 @@ public Game commitTo(Game game, boolean dontCalculate) throws ChessException } if(epPieceSquare.hasPiece()) { - synchronized(game.pieces) - { - game.pieces.remove(epPieceSquare.getPiece()); - } - epPieceSquare.unsetPiece(); + game.unsetPiece(epPieceSquare); synchronized(game.repetitionPostitions) { game.repetitionPostitions.clear(); @@ -332,14 +302,14 @@ public Game commitTo(Game game, boolean dontCalculate) throws ChessException } else { - Piece piece = game.square(fromSquare).getPiece(); - if(piece.type == PieceType.PAWN) + final Square fromSquare = game.square(this.fromSquare); + if(fromSquare.pieceType == PieceType.PAWN) { - if(piece.color == Color.WHITE && fromSquare.rank == 1 && toSquare.rank == 3) + if(fromSquare.pieceColor == Color.WHITE && fromSquare.rank == 1 && toSquare.rank == 3) { game.enPassantSquare = game.square(fromSquare.file, (byte) (fromSquare.rank + 1)); } - else if(piece.color == Color.BLACK && fromSquare.rank == 6 && toSquare.rank == 4) + else if(fromSquare.pieceColor == Color.BLACK && fromSquare.rank == 6 && toSquare.rank == 4) { game.enPassantSquare = game.square(fromSquare.file, (byte) (fromSquare.rank - 1)); } @@ -499,7 +469,8 @@ public boolean isLegal() throws ChessException public String getIllegalReason() throws ChessException { - if(this._game.square(this.fromSquare).getPiece().color != this._game.toMove) + final Square fromSquare = this._game.square(this.fromSquare); + if(fromSquare.pieceColor != this._game.toMove) { return "You can only move your own pieces"; } @@ -524,14 +495,7 @@ public String getIllegalReason() throws ChessException } if(this.commitInCopy(true, true).opponentToMove().isCheck()) { - if(this._game.isCheck()) - { - return "You need to get out of check"; - } - else - { - return "This would put you in check"; - } + return "You can't play a move which would leave you in check"; } if(this.castlingType != CastlingType.NONE) { @@ -616,13 +580,9 @@ public String getIllegalReason() throws ChessException for(byte file = (byte) (rookFile + 1); file <= rookDestination; file++) { Square s = this._game.square(file, rank); - if(s.hasPiece()) + if(s.hasPiece() && (s.pieceType != PieceType.KING || s.pieceColor != this._game.toMove)) { - Piece p = s.getPiece(); - if(p.type != PieceType.KING || p.color != this._game.toMove) - { - return "You can't castle because " + s.getAlgebraicNotation() + " is occupied"; - } + return "You can't castle because " + s.getAlgebraicNotation() + " is occupied"; } } } @@ -631,13 +591,9 @@ public String getIllegalReason() throws ChessException for(byte file = (byte) (rookFile - 1); file >= rookDestination; file--) { Square s = this._game.square(file, rank); - if(s.hasPiece()) + if(s.hasPiece() && (s.pieceType != PieceType.KING || s.pieceColor != this._game.toMove)) { - Piece p = s.getPiece(); - if(p.type != PieceType.KING || p.color != this._game.toMove) - { - return "You can't castle because " + s.getAlgebraicNotation() + " is occupied"; - } + return "You can't castle because " + s.getAlgebraicNotation() + " is occupied"; } } } @@ -659,6 +615,17 @@ public String getIllegalReason() throws ChessException } */ } + if(this.promoteTo != null) + { + if(fromSquare.pieceType != PieceType.PAWN) + { + return "Only pawns can be promoted"; + } + if(!this._game.variant.getPossiblePromotions().contains(promoteTo)) + { + return "You can't promote to " + promoteTo.name().toLowerCase() + " in the " + this._game.variant.name + " variant"; + } + } return null; } @@ -733,17 +700,17 @@ public String toAlgebraicNotation(AlgebraicNotationVariation variation, Language } break; default: - final Piece piece = _game.square(fromSquare).getPiece(); + final Square fromSquare = _game.square(this.fromSquare); final boolean capture = _game.square(toSquare).hasPiece(); - if(piece.type != PieceType.PAWN) + if(fromSquare.pieceType != PieceType.PAWN) { if(variation == AlgebraicNotationVariation.FAN) { - an.append(piece.getSymbol(false)); + an.append(fromSquare.getSymbol(false)); } else { - an.append(piece.type.getNotationChar(language)); + an.append(fromSquare.pieceType.getNotationChar(language)); } } if(variation == AlgebraicNotationVariation.LAN || variation == AlgebraicNotationVariation.RAN) @@ -752,24 +719,20 @@ public String toAlgebraicNotation(AlgebraicNotationVariation variation, Language } else { - final ArrayList ambiguities = new ArrayList<>(); - for(Piece p : _game.pieces) + final ArrayList ambiguities = new ArrayList<>(); + for(Square s : _game.getPieces(fromSquare.pieceColor, fromSquare.pieceType)) { - if(p.getSquare().equals(fromSquare) || p.color != piece.color || p.type != piece.type) - { - continue; - } - if(_game.getSquaresControlledBy(p).contains(toSquare)) + if(!s.equals(fromSquare) && _game.getSquaresControlledBy(s).contains(toSquare)) { - ambiguities.add(p); + ambiguities.add(s); } } - if(ambiguities.size() > 0 || (capture && piece.type == PieceType.PAWN)) + if(ambiguities.size() > 0 || (capture && fromSquare.pieceType == PieceType.PAWN)) { boolean solutionWorks = true; - for(Piece p : ambiguities) + for(Square s : ambiguities) { - if(p.getSquare().file == fromSquare.file) + if(s.file == fromSquare.file) { solutionWorks = false; break; @@ -782,9 +745,9 @@ public String toAlgebraicNotation(AlgebraicNotationVariation variation, Language else { solutionWorks = true; - for(Piece p : ambiguities) + for(Square s : ambiguities) { - if(p.getSquare().rank == fromSquare.rank) + if(s.rank == fromSquare.rank) { solutionWorks = false; break; @@ -808,10 +771,10 @@ public String toAlgebraicNotation(AlgebraicNotationVariation variation, Language an.append("x"); if(variation == AlgebraicNotationVariation.RAN) { - final Piece piece_ = _game.square(toSquare).getPiece(); - if(piece_ != null) + Square toSquare = _game.square(this.toSquare); + if(toSquare.hasPiece()) { - an.append(piece_.type.getDisplayChar(language)); + an.append(toSquare.pieceType.getDisplayChar(language)); } } } @@ -826,7 +789,7 @@ else if(variation == AlgebraicNotationVariation.LAN || variation == AlgebraicNot an.append("="); if(variation == AlgebraicNotationVariation.FAN) { - an.append(piece.color == Color.WHITE ? this.promoteTo.whiteSymbol : this.promoteTo.blackSymbol); + an.append(fromSquare.pieceColor == Color.WHITE ? this.promoteTo.whiteSymbol : this.promoteTo.blackSymbol); } else { @@ -851,7 +814,7 @@ public boolean equals(Object o2) { if(o2 instanceof Move) { - return this.fromSquare.equals(((Move) o2).fromSquare) && this.toSquare.equals(((Move) o2).toSquare) && ((this.promoteTo == null && ((Move) o2).promoteTo == null) || (this.promoteTo != null && ((Move) o2).promoteTo != null && this.promoteTo.equals(((Move) o2).promoteTo))) && this.isEnPassant == ((Move) o2).isEnPassant && this.castlingType == ((Move) o2).castlingType; + return this.fromSquare.coordinateEquals(((Move) o2).fromSquare) && this.toSquare.coordinateEquals(((Move) o2).toSquare) && ((this.promoteTo == null && ((Move) o2).promoteTo == null) || (this.promoteTo != null && ((Move) o2).promoteTo != null && this.promoteTo.equals(((Move) o2).promoteTo))) && this.isEnPassant == ((Move) o2).isEnPassant && this.castlingType == ((Move) o2).castlingType; } return false; } diff --git a/src/sh/hell/compactchess/game/Piece.java b/src/sh/hell/compactchess/game/Piece.java deleted file mode 100644 index 9ec1740..0000000 --- a/src/sh/hell/compactchess/game/Piece.java +++ /dev/null @@ -1,103 +0,0 @@ -package sh.hell.compactchess.game; - -import java.lang.ref.WeakReference; - -@SuppressWarnings({"WeakerAccess", "UnusedReturnValue", "unused"}) -public class Piece -{ - public final Color color; - public PieceType type; - private WeakReference square; - - public Piece(final PieceType type, final Color color) - { - this.type = type; - this.color = color; - } - - public Piece(final PieceType type, final Color color, final Square square) - { - this.type = type; - this.color = color; - this.setSquare(square); - } - - public boolean hasSquare() - { - return this.square != null; - } - - public Square getSquare() - { - if(square == null) - { - return null; - } - return square.get(); - } - - void setSquare(final Square square) - { - this.square = new WeakReference<>(square); - } - - public String getCharacter(Language language) - { - return (color == Color.WHITE ? type.getDisplayChar(language).toUpperCase() : type.getDisplayChar(language).toLowerCase()); - } - - public String getSymbol(final boolean invertColor) - { - boolean isWhite = (color == Color.WHITE); - if(invertColor) - { - isWhite = !isWhite; - } - return (isWhite ? type.whiteSymbol : type.blackSymbol); - } - - public byte getValue() - { - return this.type.value; - } - - public String getSVG() - { - return (color == Color.WHITE ? type.whiteSVG : type.blackSVG); - } - - public String toString() - { - if(!this.hasSquare()) - { - return this.toStringLite(); - } - return "{Piece " + color.name() + " " + type.name() + " on " + this.getSquare().toStringLite() + "}"; - } - - public String toStringLite() - { - return "{Piece " + color.name() + " " + type.name() + "}"; - } - - public Piece copy() - { - final Piece piece = new Piece(type, color, getSquare().copy()); - if(square != null) - { - piece.setSquare(getSquare().copy(piece)); - } - return piece; - } - - Piece copy(final Square square) - { - return new Piece(type, color, square); - } - - @Override - public boolean equals(Object o2) - { - return o2 instanceof Piece && ((Piece) o2).color == this.color && ((Piece) o2).type == this.type && ((Piece) o2).square == this.square; - } -} diff --git a/src/sh/hell/compactchess/game/PieceType.java b/src/sh/hell/compactchess/game/PieceType.java index 6fd8eea..9265468 100644 --- a/src/sh/hell/compactchess/game/PieceType.java +++ b/src/sh/hell/compactchess/game/PieceType.java @@ -2,24 +2,22 @@ public enum PieceType { - PAWN(1, 100, "♙", "♟", "", ""), - KNIGHT(3, 277, "♘", "♞", "", ""), - BISHOP(3, 300, "♗", "♝", "", ""), - ROOK(5, 500, "♖", "♜", "", ""), - QUEEN(9, 900, "♕", "♛", "", ""), - KING(1, 1100, "♔", "♚", "", ""); + PAWN(1, "♙", "♟", "", ""), + KNIGHT(3, "♘", "♞", "", ""), + BISHOP(3, "♗", "♝", "", ""), + ROOK(5, "♖", "♜", "", ""), + QUEEN(9, "♕", "♛", "", ""), + KING(0, "♔", "♚", "", ""); - public final byte value; - public final short scoreValue; + public final byte materialValue; public final String whiteSymbol; public final String blackSymbol; public final String whiteSVG; public final String blackSVG; - PieceType(int value, int scoreValue, String whiteSymbol, String blackSymbol, String whiteSVG, String blackSVG) + PieceType(int materialValue, String whiteSymbol, String blackSymbol, String whiteSVG, String blackSVG) { - this.value = (byte) value; - this.scoreValue = (short) scoreValue; + this.materialValue = (byte) materialValue; this.whiteSymbol = whiteSymbol; this.blackSymbol = blackSymbol; this.whiteSVG = whiteSVG; diff --git a/src/sh/hell/compactchess/game/Square.java b/src/sh/hell/compactchess/game/Square.java index f0a5f03..99f2b9a 100644 --- a/src/sh/hell/compactchess/game/Square.java +++ b/src/sh/hell/compactchess/game/Square.java @@ -7,7 +7,8 @@ public class Square { public final byte file; public final byte rank; - private Piece piece; + public Color pieceColor = null; + public PieceType pieceType = null; Square(final byte file, final byte rank) { @@ -15,11 +16,12 @@ public class Square this.rank = rank; } - Square(final byte file, final byte rank, final Piece piece) + Square(final byte file, final byte rank, Color pieceColor, final PieceType pieceType) { this.file = file; this.rank = rank; - this.piece = piece; + this.pieceColor = pieceColor; + this.pieceType = pieceType; } static byte index(final byte file, final byte rank) @@ -79,25 +81,10 @@ public static String fileChar(byte file) throws ChessException public boolean hasPiece() { - return this.piece != null; + return this.pieceType != null; } - public Piece getPiece() - { - return piece; - } - - void setPiece(final Piece piece) - { - this.piece = piece; - } - - void unsetPiece() - { - this.piece = null; - } - - byte index() + public byte index() { return Square.index(file, rank); } @@ -109,14 +96,11 @@ public String getAlgebraicNotation() public String getCharacter(Language language) { - if(this.piece == null) + if(this.hasPiece()) { - return " "; - } - else - { - return this.getPiece().getCharacter(language); + return (pieceColor == Color.WHITE ? pieceType.getDisplayChar(language).toUpperCase() : pieceType.getDisplayChar(language).toLowerCase()); } + return " "; } public boolean isWhite() @@ -131,14 +115,25 @@ public boolean isBlack() public String getSymbol(final boolean invertColor) { - if(this.piece == null) + if(this.hasPiece()) { - return ((invertColor ? isBlack() : isWhite()) ? "□" : "■"); + return ((pieceColor == Color.WHITE) ^ invertColor ? pieceType.whiteSymbol : pieceType.blackSymbol); } - else + return (isWhite() ^ invertColor ? "□" : "■"); + } + + public String getSVG() + { + return (pieceColor == Color.WHITE ? pieceType.whiteSVG : pieceType.blackSVG); + } + + public byte getMaterialValue() + { + if(this.pieceType != null) { - return this.getPiece().getSymbol(invertColor); + return this.pieceType.materialValue; } + return 0; } public String getFileChar() @@ -155,36 +150,22 @@ public String getFileChar() public String toString() { - if(!this.hasPiece()) - { - return toStringLite(); - } - return "{Square " + getAlgebraicNotation() + " with " + getPiece().toStringLite() + "}"; - } - - public String toStringLite() - { - return "{Square " + getAlgebraicNotation() + "}"; + return "{Square " + getAlgebraicNotation() + " containing " + pieceColor + " " + pieceType + "}"; } public Square copy() { - Square square = new Square(file, rank); - if(piece != null) - { - square.setPiece(getPiece().copy(square)); - return square; - } - return square; + return new Square(file, rank, pieceColor, pieceType); } - Square copy(final Piece piece) + public boolean coordinateEquals(final Object o2) { - return new Square(file, rank, piece); + return o2 instanceof Square && this.file == ((Square) o2).file && this.rank == ((Square) o2).rank; } + @Override public boolean equals(final Object o2) { - return o2 instanceof Square && (this.file == ((Square) o2).file && this.rank == ((Square) o2).rank); + return o2 instanceof Square && this.file == ((Square) o2).file && this.rank == ((Square) o2).rank && this.pieceColor == ((Square) o2).pieceColor && this.pieceType == ((Square) o2).pieceType; } }