Clean up engine setup loop

This commit is contained in:
Paolo Di Lorenzo
2024-04-21 12:37:14 -04:00
parent 731c2a5ce1
commit fc05bd2879
4 changed files with 65 additions and 61 deletions
+45 -39
View File
@@ -54,7 +54,7 @@ public class Engine {
/// - parameter multipv: The number of lines the engine should return,
/// sent via the `"MultiPV"` UCI option.
/// - parameter completion: The completion handler that is called when
/// the engine set up is complete. You must wait for this to be called
/// the engine setup is complete. You must wait for this to be called
/// before sending further commands to the engine.
///
/// This must be called before sending any commands
@@ -63,55 +63,45 @@ public class Engine {
messenger.responseHandler = { [weak self] response in
guard let self else { return }
Task { @MainActor in
if let parsedResponse = EngineResponse(rawValue: response) {
self.log(parsedResponse.rawValue)
if parsedResponse == .uciok && !self.isRunning {
self.send(command: .isready)
return
DispatchQueue.main.async {
guard let parsed = EngineResponse(rawValue: response) else {
if !response.isEmpty {
self.log(response)
}
return
}
if parsedResponse == .readyok && !self.isRunning {
self.log(parsed.rawValue)
guard self.isRunning else {
// engine setup loop
// <uci> <uciok> <isready> <readok> complete
switch parsed {
case .uciok:
self.send(command: .isready)
case .readyok:
self.isRunning = true
self.performInitialSetup(
coreCount: coreCount ?? ProcessInfo.processInfo.processorCount,
multipv: multipv
)
completion()
return
default:
break
}
self.receiveResponse(parsedResponse)
} else if !response.isEmpty {
self.log(response)
return
}
self.receiveResponse(parsed)
}
}
messenger.start()
// set UCI mode
// start engine setup loop
send(command: .uci)
}
private var initialSetupComplete = false
private func performInitialSetup(coreCount: Int, multipv: Int) {
guard !initialSetupComplete else { return }
// configure engine-specific options
type.setupCommands.forEach(send)
// configure common engine options
send(command: .setoption(
id: "Threads",
value: "\(max(coreCount - 1, 1))"
))
send(command: .setoption(id: "MultiPV", value: "\(multipv)"))
initialSetupComplete = true
}
/// Stops the engine.
///
/// Call this to stop all engine calculation and clean up.
@@ -142,8 +132,8 @@ public class Engine {
}
queue.sync {
self.log(command.rawValue)
self.messenger.sendCommand(command.rawValue)
log(command.rawValue)
messenger.sendCommand(command.rawValue)
}
}
@@ -157,16 +147,32 @@ public class Engine {
_ in
}
}
// MARK: - Private
extension Engine {
// MARK: - Private
/// Logs `message` if `loggingEnabled` is `true`.
private func log(_ message: String) {
if loggingEnabled {
Logging.print(message)
}
}
private var initialSetupComplete = false
/// Sets initial engine options.
private func performInitialSetup(coreCount: Int, multipv: Int) {
guard !initialSetupComplete else { return }
// configure engine-specific options
type.setupCommands.forEach(send)
// configure common engine options
send(command: .setoption(
id: "Threads",
value: "\(max(coreCount - 1, 1))"
))
send(command: .setoption(id: "MultiPV", value: "\(multipv)"))
initialSetupComplete = true
}
}
@@ -7,37 +7,37 @@
/// [Universal Chess Interface (UCI)](https://backscattering.de/chess/uci/2006-04.txt).
///
public enum EngineResponse {
/// `"id name <x>"`, `"id author <x>"`
///
/// See [UCI protocol documentation](https://backscattering.de/chess/uci/2006-04.txt)
/// for more information.
case id(ID)
/// `"uciok"`
///
/// See [UCI protocol documentation](https://backscattering.de/chess/uci/2006-04.txt)
/// for more information.
case uciok
/// `"readyok"`
///
/// See [UCI protocol documentation](https://backscattering.de/chess/uci/2006-04.txt)
/// for more information.
case readyok
/// `"bestmove <move1> [ ponder <move2> ]"`
///
/// See [UCI protocol documentation](https://backscattering.de/chess/uci/2006-04.txt)
/// for more information.
case bestmove(move: String, ponder: String?)
/// `"info"`
///
/// See [UCI protocol documentation](https://backscattering.de/chess/uci/2006-04.txt)
/// for more information.
case info(Info)
}
extension EngineResponse: Equatable {}
@@ -45,35 +45,35 @@ extension EngineResponse: Equatable {}
extension EngineResponse: RawRepresentable {
public init?(rawValue: String) {
let parsed = EngineResponseParser.parse(response: rawValue)
guard let parsed else {
return nil
}
self = parsed
}
public var rawValue: String {
switch self {
case let .id(id):
switch id {
case let .name(name):
return "<id> <name> \(name)"
"<id> <name> \(name)"
case let .author(author):
return "<id> <author> \(author)"
"<id> <author> \(author)"
}
case .uciok:
return "<uciok>"
"<uciok>"
case .readyok:
return "<readyok>"
"<readyok>"
case let .bestmove(move, ponder):
if let ponder {
return "<bestmove> \(move) <ponder> \(ponder)"
"<bestmove> \(move) <ponder> \(ponder)"
} else {
return "<bestmove> \(move)"
"<bestmove> \(move)"
}
case let .info(info):
return "<info>\(info)"
"<info>\(info)"
}
}
}
+1 -1
View File
@@ -42,7 +42,7 @@ public enum EngineType: Int {
let options = [
"EvalFile": "nn-b1a57edbea57",
"EvalFileSmall": "nn-baff1ede1f90"
].mapValues {
].compactMapValues {
Bundle.main.url(forResource: $0, withExtension: "nnue")?.path()
}
@@ -43,8 +43,6 @@ NSFileHandle *_pipeWriteHandle;
}
- (void)start {
// set up read pipe
_readPipe = [NSPipe pipe];
_pipeReadHandle = [_readPipe fileHandleForReading];
@@ -66,7 +64,7 @@ NSFileHandle *_pipeWriteHandle;
dup2([[_writePipe fileHandleForReading] fileDescriptor], fileno(stdin));
// create command dispatch queue and start engine
_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
_queue = dispatch_queue_create("ck-message-queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(_queue, ^{
_engine->initialize();
@@ -88,8 +86,8 @@ NSFileHandle *_pipeWriteHandle;
- (void)sendCommand: (NSString*) command {
dispatch_sync(_queue, ^{
const char *cCommand = [[command stringByAppendingString:@"\n"] UTF8String];
write([_pipeWriteHandle fileDescriptor], cCommand, strlen(cCommand));
const char *cmd = [[command stringByAppendingString:@"\n"] UTF8String];
write([_pipeWriteHandle fileDescriptor], cmd, strlen(cmd));
});
}