2024-02-25 00:39:40 +01:00
|
|
|
import 'dart:async';
|
|
|
|
|
2023-10-28 20:46:47 +02:00
|
|
|
import 'package:flutter/foundation.dart';
|
2024-02-23 18:58:31 +01:00
|
|
|
import 'package:flutter_stockfish_plugin/stockfish_bindings.dart';
|
|
|
|
import 'package:flutter_stockfish_plugin/stockfish_native_bindings.dart'
|
|
|
|
if (dart.library.html) 'package:flutter_stockfish_plugin/stockfish_web_bindings.dart';
|
|
|
|
import 'package:flutter_stockfish_plugin/stockfish_state.dart';
|
2023-10-28 20:46:47 +02:00
|
|
|
|
|
|
|
class Stockfish {
|
2024-02-23 18:58:31 +01:00
|
|
|
final _state = StockfishStateClass();
|
2024-02-25 00:39:40 +01:00
|
|
|
final StockfishChessEngineAbstractBindings _bindings =
|
|
|
|
StockfishChessEngineBindings();
|
2024-02-23 18:58:31 +01:00
|
|
|
|
2024-02-25 00:39:40 +01:00
|
|
|
Stockfish._({Completer<Stockfish>? completer}) {
|
2024-02-23 18:58:31 +01:00
|
|
|
_state.setValue(StockfishState.starting);
|
|
|
|
_bindings.stockfishMain(() {
|
|
|
|
_state.setValue(StockfishState.ready);
|
2024-02-25 00:39:40 +01:00
|
|
|
completer?.complete(this);
|
2024-02-23 18:58:31 +01:00
|
|
|
}).then((exitCode) {
|
|
|
|
_state.setValue(
|
|
|
|
exitCode == 0 ? StockfishState.disposed : StockfishState.error);
|
|
|
|
_instance = null;
|
|
|
|
}, onError: (error) {
|
|
|
|
_state.setValue(StockfishState.error);
|
|
|
|
_instance = null;
|
2024-02-25 00:39:40 +01:00
|
|
|
completer?.completeError(error);
|
2023-10-28 20:46:47 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
static Stockfish? _instance;
|
|
|
|
|
2024-02-25 00:39:40 +01:00
|
|
|
/// Creates the stockfish engine.
|
2023-10-28 20:46:47 +02:00
|
|
|
///
|
|
|
|
/// This may throws a [StateError] if an active instance is being used.
|
|
|
|
/// Owner must [dispose] it before a new instance can be created.
|
|
|
|
factory Stockfish() {
|
|
|
|
if (_instance != null) {
|
|
|
|
throw StateError('Multiple instances are not supported, yet.');
|
|
|
|
}
|
|
|
|
_instance = Stockfish._();
|
|
|
|
return _instance!;
|
|
|
|
}
|
|
|
|
|
2024-02-25 00:39:40 +01:00
|
|
|
/// The current state of the underlying stockfish engine.
|
2023-10-28 20:46:47 +02:00
|
|
|
ValueListenable<StockfishState> get state => _state;
|
|
|
|
|
|
|
|
/// The standard output stream.
|
2024-02-23 18:58:31 +01:00
|
|
|
Stream<String> get stdout => _bindings.read;
|
2023-10-28 20:46:47 +02:00
|
|
|
|
|
|
|
/// The standard input sink.
|
|
|
|
set stdin(String line) {
|
2024-02-23 18:58:31 +01:00
|
|
|
final stateValue = state.value;
|
2023-10-28 20:46:47 +02:00
|
|
|
if (stateValue != StockfishState.ready) {
|
|
|
|
throw StateError('Stockfish is not ready ($stateValue)');
|
|
|
|
}
|
2024-02-23 18:58:31 +01:00
|
|
|
_bindings.write(line);
|
2023-10-28 20:46:47 +02:00
|
|
|
}
|
|
|
|
|
2024-02-25 00:39:40 +01:00
|
|
|
/// Stops the stockfish engine.
|
2023-10-28 20:46:47 +02:00
|
|
|
void dispose() {
|
2024-02-23 18:58:31 +01:00
|
|
|
final stateValue = state.value;
|
2023-10-28 20:46:47 +02:00
|
|
|
if (stateValue == StockfishState.ready) {
|
|
|
|
stdin = 'quit';
|
|
|
|
}
|
|
|
|
}
|
2024-02-25 00:39:40 +01:00
|
|
|
}
|
2023-10-28 20:46:47 +02:00
|
|
|
|
2024-02-25 00:39:40 +01:00
|
|
|
/// Creates the stockfish engine asynchronously.
|
|
|
|
///
|
|
|
|
/// This method is different from the factory method [Stockfish] that
|
|
|
|
/// it will wait for the engine to be ready before returning the instance.
|
|
|
|
Future<Stockfish> stockfishAsync() {
|
|
|
|
if (Stockfish._instance != null) {
|
|
|
|
return Future.error(StateError('Only one instance can be used at a time'));
|
2023-10-28 20:46:47 +02:00
|
|
|
}
|
2024-02-25 00:39:40 +01:00
|
|
|
|
|
|
|
final completer = Completer<Stockfish>();
|
|
|
|
Stockfish._instance = Stockfish._(completer: completer);
|
|
|
|
return completer.future;
|
2023-10-28 20:46:47 +02:00
|
|
|
}
|