diff --git a/src/dev/kske/chess/game/UCIPlayer.java b/src/dev/kske/chess/game/UCIPlayer.java
index 5e57a45..79d12bc 100644
--- a/src/dev/kske/chess/game/UCIPlayer.java
+++ b/src/dev/kske/chess/game/UCIPlayer.java
@@ -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: Chess
@@ -20,11 +16,9 @@ import dev.kske.chess.uci.UCIOption;
public class UCIPlayer extends Player implements UCIListener {
private UCIHandle handle;
- private List 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 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/uci/UCIHandle.java b/src/dev/kske/chess/uci/UCIHandle.java
index cf13e63..d9dd77a 100644
--- a/src/dev/kske/chess/uci/UCIHandle.java
+++ b/src/dev/kske/chess/uci/UCIHandle.java
@@ -138,6 +138,6 @@ public class UCIHandle {
}
public void setListener(UCIListener listener) {
- receiver.setListener(listener);
+ receiver.addListener(listener);
}
}
diff --git a/src/dev/kske/chess/uci/UCIInfo.java b/src/dev/kske/chess/uci/UCIInfo.java
new file mode 100644
index 0000000..4606874
--- /dev/null
+++ b/src/dev/kske/chess/uci/UCIInfo.java
@@ -0,0 +1,133 @@
+package dev.kske.chess.uci;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import dev.kske.chess.board.Move;
+
+/**
+ * Project: Chess
+ * File: UCIInfo.java
+ * Created: 28.07.2019
+ * Author: Kai S. K. Engelbart
+ */
+public class UCIInfo {
+
+ private int depth, seldepth, time, nodes, multipv, currmovenumber, hashfull, nps, tbhits, sbhits, cpuload,
+ cpunr;
+ private List 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 getPv() { return pv; }
+
+ public List getRefutation() { return refutation; }
+
+ public List 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; }
+ }
+}
diff --git a/src/dev/kske/chess/uci/UCIListener.java b/src/dev/kske/chess/uci/UCIListener.java
index 6bb84ed..75efd16 100644
--- a/src/dev/kske/chess/uci/UCIListener.java
+++ b/src/dev/kske/chess/uci/UCIListener.java
@@ -1,6 +1,6 @@
package dev.kske.chess.uci;
-import java.util.Map;
+import dev.kske.chess.board.Move;
/**
* Project: Chess
@@ -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 additionalInfo) {}
+ default void onInfo(UCIInfo info) {}
/**
* Tells the GUI which parameters can be changed in the engine.
diff --git a/src/dev/kske/chess/uci/UCIOption.java b/src/dev/kske/chess/uci/UCIOption.java
index ba6df2a..eab40bd 100644
--- a/src/dev/kske/chess/uci/UCIOption.java
+++ b/src/dev/kske/chess/uci/UCIOption.java
@@ -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: Chess
@@ -11,14 +13,55 @@ import java.util.List;
*/
public class UCIOption {
- public String name, defaultVal, minVal, maxVal;
- public GUIType type;
- public List varList;
+ private String name, defaultVal, minVal, maxVal;
+ private GUIType type;
+ private List 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 getVarList() { return varList; }
+
public static enum GUIType {
CHECK, SPIN, COMBO, BUTTON, STRING
}
diff --git a/src/dev/kske/chess/uci/UCIReceiver.java b/src/dev/kske/chess/uci/UCIReceiver.java
index fbfb686..f9d8cdc 100644
--- a/src/dev/kske/chess/uci/UCIReceiver.java
+++ b/src/dev/kske/chess/uci/UCIReceiver.java
@@ -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: Chess
@@ -21,10 +19,11 @@ public class UCIReceiver implements Runnable {
private final BufferedReader in;
- private UCIListener listener;
+ private List listeners;
public UCIReceiver(InputStream in) {
- this.in = new BufferedReader(new InputStreamReader(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 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);
+ }
}