diff --git a/README.md b/README.md index a8eb77c..e4a9f61 100644 --- a/README.md +++ b/README.md @@ -31,4 +31,4 @@ The duration and accuracy of this test greatly depends on its search depth. This depth is 1 by default (to limit Github's resources consumption - Every push trigger a mvn test action). In order to perform better test, you can set the **perftDepth** system property to a higher value. ### TODO -- The detection of invalid UNSAFE move does not work. \ No newline at end of file +- The detection of invalid UNSAFE move does not work (An [issue](https://github.com/bhlangonijr/chesslib/issues/114) is opened in Chesslib project about this problem). \ No newline at end of file diff --git a/pom.xml b/pom.xml index 7696b25..588835a 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ 1.0.8 chesslib-uci-engine - 0.0.1 + 0.0.2 chesslib-uci-engine A basic uci engine plugin for jchess-uci. @@ -50,12 +50,12 @@ com.fathzer games-core - 0.0.11-SNAPSHOT + 0.0.12-SNAPSHOT com.fathzer jchess-uci - 2.0.3-SNAPSHOT + 2.0.4-SNAPSHOT org.json @@ -84,6 +84,12 @@ 5.10.2 test + + org.awaitility + awaitility + 4.2.2 + test + diff --git a/src/main/java/com/fathzer/jchess/chesslib/ai/ChessLibDeepeningPolicy.java b/src/main/java/com/fathzer/jchess/chesslib/ai/ChessLibDeepeningPolicy.java index 45f3b63..12de2a7 100644 --- a/src/main/java/com/fathzer/jchess/chesslib/ai/ChessLibDeepeningPolicy.java +++ b/src/main/java/com/fathzer/jchess/chesslib/ai/ChessLibDeepeningPolicy.java @@ -16,6 +16,7 @@ public int getNextDepth(int currentDepth) { @Override public boolean isEnoughTimeToDeepen(int depth) { - return depth<5 || getSpent() implements TestableMoveGeneratorBuilder, Displayable { private static final List> EVALUATORS = Arrays.asList(new EvaluatorConfiguration<>("simplified",SimplifiedEvaluator::new),new EvaluatorConfiguration<>("naive",NaiveEvaluator::new)); - private final MoveLibrary ownBook; + private final DeferredReadMoveLibrary ownBook; public ChessLibEngine() { this (null); } - public ChessLibEngine(MoveLibrary ownBook) { + public ChessLibEngine(DeferredReadMoveLibrary ownBook) { super (buildEngine(EVALUATORS.get(0).getBuilder(), 20), new BasicTimeManager<>(RemainingMoveOracle.INSTANCE)); setEvaluators(EVALUATORS); this.ownBook = ownBook; @@ -63,6 +63,10 @@ public String getId() { public String getAuthor() { return "Jean-Marc Astesana (Fathzer), Move generator is from Ben-Hur Carlos Vieira Langoni Junior"; } + + DeferredReadMoveLibrary getOwnBook() { + return ownBook; + } @Override public boolean hasOwnBook() { @@ -111,7 +115,7 @@ public String getBoardAsString() { @Override public String getFEN() { - return board.getBoard().getFen(); + return board==null ? null : board.getBoard().getFen(); } @Override diff --git a/src/main/java/com/fathzer/jchess/chesslib/uci/Main.java b/src/main/java/com/fathzer/jchess/chesslib/uci/Main.java index 8314f47..194ddd1 100644 --- a/src/main/java/com/fathzer/jchess/chesslib/uci/Main.java +++ b/src/main/java/com/fathzer/jchess/chesslib/uci/Main.java @@ -1,11 +1,8 @@ package com.fathzer.jchess.chesslib.uci; -import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; -import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Collection; @@ -23,6 +20,7 @@ import com.fathzer.jchess.uci.UCI; import com.fathzer.jchess.uci.extended.ExtendedUCI; import com.fathzer.jchess.uci.extended.SpeedTest; +import com.fathzer.jchess.uci.helper.DeferredReadMoveLibrary; import com.github.bhlangonijr.chesslib.move.Move; public class Main extends ExtendedUCI { @@ -30,49 +28,42 @@ public class Main extends ExtendedUCI { public static void main(String[] args) { final String pathProperty = System.getProperty("openingsUrl"); - final MoveLibrary openings = pathProperty==null ? null : readOpenings(pathProperty); + final DeferredReadMoveLibrary openings = pathProperty==null ? null : new DeferredReadMoveLibrary<>(pathProperty, Main::readOpenings); try (UCI uci = new Main(new ChessLibEngine(openings))) { uci.run(); } } - private static URL toURL(String path) throws IOException { - URL url; - try { - url = new URL(path); - } catch (MalformedURLException e) { - File file = new File(path); - if (!file.exists()) { - throw new FileNotFoundException(); - } - url = file.toURI().toURL(); - } - return url; + public Main(Engine defaultEngine) { + super(defaultEngine); + addCommand(this::speedTest, "st"); } - private static MoveLibrary readOpenings(String url) { - try { - return readOpenings(url, toURL(url)); - } catch (IOException e) { - LOGGER.error("Unable to load opening library at "+url, e); - return null; + @Override + protected void doIsReady(Deque tokens) { + if (engine instanceof ChessLibEngine cle) { + final DeferredReadMoveLibrary book = cle.getOwnBook(); + if (book!=null && book.isInitRequired()) { + LOGGER.debug("Start reading opening library from {}", book.getUrl()); + try { + book.init(); + LOGGER.debug("Opening library read from {}", book.getUrl()); + } catch (IOException e) { + LOGGER.error("Unable to load opening library at {}", book.getUrl(), e); + debug("An error occurred while reading opening book"); + } + } } + super.doIsReady(tokens); } - - private static MoveLibrary readOpenings(String url, final URL location) throws IOException { + + static MoveLibrary readOpenings(final URL location) throws IOException { final boolean compressed = location.getFile().endsWith(".gz"); try (InputStream stream = location.openStream()) { - final DefaultOpenings result = new DefaultOpenings(()->stream, compressed); - LOGGER.info("Opening library read from {}", url); - return result; + return new DefaultOpenings(()->stream, compressed); } } - public Main(Engine defaultEngine) { - super(defaultEngine); - addCommand(this::speedTest, "st"); - } - @Override protected Collection readTestData() { try (InputStream stream = Main.class.getResourceAsStream("/Perft.txt")) { @@ -83,10 +74,20 @@ protected Collection readTestData() { } private void speedTest(Deque args) { - if (engine instanceof ChessLibEngine) { - out("completed in "+new SpeedTest<>((ChessLibEngine)engine).run()+"ms"); + if (engine instanceof ChessLibEngine chesslibEngine) { + out("completed in "+new SpeedTest<>(chesslibEngine, this::out).run()+"ms"); } else { debug("This engine does not support this command"); } } + + @Override + protected void err(String tag, Throwable e) { + LOGGER.error("An error occurred in {}", tag, e); + } + + @Override + protected void err(CharSequence message) { + LOGGER.error("{}", message); + } } diff --git a/src/test/java/com/fathzer/jchess/chesslib/ai/ChessLibDeepeningPolicyTest.java b/src/test/java/com/fathzer/jchess/chesslib/ai/ChessLibDeepeningPolicyTest.java new file mode 100644 index 0000000..02599d1 --- /dev/null +++ b/src/test/java/com/fathzer/jchess/chesslib/ai/ChessLibDeepeningPolicyTest.java @@ -0,0 +1,40 @@ +package com.fathzer.jchess.chesslib.ai; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.concurrent.TimeUnit; + +import static org.awaitility.Awaitility.*; + +import org.junit.jupiter.api.Test; + +import com.fathzer.games.ai.iterativedeepening.DeepeningPolicy; + +class ChessLibDeepeningPolicyTest { + @Test + void test() { + final DeepeningPolicy policy = new ChessLibDeepeningPolicy(10); + assertEquals(10, policy.getDepth()); + assertEquals(2, policy.getStartDepth()); + assertEquals(4, policy.getNextDepth(2)); + assertEquals(6, policy.getNextDepth(4)); + assertEquals(7, policy.getNextDepth(6)); + assertEquals(10, policy.getNextDepth(10)); + } + + @Test + void bug20241127() { + // isEnoughTimeToDeepen returned true when time was spent and depth was < 5 + final DeepeningPolicy policy = new ChessLibDeepeningPolicy(10); + policy.setMaxTime(1); + assertThrows(IllegalStateException.class, () -> policy.getSpent()); + policy.start(); + await().atLeast(10, TimeUnit.MILLISECONDS); + assertFalse(policy.isEnoughTimeToDeepen(4)); + + policy.setMaxTime(10000); + policy.start(); + } + + +}