Skip to content

Commit

Permalink
Start implementing some tests
Browse files Browse the repository at this point in the history
  • Loading branch information
fathzer committed Mar 28, 2024
1 parent 817ec1e commit c144201
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 53 deletions.
35 changes: 21 additions & 14 deletions src/main/java/com/fathzer/jchess/uci/BackgroundTaskManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,30 @@
import java.util.function.Consumer;

class BackgroundTaskManager implements AutoCloseable {
private final ExecutorService exec = Executors.newFixedThreadPool(1);
private final AtomicReference<Runnable> stopper = new AtomicReference<>();
private final Consumer<Exception> logger;

public BackgroundTaskManager(Consumer<Exception> logger) {
this.logger = logger;
static class Task {
private final Consumer<Exception> logger;
private final Runnable task;
private final Runnable stopTask;

Task(Runnable task, Runnable stopTask, Consumer<Exception> logger) {
this.task = task;
this.stopTask = stopTask;
this.logger = logger;
}
}

boolean doBackground(Runnable task, Runnable stopTask) {
final boolean result = this.stopper.compareAndSet(null, stopTask);

private final ExecutorService exec = Executors.newFixedThreadPool(1);
private final AtomicReference<Task> current = new AtomicReference<>();

boolean doBackground(Task task) {
final boolean result = this.current.compareAndSet(null, task);
if (result) {
exec.submit(() -> {
try {
task.run();
this.stopper.set(null);
task.task.run();
this.current.set(null);
} catch (Exception e) {
logger.accept(e);
stop();
}
});
Expand All @@ -34,12 +41,12 @@ boolean doBackground(Runnable task, Runnable stopTask) {
* @return true if a task was executed.
*/
boolean stop() {
final Runnable stopTask = stopper.getAndSet(null);
final Task stopTask = current.getAndSet(null);
if (stopTask!=null) {
try {
stopTask.run();
stopTask.stopTask.run();
} catch (Exception e) {
logger.accept(e);
stopTask.logger.accept(e);
}
}
return stopTask!=null;
Expand Down
69 changes: 42 additions & 27 deletions src/main/java/com/fathzer/jchess/uci/UCI.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.function.Supplier;
import java.util.stream.Collectors;

import com.fathzer.jchess.uci.BackgroundTaskManager.Task;
import com.fathzer.jchess.uci.option.CheckOption;
import com.fathzer.jchess.uci.option.IntegerSpinOption;
import com.fathzer.jchess.uci.option.Option;
Expand All @@ -39,6 +40,7 @@ public class UCI implements Runnable, AutoCloseable {
private static final BufferedReader IN = new BufferedReader(new InputStreamReader(System.in));
private static final String MOVES = "moves";
private static final String ENGINE_CMD = "engine";
private static final String GO_CMD = "go";
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.nnnnnnnn");

private static final String CHESS960_OPTION = "UCI_Chess960";
Expand All @@ -49,7 +51,7 @@ public class UCI implements Runnable, AutoCloseable {
private final Map<String, Consumer<Deque<String>>> executors = new HashMap<>();
private final Map<String, Engine> engines = new HashMap<>();

private final BackgroundTaskManager backTasks = new BackgroundTaskManager(e -> out(e, 0));
private final BackgroundTaskManager backTasks = new BackgroundTaskManager();
private boolean debug = Boolean.getBoolean("logToFile");
private boolean debugUCI = Boolean.getBoolean("debugUCI");
private Map<String, Option<?>> options;
Expand All @@ -64,7 +66,7 @@ public UCI(Engine defaultEngine) {
addCommand(this::doIsReady, "isready");
addCommand(this::doNewGame, "ucinewgame", "ng");
addCommand(this::doPosition, "position");
addCommand(this::doGo, "go");
addCommand(this::doGo, GO_CMD);
addCommand(this::doStop, "stop");
addCommand(this::doEngine,ENGINE_CMD);
if (System.console()!=null) {
Expand Down Expand Up @@ -177,11 +179,12 @@ private String getFEN(Collection<String> tokens) {

/** Launches a task on the background thread.
* @param task The task to launch
* @param stopper A runnable that stops the task when invoked (it is user by the <i>stop</i> command in order to stop the task.
* @param stopper A runnable that stops the task when invoked (it is user by the <i>stop</i> command in order to stop the task.
* @param logger Where to send the exceptions
* @return true if the task is launched, false if another task is already running.
*/
protected boolean doBackground(Runnable task, Runnable stopper) {
return backTasks.doBackground(task, stopper);
protected boolean doBackground(Runnable task, Runnable stopper, Consumer<Exception> logger) {
return backTasks.doBackground(new Task(task, stopper, logger));
}

protected void doGo(Deque<String> tokens) {
Expand All @@ -196,7 +199,7 @@ protected void doGo(Deque<String> tokens) {
final Optional<String> mainInfo = goReply.getMainInfoString();
mainInfo.ifPresent(this::out);
out(goReply.toString());
}, task::stop);
}, task::stop, e -> err(GO_CMD, e));
if (!started) {
debug("Engine is already working");
}
Expand Down Expand Up @@ -271,12 +274,14 @@ public void run() {
init();
while (true) {
log("Waiting for command...");
final String command=getNextCommand();
final String command=getNextCommand().trim();
if ("quit".equals(command) || "q".equals(command)) {
log(">",command);
break;
}
doCommand(command);
if (!command.isEmpty()) {
doCommand(command);
}
}
}

Expand All @@ -286,40 +291,50 @@ private void init() {
try {
Files.readAllLines(Paths.get(initFile)).stream().map(String::trim).filter(s -> !s.isEmpty()).forEach(this::doCommand);
} catch (IOException e) {
out(e,0);
err("init engine", e);
}
}

}

/** Executes a command.
* @param command The command to execute
* @param returns true if the command was found
*/
protected void doCommand(final String command) {
protected boolean doCommand(final String command) {
log(">",command);
final Deque<String> tokens = new LinkedList<>(Arrays.asList(command.split(" ")));
if (!command.isEmpty() && !tokens.isEmpty()) {
final Consumer<Deque<String>> executor = executors.get(tokens.pop());
if (executor==null) {
debug("unknown command");
} else {
try {
executor.accept(tokens);
} catch (RuntimeException e) {
out(e,0);
}
final Consumer<Deque<String>> executor = executors.get(tokens.pop());
if (executor==null) {
debug("unknown command");
return false;
} else {
try {
executor.accept(tokens);
} catch (RuntimeException e) {
err(command, e);
}
return true;
}
}

protected void out(Throwable e, int level) {
out((level>0 ? "caused by":"")+e.toString());
Arrays.stream(e.getStackTrace()).forEach(f -> out(f.toString()));
protected void err(String tag, Throwable e) {
err("Error with "+tag+" tag");
err(e,0);
}

private void err(Throwable e, int level) {
err((level>0 ? "caused by":"")+e.toString());
Arrays.stream(e.getStackTrace()).forEach(f -> err(f.toString()));
if (e.getCause()!=null) {
out(e.getCause(),level+1);
err(e.getCause(),level+1);
}
}

protected void err(CharSequence message) {
System.err.println(message);
}

private void log(String... message) {
log(true, message);
}
Expand All @@ -344,7 +359,7 @@ private synchronized void log(boolean append, String... messages) {
/** Gets the next command from UCI client.
* <br>This method blocks until a command is available.
* <br>One can override this method in order to get commands from somewhere other than standard console input.
* @return The net command
* @return The next command
*/
protected String getNextCommand() {
String line;
Expand Down Expand Up @@ -373,8 +388,8 @@ protected void out(CharSequence message) {
protected void debug(CharSequence message) {
log(":","info","UCI debug is", Boolean.toString(debugUCI),message.toString());
if (debugUCI) {
System.out.print("info string ");
System.out.println(message.toString());
out("info string ");
out(message.toString());
}
}

Expand Down
29 changes: 17 additions & 12 deletions src/main/java/com/fathzer/jchess/uci/extended/ExtendedUCI.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@
import com.fathzer.jchess.uci.parameters.PerfTParameters;

public class ExtendedUCI extends UCI {
private static final String PERFT_COMMAND = "perft";
private static final String TEST_COMMAND = "test";

private static final String NO_POSITION_DEFINED = "No position defined";

public ExtendedUCI(Engine defaultEngine) {
super(defaultEngine);
addCommand(this::doPerft, "perft");
addCommand(this::doPerfStat,"test");
addCommand(this::doPerft, PERFT_COMMAND);
addCommand(this::doPerfStat,TEST_COMMAND);
addCommand(this::doDisplay, "d");
addCommand(this::wait, "block");
}
Expand Down Expand Up @@ -64,15 +67,17 @@ protected <M> void doPerft(Deque<String> tokens) {
if (params.isPresent()) {
@SuppressWarnings("unchecked")
final LongRunningTask<PerfTResult<M>> task = new PerftTask<>((MoveGeneratorSupplier<M>)engine, params.get());
background(() -> doPerft(task, params.get()), task::stop);
if (!doBackground(() -> doPerft(task, params.get()), task::stop, e -> err(PERFT_COMMAND,e))) {
debug("Engine is already working");
}
}
}

private void background(Runnable task, Runnable stopper) {
if (!doBackground(task, stopper)) {
debug("Engine is already working");
}
}
// private void background(Runnable task, Runnable stopper) {
// if (!doBackground(task, stopper, e -> out(e,0))) {
// debug("Engine is already working");
// }
// }

private <M> void doPerft(LongRunningTask<PerfTResult<M>> task, PerfTParameters params) {
final long start = System.currentTimeMillis();
Expand Down Expand Up @@ -114,15 +119,15 @@ protected void doPerfStat(Deque<String> tokens) {

private <M, B extends MoveGenerator<M>> void doPerfStat(Collection<PerfTTestData> testData, TestableMoveGeneratorBuilder<M, B> engine, PerfStatsParameters params) {
final MoveGeneratorChecker test = new MoveGeneratorChecker(testData);
test.setErrorManager(e-> out(e,0));
test.setErrorManager(e-> err(TEST_COMMAND, e));
test.setCountErrorManager(e -> out("Error for "+e.getStartPosition()+" expected "+e.getExpectedCount()+" got "+e.getActualCount()));
final TimerTask task = new TimerTask() {
@Override
public void run() {
doStop(null);
}
};
background(() -> {
doBackground(() -> {
final Timer timer = new Timer();
timer.schedule(task, 1000L*params.getCutTime());
try {
Expand All @@ -133,7 +138,7 @@ public void run() {
} finally {
timer.cancel();
}
}, test::cancel);
}, test::cancel, e -> err(TEST_COMMAND, e));
}

protected Collection<PerfTTestData> readTestData() {
Expand All @@ -147,7 +152,7 @@ private static String f(long num) {

private void wait(Deque<String> args) {
while (true) {
if (doBackground(()->{}, ()->{})) {
if (doBackground(()->{}, ()->{}, e ->{})) {
return;
}
try {
Expand Down
38 changes: 38 additions & 0 deletions src/test/java/com/fathzer/jchess/uci/UCITest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.fathzer.jchess.uci;
import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import com.fathzer.jchess.uci.util.InstrumentedUCI;
import com.fathzer.jchess.uci.util.InstrumentedEngine;

class UCITest {
private static InstrumentedUCI uci;
private static InstrumentedEngine engine;

@BeforeAll
static void init() {
engine = new InstrumentedEngine();
uci = new InstrumentedUCI(engine);
final Thread uciThread = new Thread(uci);
uciThread.setDaemon(true);
uciThread.start();
}

@BeforeEach
void clear() {
uci.clear();
}

@Test
void test() {
// assertFalse(uci.post("cjhjhl",500));
// assertFalse(uci.getDebug().isEmpty());

clear();
assertTrue(uci.post("position", 60000));
System.out.println(uci.getOutput());
}
}
40 changes: 40 additions & 0 deletions src/test/java/com/fathzer/jchess/uci/util/InstrumentedEngine.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.fathzer.jchess.uci.util;

import com.fathzer.jchess.uci.Engine;
import com.fathzer.jchess.uci.GoReply;
import com.fathzer.jchess.uci.LongRunningTask;
import com.fathzer.jchess.uci.UCIMove;
import com.fathzer.jchess.uci.parameters.GoParameters;

public class InstrumentedEngine implements Engine {

@Override
public String getId() {
return "My Engine";
}

@Override
public void setStartPosition(String fen) {
// TODO Auto-generated method stub

}

@Override
public void move(UCIMove move) {
// TODO Auto-generated method stub

}

@Override
public LongRunningTask<GoReply> go(GoParameters params) {
// TODO Auto-generated method stub
return null;
}

@Override
public boolean isPositionSet() {
// TODO Auto-generated method stub
return false;
}

}
Loading

0 comments on commit c144201

Please sign in to comment.