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 Map<Color, Position> kingPos;
|
||||
private Color activeColor;
|
||||
private Log log;
|
||||
|
||||
private int fullmoveCounter, halfmoveClock;
|
||||
|
||||
private static final Map<Type, int[][]> 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; }
|
||||
}
|
||||
|
@ -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; }
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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: <strong>Chess</strong><br>
|
||||
@ -11,17 +17,19 @@ import dev.kske.chess.uci.UCIHandle;
|
||||
* Created: <strong>18.07.2019</strong><br>
|
||||
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||
*/
|
||||
public class UCIPlayer extends Player {
|
||||
public class UCIPlayer extends Player implements UCIListener {
|
||||
|
||||
private UCIHandle handle;
|
||||
private UCIPlayerListener listener;
|
||||
|
||||
private String name, author;
|
||||
private List<UCIOption> 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<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: 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.
|
||||
|
@ -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":
|
||||
|
Reference in New Issue
Block a user