diff --git a/src/main/java/featurecat/lizzie/analysis/Leelaz.java b/src/main/java/featurecat/lizzie/analysis/Leelaz.java index 29bab4450..d0a21a58e 100644 --- a/src/main/java/featurecat/lizzie/analysis/Leelaz.java +++ b/src/main/java/featurecat/lizzie/analysis/Leelaz.java @@ -34,6 +34,7 @@ public class Leelaz { private long maxAnalyzeTimeMillis;//, maxThinkingTimeMillis; private int cmdNumber; private int currentCmdNum; + private ArrayDeque cmdQueue; private Process process; @@ -72,7 +73,8 @@ public Leelaz() throws IOException, JSONException { isPondering = false; startPonderTime = System.currentTimeMillis(); cmdNumber = 1; - currentCmdNum = -1; + currentCmdNum = 0; + cmdQueue = new ArrayDeque<>(); JSONObject config = Lizzie.config.config.getJSONObject("leelaz"); @@ -182,7 +184,7 @@ private void parseLine(String line) { // End of response } else if (line.startsWith("info")) { isLoaded = true; - if (currentCmdNum == cmdNumber - 1) { + if (isResponseUpToDate()) { // This should not be stale data when the command number match parseInfo(line.substring(5)); notifyBestMoveListeners(); @@ -199,14 +201,16 @@ private void parseLine(String line) { } isThinking = false; - } else if (Lizzie.frame != null && line.startsWith("=")) { + } else if (Lizzie.frame != null && (line.startsWith("=") || line.startsWith("?"))) { if (printCommunication) { System.out.print(line); } String[] params = line.trim().split(" "); currentCmdNum = Integer.parseInt(params[0].substring(1).trim()); - if (params.length == 1) return; + trySendCommandFromQueue(); + + if (line.startsWith("?") || params.length == 1) return; if (isSettingHandicap) { @@ -278,11 +282,48 @@ private void read() { } /** - * Sends a command for leelaz to execute + * Sends a command to command queue for leelaz to execute * * @param command a GTP command containing no newline characters */ public void sendCommand(String command) { + synchronized(cmdQueue) { + String lastCommand = cmdQueue.peekLast(); + // For efficiency, delete unnecessary "lz-analyze" that will be stopped immediately + if (lastCommand != null && lastCommand.startsWith("lz-analyze")) { + cmdQueue.removeLast(); + } + cmdQueue.addLast(command); + trySendCommandFromQueue(); + } + } + + /** + * Sends a command from command queue for leelaz to execute if it is ready + */ + private void trySendCommandFromQueue() { + // Defer sending "lz-analyze" if leelaz is not ready yet. + // Though all commands should be deferred theoretically, + // only "lz-analyze" is differed here for fear of + // possible hang-up by missing response for some reason. + // cmdQueue can be replaced with a mere String variable in this case, + // but it is kept for future change of our mind. + synchronized(cmdQueue) { + String command = cmdQueue.peekFirst(); + if (command == null || (command.startsWith("lz-analyze") && !isResponseUpToDate())) { + return; + } + cmdQueue.removeFirst(); + sendCommandToLeelaz(command); + } + } + + /** + * Sends a command for leelaz to execute + * + * @param command a GTP command containing no newline characters + */ + private void sendCommandToLeelaz(String command) { if (command.startsWith("fixed_handicap")) isSettingHandicap = true; command = cmdNumber + " " + command; @@ -298,6 +339,14 @@ public void sendCommand(String command) { } } + /** + * Check whether leelaz is responding to the last command + */ + private boolean isResponseUpToDate() { + // Use >= instead of == for avoiding hang-up, though it cannot happen + return currentCmdNum >= cmdNumber - 1; + } + /** * @param color color of stone to play * @param move coordinate of the coordinate