void GoAutoBook::TruncateByDepth(int depth, GoAutoBookState& state, GoAutoBook& other, std::set<SgHashCode>& seen) const { if (seen.count(state.GetHashCode())) return; SgBookNode node; if (! Get(state, node)) return; seen.insert(state.GetHashCode()); if (depth == 0) { // Set this node to be a leaf: copy its heuristic value into // its propagated value and set count to 0. node.m_count = 0; node.m_priority = SgBookNode::LEAF_PRIORITY; node.m_value = node.m_heurValue; other.Put(state, node); return; } other.Put(state, node); if (node.IsLeaf() || node.IsTerminal()) return; for (GoBoard::Iterator it(state.Board()); it; ++it) { if (state.Board().IsLegal(*it)) { state.Play(*it); TruncateByDepth(depth - 1, state, other, seen); state.Undo(); } } }
SgMove GoAutoBook::FindBestChild(GoAutoBookState& state) const { std::size_t bestCount = 0; SgMove bestMove = SG_NULLMOVE; SgMove bestIgnoredMove = SG_NULLMOVE; float bestScore = 100.0f; float bestIgnoredMoveScore = 100.0f; SgBookNode node; // Check for forced moves first // Note this will check for forced moves even if the current // state is not in the book. for (GoBoard::Iterator it(state.Board()); it; ++it) { if (state.Board().IsLegal(*it)) { state.Play(*it); if (m_forced.count(state.GetHashCode()) > 0) { SgDebug() << "Playing forced move " << SgWritePoint(*it) << '\n'; return *it; } state.Undo(); } } if (! Get(state, node)) return SG_NULLMOVE; if (node.IsLeaf()) return SG_NULLMOVE; for (GoBoard::Iterator it(state.Board()); it; ++it) { if (state.Board().IsLegal(*it)) { state.Play(*it); if (m_disabled.count(state.GetHashCode()) > 0) SgDebug() << "Ignoring disabled move " << SgWritePoint(*it) << '\n'; // NOTE: Terminal nodes aren't supported at this time, so // we ignore them here. else if ( Get(state, node) && ! node.IsTerminal() ) { if (node.m_count >= m_param.m_usageCountThreshold) { if (m_param.m_selectType == GO_AUTOBOOK_SELECT_COUNT) { // Select by count, tiebreak by value. if (node.m_count > bestCount) { bestCount = node.m_count; bestMove = *it; bestScore = node.m_value; } // NOTE: do not have access to inverse function, // so we're minimizing here as a temporary solution. else if (node.m_count == bestCount && node.m_value < bestScore) { bestMove = *it; bestScore = node.m_value; } } else if (m_param.m_selectType == GO_AUTOBOOK_SELECT_VALUE) { // NOTE: do not have access to inverse function, // so we're minimizing here as a temporary solution. if (node.m_value < bestScore) { bestMove = *it; bestScore = node.m_value; } } } else // node.m_count < m_param.m_usageCountThreshold if ( m_param.m_selectType == GO_AUTOBOOK_SELECT_VALUE && node.m_value < bestIgnoredMoveScore ) { bestIgnoredMove = *it; bestIgnoredMoveScore = node.m_value; } } state.Undo(); } } if (bestMove != SG_NULLMOVE && bestIgnoredMoveScore < bestScore) { SgDebug() << "Ignoring autobook move " << SgWritePoint(bestMove) << " since best ignored inverse value " << std::setprecision(5) << bestIgnoredMoveScore << " of move " << SgWritePoint(bestIgnoredMove) << " is better than best value above usage threshold " << bestScore << '\n'; return SG_NULLMOVE; } return bestMove; }
void GoAutoBook::Put(const GoAutoBookState& state, const SgBookNode& node) { m_data[state.GetHashCode()] = node; }