UCI refactoring
+ Multiple listener support in UCIHandle + UCIInfo class - Moved info and option parsing into the respective classes - Removed unimplemented UCI callback methods from UCIPlayer
This commit is contained in:
parent
d173fb7851
commit
e37450c144
@ -1,15 +1,11 @@
|
|||||||
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.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.UCIListener;
|
||||||
import dev.kske.chess.uci.UCIOption;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Project: <strong>Chess</strong><br>
|
* Project: <strong>Chess</strong><br>
|
||||||
@ -20,11 +16,9 @@ import dev.kske.chess.uci.UCIOption;
|
|||||||
public class UCIPlayer extends Player implements UCIListener {
|
public class UCIPlayer extends Player implements UCIListener {
|
||||||
|
|
||||||
private UCIHandle handle;
|
private UCIHandle handle;
|
||||||
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);
|
||||||
handle.setListener(this);
|
handle.setListener(this);
|
||||||
@ -55,16 +49,6 @@ public class UCIPlayer extends Player implements UCIListener {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onUCIOk() {
|
|
||||||
System.out.println("UCI ok");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReadyOk() {
|
|
||||||
System.out.println("Ready ok");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBestMove(String move) {
|
public void onBestMove(String move) {
|
||||||
Move moveObj = Move.fromSAN(move);
|
Move moveObj = Move.fromSAN(move);
|
||||||
@ -72,7 +56,7 @@ public class UCIPlayer extends Player implements UCIListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBestMove(String move, String ponderMove) {
|
public void onBestMove(String move, Move ponderMove) {
|
||||||
onBestMove(move);
|
onBestMove(move);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,15 +89,4 @@ public class UCIPlayer extends Player implements UCIListener {
|
|||||||
public void onRegistrationError() {
|
public void onRegistrationError() {
|
||||||
System.err.println("Registration error!");
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -138,6 +138,6 @@ public class UCIHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setListener(UCIListener listener) {
|
public void setListener(UCIListener listener) {
|
||||||
receiver.setListener(listener);
|
receiver.addListener(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
133
src/dev/kske/chess/uci/UCIInfo.java
Normal file
133
src/dev/kske/chess/uci/UCIInfo.java
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
package dev.kske.chess.uci;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import dev.kske.chess.board.Move;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>Chess</strong><br>
|
||||||
|
* File: <strong>UCIInfo.java</strong><br>
|
||||||
|
* Created: <strong>28.07.2019</strong><br>
|
||||||
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
|
*/
|
||||||
|
public class UCIInfo {
|
||||||
|
|
||||||
|
private int depth, seldepth, time, nodes, multipv, currmovenumber, hashfull, nps, tbhits, sbhits, cpuload,
|
||||||
|
cpunr;
|
||||||
|
private List<Move> pv, refutation, currline;
|
||||||
|
private Move currmove;
|
||||||
|
private Score score;
|
||||||
|
private String displayString;
|
||||||
|
|
||||||
|
public UCIInfo(String line) {
|
||||||
|
pv = new ArrayList<>();
|
||||||
|
refutation = new ArrayList<>();
|
||||||
|
currline = new ArrayList<>();
|
||||||
|
String[] tokens = line.split(" ");
|
||||||
|
|
||||||
|
for (int i = 0; i < tokens.length; i++)
|
||||||
|
switch (tokens[i]) {
|
||||||
|
// Single parameter info
|
||||||
|
case "depth":
|
||||||
|
case "seldepth":
|
||||||
|
case "time":
|
||||||
|
case "nodes":
|
||||||
|
case "multipv":
|
||||||
|
case "currmove":
|
||||||
|
case "currmovenumber":
|
||||||
|
case "hashfull":
|
||||||
|
case "nps":
|
||||||
|
case "tbhits":
|
||||||
|
case "sbhits":
|
||||||
|
case "cpuload":
|
||||||
|
case "string":
|
||||||
|
try {
|
||||||
|
getClass().getField(tokens[i]).set(this, tokens[++i]);
|
||||||
|
} catch (Exception ex) {}
|
||||||
|
break;
|
||||||
|
// TODO: pv
|
||||||
|
// TODO: refutation
|
||||||
|
// TODO: currline
|
||||||
|
case "pv":
|
||||||
|
// TODO: parse infos after score
|
||||||
|
case "score":
|
||||||
|
score = new Score(line.substring(tokens[i].length() + 1));
|
||||||
|
case "refutation":
|
||||||
|
case "currline":
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
System.err.printf("Unknown parameter '%s' for command 'info' found!%n", tokens[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDepth() { return depth; }
|
||||||
|
|
||||||
|
public int getSeldepth() { return seldepth; }
|
||||||
|
|
||||||
|
public int getTime() { return time; }
|
||||||
|
|
||||||
|
public int getNodes() { return nodes; }
|
||||||
|
|
||||||
|
public int getMultipv() { return multipv; }
|
||||||
|
|
||||||
|
public int getCurrmovenumber() { return currmovenumber; }
|
||||||
|
|
||||||
|
public int getHashfull() { return hashfull; }
|
||||||
|
|
||||||
|
public int getNps() { return nps; }
|
||||||
|
|
||||||
|
public int getTbhits() { return tbhits; }
|
||||||
|
|
||||||
|
public int getSbhits() { return sbhits; }
|
||||||
|
|
||||||
|
public int getCpuload() { return cpuload; }
|
||||||
|
|
||||||
|
public int getCpunr() { return cpunr; }
|
||||||
|
|
||||||
|
public List<Move> getPv() { return pv; }
|
||||||
|
|
||||||
|
public List<Move> getRefutation() { return refutation; }
|
||||||
|
|
||||||
|
public List<Move> getCurrline() { return currline; }
|
||||||
|
|
||||||
|
public Move getCurrmove() { return currmove; }
|
||||||
|
|
||||||
|
public Score getScore() { return score; }
|
||||||
|
|
||||||
|
public String getDisplayString() { return displayString; }
|
||||||
|
|
||||||
|
public static class Score {
|
||||||
|
|
||||||
|
private int cp, mate;
|
||||||
|
private boolean lowerbound, upperbound;
|
||||||
|
|
||||||
|
public Score(String line) {
|
||||||
|
String[] tokens = line.split(" ");
|
||||||
|
switch (tokens[0]) {
|
||||||
|
case "cp":
|
||||||
|
cp = Integer.parseInt(tokens[1]);
|
||||||
|
break;
|
||||||
|
case "mate":
|
||||||
|
mate = Integer.parseInt(tokens[1]);
|
||||||
|
break;
|
||||||
|
case "lowerbound":
|
||||||
|
lowerbound = true;
|
||||||
|
break;
|
||||||
|
case "upperbound":
|
||||||
|
upperbound = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
System.err.printf("Unknown parameter '%s' for command 'score' found!%n", tokens[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCp() { return cp; }
|
||||||
|
|
||||||
|
public int getMate() { return mate; }
|
||||||
|
|
||||||
|
public boolean isLowerbound() { return lowerbound; }
|
||||||
|
|
||||||
|
public boolean isUpperbound() { return upperbound; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package dev.kske.chess.uci;
|
package dev.kske.chess.uci;
|
||||||
|
|
||||||
import java.util.Map;
|
import dev.kske.chess.board.Move;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Project: <strong>Chess</strong><br>
|
* Project: <strong>Chess</strong><br>
|
||||||
@ -47,7 +47,7 @@ public interface UCIListener {
|
|||||||
* @param move The best move the engine has found
|
* @param move The best move the engine has found
|
||||||
* @param ponderMove The move the engine likes to ponder on
|
* @param ponderMove The move the engine likes to ponder on
|
||||||
*/
|
*/
|
||||||
default void onBestMove(String move, String ponderMove) {}
|
default void onBestMove(String move, Move ponderMove) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The engine will check the copy protection now.
|
* The engine will check the copy protection now.
|
||||||
@ -83,7 +83,7 @@ public interface UCIListener {
|
|||||||
*
|
*
|
||||||
* @param additionalInfo Contains all pieces of information to be sent
|
* @param additionalInfo Contains all pieces of information to be sent
|
||||||
*/
|
*/
|
||||||
default void onInfo(Map<String, String> additionalInfo) {}
|
default void onInfo(UCIInfo info) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells the GUI which parameters can be changed in the engine.
|
* Tells the GUI which parameters can be changed in the engine.
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package dev.kske.chess.uci;
|
package dev.kske.chess.uci;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Project: <strong>Chess</strong><br>
|
* Project: <strong>Chess</strong><br>
|
||||||
@ -11,13 +13,54 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class UCIOption {
|
public class UCIOption {
|
||||||
|
|
||||||
public String name, defaultVal, minVal, maxVal;
|
private String name, defaultVal, minVal, maxVal;
|
||||||
public GUIType type;
|
private GUIType type;
|
||||||
public List<String> varList;
|
private List<String> varList;
|
||||||
|
|
||||||
public UCIOption() {
|
public UCIOption(String line) {
|
||||||
varList = new ArrayList<>();
|
varList = new ArrayList<>();
|
||||||
|
String[] tokens = line.split(" ");
|
||||||
|
|
||||||
|
for (int i = 0; i < tokens.length; i++)
|
||||||
|
switch (tokens[i]) {
|
||||||
|
case "name":
|
||||||
|
StringJoiner nameJoiner = new StringJoiner(" ");
|
||||||
|
while (!Arrays.asList("type", "default", "min", "max", "var").contains(tokens[i + 1]))
|
||||||
|
nameJoiner.add(tokens[++i]);
|
||||||
|
name = nameJoiner.toString();
|
||||||
|
break;
|
||||||
|
case "type":
|
||||||
|
type = GUIType.valueOf(tokens[++i].toUpperCase());
|
||||||
|
break;
|
||||||
|
case "default":
|
||||||
|
// Default string may be empty
|
||||||
|
defaultVal = i == tokens.length - 1 ? "" : tokens[++i];
|
||||||
|
break;
|
||||||
|
case "min":
|
||||||
|
minVal = tokens[++i];
|
||||||
|
break;
|
||||||
|
case "max":
|
||||||
|
maxVal = tokens[++i];
|
||||||
|
break;
|
||||||
|
case "var":
|
||||||
|
varList.add(tokens[++i]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
System.err.printf("Unknown parameter '%s' for command 'option' found!%n", tokens[i]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
|
||||||
|
public String getDefaultVal() { return defaultVal; }
|
||||||
|
|
||||||
|
public String getMinVal() { return minVal; }
|
||||||
|
|
||||||
|
public String getMaxVal() { return maxVal; }
|
||||||
|
|
||||||
|
public GUIType getType() { return type; }
|
||||||
|
|
||||||
|
public List<String> getVarList() { return varList; }
|
||||||
|
|
||||||
public static enum GUIType {
|
public static enum GUIType {
|
||||||
CHECK, SPIN, COMBO, BUTTON, STRING
|
CHECK, SPIN, COMBO, BUTTON, STRING
|
||||||
|
@ -4,12 +4,10 @@ import java.io.BufferedReader;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.Arrays;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.StringJoiner;
|
|
||||||
|
|
||||||
import dev.kske.chess.uci.UCIOption.GUIType;
|
import dev.kske.chess.board.Move;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Project: <strong>Chess</strong><br>
|
* Project: <strong>Chess</strong><br>
|
||||||
@ -21,10 +19,11 @@ public class UCIReceiver implements Runnable {
|
|||||||
|
|
||||||
private final BufferedReader in;
|
private final BufferedReader in;
|
||||||
|
|
||||||
private UCIListener listener;
|
private List<UCIListener> listeners;
|
||||||
|
|
||||||
public UCIReceiver(InputStream in) {
|
public UCIReceiver(InputStream in) {
|
||||||
this.in = new BufferedReader(new InputStreamReader(in));
|
this.in = new BufferedReader(new InputStreamReader(in));
|
||||||
|
listeners = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -49,10 +48,10 @@ public class UCIReceiver implements Runnable {
|
|||||||
parseId(line.substring(command.length() + 1));
|
parseId(line.substring(command.length() + 1));
|
||||||
break;
|
break;
|
||||||
case "uciok":
|
case "uciok":
|
||||||
listener.onUCIOk();
|
listeners.forEach(UCIListener::onUCIOk);
|
||||||
break;
|
break;
|
||||||
case "readyok":
|
case "readyok":
|
||||||
listener.onReadyOk();
|
listeners.forEach(UCIListener::onReadyOk);
|
||||||
break;
|
break;
|
||||||
case "bestmove":
|
case "bestmove":
|
||||||
parseBestMove(line.substring(command.length() + 1));
|
parseBestMove(line.substring(command.length() + 1));
|
||||||
@ -79,10 +78,10 @@ public class UCIReceiver implements Runnable {
|
|||||||
String arg = line.substring(param.length() + 1);
|
String arg = line.substring(param.length() + 1);
|
||||||
switch (param) {
|
switch (param) {
|
||||||
case "name":
|
case "name":
|
||||||
listener.onIdName(arg);
|
listeners.forEach(l -> l.onIdName(arg));
|
||||||
break;
|
break;
|
||||||
case "author":
|
case "author":
|
||||||
listener.onIdAuthor(arg);
|
listeners.forEach(l -> l.onIdAuthor(arg));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
System.err.printf("Unknown parameter '%s' for command 'id' found!%n", param);
|
System.err.printf("Unknown parameter '%s' for command 'id' found!%n", param);
|
||||||
@ -94,20 +93,20 @@ public class UCIReceiver implements Runnable {
|
|||||||
String move = tokens[0];
|
String move = tokens[0];
|
||||||
|
|
||||||
// Ponder move
|
// Ponder move
|
||||||
if (tokens.length == 3) listener.onBestMove(move, tokens[2]);
|
if (tokens.length == 3) listeners.forEach(l -> l.onBestMove(move, Move.fromSAN(tokens[2])));
|
||||||
else listener.onBestMove(move);
|
else listeners.forEach(l -> l.onBestMove(move));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseCopyProtection(String line) {
|
private void parseCopyProtection(String line) {
|
||||||
switch (line) {
|
switch (line) {
|
||||||
case "checking":
|
case "checking":
|
||||||
listener.onCopyProtectionChecking();
|
listeners.forEach(UCIListener::onCopyProtectionChecking);
|
||||||
break;
|
break;
|
||||||
case "ok":
|
case "ok":
|
||||||
listener.onCopyProtectionOk();
|
listeners.forEach(UCIListener::onCopyProtectionOk);
|
||||||
break;
|
break;
|
||||||
case "error":
|
case "error":
|
||||||
listener.onCopyProtectionError();
|
listeners.forEach(UCIListener::onCopyProtectionError);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
System.err.printf("Unknown parameter '%s' for command 'copyprotection' found!%n", line);
|
System.err.printf("Unknown parameter '%s' for command 'copyprotection' found!%n", line);
|
||||||
@ -117,13 +116,13 @@ public class UCIReceiver implements Runnable {
|
|||||||
private void parseRegistration(String line) {
|
private void parseRegistration(String line) {
|
||||||
switch (line) {
|
switch (line) {
|
||||||
case "checking":
|
case "checking":
|
||||||
listener.onRegistrationChecking();
|
listeners.forEach(UCIListener::onRegistrationChecking);
|
||||||
break;
|
break;
|
||||||
case "ok":
|
case "ok":
|
||||||
listener.onRegistrationOk();
|
listeners.forEach(UCIListener::onRegistrationOk);
|
||||||
break;
|
break;
|
||||||
case "error":
|
case "error":
|
||||||
listener.onRegistrationError();
|
listeners.forEach(UCIListener::onRegistrationError);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
System.err.printf("Unknown parameter '%s' for command 'registration' found!%n", line);
|
System.err.printf("Unknown parameter '%s' for command 'registration' found!%n", line);
|
||||||
@ -131,70 +130,14 @@ public class UCIReceiver implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void parseInfo(String line) {
|
private void parseInfo(String line) {
|
||||||
String[] tokens = line.split(" ");
|
listeners.forEach(l -> l.onInfo(new UCIInfo(line)));
|
||||||
Map<String, String> additionalInfo = new HashMap<>();
|
|
||||||
|
|
||||||
for (int i = 0; i < tokens.length; i++)
|
|
||||||
switch (tokens[i]) {
|
|
||||||
// Single parameter info
|
|
||||||
case "depth":
|
|
||||||
case "seldepth":
|
|
||||||
case "time":
|
|
||||||
case "nodes":
|
|
||||||
case "multipv":
|
|
||||||
case "currmove":
|
|
||||||
case "currmovenumber":
|
|
||||||
case "hashfull":
|
|
||||||
case "nps":
|
|
||||||
case "tbhits":
|
|
||||||
case "sbhits":
|
|
||||||
case "cpuload":
|
|
||||||
case "string":
|
|
||||||
additionalInfo.put(tokens[i], tokens[++i]);
|
|
||||||
break;
|
|
||||||
// TODO: pv
|
|
||||||
// TODO: score
|
|
||||||
// TODO: refutation
|
|
||||||
// TODO: currline
|
|
||||||
default:
|
|
||||||
System.err.printf("Unknown parameter '%s' for command 'info' found!%n", tokens[i]);
|
|
||||||
}
|
|
||||||
listener.onInfo(additionalInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseOption(String line) {
|
private void parseOption(String line) {
|
||||||
String[] tokens = line.split(" ");
|
listeners.forEach(l -> l.onOption(new UCIOption((line))));
|
||||||
UCIOption option = new UCIOption();
|
|
||||||
|
|
||||||
for (int i = 0; i < tokens.length; i++)
|
|
||||||
switch (tokens[i]) {
|
|
||||||
case "name":
|
|
||||||
StringJoiner name = new StringJoiner(" ");
|
|
||||||
while (!Arrays.asList("type", "default", "min", "max", "var").contains(tokens[i + 1]))
|
|
||||||
name.add(tokens[++i]);
|
|
||||||
option.name = name.toString();
|
|
||||||
break;
|
|
||||||
case "type":
|
|
||||||
option.type = GUIType.valueOf(tokens[++i].toUpperCase());
|
|
||||||
break;
|
|
||||||
case "default":
|
|
||||||
// Default string may be empty
|
|
||||||
option.defaultVal = i == tokens.length - 1 ? "" : tokens[++i];
|
|
||||||
break;
|
|
||||||
case "min":
|
|
||||||
option.minVal = tokens[++i];
|
|
||||||
break;
|
|
||||||
case "max":
|
|
||||||
option.maxVal = tokens[++i];
|
|
||||||
break;
|
|
||||||
case "var":
|
|
||||||
option.varList.add(tokens[++i]);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
System.err.printf("Unknown parameter '%s' for command 'option' found!%n", tokens[i]);
|
|
||||||
}
|
|
||||||
listener.onOption(option);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setListener(UCIListener listener) { this.listener = listener; }
|
public void addListener(UCIListener listener) {
|
||||||
|
listeners.add(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user