void SgUctTree::ApplyFilter(std::size_t allocatorId, const SgUctNode& node, const vector<SgMove>& rootFilter) { SG_ASSERT(Contains(node)); SG_ASSERT(Allocator(allocatorId).HasCapacity(node.NuChildren())); if (! node.HasChildren()) return; SgUctAllocator& allocator = Allocator(allocatorId); const SgUctNode* firstChild = allocator.Finish(); int nuChildren = 0; for (SgUctChildIterator it(*this, node); it; ++it) { SgMove move = (*it).Move(); if (find(rootFilter.begin(), rootFilter.end(), move) == rootFilter.end()) { SgUctNode* child = allocator.CreateOne(move); child->CopyDataFrom(*it); int childNuChildren = (*it).NuChildren(); child->SetNuChildren(childNuChildren); if (childNuChildren > 0) child->SetFirstChild((*it).FirstChild()); ++nuChildren; } } SgUctNode& nonConstNode = const_cast<SgUctNode&>(node); // Write order dependency: SgUctSearch in lock-free mode assumes that // m_firstChild is valid if m_nuChildren is greater zero nonConstNode.SetFirstChild(firstChild); SgSynchronizeThreadMemory(); nonConstNode.SetNuChildren(nuChildren); }
void SgUctTree::SetChildren(std::size_t allocatorId, const SgUctNode& node, const vector<SgMove>& moves) { SG_ASSERT(Contains(node)); SG_ASSERT(Allocator(allocatorId).HasCapacity(moves.size())); SG_ASSERT(node.HasChildren()); SgUctAllocator& allocator = Allocator(allocatorId); const SgUctNode* firstChild = allocator.Finish(); int nuChildren = 0; for (size_t i = 0; i < moves.size(); ++i) { bool found = false; for (SgUctChildIterator it(*this, node); it; ++it) { SgMove move = (*it).Move(); if (move == moves[i]) { found = true; SgUctNode* child = allocator.CreateOne(move); child->CopyDataFrom(*it); int childNuChildren = (*it).NuChildren(); child->SetNuChildren(childNuChildren); if (childNuChildren > 0) child->SetFirstChild((*it).FirstChild()); ++nuChildren; break; } } if (! found) { allocator.CreateOne(moves[i]); ++nuChildren; } } SG_ASSERT((size_t)nuChildren == moves.size()); SgUctNode& nonConstNode = const_cast<SgUctNode&>(node); // Write order dependency: SgUctSearch in lock-free mode assumes that // m_firstChild is valid if m_nuChildren is greater zero SgSynchronizeThreadMemory(); nonConstNode.SetFirstChild(firstChild); SgSynchronizeThreadMemory(); nonConstNode.SetNuChildren(nuChildren); }
/** 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); } }