Flutter国际象棋引擎插件stockfish_chess_engine的使用
Flutter国际象棋引擎插件stockfish_chess_engine的使用
使用Stockfish国际象棋引擎在您的Flutter项目中。
此项目基于Stockfish 17的源码。
使用
final stockfish = new Stockfish()
// 创建一个监听stdout的订阅:您需要在处理Stockfish之前取消此订阅。
final stockfishSubscription = stockfish.stdout.listen((message) {
print(message);
});
// 创建一个监听stderr的订阅:您需要在处理Stockfish之前取消此订阅。
final stockfishErrorsSubscription = stockfish.stderr.listen((message) {
print(message);
});
// 让Stockfish准备好
stockfish.stdin = 'isready'
// 向Stockfish的stdin发送命令
stockfish.stdin = 'position startpos' // 设置起始位置
stockfish.stdin = 'position fen rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2' // 设置自定义位置
stockfish.stdin = 'go movetime 1500' // 搜索最多1500毫秒的移动
// 别忘了在完成操作后释放Stockfish。
stockfishErrorsSubscription.cancel();
stockfishSubscription.cancel();
stockfish.dispose();
您可以在example
文件夹中查看示例用法。
重要注意事项
- 必须在将位置发送到stdin之前检查其有效性,否则程序会在非法位置上崩溃!为此,您可以使用chess包。
- 由于库创建了两个隔离区,您必须在执行热重载/热重启之前释放Stockfish,并且在此之后创建一个新的Stockfish实例。
- 如果在IPhone模拟器上进行测试,请考虑禁用Impeller(如果程序无法显示UI):
flutter run --no-enable-impeller
。
对于Stockfish国际象棋引擎开发者
- 调整
ffigen.yaml
文件中的"llvm-path"路径(仅限Linux用户) - 运行
flutter pub get
- 取消注释
src/stockfish.h
文件顶部的#define _ffigen
行(为了使ffi生成通过) - 运行命令
dart run ffigen --config ffigen.yaml
。 更多信息请参阅https://pub.dev/packages/ffigen以了解不同操作系统的要求。 - 在
src/stockfish.h
文件中重新注释#define _ffigen
行(否则Stockfish引擎编译会通过但不正确)。
更改Stockfish源文件
请参阅文件UPGRADING_STOCKFISH.md中的说明。
更改下载的NNUE文件(大或小NNUE)
- 前往Stockfish NNUE文件页面,从列表中选择一个参考文件。
- 修改
CMakeLists.txt
,通过替换以set (NNUE_NAME )
开头的行来设置您的参考文件名,不加任何引号。 - 在包含
#define EvalFileDefaultName
的行中修改evaluate.h
文件中的参考文件名(大或小),并设置您的nnue文件名,当然要带上引号。 - 不要忘记在重新构建之前清理项目(
flutter clean
然后flutter pub get
)。
致谢
- 使用来自Stockfish的源代码。
- 使用来自Flutter Stockfish的源代码。
- 使用来自Flutter Stockfish Plugin的源代码。
示例代码
import 'dart:async';
import 'package:editable_chess_board/editable_chess_board.dart';
import './edit_position_page.dart';
import './fen_validation.dart';
import 'package:flutter/material.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:simple_chess_board/widgets/chessboard.dart';
import 'package:window_manager/window_manager.dart';
import 'package:stockfish_chess_engine/stockfish_chess_engine.dart';
import 'package:stockfish_chess_engine/stockfish_chess_engine_state.dart';
void main() {
runApp(const MaterialApp(
home: MyApp(),
));
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
[@override](/user/override)
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> with WindowListener {
late Stockfish _stockfish;
String _fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
late StreamSubscription _stockfishOutputSubsciption;
late StreamSubscription _stockfishErrorSubsciption;
var _timeMs = 1000.0;
var _nextMove = '';
var _stockfishOutputText = '';
[@override](/user/override)
void initState() {
windowManager.addListener(this);
_doStartStockfish();
super.initState();
windowManager.setPreventClose(true).then((_) {
setState(() {});
});
}
[@override](/user/override)
void dispose() {
_stopStockfish();
super.dispose();
}
[@override](/user/override)
void onWindowClose() async {
await _stopStockfish();
await windowManager.destroy();
}
void _readStockfishOutput(String output) {
// 至少现在,Stockfish已经准备好了:更新UI。
setState(() {
_stockfishOutputText += "$output\n";
});
if (output.startsWith('bestmove')) {
final parts = output.split(' ');
setState(() {
_nextMove = parts[1];
});
}
}
void _readStockfishError(String error) {
// 至少现在,Stockfish已经准备好了:更新UI。
setState(() {
debugPrint("@@@$error@@@");
});
}
void _editPosition(BuildContext context) async {
final initialFen = isStrictlyValidFEN(_fen)
? _fen
: 'RNBQKBNR/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
final controller = PositionController(initialFen);
final resultFen = await Navigator.of(context)
.push(MaterialPageRoute<String>(builder: (context) {
return EditPositionPage(
positionController: controller,
);
}));
if (resultFen != null) {
setState(() {
_fen = resultFen;
});
if (!isStrictlyValidFEN(_fen)) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('非法位置:因此未进行更改!'),
backgroundColor: Colors.red,
));
setState(() {
_fen = initialFen;
});
}
}
}
void _updateThinkingTime(double newValue) {
setState(() {
_timeMs = newValue;
});
}
void _computeNextMove() {
if (!isStrictlyValidFEN(_fen)) {
final message = "非法位置: '$_fen' !\n";
setState(() {
_stockfishOutputText = message;
});
return;
}
setState(() {
_stockfishOutputText = '';
});
_stockfish.stdin = 'position fen $_fen';
_stockfish.stdin = 'go movetime ${_timeMs.toInt()}';
}
Future<void> _stopStockfish() async {
if (_stockfish.state.value == StockfishState.disposed ||
_stockfish.state.value == StockfishState.error) {
return;
}
_stockfishErrorSubsciption.cancel();
_stockfishOutputSubsciption.cancel();
_stockfish.dispose();
await Future.delayed(const Duration(seconds: 2));
setState(() {});
}
void _doStartStockfish() async {
_stockfish = Stockfish();
_stockfishOutputSubsciption =
_stockfish.stdout.listen(_readStockfishOutput);
setState(() {
_stockfishOutputText = '';
});
_stockfishErrorSubsciption = _stockfish.stderr.listen(_readStockfishError);
await Future.delayed(const Duration(milliseconds: 1500));
_stockfish.stdin = 'uci';
await Future.delayed(const Duration(milliseconds: 3000));
_stockfish.stdin = 'isready';
}
void _startStockfishIfNecessary() {
setState(() {
if (_stockfish.state.value == StockfishState.ready ||
_stockfish.state.value == StockfishState.starting) {
return;
}
_doStartStockfish();
});
}
Icon _getStockfishStatusIcon() {
Color color;
switch (_stockfish.state.value) {
case StockfishState.ready:
color = Colors.green;
break;
case StockfishState.disposed:
case StockfishState.error:
color = Colors.red;
break;
case StockfishState.starting:
color = Colors.orange;
}
return Icon(MdiIcons.circle, color: color);
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Stockfish Chess Engine 示例"),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 130,
height: 130,
child: SimpleChessBoard(
engineThinking: false,
fen: _fen,
whitePlayerType: PlayerType.computer,
blackPlayerType: PlayerType.computer,
blackSideAtBottom: false,
cellHighlights: {},
chessBoardColors: ChessBoardColors(),
onMove: ({required move}) {},
onPromote: () => Future.value(null),
onPromotionCommited: ({required moveDone, required pieceType}) {},
onTap: ({required cellCoordinate}) {},
),
),
ElevatedButton(
onPressed: () => _editPosition(context),
child: const Text('编辑位置'),
),
Slider(
value: _timeMs,
onChanged: _updateThinkingTime,
min: 500,
max: 3000,
),
Text('思考时间 : ${_timeMs.toInt()} 毫秒'),
ElevatedButton(
onPressed: _computeNextMove,
child: const Text('搜索下一步'),
),
Text('最佳移动: $_nextMove'),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_getStockfishStatusIcon(),
ElevatedButton(
onPressed: _startStockfishIfNecessary,
child: const Text('启动Stockfish'),
),
ElevatedButton(
onPressed: _stopStockfish,
child: const Text('停止Stockfish'),
),
],
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: 850.0,
height: 300.0,
decoration: BoxDecoration(
border: Border.all(
width: 2.0,
),
borderRadius: const BorderRadius.all(
Radius.circular(8.0),
),
),
child: SingleChildScrollView(
child: Text(
_stockfishOutputText,
),
),
),
),
],
),
),
),
);
}
}
更多关于Flutter国际象棋引擎插件stockfish_chess_engine的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter国际象棋引擎插件stockfish_chess_engine的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
stockfish_chess_engine
是一个用于在 Flutter 应用中集成国际象棋引擎 Stockfish 的插件。Stockfish 是一个开源的国际象棋引擎,提供强大的国际象棋计算能力。通过这个插件,你可以在 Flutter 应用中实现国际象棋对弈、分析棋局等功能。
以下是使用 stockfish_chess_engine
的基本步骤:
1. 添加依赖
在 pubspec.yaml
文件中添加 stockfish_chess_engine
的依赖:
dependencies:
flutter:
sdk: flutter
stockfish_chess_engine: ^1.0.0 # 请使用最新的版本号
然后运行 flutter pub get
以安装依赖。
2. 初始化引擎
在你的 Dart 文件中导入 stockfish_chess_engine
并初始化引擎:
import 'package:stockfish_chess_engine/stockfish_chess_engine.dart';
void initEngine() async {
Stockfish stockfish = Stockfish();
await stockfish.init();
}
3. 发送指令和接收响应
你可以通过 sendCommand
方法向引擎发送指令,并通过监听输出流来接收引擎的响应。
void communicateWithEngine() async {
Stockfish stockfish = Stockfish();
await stockfish.init();
// 发送指令
stockfish.sendCommand("uci");
// 监听引擎的输出
stockfish.outputStream.listen((output) {
print("Engine output: $output");
});
}
4. 设置棋局和获取最佳着法
你可以使用 position
命令设置当前的棋局,然后使用 go
命令让引擎计算最佳着法。
void getBestMove() async {
Stockfish stockfish = Stockfish();
await stockfish.init();
stockfish.sendCommand("uci");
stockfish.sendCommand("isready");
// 设置棋局
stockfish.sendCommand("position startpos moves e2e4 e7e5");
// 获取最佳着法
stockfish.sendCommand("go depth 15");
stockfish.outputStream.listen((output) {
print("Engine output: $output");
if (output.startsWith("bestmove")) {
// 解析最佳着法
String bestMove = output.split(" ")[1];
print("Best move: $bestMove");
}
});
}
5. 关闭引擎
在使用完引擎后,记得关闭它:
void closeEngine() async {
Stockfish stockfish = Stockfish();
await stockfish.init();
// 关闭引擎
await stockfish.dispose();
}
6. 处理错误
在实际使用中,可能会遇到一些错误,建议在代码中添加错误处理逻辑:
void handleErrors() async {
Stockfish stockfish = Stockfish();
try {
await stockfish.init();
} catch (e) {
print("Error initializing engine: $e");
}
stockfish.outputStream.listen((output) {
print("Engine output: $output");
}, onError: (error) {
print("Engine error: $error");
});
}
7. 复杂用法
你可以结合 chess
包(package:chess/chess.dart
)来处理棋局的状态和着法。chess
包提供了国际象棋的基本规则和棋局管理功能,可以与 stockfish_chess_engine
配合使用。
import 'package:chess/chess.dart';
void useChessPackage() async {
Chess chess = Chess();
Stockfish stockfish = Stockfish();
await stockfish.init();
// 执行一步着法
chess.move("e4");
// 将棋局发送给引擎
stockfish.sendCommand("position fen ${chess.fen}");
// 获取最佳着法
stockfish.sendCommand("go depth 15");
stockfish.outputStream.listen((output) {
if (output.startsWith("bestmove")) {
String bestMove = output.split(" ")[1];
chess.move(bestMove);
print("Updated board: ${chess.ascii}");
}
});
}