diff --git a/DroidFish/AndroidManifest.xml b/DroidFish/AndroidManifest.xml index a7045a3..8e45955 100644 --- a/DroidFish/AndroidManifest.xml +++ b/DroidFish/AndroidManifest.xml @@ -6,6 +6,8 @@ android:installLocation="auto"> + + @@ -45,9 +47,6 @@ android:label="@string/cpu_warning_title"> - - - diff --git a/DroidFish/src/org/petero/droidfish/engine/DroidComputerPlayer.java b/DroidFish/src/org/petero/droidfish/engine/DroidComputerPlayer.java index 15d9a84..4b22e74 100644 --- a/DroidFish/src/org/petero/droidfish/engine/DroidComputerPlayer.java +++ b/DroidFish/src/org/petero/droidfish/engine/DroidComputerPlayer.java @@ -803,7 +803,7 @@ public class DroidComputerPlayer { private final synchronized int getReadTimeout() { boolean needGuiUpdate = depthModified || currMoveModified || pvModified || statsModified; - int timeout = 1000; + int timeout = 2000000000; if (needGuiUpdate) { long now = System.currentTimeMillis(); timeout = (int)(lastGUIUpdate + guiUpdateInterval - now + 1); diff --git a/DroidFish/src/org/petero/droidfish/engine/ExternalEngine.java b/DroidFish/src/org/petero/droidfish/engine/ExternalEngine.java index 6360b05..c531553 100644 --- a/DroidFish/src/org/petero/droidfish/engine/ExternalEngine.java +++ b/DroidFish/src/org/petero/droidfish/engine/ExternalEngine.java @@ -26,8 +26,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.channels.FileChannel; -import java.util.LinkedList; -import java.util.List; import org.petero.droidfish.R; import android.content.Context; @@ -45,7 +43,7 @@ public class ExternalEngine extends UCIEngineBase { private Thread exitThread; private Thread stdInThread; private Thread stdErrThread; - private List inLines; + private LocalPipe inLines; private boolean startedOk; private boolean isRunning; @@ -58,7 +56,7 @@ public class ExternalEngine extends UCIEngineBase { exitThread = null; stdInThread = null; stdErrThread = null; - inLines = new LinkedList(); + inLines = new LocalPipe(); startedOk = false; isRunning = false; } @@ -120,8 +118,7 @@ public class ExternalEngine extends UCIEngineBase { if ((ep == null) || Thread.currentThread().isInterrupted()) return; synchronized (inLines) { - inLines.add(line); - inLines.notify(); + inLines.addLine(line); if (first) { startedOk = true; isRunning = true; @@ -130,8 +127,8 @@ public class ExternalEngine extends UCIEngineBase { } } } catch (IOException e) { - return; } + inLines.close(); } }); stdInThread.start(); @@ -176,26 +173,13 @@ public class ExternalEngine extends UCIEngineBase { /** @inheritDoc */ @Override public String readLineFromEngine(int timeoutMillis) { - try { - synchronized (inLines) { - if (inLines.size() == 0) { - Thread inThread = stdInThread; - if ((inThread == null) || !inThread.isAlive()) - return null; - inLines.wait(timeoutMillis); - } - } - synchronized (inLines) { - if (inLines.size() > 0) { - String ret = inLines.get(0); - inLines.remove(0); -// System.out.printf("Engine -> GUI: %s\n", ret); - return ret; - } - } - } catch (InterruptedException e) { + String ret = inLines.readLine(timeoutMillis); + if (ret == null) + return null; + if (ret.length() > 0) { +// System.out.printf("Engine -> GUI: %s\n", ret); } - return ""; + return ret; } /** @inheritDoc */ diff --git a/DroidFish/src/org/petero/droidfish/engine/LocalPipe.java b/DroidFish/src/org/petero/droidfish/engine/LocalPipe.java new file mode 100644 index 0000000..82477b8 --- /dev/null +++ b/DroidFish/src/org/petero/droidfish/engine/LocalPipe.java @@ -0,0 +1,63 @@ +package org.petero.droidfish.engine; + +import java.util.LinkedList; + +/** Implements line-based text communication between threads. */ +public class LocalPipe { + private LinkedList lines = new LinkedList(); + private boolean closed = false; + + /** Write a line to the pipe. */ + public final synchronized void printLine(String format) { + String s = String.format(format, new Object[]{}); + addLine(s); + } + + /** Write a line to the pipe. */ + public final synchronized void printLine(String format, Object ... args) { + String s = String.format(format, args); + addLine(s); + } + + public final synchronized void addLine(String line) { + lines.add(line); + notify(); + } + + /** Read a line from the pipe. Returns null on failure. */ + public final synchronized String readLine() { + return readLine(-1); + } + + /** Read a line from the pipe. Returns null on failure. Returns empty string on timeout. */ + public final synchronized String readLine(int timeoutMillis) { + if (closed) + return null; + try { + if (lines.isEmpty()) { + if (timeoutMillis > 0) + wait(timeoutMillis); + else + wait(); + } + if (lines.isEmpty()) + return closed ? null : ""; + String ret = lines.get(0); + lines.remove(0); + return ret; + } catch (InterruptedException e) { + return null; + } + } + + /** Close pipe. Makes readLine() return null. */ + public final synchronized void close() { + closed = true; + notify(); + } + + /** Return true if writer side has closed the pipe. */ + public final synchronized boolean isClosed() { + return closed; + } +} diff --git a/DroidFish/src/org/petero/droidfish/engine/cuckoochess/CuckooChessEngine.java b/DroidFish/src/org/petero/droidfish/engine/cuckoochess/CuckooChessEngine.java index 46911a6..9d09d6e 100644 --- a/DroidFish/src/org/petero/droidfish/engine/cuckoochess/CuckooChessEngine.java +++ b/DroidFish/src/org/petero/droidfish/engine/cuckoochess/CuckooChessEngine.java @@ -23,11 +23,9 @@ import chess.ComputerPlayer; import chess.Move; import chess.Position; import chess.TextIO; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.Pipe; import java.util.ArrayList; +import org.petero.droidfish.engine.LocalPipe; import org.petero.droidfish.engine.UCIEngineBase; /** @@ -46,22 +44,16 @@ public class CuckooChessEngine extends UCIEngineBase { // Set to true to break out of main loop private boolean quit; - private Pipe guiToEngine; - private Pipe engineToGui; - private NioInputStream inFromEngine; + private LocalPipe guiToEngine; + private LocalPipe engineToGui; private Thread engineThread; public CuckooChessEngine(Report report) { pos = null; moves = new ArrayList(); quit = false; - try { - guiToEngine = Pipe.open(); - engineToGui = Pipe.open(); - inFromEngine = new NioInputStream(engineToGui); - } catch (IOException e) { - report.reportError(e.getMessage()); - } + guiToEngine = new LocalPipe(); + engineToGui = new LocalPipe(); } /** @inheritDoc */ @@ -69,9 +61,7 @@ public class CuckooChessEngine extends UCIEngineBase { protected final void startProcess() { engineThread = new Thread(new Runnable() { public void run() { - NioInputStream in = new NioInputStream(guiToEngine); - NioPrintStream out = new NioPrintStream(engineToGui); - mainLoop(in, out); + mainLoop(guiToEngine, engineToGui); } }); int pMin = Thread.MIN_PRIORITY; @@ -93,7 +83,7 @@ public class CuckooChessEngine extends UCIEngineBase { setOption("strength", strength); } - private final void mainLoop(NioInputStream is, NioPrintStream os) { + private final void mainLoop(LocalPipe is, LocalPipe os) { String line; while ((line = is.readLine()) != null) { handleCommand(line, os); @@ -103,12 +93,18 @@ public class CuckooChessEngine extends UCIEngineBase { } } + @Override + public void shutDown() { + super.shutDown(); + guiToEngine.close(); + } + /** @inheritDoc */ @Override public final String readLineFromEngine(int timeoutMillis) { if ((engineThread != null) && !engineThread.isAlive()) return null; - String ret = inFromEngine.readLine(timeoutMillis); + String ret = engineToGui.readLine(timeoutMillis); if (ret == null) return null; if (ret.length() > 0) { @@ -121,25 +117,21 @@ public class CuckooChessEngine extends UCIEngineBase { @Override public final synchronized void writeLineToEngine(String data) { // System.out.printf("GUI -> Engine: %s\n", data); - try { - String s = data + "\n"; - guiToEngine.sink().write(ByteBuffer.wrap(s.getBytes())); - } catch (IOException e) { - } + guiToEngine.addLine(data); } - private final void handleCommand(String cmdLine, NioPrintStream os) { + private final void handleCommand(String cmdLine, LocalPipe os) { String[] tokens = tokenize(cmdLine); try { String cmd = tokens[0]; if (cmd.equals("uci")) { - os.printf("id name %s%n", ComputerPlayer.engineName); - os.printf("id author Peter Osterlund%n"); + os.printLine("id name %s", ComputerPlayer.engineName); + os.printLine("id author Peter Osterlund"); DroidEngineControl.printOptions(os); - os.printf("uciok%n"); + os.printLine("uciok"); } else if (cmd.equals("isready")) { initEngine(os); - os.printf("readyok%n"); + os.printLine("readyok"); } else if (cmd.equals("setoption")) { initEngine(os); StringBuilder optionName = new StringBuilder(); @@ -260,7 +252,7 @@ public class CuckooChessEngine extends UCIEngineBase { } } - private final void initEngine(NioPrintStream os) { + private final void initEngine(LocalPipe os) { if (engine == null) { engine = new DroidEngineControl(os); } diff --git a/DroidFish/src/org/petero/droidfish/engine/cuckoochess/DroidEngineControl.java b/DroidFish/src/org/petero/droidfish/engine/cuckoochess/DroidEngineControl.java index 926c7c8..896fc20 100644 --- a/DroidFish/src/org/petero/droidfish/engine/cuckoochess/DroidEngineControl.java +++ b/DroidFish/src/org/petero/droidfish/engine/cuckoochess/DroidEngineControl.java @@ -35,12 +35,14 @@ import java.util.Arrays; import java.util.List; import java.util.Random; +import org.petero.droidfish.engine.LocalPipe; + /** * Control the search thread. * @author petero */ public class DroidEngineControl { - NioPrintStream os; + LocalPipe os; Thread engineThread; private final Object threadMutex; @@ -76,18 +78,18 @@ public class DroidEngineControl { * This class is responsible for sending "info" strings during search. */ static class SearchListener implements Search.Listener { - NioPrintStream os; + LocalPipe os; - SearchListener(NioPrintStream os) { + SearchListener(LocalPipe os) { this.os = os; } public void notifyDepth(int depth) { - os.printf("info depth %d%n", depth); + os.printLine("info depth %d", depth); } public void notifyCurrMove(Move m, int moveNr) { - os.printf("info currmove %s currmovenumber %d%n", moveToString(m), moveNr); + os.printLine("info currmove %s currmovenumber %d", moveToString(m), moveNr); } public void notifyPV(int depth, int score, int time, long nodes, int nps, boolean isMate, @@ -103,16 +105,16 @@ public class DroidEngineControl { } else if (lowerBound) { bound = " lowerbound"; } - os.printf("info depth %d score %s %d%s time %d nodes %d nps %d pv%s%n", + os.printLine("info depth %d score %s %d%s time %d nodes %d nps %d pv%s", depth, isMate ? "mate" : "cp", score, bound, time, nodes, nps, pvBuf.toString()); } public void notifyStats(long nodes, int nps, int time) { - os.printf("info nodes %d nps %d time %d%n", nodes, nps, time); + os.printLine("info nodes %d nps %d time %d", nodes, nps, time); } } - public DroidEngineControl(NioPrintStream os) { + public DroidEngineControl(LocalPipe os) { this.os = os; threadMutex = new Object(); setupTT(); @@ -260,9 +262,9 @@ public class DroidEngineControl { Move ponderMove = getPonderMove(pos, m); synchronized (threadMutex) { if (ponderMove != null) { - os.printf("bestmove %s ponder %s%n", moveToString(m), moveToString(ponderMove)); + os.printLine("bestmove %s ponder %s", moveToString(m), moveToString(ponderMove)); } else { - os.printf("bestmove %s%n", moveToString(m)); + os.printLine("bestmove %s", moveToString(m)); } engineThread = null; sc = null; @@ -362,14 +364,14 @@ public class DroidEngineControl { return ret; } - static void printOptions(NioPrintStream os) { - os.printf("option name Hash type spin default 2 min 1 max 2048%n"); - os.printf("option name OwnBook type check default false%n"); - os.printf("option name Ponder type check default true%n"); - os.printf("option name UCI_AnalyseMode type check default false%n"); - os.printf("option name UCI_EngineAbout type string default %s by Peter Osterlund, see http://web.comhem.se/petero2home/javachess/index.html%n", + static void printOptions(LocalPipe os) { + os.printLine("option name Hash type spin default 2 min 1 max 2048"); + os.printLine("option name OwnBook type check default false"); + os.printLine("option name Ponder type check default true"); + os.printLine("option name UCI_AnalyseMode type check default false"); + os.printLine("option name UCI_EngineAbout type string default %s by Peter Osterlund, see http://web.comhem.se/petero2home/javachess/index.html", ComputerPlayer.engineName); - os.printf("option name Strength type spin default 1000 min 0 max 1000\n"); + os.printLine("option name Strength type spin default 1000 min 0 max 1000"); } final void setOption(String optionName, String optionValue) { diff --git a/DroidFish/src/org/petero/droidfish/engine/cuckoochess/NioInputStream.java b/DroidFish/src/org/petero/droidfish/engine/cuckoochess/NioInputStream.java deleted file mode 100644 index 5bb60b8..0000000 --- a/DroidFish/src/org/petero/droidfish/engine/cuckoochess/NioInputStream.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - DroidFish - An Android chess program. - Copyright (C) 2011 Peter Ă–sterlund, peterosterlund2@gmail.com - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -package org.petero.droidfish.engine.cuckoochess; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.Pipe; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.util.ArrayList; - -/** Simple InputStream look-alike on top of nio. */ -class NioInputStream { - Pipe.SourceChannel in; - ByteBuffer buffer; - Selector selector; - - ArrayList inBuf; - StringBuilder lineBuf; - - public NioInputStream(Pipe pipe) { - in = pipe.source(); - try { - in.configureBlocking(false); - selector = Selector.open(); - in.register(selector, SelectionKey.OP_READ); - - buffer = ByteBuffer.allocate(1024); - inBuf = new ArrayList(); - lineBuf = new StringBuilder(128); - } catch (IOException e) { - } - } - - public String readLine() { - while (true) { - String s = readLine(1000); - if (s != null) - return s; - } - } - - public String readLine(int timeoutMillis) { - try { - boolean haveNewLine = false; - for (int i = 0; i < inBuf.size(); i++) { - if (inBuf.get(i) == '\n') { - haveNewLine = true; - break; - } - } - if (!haveNewLine) { - // Refill inBuf - if (timeoutMillis < 1) - timeoutMillis = 1; - selector.select(timeoutMillis); - buffer.clear(); - for (SelectionKey sk : selector.selectedKeys()) - if (sk.isValid() && sk.isReadable()) - in.read(buffer); - buffer.flip(); - while (buffer.position() < buffer.limit()) { - byte b = buffer.get(); - inBuf.add((char)b); - } - } - - // Extract line - String ret = ""; - int i; - for (i = 0; i < inBuf.size(); i++) { - char c = inBuf.get(i); - if (c == '\n') { - int newSize = inBuf.size() - i - 1; - for (int j = 0; j < newSize; j++) - inBuf.set(j, inBuf.get(j+i+1)); - while (inBuf.size() > newSize) - inBuf.remove(inBuf.size() - 1); - ret = lineBuf.toString(); - lineBuf = new StringBuilder(128); - break; - } else { - lineBuf.append(c); - } - } - if (i == inBuf.size()) - inBuf.clear(); - return ret; - } catch (IOException e) { - } - return null; - } -} diff --git a/DroidFish/src/org/petero/droidfish/engine/cuckoochess/NioPrintStream.java b/DroidFish/src/org/petero/droidfish/engine/cuckoochess/NioPrintStream.java deleted file mode 100644 index f367777..0000000 --- a/DroidFish/src/org/petero/droidfish/engine/cuckoochess/NioPrintStream.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - DroidFish - An Android chess program. - Copyright (C) 2011 Peter Ă–sterlund, peterosterlund2@gmail.com - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -package org.petero.droidfish.engine.cuckoochess; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.Pipe; - -/** Simple PrintStream look-alike on top of nio. */ -class NioPrintStream { - Pipe.SinkChannel out; - - public NioPrintStream(Pipe pipe) { - out = pipe.sink(); - } - - public void printf(String format) { - try { - String s = String.format(format, new Object[]{}); - out.write(ByteBuffer.wrap(s.getBytes())); - } catch (IOException e) { - } - } - - public void printf(String format, Object ... args) { - try { - String s = String.format(format, args); - out.write(ByteBuffer.wrap(s.getBytes())); - } catch (IOException e) { - } - } -}