First working UCI implementation
+ bestmove, position and go command implementations + Move initialization from algebraic notation + FEN string generation
This commit is contained in:
parent
660d33d3b3
commit
46b74a527d
@ -19,8 +19,11 @@ public class Board implements Cloneable {
|
|||||||
|
|
||||||
private Piece[][] boardArr;
|
private Piece[][] boardArr;
|
||||||
private Map<Color, Position> kingPos;
|
private Map<Color, Position> kingPos;
|
||||||
|
private Color activeColor;
|
||||||
private Log log;
|
private Log log;
|
||||||
|
|
||||||
|
private int fullmoveCounter, halfmoveClock;
|
||||||
|
|
||||||
private static final Map<Type, int[][]> positionScores;
|
private static final Map<Type, int[][]> positionScores;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@ -101,6 +104,8 @@ public class Board implements Cloneable {
|
|||||||
Piece piece = getPos(move);
|
Piece piece = getPos(move);
|
||||||
Piece capturePiece = getDest(move);
|
Piece capturePiece = getDest(move);
|
||||||
|
|
||||||
|
// TODO: reset halfmove clock
|
||||||
|
|
||||||
switch (move.type) {
|
switch (move.type) {
|
||||||
case PAWN_PROMOTION:
|
case PAWN_PROMOTION:
|
||||||
setPos(move, null);
|
setPos(move, null);
|
||||||
@ -140,6 +145,15 @@ public class Board implements Cloneable {
|
|||||||
|
|
||||||
// Update log
|
// Update log
|
||||||
log.add(move, capturePiece);
|
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
|
// Update log
|
||||||
log.removeLast();
|
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 i = 0; i < 8; i++)
|
||||||
for (int j = 2; j < 6; j++)
|
for (int j = 2; j < 6; j++)
|
||||||
boardArr[i][j] = null;
|
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 = new HashMap<>();
|
||||||
board.kingPos.putAll(kingPos);
|
board.kingPos.putAll(kingPos);
|
||||||
|
// TODO: activeColor clone ?
|
||||||
board.log = (Log) log.clone();
|
board.log = (Log) log.clone();
|
||||||
|
|
||||||
return board;
|
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) {
|
public Piece get(Position pos) {
|
||||||
return boardArr[pos.x][pos.y];
|
return boardArr[pos.x][pos.y];
|
||||||
}
|
}
|
||||||
@ -388,4 +480,6 @@ public class Board implements Cloneable {
|
|||||||
* @return The board array
|
* @return The board array
|
||||||
*/
|
*/
|
||||||
public Piece[][] getBoardArr() { return boardArr; }
|
public Piece[][] getBoardArr() { return boardArr; }
|
||||||
|
|
||||||
|
public Color getActiveColor() { return activeColor; }
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,11 @@ public class Move {
|
|||||||
this(new Position(xPos, yPos), new Position(xDest, yDest));
|
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 isHorizontal() { return yDist == 0; }
|
||||||
|
|
||||||
public boolean isVertical() { return xDist == 0; }
|
public boolean isVertical() { return xDist == 0; }
|
||||||
|
@ -15,6 +15,10 @@ public class Position {
|
|||||||
this.y = y;
|
this.y = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Position fromAlgebraicNotation(String pos) {
|
||||||
|
return new Position(pos.charAt(0) - 97, 7 - (Character.getNumericValue(pos.charAt(1)) - 1));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("[%d, %d]", x, y);
|
return String.format("[%d, %d]", x, y);
|
||||||
|
@ -73,6 +73,7 @@ public class Game {
|
|||||||
public void onMove(Player player, Move move) {
|
public void onMove(Player player, Move move) {
|
||||||
if (board.getPos(move).getColor() == player.color && board.attemptMove(move)) {
|
if (board.getPos(move).getColor() == player.color && board.attemptMove(move)) {
|
||||||
System.out.printf("%s: %s%n", player.color, 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());
|
GameState eventType = board.getGameEventType(board.getDest(move).getColor().opposite());
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case CHECKMATE:
|
case CHECKMATE:
|
||||||
@ -83,7 +84,7 @@ public class Game {
|
|||||||
System.out.printf("%s in check!%n", player.color.opposite());
|
System.out.printf("%s in check!%n", player.color.opposite());
|
||||||
default:
|
default:
|
||||||
boardComponent.repaint();
|
boardComponent.repaint();
|
||||||
players.get(player.color.opposite()).requestMove();
|
players.get(board.getActiveColor()).requestMove();
|
||||||
}
|
}
|
||||||
overlayComponent.displayArrow(move);
|
overlayComponent.displayArrow(move);
|
||||||
} else player.requestMove();
|
} else player.requestMove();
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
package dev.kske.chess.game;
|
package dev.kske.chess.game;
|
||||||
|
|
||||||
import java.io.IOException;
|
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.board.Piece.Color;
|
||||||
import dev.kske.chess.uci.UCIHandle;
|
import dev.kske.chess.uci.UCIHandle;
|
||||||
|
import dev.kske.chess.uci.UCIListener;
|
||||||
|
import dev.kske.chess.uci.UCIOption;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Project: <strong>Chess</strong><br>
|
* Project: <strong>Chess</strong><br>
|
||||||
@ -11,17 +17,19 @@ import dev.kske.chess.uci.UCIHandle;
|
|||||||
* Created: <strong>18.07.2019</strong><br>
|
* Created: <strong>18.07.2019</strong><br>
|
||||||
* Author: <strong>Kai S. K. Engelbart</strong>
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
*/
|
*/
|
||||||
public class UCIPlayer extends Player {
|
public class UCIPlayer extends Player implements UCIListener {
|
||||||
|
|
||||||
private UCIHandle handle;
|
private UCIHandle handle;
|
||||||
private UCIPlayerListener listener;
|
|
||||||
|
private String name, author;
|
||||||
|
private List<UCIOption> options;
|
||||||
|
|
||||||
public UCIPlayer(Color color, String enginePath) {
|
public UCIPlayer(Color color, String enginePath) {
|
||||||
super(color);
|
super(color);
|
||||||
|
options = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
handle = new UCIHandle(enginePath);
|
handle = new UCIHandle(enginePath);
|
||||||
listener = new UCIPlayerListener();
|
handle.setListener(this);
|
||||||
handle.setListener(listener);
|
|
||||||
handle.start();
|
handle.start();
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
@ -30,7 +38,8 @@ public class UCIPlayer extends Player {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void requestMove() {
|
public void requestMove() {
|
||||||
|
handle.positionFEN(board.toFEN());
|
||||||
|
handle.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -38,7 +47,80 @@ public class UCIPlayer extends Player {
|
|||||||
handle.stop();
|
handle.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void disconnect() {
|
public void disconnect() {
|
||||||
handle.quit();
|
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<String, String> 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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: <strong>Chess</strong><br>
|
|
||||||
* File: <strong>UCIPlayerListener.java</strong><br>
|
|
||||||
* Created: <strong>20.07.2019</strong><br>
|
|
||||||
* Author: <strong>Kai S. K. Engelbart</strong>
|
|
||||||
*/
|
|
||||||
class UCIPlayerListener implements UCIListener {
|
|
||||||
|
|
||||||
private String name, author;
|
|
||||||
private List<UCIOption> 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<String, String> 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; };
|
|
||||||
}
|
|
@ -93,7 +93,21 @@ public class UCIHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: position
|
// 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.
|
* Stops calculation as soon as possible.
|
||||||
|
@ -54,7 +54,9 @@ public class UCIReceiver implements Runnable {
|
|||||||
case "readyok":
|
case "readyok":
|
||||||
listener.onReadyOk();
|
listener.onReadyOk();
|
||||||
break;
|
break;
|
||||||
// TODO: bestmove
|
case "bestmove":
|
||||||
|
parseBestMove(line.substring(command.length() + 1));
|
||||||
|
break;
|
||||||
case "copyprotection":
|
case "copyprotection":
|
||||||
parseCopyProtection(line.substring(command.length() + 1));
|
parseCopyProtection(line.substring(command.length() + 1));
|
||||||
break;
|
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) {
|
private void parseCopyProtection(String line) {
|
||||||
switch (line) {
|
switch (line) {
|
||||||
case "checking":
|
case "checking":
|
||||||
|
Reference in New Issue
Block a user