/// <summary> /// Generates all immediate children of the specified node /// WARNING: this method is not thread-safe, run it in parallel only /// for nodes in non-overlapping subtrees /// </summary> public void GenerateAllChildren(AnalysisNode parent, uint depth, bool is_restore_missing_positions = true) { Debug.Assert(parent != null && parent.Position != null); Debug.Assert(depth > 0); // Check if all moves were generated for the parent node // If not, clear the list of children and re-generate them all if (parent.IsSomeChildrenErased) { parent.SetChildrenList(null); } if (parent.Children == null) { parent.InitChildrenList(); // TODO - conside memory pooling for the list of moves var moves = new List<Move>(64); MovesGenerator.GenerateMoves(parent.Position, moves); var player_to_move = parent.Position.PlayerToMove; bool is_parent_in_check = parent.Position.IsInCheck; foreach (var m in moves) { var child_position = parent.Position.PlayMove(m); // Last check if the move is a legal move // Didn't do it earlier, as we need resulting position // for this if (!PositionValidator.IsMyMovePutsMyKingInCheck( player_to_move, m, child_position, is_parent_in_check)) { parent.Children.Add(new AnalysisNode(parent, child_position, m)); Interlocked.Increment(ref m_node_count); } } // Let the position to know whether there are legal moves parent.Position.HasLegalMoves = !parent.IsLeaf; } else if (is_restore_missing_positions) { foreach (var node in parent.Children) { if (node.Position == null) { // Position object was destroyed to free the memory // Re-create it now node.RecreatePosition(); } } } // Indicate that all children are generated and present in the collection // Unless we have generated the moves having some restrictions imposed parent.IsSomeChildrenErased = false; // Generate next levels of child nodes recursively if (depth > 1) { foreach (var node in parent.Children) { GenerateAllChildren(node, depth - 1, is_restore_missing_positions); } } if (ReferenceEquals(parent, Root)) { m_listener.RootChildrenGenerated(Root.Children); } }
/// <summary> /// Erases all children nodes which satisfy the specified condition /// WARNING: this method is not thread-safe, run it in parallel only /// for nodes in non-overlapping subtrees /// </summary> public void ReleaseChildren(Predicate<AnalysisNode> condition, AnalysisNode subtree_root) { if (subtree_root.Children == null) { return; } List<AnalysisNode> new_children = null; foreach (var child in subtree_root.Children) { if (condition(child)) { ReleaseNode(child); } else { if (new_children == null) { new_children = new List<AnalysisNode>( subtree_root.Children.Count); } new_children.Add(child); } } if (new_children == null || (new_children.Count != subtree_root.Children.Count)) { subtree_root.IsSomeChildrenErased = true; subtree_root.SetChildrenList(new_children); } }
// Assumes the tree is locked private void ReleaseNode(AnalysisNode subtree_root) { if (subtree_root.Children != null) { foreach (var child in subtree_root.Children) { ReleaseNode(child); } subtree_root.SetChildrenList(null); } Interlocked.Decrement(ref m_node_count); }
/// <summary> /// Erases all children nodes /// WARNING: this method is not thread-safe, run it in parallel only /// for nodes in non-overlapping subtrees /// </summary> public void ReleaseChildren(AnalysisNode subtree_root) { if (subtree_root.Children != null) { foreach (var child in subtree_root.Children) { ReleaseNode(child); } } subtree_root.IsSomeChildrenErased = true; subtree_root.SetChildrenList(null); }