From ff68def767c533781f849664585a7d1ed5a28d13 Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Mon, 22 Jul 2019 14:51:24 +0200 Subject: [PATCH] First working UCI implementation + bestmove, position and go command implementations + Move initialization from algebraic notation + FEN string generation --- src/dev/kske/chess/board/Board.java | 96 +++++++++++++++++- src/dev/kske/chess/board/Move.java | 5 + src/dev/kske/chess/board/Position.java | 4 + src/dev/kske/chess/game/Game.java | 3 +- src/dev/kske/chess/game/UCIPlayer.java | 94 ++++++++++++++++-- .../kske/chess/game/UCIPlayerListener.java | 99 ------------------- src/dev/kske/chess/uci/UCIHandle.java | 16 ++- src/dev/kske/chess/uci/UCIReceiver.java | 13 ++- 8 files changed, 221 insertions(+), 109 deletions(-) delete mode 100644 src/dev/kske/chess/game/UCIPlayerListener.java diff --git a/src/dev/kske/chess/board/Board.java b/src/dev/kske/chess/board/Board.java index 546ccde..7404fc3 100644 --- a/src/dev/kske/chess/board/Board.java +++ b/src/dev/kske/chess/board/Board.java @@ -19,8 +19,11 @@ public class Board implements Cloneable { private Piece[][] boardArr; private Map kingPos; + private Color activeColor; private Log log; + private int fullmoveCounter, halfmoveClock; + private static final Map positionScores; static { @@ -101,6 +104,8 @@ public class Board implements Cloneable { Piece piece = getPos(move); Piece capturePiece = getDest(move); + // TODO: reset halfmove clock + switch (move.type) { case PAWN_PROMOTION: setPos(move, null); @@ -140,6 +145,15 @@ public class Board implements Cloneable { // Update log log.add(move, capturePiece); + + // Swap active color + activeColor = activeColor.opposite(); + + // Increment fullmove counter + if (piece.getColor() == Color.BLACK) ++fullmoveCounter; + + // Increment halfmove clock + ++halfmoveClock; } /** @@ -188,6 +202,15 @@ public class Board implements Cloneable { // Update log log.removeLast(); + + // Swap active color + activeColor = activeColor.opposite(); + + // Decrement fullmove counter + if (getPos(move).getColor() == Color.BLACK) --fullmoveCounter; + + // Decrement halfmove clock + --halfmoveClock; } /** @@ -330,6 +353,11 @@ public class Board implements Cloneable { for (int i = 0; i < 8; i++) for (int j = 2; j < 6; j++) boardArr[i][j] = null; + + activeColor = Color.WHITE; + + fullmoveCounter = 1; + halfmoveClock = 0; } /** @@ -354,12 +382,76 @@ public class Board implements Cloneable { board.kingPos = new HashMap<>(); board.kingPos.putAll(kingPos); - + // TODO: activeColor clone ? board.log = (Log) log.clone(); return board; } + /** + * @return A FEN string representing the board + */ + public String toFEN() { + StringBuilder sb = new StringBuilder(); + + // Piece placement (from white's perspective) + for (int i = 0; i < 8; i++) { + int emptyCount = 0; + for (int j = 0; j < 8; j++) + if (boardArr[j][i] == null) ++emptyCount; + else { + if (emptyCount != 0) { + sb.append(emptyCount); + emptyCount = 0; + } + char piece; + switch (boardArr[j][i].getType()) { + case KING: + piece = 'K'; + break; + case QUEEN: + piece = 'Q'; + break; + case ROOK: + piece = 'R'; + break; + case KNIGHT: + piece = 'N'; + break; + case BISHOP: + piece = 'B'; + break; + case PAWN: + piece = 'P'; + break; + default: + piece = '-'; + } + if (boardArr[j][i].getColor() == Color.BLACK) piece = Character.toLowerCase(piece); + sb.append(piece); + } + if (emptyCount != 0) sb.append(emptyCount); + if (i < 7) sb.append('/'); + } + + // Active color + sb.append(" " + (activeColor == Color.WHITE ? 'w' : 'b')); + + // TODO: castling rights + sb.append(" -"); + + // TODO: en passant availability + sb.append(" -"); + + // Halfmove clock + sb.append(" " + halfmoveClock); + + // Fullmove counter + sb.append(" " + fullmoveCounter); + + return sb.toString(); + } + public Piece get(Position pos) { return boardArr[pos.x][pos.y]; } @@ -388,4 +480,6 @@ public class Board implements Cloneable { * @return The board array */ public Piece[][] getBoardArr() { return boardArr; } + + public Color getActiveColor() { return activeColor; } } diff --git a/src/dev/kske/chess/board/Move.java b/src/dev/kske/chess/board/Move.java index 003f493..eafb9b9 100644 --- a/src/dev/kske/chess/board/Move.java +++ b/src/dev/kske/chess/board/Move.java @@ -30,6 +30,11 @@ public class Move { this(new Position(xPos, yPos), new Position(xDest, yDest)); } + public static Move fromAlgebraicNotation(String move) { + return new Move(Position.fromAlgebraicNotation(move.substring(0, 2)), + Position.fromAlgebraicNotation(move.substring(2))); + } + public boolean isHorizontal() { return yDist == 0; } public boolean isVertical() { return xDist == 0; } diff --git a/src/dev/kske/chess/board/Position.java b/src/dev/kske/chess/board/Position.java index c007f50..a3dcadd 100644 --- a/src/dev/kske/chess/board/Position.java +++ b/src/dev/kske/chess/board/Position.java @@ -15,6 +15,10 @@ public class Position { this.y = y; } + public static Position fromAlgebraicNotation(String pos) { + return new Position(pos.charAt(0) - 97, 7 - (Character.getNumericValue(pos.charAt(1)) - 1)); + } + @Override public String toString() { return String.format("[%d, %d]", x, y); diff --git a/src/dev/kske/chess/game/Game.java b/src/dev/kske/chess/game/Game.java index 40cd720..18d3228 100644 --- a/src/dev/kske/chess/game/Game.java +++ b/src/dev/kske/chess/game/Game.java @@ -73,6 +73,7 @@ public class Game { public void onMove(Player player, Move move) { if (board.getPos(move).getColor() == player.color && board.attemptMove(move)) { System.out.printf("%s: %s%n", player.color, move); + System.out.println("FEN: " + board.toFEN()); GameState eventType = board.getGameEventType(board.getDest(move).getColor().opposite()); switch (eventType) { case CHECKMATE: @@ -83,7 +84,7 @@ public class Game { System.out.printf("%s in check!%n", player.color.opposite()); default: boardComponent.repaint(); - players.get(player.color.opposite()).requestMove(); + players.get(board.getActiveColor()).requestMove(); } overlayComponent.displayArrow(move); } else player.requestMove(); diff --git a/src/dev/kske/chess/game/UCIPlayer.java b/src/dev/kske/chess/game/UCIPlayer.java index a34188d..0af08b2 100644 --- a/src/dev/kske/chess/game/UCIPlayer.java +++ b/src/dev/kske/chess/game/UCIPlayer.java @@ -1,9 +1,15 @@ package dev.kske.chess.game; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import dev.kske.chess.board.Move; import dev.kske.chess.board.Piece.Color; import dev.kske.chess.uci.UCIHandle; +import dev.kske.chess.uci.UCIListener; +import dev.kske.chess.uci.UCIOption; /** * Project: Chess
@@ -11,17 +17,19 @@ import dev.kske.chess.uci.UCIHandle; * Created: 18.07.2019
* Author: Kai S. K. Engelbart */ -public class UCIPlayer extends Player { +public class UCIPlayer extends Player implements UCIListener { - private UCIHandle handle; - private UCIPlayerListener listener; + private UCIHandle handle; + + private String name, author; + private List options; public UCIPlayer(Color color, String enginePath) { super(color); + options = new ArrayList<>(); try { handle = new UCIHandle(enginePath); - listener = new UCIPlayerListener(); - handle.setListener(listener); + handle.setListener(this); handle.start(); } catch (IOException ex) { ex.printStackTrace(); @@ -30,7 +38,8 @@ public class UCIPlayer extends Player { @Override public void requestMove() { - + handle.positionFEN(board.toFEN()); + handle.go(); } @Override @@ -38,7 +47,80 @@ public class UCIPlayer extends Player { handle.stop(); } + @Override public void disconnect() { handle.quit(); } + + @Override + public void onIdName(String name) { + this.name = name; + } + + @Override + public void onIdAuthor(String author) { + this.author = author; + } + + @Override + public void onUCIOk() { + System.out.println("UCI ok"); + } + + @Override + public void onReadyOk() { + System.out.println("Ready ok"); + } + + @Override + public void onBestMove(String move) { + Move moveObj = Move.fromAlgebraicNotation(move); + game.onMove(this, moveObj); + } + + @Override + public void onBestMove(String move, String ponderMove) { + onBestMove(move); + } + + @Override + public void onCopyProtectionChecking() { + System.out.println("Copy protection checking..."); + } + + @Override + public void onCopyProtectionOk() { + System.out.println("Copy protection ok"); + } + + @Override + public void onCopyProtectionError() { + System.err.println("Copy protection error!"); + } + + @Override + public void onRegistrationChecking() { + System.out.println("Registration checking..."); + } + + @Override + public void onRegistrationOk() { + System.out.println("Registration ok"); + } + + @Override + public void onRegistrationError() { + System.err.println("Registration error!"); + } + + @Override + public void onInfo(Map additionalInfo) { + System.out.println("Info:"); + additionalInfo.forEach((k, v) -> System.out.printf("%s: %s%n", k, v)); + } + + @Override + public void onOption(UCIOption option) { + options.add(option); + } } diff --git a/src/dev/kske/chess/game/UCIPlayerListener.java b/src/dev/kske/chess/game/UCIPlayerListener.java deleted file mode 100644 index 8bf61cd..0000000 --- a/src/dev/kske/chess/game/UCIPlayerListener.java +++ /dev/null @@ -1,99 +0,0 @@ -package dev.kske.chess.game; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import dev.kske.chess.uci.UCIListener; -import dev.kske.chess.uci.UCIOption; - -/** - * Project: Chess
- * File: UCIPlayerListener.java
- * Created: 20.07.2019
- * Author: Kai S. K. Engelbart - */ -class UCIPlayerListener implements UCIListener { - - private String name, author; - private List options; - - public UCIPlayerListener() { - options = new ArrayList<>(); - } - - @Override - public void onIdName(String name) { - this.name = name; - } - - @Override - public void onIdAuthor(String author) { - this.author = author; - } - - @Override - public void onUCIOk() { - System.out.println("UCI ok"); - } - - @Override - public void onReadyOk() { - System.out.println("Ready ok"); - } - - @Override - public void onBestMove(String move) { - // TODO Auto-generated method stub - } - - @Override - public void onBestMove(String move, String ponderMove) { - // TODO Auto-generated method stub - } - - @Override - public void onCopyProtectionChecking() { - System.out.println("Copy protection checking..."); - } - - @Override - public void onCopyProtectionOk() { - System.out.println("Copy protection ok"); - } - - @Override - public void onCopyProtectionError() { - System.err.println("Copy protection error!"); - } - - @Override - public void onRegistrationChecking() { - System.out.println("Registration checking..."); - } - - @Override - public void onRegistrationOk() { - System.out.println("Registration ok"); - } - - @Override - public void onRegistrationError() { - System.err.println("Registration error!"); - } - - @Override - public void onInfo(Map additionalInfo) { - System.out.println("Info:"); - additionalInfo.forEach((k, v) -> System.out.printf("%s: %s%n", k, v)); - } - - @Override - public void onOption(UCIOption option) { - options.add(option); - } - - public String getName() { return name; }; - - public String getAuthor() { return author; }; -} diff --git a/src/dev/kske/chess/uci/UCIHandle.java b/src/dev/kske/chess/uci/UCIHandle.java index a032473..8c8be2a 100644 --- a/src/dev/kske/chess/uci/UCIHandle.java +++ b/src/dev/kske/chess/uci/UCIHandle.java @@ -93,7 +93,21 @@ public class UCIHandle { } // TODO: position - // TODO: go + + /** + * Sets up the position described in the FEN string. + * + * @param fen FEN representation of the current board + */ + public void positionFEN(String fen) { + out.println("position fen " + fen); + } + + // TODO: go with parameters + + public void go() { + out.println("go"); + } /** * Stops calculation as soon as possible. diff --git a/src/dev/kske/chess/uci/UCIReceiver.java b/src/dev/kske/chess/uci/UCIReceiver.java index 2cce6cf..5793356 100644 --- a/src/dev/kske/chess/uci/UCIReceiver.java +++ b/src/dev/kske/chess/uci/UCIReceiver.java @@ -54,7 +54,9 @@ public class UCIReceiver implements Runnable { case "readyok": listener.onReadyOk(); break; - // TODO: bestmove + case "bestmove": + parseBestMove(line.substring(command.length() + 1)); + break; case "copyprotection": parseCopyProtection(line.substring(command.length() + 1)); break; @@ -87,6 +89,15 @@ public class UCIReceiver implements Runnable { } } + private void parseBestMove(String line) { + String[] tokens = line.split(" "); + String move = tokens[0]; + + // Ponder move + if (tokens.length == 3) listener.onBestMove(move, tokens[2]); + else listener.onBestMove(move); + } + private void parseCopyProtection(String line) { switch (line) { case "checking":