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;
|
||||
|
||||
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>
|
||||
@ -20,11 +16,9 @@ import dev.kske.chess.uci.UCIOption;
|
||||
public class UCIPlayer extends Player implements UCIListener {
|
||||
|
||||
private UCIHandle handle;
|
||||
private List<UCIOption> options;
|
||||
|
||||
public UCIPlayer(Color color, String enginePath) {
|
||||
super(color);
|
||||
options = new ArrayList<>();
|
||||
try {
|
||||
handle = new UCIHandle(enginePath);
|
||||
handle.setListener(this);
|
||||
@ -55,16 +49,6 @@ public class UCIPlayer extends Player implements UCIListener {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@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.fromSAN(move);
|
||||
@ -72,7 +56,7 @@ public class UCIPlayer extends Player implements UCIListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBestMove(String move, String ponderMove) {
|
||||
public void onBestMove(String move, Move ponderMove) {
|
||||
onBestMove(move);
|
||||
}
|
||||
|
||||
@ -105,15 +89,4 @@ public class UCIPlayer extends Player implements UCIListener {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -138,6 +138,6 @@ public class UCIHandle {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
import java.util.Map;
|
||||
import dev.kske.chess.board.Move;
|
||||
|
||||
/**
|
||||
* Project: <strong>Chess</strong><br>
|
||||
@ -47,7 +47,7 @@ public interface UCIListener {
|
||||
* @param move The best move the engine has found
|
||||
* @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.
|
||||
@ -83,7 +83,7 @@ public interface UCIListener {
|
||||
*
|
||||
* @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.
|
||||
|
@ -1,7 +1,9 @@
|
||||
package dev.kske.chess.uci;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* Project: <strong>Chess</strong><br>
|
||||
@ -11,13 +13,54 @@ import java.util.List;
|
||||
*/
|
||||
public class UCIOption {
|
||||
|
||||
public String name, defaultVal, minVal, maxVal;
|
||||
public GUIType type;
|
||||
public List<String> varList;
|
||||
private String name, defaultVal, minVal, maxVal;
|
||||
private GUIType type;
|
||||
private List<String> varList;
|
||||
|
||||
public UCIOption() {
|
||||
public UCIOption(String line) {
|
||||
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 {
|
||||
CHECK, SPIN, COMBO, BUTTON, STRING
|
||||
|
@ -4,12 +4,10 @@ import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import dev.kske.chess.uci.UCIOption.GUIType;
|
||||
import dev.kske.chess.board.Move;
|
||||
|
||||
/**
|
||||
* Project: <strong>Chess</strong><br>
|
||||
@ -21,10 +19,11 @@ public class UCIReceiver implements Runnable {
|
||||
|
||||
private final BufferedReader in;
|
||||
|
||||
private UCIListener listener;
|
||||
private List<UCIListener> listeners;
|
||||
|
||||
public UCIReceiver(InputStream in) {
|
||||
this.in = new BufferedReader(new InputStreamReader(in));
|
||||
listeners = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -49,10 +48,10 @@ public class UCIReceiver implements Runnable {
|
||||
parseId(line.substring(command.length() + 1));
|
||||
break;
|
||||
case "uciok":
|
||||
listener.onUCIOk();
|
||||
listeners.forEach(UCIListener::onUCIOk);
|
||||
break;
|
||||
case "readyok":
|
||||
listener.onReadyOk();
|
||||
listeners.forEach(UCIListener::onReadyOk);
|
||||
break;
|
||||
case "bestmove":
|
||||
parseBestMove(line.substring(command.length() + 1));
|
||||
@ -79,10 +78,10 @@ public class UCIReceiver implements Runnable {
|
||||
String arg = line.substring(param.length() + 1);
|
||||
switch (param) {
|
||||
case "name":
|
||||
listener.onIdName(arg);
|
||||
listeners.forEach(l -> l.onIdName(arg));
|
||||
break;
|
||||
case "author":
|
||||
listener.onIdAuthor(arg);
|
||||
listeners.forEach(l -> l.onIdAuthor(arg));
|
||||
break;
|
||||
default:
|
||||
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];
|
||||
|
||||
// Ponder move
|
||||
if (tokens.length == 3) listener.onBestMove(move, tokens[2]);
|
||||
else listener.onBestMove(move);
|
||||
if (tokens.length == 3) listeners.forEach(l -> l.onBestMove(move, Move.fromSAN(tokens[2])));
|
||||
else listeners.forEach(l -> l.onBestMove(move));
|
||||
}
|
||||
|
||||
private void parseCopyProtection(String line) {
|
||||
switch (line) {
|
||||
case "checking":
|
||||
listener.onCopyProtectionChecking();
|
||||
listeners.forEach(UCIListener::onCopyProtectionChecking);
|
||||
break;
|
||||
case "ok":
|
||||
listener.onCopyProtectionOk();
|
||||
listeners.forEach(UCIListener::onCopyProtectionOk);
|
||||
break;
|
||||
case "error":
|
||||
listener.onCopyProtectionError();
|
||||
listeners.forEach(UCIListener::onCopyProtectionError);
|
||||
break;
|
||||
default:
|
||||
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) {
|
||||
switch (line) {
|
||||
case "checking":
|
||||
listener.onRegistrationChecking();
|
||||
listeners.forEach(UCIListener::onRegistrationChecking);
|
||||
break;
|
||||
case "ok":
|
||||
listener.onRegistrationOk();
|
||||
listeners.forEach(UCIListener::onRegistrationOk);
|
||||
break;
|
||||
case "error":
|
||||
listener.onRegistrationError();
|
||||
listeners.forEach(UCIListener::onRegistrationError);
|
||||
break;
|
||||
default:
|
||||
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) {
|
||||
String[] tokens = line.split(" ");
|
||||
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);
|
||||
listeners.forEach(l -> l.onInfo(new UCIInfo(line)));
|
||||
}
|
||||
|
||||
private void parseOption(String line) {
|
||||
String[] tokens = line.split(" ");
|
||||
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);
|
||||
listeners.forEach(l -> l.onOption(new UCIOption((line))));
|
||||
}
|
||||
|
||||
public void setListener(UCIListener listener) { this.listener = listener; }
|
||||
public void addListener(UCIListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user