import 'package:omnichess/classes/best_move_searcher.dart'; import 'package:omnichess/classes/position.dart'; import 'package:omnichess/constants/numbers.dart'; import 'package:omnichess/data_structures/search_heap.dart'; import 'package:omnichess/functions/legal_moves.dart'; class PositionTreeNode { int priority; int value; int nodeCount; int depth; final String move; final Position position; final Iterable<(String, Position)> legalMoves; SearchHeap children; SearchHeap childrenByValue; String get bestLine { final String? childBestLine = childrenByValue.firstOrNull?.bestLine; return "$move${null == childBestLine ? "" : " $childBestLine"}"; } PositionTreeNode({ required this.priority, required this.value, required this.move, required this.position, required this.legalMoves, required this.children, required this.childrenByValue, }): nodeCount = 0, depth = 0; factory PositionTreeNode.fromPosition(Position position, [ String move = "" ]) { final int value = BestMoveSearcher.getPositionBaseValue(position); final Iterable<(String, Position)> legalMoves = LegalMoves.getLegalMoves(position); return PositionTreeNode( priority: getPositionBasePriority(position, value, legalMoves, 0), value: value, move: move, position: position, legalMoves: legalMoves, children: SearchHeap( getKey: (PositionTreeNode node) => node.move, getPriority: (PositionTreeNode node) => node.priority, ), childrenByValue: SearchHeap( getKey: (PositionTreeNode node) => node.move, getPriority: position.isWhiteTurn ? (PositionTreeNode node) => -node.value : (PositionTreeNode node) => node.value, ), ); } void computeStep() { if (legalMoves.isEmpty) { return; } if (children.isEmpty) { _computeChildren(); return; } final PositionTreeNode firstChild = children.first; final int initialFirstChildNodeCount = firstChild.nodeCount; firstChild.computeStep(); children.updateElement(firstChild.move); childrenByValue.updateElement(firstChild.move); nodeCount += firstChild.nodeCount - initialFirstChildNodeCount; if (firstChild.depth + 1 > depth) { depth = firstChild.depth + 1; } priority = (children.first.priority == Numbers.maxInteger) ? children.first.priority : children.first.priority + 1; value = childrenByValue.first.value; } void _computeChildren() { for (final (String, Position) move in legalMoves) { final PositionTreeNode positionTreeNode = PositionTreeNode.fromPosition(move.$2, move.$1); children.add(positionTreeNode); childrenByValue.add(positionTreeNode); } nodeCount = legalMoves.length; priority = (children.first.priority == Numbers.maxInteger) ? children.first.priority : children.first.priority + 1; depth = 1; value = childrenByValue.first.value; } static int getPositionBasePriority(Position position, int value, Iterable<(String, Position)> legalMoves, int depth) => legalMoves.isEmpty ? Numbers.maxInteger : 0; }