mirror of
https://github.com/chesskit-app/chesskit-engine.git
synced 2026-05-19 15:50:35 +00:00
Clean up engine setup loop
This commit is contained in:
@@ -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)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user