143 lines
4.8 KiB
Dart
143 lines
4.8 KiB
Dart
|
|
import 'package:omnichess/classes/position.dart';
|
|
import 'package:omnichess/constants/numbers.dart';
|
|
import 'package:omnichess/constants/piece.dart';
|
|
import 'package:omnichess/data_structures/position_tree_node.dart';
|
|
import 'package:omnichess/functions/legal_moves.dart';
|
|
|
|
class BestMoveSearcher {
|
|
|
|
bool _isRunning = false;
|
|
bool _isStopped = false;
|
|
|
|
static int getPositionBaseValue(Position position) {
|
|
if (LegalMoves.isCheckmate(position)) {
|
|
return position.isWhiteTurn
|
|
? Numbers.minInteger
|
|
: Numbers.maxInteger;
|
|
}
|
|
int value = 0;
|
|
List<int> whitePawnFiles = List.filled(8, 0, growable: false); // isolated pawns, doubled pawns
|
|
List<int> blackPawnFiles = List.filled(8, 0, growable: false);
|
|
List<bool> whiteBishopColors = List.filled(2, false, growable: false); // bishop pairs
|
|
List<bool> blackBishopColors = List.filled(2, false, growable: false);
|
|
int row = 0;
|
|
int column = 0;
|
|
for (int i = 0; i < 64; i++) {
|
|
final int verticalDisplacementFromCenter = 7 - 2*row;
|
|
final int horizontalDisplacementFromCenter = 7 - 2*column;
|
|
final int attackingValue = (98-(verticalDisplacementFromCenter*verticalDisplacementFromCenter + horizontalDisplacementFromCenter*horizontalDisplacementFromCenter)) >> 4;
|
|
if (LegalMoves.isWhiteAttacking(position, i)) {
|
|
value += attackingValue;
|
|
}
|
|
if (LegalMoves.isBlackAttacking(position, i)) {
|
|
value -= attackingValue;
|
|
}
|
|
value += switch (position.board[i]) {
|
|
Piece.whiteQueen => 900,
|
|
Piece.whiteRook => 500,
|
|
Piece.whiteBishop => 300,
|
|
Piece.whiteKnight => 300,
|
|
Piece.whitePawn => 100,
|
|
Piece.blackQueen => -900,
|
|
Piece.blackRook => -500,
|
|
Piece.blackBishop => -300,
|
|
Piece.blackKnight => -300,
|
|
Piece.blackPawn => -100,
|
|
_ => 0,
|
|
};
|
|
if (position.board[i] == Piece.whitePawn) {
|
|
whitePawnFiles[i % 8]++;
|
|
if (whitePawnFiles[i % 8] > 1) {
|
|
value -= 25;
|
|
}
|
|
}
|
|
if (position.board[i] == Piece.blackPawn) {
|
|
blackPawnFiles[i % 8]++;
|
|
if (blackPawnFiles[i % 8] > 1) {
|
|
value += 25;
|
|
}
|
|
}
|
|
if (position.board[i] == Piece.whiteBishop) {
|
|
whiteBishopColors[i%2] = true;
|
|
}
|
|
if (position.board[i] == Piece.blackBishop) {
|
|
blackBishopColors[i%2] = true;
|
|
}
|
|
column = (column + 1) % 8;
|
|
if (column == 0) {
|
|
row++;
|
|
}
|
|
}
|
|
if (whitePawnFiles[0] > 0 && whitePawnFiles[1] == 0) {
|
|
value -= 50;
|
|
}
|
|
if (whitePawnFiles[7] > 0 && whitePawnFiles[6] == 0) {
|
|
value -= 50;
|
|
}
|
|
if (blackPawnFiles[0] > 0 && blackPawnFiles[1] == 0) {
|
|
value += 50;
|
|
}
|
|
if (blackPawnFiles[7] > 0 && blackPawnFiles[6] == 0) {
|
|
value += 50;
|
|
}
|
|
for (int i = 1; i < 7; i++) {
|
|
if (whitePawnFiles[i-1] == 0 && whitePawnFiles[i] > 0 && whitePawnFiles[i+1] == 0) {
|
|
value -= 50;
|
|
}
|
|
if (blackPawnFiles[i-1] == 0 && blackPawnFiles[i] > 0 && blackPawnFiles[i+1] == 0) {
|
|
value += 50;
|
|
}
|
|
}
|
|
if (whiteBishopColors[0] && whiteBishopColors[1]){
|
|
value += 50;
|
|
}
|
|
if (blackBishopColors[0] && blackBishopColors[1]){
|
|
value -= 50;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static int getPositionBasePriority(Position position, int value, Iterable<(String, Position)> legalMoves, int depth) =>
|
|
legalMoves.isEmpty
|
|
? Numbers.maxInteger
|
|
: 0;
|
|
|
|
Future<String?> search(Position position, int? moveTime) async {
|
|
_isRunning = true;
|
|
PositionTreeNode positionTree = PositionTreeNode.fromPosition(position);
|
|
String bestLine = "";
|
|
int? bestValue;
|
|
int depth = 0;
|
|
final int startTime = DateTime.now().millisecondsSinceEpoch;
|
|
int time = 0;
|
|
while (!_isStopped && positionTree.priority < Numbers.maxInteger) {
|
|
positionTree.computeStep();
|
|
time = DateTime.now().millisecondsSinceEpoch - startTime;
|
|
if (null != moveTime && time >= moveTime) {
|
|
_isStopped = true;
|
|
}
|
|
if (positionTree.childrenByValue.isNotEmpty || bestValue != positionTree.value) {
|
|
final String thisBestLine = positionTree.childrenByValue.firstOrNull?.bestLine ?? "";
|
|
if (bestLine != thisBestLine || bestValue != positionTree.value || depth != positionTree.depth) {
|
|
bestLine = thisBestLine;
|
|
bestValue = positionTree.value;
|
|
depth = positionTree.depth;
|
|
print("info depth $depth multipv 1 nodes ${positionTree.nodeCount} time $time score cp ${(position.isWhiteTurn ? 1 : -1) * (positionTree.value)} pv $bestLine");
|
|
}
|
|
}
|
|
await Future.delayed(Duration.zero);
|
|
}
|
|
_isRunning = false;
|
|
_isStopped = false;
|
|
return positionTree.childrenByValue.firstOrNull?.move;
|
|
}
|
|
|
|
Future<void> stop() async {
|
|
if (!_isRunning) {
|
|
return;
|
|
}
|
|
_isStopped = true;
|
|
}
|
|
|
|
} |