void SgUctTree::MergeChildren(std::size_t allocatorId, const SgUctNode& node, const std::vector<SgMoveInfo>& moves, bool deleteChildTrees) { SG_ASSERT(Contains(node)); // Parameters are const-references, because only the tree is allowed // to modify nodes SgUctNode& nonConstNode = const_cast<SgUctNode&>(node); size_t nuNewChildren = moves.size(); if (nuNewChildren == 0) { // Write order dependency nonConstNode.SetNuChildren(0); SgSynchronizeThreadMemory(); nonConstNode.SetFirstChild(0); return; } SgUctAllocator& allocator = Allocator(allocatorId); SG_ASSERT(allocator.HasCapacity(nuNewChildren)); const SgUctNode* newFirstChild = allocator.Finish(); std::size_t parentCount = allocator.Create(moves); // Update new children with data in old children for (std::size_t i = 0; i < moves.size(); ++i) { SgUctNode* newChild = const_cast<SgUctNode*>(&newFirstChild[i]); for (SgUctChildIterator it(*this, node); it; ++it) { const SgUctNode& oldChild = *it; if (oldChild.Move() == moves[i].m_move) { newChild->MergeResults(oldChild); newChild->SetKnowledgeCount(oldChild.KnowledgeCount()); if (! deleteChildTrees) { newChild->SetPosCount(oldChild.PosCount()); parentCount += oldChild.MoveCount(); if (oldChild.HasChildren()) { newChild->SetFirstChild(oldChild.FirstChild()); newChild->SetNuChildren(oldChild.NuChildren()); } } break; } } } nonConstNode.SetPosCount(parentCount); // Write order dependency: We do not want an SgUctChildIterator to // run past the end of a node's children, which can happen if one // is created between the two statements below. We modify node in // such a way so as to avoid that. if (nonConstNode.NuChildren() < (int)nuNewChildren) { nonConstNode.SetFirstChild(newFirstChild); SgSynchronizeThreadMemory(); nonConstNode.SetNuChildren(nuNewChildren); } else { nonConstNode.SetNuChildren(nuNewChildren); SgSynchronizeThreadMemory(); nonConstNode.SetFirstChild(newFirstChild); } }
/** Recursive function used by SgUctTree::ExtractSubtree and SgUctTree::CopyPruneLowCount. @param target The target tree. @param targetNode The target node; it is already created but the content not yet copied @param node The node in the source tree to be copied. @param minCount The minimum count (SgUctNode::MoveCount()) of a non-root node in the source tree to copy @param currentAllocatorId The current node allocator. Will be incremented in each call to CopySubtree to use node allocators of target tree evenly. @param warnTruncate Print warning to SgDebug() if tree was truncated (e.g due to reassigning nodes to different allocators) @param[in,out] abort Flag to abort copying. Must be initialized to false by top-level caller @param timer @param maxTime See ExtractSubtree() */ void SgUctTree::CopySubtree(SgUctTree& target, SgUctNode& targetNode, const SgUctNode& node, std::size_t minCount, std::size_t& currentAllocatorId, bool warnTruncate, bool& abort, SgTimer& timer, double maxTime) const { SG_ASSERT(Contains(node)); SG_ASSERT(target.Contains(targetNode)); targetNode.CopyDataFrom(node); if (! node.HasChildren() || node.MoveCount() < minCount) return; SgUctAllocator& targetAllocator = target.Allocator(currentAllocatorId); int nuChildren = node.NuChildren(); if (! abort) { if (! targetAllocator.HasCapacity(nuChildren)) { // This can happen even if target tree has same maximum number of // nodes, because allocators are used differently. if (warnTruncate) SgDebug() << "SgUctTree::CopySubtree: Truncated (allocator capacity)\n"; abort = true; } if (timer.IsTimeOut(maxTime, 10000)) { if (warnTruncate) SgDebug() << "SgUctTree::CopySubtree: Truncated (max time)\n"; abort = true; } if (SgUserAbort()) { if (warnTruncate) SgDebug() << "SgUctTree::CopySubtree: Truncated (aborted)\n"; abort = true; } } if (abort) { // Don't copy the children and set the pos count to zero (should // reflect the sum of children move counts) targetNode.SetPosCount(0); return; } SgUctNode* firstTargetChild = targetAllocator.Finish(); targetNode.SetFirstChild(firstTargetChild); targetNode.SetNuChildren(nuChildren); // Create target nodes first (must be contiguous in the target tree) targetAllocator.CreateN(nuChildren); // Recurse SgUctNode* targetChild = firstTargetChild; for (SgUctChildIterator it(*this, node); it; ++it, ++targetChild) { const SgUctNode& child = *it; ++currentAllocatorId; // Cycle to use allocators uniformly if (currentAllocatorId >= target.NuAllocators()) currentAllocatorId = 0; CopySubtree(target, *targetChild, child, minCount, currentAllocatorId, warnTruncate, abort, timer, maxTime); } }