Skip to content

Commit

Permalink
Adds ability to return depth, score & pv in info line before go reply
Browse files Browse the repository at this point in the history
  • Loading branch information
fathzer committed Mar 27, 2024
1 parent 6d0b6e1 commit 817ec1e
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 8 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
A partial (but yet usable) java implementation of the [chess UCI protocol](https://www.shredderchess.com/chess-features/uci-universal-chess-interface.html) with pluggable chess engines.

## How to use it
This library requires Java 11+ and is available in [Maven central](https://central.sonatype.com/artifact/com.fathzer/jchess-uci).
This library requires Java 17+ and is available in [Maven central](https://central.sonatype.com/artifact/com.fathzer/jchess-uci).

- First create a class that implements the **com.fathzer.jchess.uci.Engine** interface.
Let say this implementation class is **MyEngine**.
Expand All @@ -30,6 +30,7 @@ It does not directly support the following commands (but you can add them in an
- **Dont miss the ShredderChess Annual Barbeque**: This command was in the original specification ... But was a joke.
- **register**: As a promoter of open source free sofware, I will not encourage you to develop software that requires registration.
- **ponderhit** is not yet implemented.
- Only depth, score and pv are implemented in info lines preceeding go reply.

It also does not recognize commands starting with unknown token (to be honest, it's not very hard to implement but seemed a very bad, error prone, idea to me).

Expand Down Expand Up @@ -90,4 +91,5 @@ If you do not use the *com.fathzer.jchess.uci.extended* and *com.fathzer.jchess.

## TODO
* Verify the engine is protected against strange client behavior (like changing the position during a go request).
* Implement support for multi-PV search.
* Implement support for pondering.
4 changes: 2 additions & 2 deletions 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.1-SNAPSHOT</version>
<version>2.0.2-SNAPSHOT</version>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Expand All @@ -22,7 +22,7 @@
<dependency>
<groupId>com.fathzer</groupId>
<artifactId>games-core</artifactId>
<version>0.0.9-SNAPSHOT</version>
<version>0.0.10-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
Expand Down
26 changes: 24 additions & 2 deletions src/main/java/com/fathzer/jchess/uci/GoReply.java
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ public GoReply(UCIMove move, UCIMove ponderMove) {
this.ponderMove = ponderMove;
}

public void setInfo(Info info) {
this.info = info;
}

public Optional<UCIMove> getMove() {
return Optional.ofNullable(bestMove);
}
Expand Down Expand Up @@ -140,7 +144,25 @@ public Optional<String> getMainInfoString() {
if (info==null) {
return Optional.empty();
}
//TODO
return Optional.of("depth "+info.depth);
final StringBuilder builder = new StringBuilder();
if (info.depth>0) {
builder.append("depth ").append(info.depth);
}
final Optional<Score> score = info.scoreBuilder.apply(bestMove);
if (score.isPresent()) {
if (!builder.isEmpty()) {
builder.append(' ');
}
builder.append("score ").append(score.get().toUCI());
}
final Optional<List<UCIMove>> pv = info.pvBuilder.apply(bestMove);
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);
}
return builder.isEmpty() ? Optional.empty() : Optional.of("info "+builder);
}
}
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 @@ -191,7 +191,12 @@ protected void doGo(Deque<String> tokens) {
final Optional<GoParameters> goOptions = parse(GoParameters::new, GoParameters.PARSER, tokens);
if (goOptions.isPresent()) {
final LongRunningTask<GoReply> task = engine.go(goOptions.get());
final boolean started = doBackground(() -> out(task.get().toString()), task::stop);
final boolean started = doBackground(() -> {
final GoReply goReply = task.get();
final Optional<String> mainInfo = goReply.getMainInfoString();
mainInfo.ifPresent(this::out);
out(goReply.toString());
}, task::stop);
if (!started) {
debug("Engine is already working");
}
Expand Down
44 changes: 42 additions & 2 deletions src/main/java/com/fathzer/jchess/uci/helper/AbstractEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,24 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;

import com.fathzer.games.MoveGenerator;
import com.fathzer.games.MoveGenerator.MoveConfidence;
import com.fathzer.games.ai.evaluation.EvaluatedMove;
import com.fathzer.games.ai.evaluation.Evaluation;
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.time.TimeManager;
import com.fathzer.games.ai.transposition.TranspositionTable;
import com.fathzer.jchess.uci.GoReply;
import com.fathzer.jchess.uci.GoReply.CpScore;
import com.fathzer.jchess.uci.GoReply.Info;
import com.fathzer.jchess.uci.GoReply.MateScore;
import com.fathzer.jchess.uci.GoReply.Score;
import com.fathzer.jchess.uci.Engine;
import com.fathzer.jchess.uci.LongRunningTask;
import com.fathzer.jchess.uci.UCIMove;
Expand Down Expand Up @@ -120,9 +129,21 @@ 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 M move = engine.getBestMove(board, candidates.isEmpty() ? null : candidates);
final Optional<BestMove<M>> best = engine.getBestMove(board, candidates.isEmpty() ? null : candidates);
c.set(engine, previous);
return new GoReply(move==null ? null : toUCI(move));
if (best.isEmpty()) {
return new GoReply(null);
}
final EvaluatedMove<M> move = best.get().move();
final GoReply goReply = new GoReply(toUCI(move.getContent()));
final Info info = new Info(best.get().depth());
info.setScoreBuilder(m -> toScore(toMove(m), move));
info.setPvBuilder(m -> {
final List<UCIMove> list = engine.getTranspositionTable().collectPV(board, toMove(m), info.getDepth()).stream().map(x -> toUCI(x)).toList();
return list.isEmpty() ? Optional.empty() : Optional.of(list);
});
goReply.setInfo(info);
return goReply;
}

@Override
Expand All @@ -133,6 +154,25 @@ public void stop() {
};
}

private Optional<Score> toScore(M m, EvaluatedMove<M> known) {
final Evaluation evaluation = known.getEvaluation();
final Type type = evaluation.getType();
if (!m.equals(known.getContent()) || type==Type.UNKNOWN) {
return Optional.empty();
}
final Score score;
if (type==Type.EVAL) {
score = new CpScore(evaluation.getScore());
} else if (type==Type.WIN) {
score = new MateScore(evaluation.getCountToEnd());
} else if (type==Type.LOOSE) {
score = new MateScore(-evaluation.getCountToEnd());
} else {
throw new IllegalArgumentException("Type "+type+" is not supported");
}
return Optional.of(score);
}

@SuppressWarnings("unchecked")
@Override
public B get() {
Expand Down

0 comments on commit 817ec1e

Please sign in to comment.