Skip to content

Commit

Permalink
Adds MultiPV support in AbstractEngine and UCI
Browse files Browse the repository at this point in the history
  • Loading branch information
fathzer committed Apr 24, 2024
1 parent 8726b02 commit 7a16381
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 20 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<version>1.0.8</version>
</parent>
<artifactId>jchess-uci</artifactId>
<version>2.0.2-SNAPSHOT</version>
<version>2.0.3-SNAPSHOT</version>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/fathzer/jchess/uci/ClassicalOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ public static CheckOption limitStrength(Consumer<Boolean> trigger) {
return new CheckOption(LIMIT_STRENGTH_NAME, trigger, false);
}

public static SpinOption<Integer> multiPV(Consumer<Integer> trigger, int maxValue) {
return new IntegerSpinOption(MULTI_PV_NAME, trigger, 1, 1, maxValue);
public static SpinOption<Integer> multiPV(Consumer<Integer> trigger) {
return new IntegerSpinOption(MULTI_PV_NAME, trigger, 1, 1, 256);
}

public static CheckOption ownBook(Consumer<Boolean> trigger, boolean defaultValue) {
Expand Down
18 changes: 12 additions & 6 deletions src/main/java/com/fathzer/jchess/uci/GoReply.java
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,20 @@ public String toString() {
* @return The line or an empty optional if no information is available
*/
public Optional<String> getMainInfoString() {
if (info==null) {
return Optional.empty();
}
return getInfoString(0);
}

/** Gets the uci info line to return just before sending the reply.
* @param index The move index (0 for the best move or the index or the extra moves passed to {@code Info#setExtraMoves(List)} +1
* @return The line or an empty optional if no information is available
*/
public Optional<String> getInfoString(int index) {
final StringBuilder builder = new StringBuilder();
if (info.depth>0) {
builder.append("depth ").append(info.depth);
}
final Optional<Score> score = info.scoreBuilder.apply(bestMove);
final UCIMove move = index==0 ? bestMove : info.extraMoves.get(index-1);
final Optional<Score> score = info.scoreBuilder.apply(move);
if (score.isPresent()) {
if (!builder.isEmpty()) {
builder.append(' ');
Expand All @@ -174,13 +180,13 @@ public Optional<String> getMainInfoString() {
}
builder.append("hashfull ").append(info.hashFull);
}
final Optional<List<UCIMove>> pv = info.pvBuilder.apply(bestMove);
final Optional<List<UCIMove>> pv = info.pvBuilder.apply(move);
if (pv.isPresent()) {
if (!builder.isEmpty()) {
builder.append(' ');
}
final String moves = String.join(" ", pv.get().stream().map(UCIMove::toString).toList());
builder.append("multipv 1 pv ").append(moves);
builder.append("multipv ").append(index+1).append(" pv ").append(moves);
}
return builder.isEmpty() ? Optional.empty() : Optional.of("info "+builder);
}
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/com/fathzer/jchess/uci/UCI.java
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,12 @@ protected void doGo(Deque<String> tokens) {
final boolean started = doBackground(() -> {
final GoReply goReply = task.get();
final Optional<String> mainInfo = goReply.getMainInfoString();
mainInfo.ifPresent(this::out);
if (mainInfo.isPresent()) {
this.out(mainInfo.get());
for (int i = 1; i <= goReply.getInfo().get().getExtraMoves().size(); i++) {
this.out(goReply.getInfoString(i).get());
}
}
out(goReply.toString());
}, task::stop, e -> err(GO_CMD, e));
if (!started) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public SpeedTest(AbstractEngine<M, B> engine) {
private Result<M> fill(String fen) {
uciEngine.newGame();
uciEngine.setStartPosition(fen);
return new Result<>(fen, uciEngine.getEngine().getBestMoves(uciEngine.get()));
return new Result<>(fen, uciEngine.getEngine().getBestMoves(uciEngine.get()).getBestMoves());
}

/** Launches the test.
Expand Down
26 changes: 17 additions & 9 deletions src/main/java/com/fathzer/jchess/uci/helper/AbstractEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import com.fathzer.games.MoveGenerator;
import com.fathzer.games.MoveGenerator.MoveConfidence;
Expand All @@ -14,7 +15,7 @@
import com.fathzer.games.ai.evaluation.Evaluation.Type;
import com.fathzer.games.ai.evaluation.Evaluator;
import com.fathzer.games.ai.iterativedeepening.IterativeDeepeningEngine;
import com.fathzer.games.ai.iterativedeepening.IterativeDeepeningEngine.BestMove;
import com.fathzer.games.ai.iterativedeepening.SearchHistory;
import com.fathzer.games.ai.time.TimeManager;
import com.fathzer.games.ai.transposition.TranspositionTable;
import com.fathzer.jchess.uci.GoReply;
Expand Down Expand Up @@ -98,6 +99,7 @@ public List<Option<?>> getOptions() {
options.add(new ComboOption("evaluation", this::setEvaluator, defaultEvaluator, evaluatorBuilders.keySet()));
}
options.add(ClassicalOptions.threads(this.engine::setParallelism, defaultThreads));
options.add(ClassicalOptions.multiPV(this.engine.getDeepeningPolicy()::setSize));
options.add(new IntegerSpinOption("depth", this.engine.getDeepeningPolicy()::setDepth, defaultDepth, 1, 128));
options.add(new LongSpinOption("maxtime", this.engine.getDeepeningPolicy()::setMaxTime, defaultMaxTime, 1, Long.MAX_VALUE));
return options;
Expand Down Expand Up @@ -130,24 +132,27 @@ public GoReply get() {
final UCIEngineSearchConfiguration<M, B> c = new UCIEngineSearchConfiguration<>(timeManager);
final UCIEngineSearchConfiguration.EngineConfiguration previous = c.configure(engine, options, board);
final List<M> candidates = options.getMoveToSearch().stream().map(AbstractEngine.this::toMove).toList();
final Optional<BestMove<M>> best = engine.getBestMove(board, candidates.isEmpty() ? null : candidates);
final SearchHistory<M> search = engine.getBestMoves(board, candidates.isEmpty() ? null : candidates);
c.set(engine, previous);
if (best.isEmpty()) {
if (search.isEmpty()) {
return new GoReply(null);
}
final EvaluatedMove<M> move = best.get().move();
final EvaluatedMove<M> move = getChoice(search);
final GoReply goReply = new GoReply(toUCI(move.getContent()));
final Info info = new Info(best.get().depth());
final Info info = new Info(search.getDepth());
final TranspositionTable<M> tt = engine.getTranspositionTable();
final int entryCount = tt.getEntryCount();
if (entryCount>0) {
info.setHashFull((int)(1000L*entryCount/tt.getSize()));
}
info.setScoreBuilder(m -> toScore(toMove(m), move));
final List<EvaluatedMove<M>> bestMoves = search.getBestMoves();
final Map<String, Optional<Score>> scores = bestMoves.stream().collect(Collectors.toMap(em -> toUCI(em.getContent()).toString(), em -> toScore(em.getEvaluation())));
info.setScoreBuilder(m -> scores.get(m.toString()));
info.setPvBuilder(m -> {
final List<UCIMove> list = tt.collectPV(board, toMove(m), info.getDepth()).stream().map(x -> toUCI(x)).toList();
return list.isEmpty() ? Optional.empty() : Optional.of(list);
});
info.setExtraMoves(bestMoves.stream().filter(em -> !move.getContent().equals(em.getContent())).map(em->toUCI(em.getContent())).toList());
goReply.setInfo(info);
return goReply;
}
Expand All @@ -160,10 +165,13 @@ public void stop() {
};
}

private Optional<Score> toScore(M m, EvaluatedMove<M> known) {
final Evaluation evaluation = known.getEvaluation();
private EvaluatedMove<M> getChoice(SearchHistory<M> history) {
return history.getBestMoves().get(0);
}

private Optional<Score> toScore(Evaluation evaluation) {
final Type type = evaluation.getType();
if (!m.equals(known.getContent()) || type==Type.UNKNOWN) {
if (type==Type.UNKNOWN) {
return Optional.empty();
}
final Score score;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ public class UCIEngineSearchConfiguration<M, B extends MoveGenerator<M>> {
public static class EngineConfiguration {
private long maxTime;
private int depth;
private boolean deepenOnForced;

private EngineConfiguration(IterativeDeepeningEngine<?, ?> engine) {
maxTime = engine.getDeepeningPolicy().getMaxTime();
depth = engine.getDeepeningPolicy().getDepth();
deepenOnForced = engine.getDeepeningPolicy().isDeepenOnForced();
}
}

Expand All @@ -35,6 +37,7 @@ public EngineConfiguration configure(IterativeDeepeningEngine<M, B> engine, GoPa
}
if (options.getDepth()>0) {
engine.getDeepeningPolicy().setDepth(options.getDepth());
engine.getDeepeningPolicy().setDeepenOnForced(true);
}
if (timeOptions.getMoveTimeMs()>0) {
engine.getDeepeningPolicy().setMaxTime(timeOptions.getMoveTimeMs());
Expand All @@ -50,6 +53,7 @@ public EngineConfiguration configure(IterativeDeepeningEngine<M, B> engine, GoPa
public void set(IterativeDeepeningEngine<M, B> engine, EngineConfiguration c) {
engine.getDeepeningPolicy().setMaxTime(c.maxTime);
engine.getDeepeningPolicy().setDepth(c.depth);
engine.getDeepeningPolicy().setDeepenOnForced(c.deepenOnForced);
}

public long getMaxTime(B board, long remainingMs, long incrementMs, int movesToGo) {
Expand Down

0 comments on commit 7a16381

Please sign in to comment.