void SgUctTree::SetMustplay(const SgUctNode& node, const std::vector<SgUctMoveInfo>& moves, bool deleteChildTrees) { for (SgUctChildIterator it(*this, node); it; ++it) { SgUctNode* child = const_cast<SgUctNode*>(&(*it)); bool found = false; for (size_t j = 0; j < moves.size(); ++j) { if (child->Move() == moves[j].m_move) { found = true; if (moves[j].m_count > 0) child->AddGameResults(moves[j].m_value, moves[j].m_count); if (moves[j].m_raveCount > 0) child->AddRaveValue(moves[j].m_raveValue, moves[j].m_raveCount); if (deleteChildTrees) { // Write order dependency child->SetNuChildren(0); SgSynchronizeThreadMemory(); child->SetFirstChild(0); } break; } } if (!found) child->SetProvenType(SG_PROVEN_WIN); // mark as loss } SgSynchronizeThreadMemory(); }
/** 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() @param alwaysKeepProven Copy proven nodes even if below minCount */ SgUctProvenType SgUctTree::CopySubtree(SgUctTree& target, SgUctNode& targetNode, const SgUctNode& node, SgUctValue minCount, std::size_t& currentAllocatorId, bool warnTruncate, bool& abort, SgTimer& timer, double maxTime, bool alwaysKeepProven) const { SG_ASSERT(Contains(node)); SG_ASSERT(target.Contains(targetNode)); targetNode.CopyDataFrom(node); if (! node.HasChildren()) return node.ProvenType(); if ( node.MoveCount() < minCount && (! node.IsProven() || ! alwaysKeepProven) ) { targetNode.SetProvenType(SG_NOT_PROVEN); return SG_NOT_PROVEN; } 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); targetNode.SetProvenType(SG_NOT_PROVEN); return SG_NOT_PROVEN; } 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 SgUctProvenType childProvenType; SgUctProvenType parentProvenType = SG_PROVEN_LOSS; 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; childProvenType = CopySubtree(target, *targetChild, child, minCount, currentAllocatorId, warnTruncate, abort, timer, maxTime, alwaysKeepProven); if (childProvenType == SG_PROVEN_LOSS) parentProvenType = SG_PROVEN_WIN; else if ( parentProvenType != SG_PROVEN_WIN && childProvenType == SG_NOT_PROVEN) parentProvenType = SG_NOT_PROVEN; } targetNode.SetProvenType(parentProvenType); return parentProvenType; }