SgPoint GoUctSearchUtil::TrompTaylorPassCheck(SgPoint move, const GoUctSearch& search) { const GoBoard& bd = search.Board(); bool isFirstPass = (bd.GetLastMove() != SG_PASS); bool isTrompTaylorRules = bd.Rules().CaptureDead(); if (move != SG_PASS || ! isTrompTaylorRules || ! isFirstPass) return move; float komi = bd.Rules().Komi().ToFloat(); float trompTaylorScore = GoBoardUtil::TrompTaylorScore(bd, komi); if (search.ToPlay() != SG_BLACK) trompTaylorScore *= -1; const SgUctTree& tree = search.Tree(); const SgUctNode& root = tree.Root(); SgUctValue value = root.Mean(); SgUctValue trompTaylorWinValue = (trompTaylorScore > 0 ? 1 : 0); if (value < trompTaylorWinValue) return move; SgDebug() << "GoUctSearchUtil::TrompTaylorPassCheck: bad pass move value=" << value << " trompTaylorScore=" << trompTaylorScore << '\n'; std::vector<SgMove> excludeMoves; excludeMoves.push_back(SG_PASS); const SgUctNode* bestChild = search.FindBestChild(root, &excludeMoves); if (bestChild == 0) { SgDebug() << "GoUctSearchUtil::TrompTaylorPassCheck: " "(no second best move found)\n"; return move; } return bestChild->Move(); }
bool DfpnSolver::CheckAbort() { if (! m_aborted) { if (SgUserAbort()) { m_aborted = true; SgDebug() << "DfpnSolver::CheckAbort(): Abort flag!\n"; } else if (m_timelimit > 0) { if (m_checkTimerAbortCalls == 0) { double elapsed = m_timer.GetTime(); if (elapsed > m_timelimit) { m_aborted = true; SgDebug() << "DfpnSolver::CheckAbort(): Timelimit!\n"; } else if (m_numMIDcalls < 100) m_checkTimerAbortCalls = 10; else { size_t midsPerSec = static_cast<size_t> (double(m_numMIDcalls) / elapsed); m_checkTimerAbortCalls = midsPerSec / 2; } } else --m_checkTimerAbortCalls; } } return m_aborted; }
void GoAutoBook::Merge(const GoAutoBook& other) { SgDebug() << "GoAutoBook::Merge()\n"; std::size_t newLeafs = 0; std::size_t newInternal = 0; std::size_t leafsInCommon = 0; std::size_t internalInCommon = 0; std::size_t leafToInternal = 0; for (Map::const_iterator it = other.m_data.begin(); it != other.m_data.end(); ++it) { Map::iterator mine = m_data.find(it->first); SgBookNode newNode(it->second); if (mine == m_data.end()) { m_data[it->first] = it->second; if (newNode.IsLeaf()) newLeafs++; else newInternal++; } else { SgBookNode oldNode(mine->second); if (newNode.IsLeaf() && oldNode.IsLeaf()) { newNode.m_heurValue = 0.5f * (newNode.m_heurValue + oldNode.m_heurValue); m_data[it->first] = newNode; leafsInCommon++; } else if (! newNode.IsLeaf()) { // Take the max of the count; can't just add them // together because then merging a book with itself // doubles the counts of everything, which doesn't // make sense. Need the parent of these books and do a // three-way merge if we want the counts to be // accurate after the merge. I don't think it matters // that much. newNode.m_count = std::max(newNode.m_count, oldNode.m_count); m_data[it->first] = newNode; if (! oldNode.IsLeaf()) internalInCommon++; else leafToInternal++; } } } SgDebug() << "Statistics\n" << "New Leafs " << newLeafs << '\n' << "New Internal " << newInternal << '\n' << "Common Leafs " << leafsInCommon << '\n' << "Common Internal " << internalInCommon << '\n' << "Leaf to Internal " << leafToInternal << '\n'; }
GoAutoBook::GoAutoBook(const std::string& filename, const GoAutoBookParam& param) : m_param(param), m_filename(filename) { std::ifstream is(filename.c_str()); if (! is) { std::ofstream of(filename.c_str()); if (! of) throw SgException("Invalid file name!"); of.close(); } else { while (is) { std::string line; std::getline(is, line); if (line.size() < 19) continue; std::string str; std::istringstream iss(line); iss >> str; SgHashCode hash; hash.FromString(str); SgBookNode node(line.substr(19)); m_data[hash] = node; } SgDebug() << "GoAutoBook: Parsed " << m_data.size() << " lines.\n"; } }
void GoAutoBook::ImportHashValuePairs(std::istream& in) { std::size_t count = 0; while (in) { SgHashCode hash; std::string hashStr; in >> hashStr; hash.FromString(hashStr); float value; if (! in) break; in >> value; if (m_data.count(hash) == 0) { std::ostringstream os; os << "Unknown hash: " << hash << '\n'; throw SgException(os.str()); } SgBookNode node(m_data[hash]); node.m_heurValue = value; node.m_value = value; m_data[hash] = node; count++; } SgDebug() << "GoAutoBook::ImportHashValue: imported " << count << " values.\n"; }
void DfpnSolver::PrintStatistics(SgEmptyBlackWhite winner, const PointSequence& pv) const { std::ostringstream os; os << '\n' << SgWriteLabel("MID calls") << m_numMIDcalls << '\n' << SgWriteLabel("Generate moves") << m_generateMoves << '\n' << SgWriteLabel("Terminal") << m_numTerminal << '\n' << SgWriteLabel("Work") << m_numMIDcalls + m_numTerminal << '\n' << SgWriteLabel("Wasted Work") << m_totalWastedWork << " (" << (double(m_totalWastedWork) * 100.0 / double(m_numMIDcalls + m_numTerminal)) << "%)\n" << SgWriteLabel("Elapsed Time") << m_timer.GetTime() << '\n' << SgWriteLabel("MIDs/sec") << double(m_numMIDcalls) / m_timer.GetTime() << '\n' << SgWriteLabel("generates/sec") << double(m_generateMoves) / m_timer.GetTime() << '\n' << SgWriteLabel("Cnt prune sib") << m_prunedSiblingStats.Count() << '\n' << SgWriteLabel("Avg prune sib"); m_prunedSiblingStats.Write(os); os << '\n' << SgWriteLabel("Move Index"); m_moveOrderingIndex.Write(os); os << '\n' << SgWriteLabel("Move Percent"); m_moveOrderingPercent.Write(os); os << '\n' << SgWriteLabel("Delta Increase"); m_deltaIncrease.Write(os); os << '\n' << SgWriteLabel("Winner") << SgEBW(winner) << '\n'; WriteMoveSequence(os, pv); os << '\n'; if (m_hashTable) os << '\n' << *m_hashTable << '\n'; SgDebug() << os.str(); }
void GoRegionBoard::OnExecutedUncodedMove(int move, SgBlackWhite moveColor) { if (DEBUG_REGION_BOARD) SgDebug() << "OnExecutedUncodedMove " << SgWritePoint(move) << '\n'; { m_stack.StartMoveInfo(); if (move != SG_PASS) { SG_ASSERT(! Board().LastMoveInfo(GO_MOVEFLAG_SUICIDE)); // can't handle yet, // should be forbidden anyway. @todo allowed in Chinese rules. bool fWasCapture = Board().LastMoveInfo(GO_MOVEFLAG_CAPTURING); UpdateBlock(move, moveColor); { GoRegion* r = PreviousRegionAt(move, moveColor); bool split = GoEyeUtil::IsSplitPt(move, r->Points()); r->OnAddStone(move); PushStone(r, move); SgPointSet points = r->Points(); // needed even after RemoveRegion(r). if (split || points.IsEmpty()) // must remove old region before generating new ones, // because removing clears m_anchor[] RemoveRegion(r); if (split) // find new regions { for (SgConnCompIterator it(points, Board().Size()); it; ++it) GenRegion(*it, moveColor); } } if (fWasCapture) { // FindNewNeighborRegions(move, moveColor); MergeAdjacentAndAddBlock(move, SgOppBW(moveColor)); } m_code = Board().GetHashCode(); if (HEAVYCHECK) CheckConsistency(); } } { for (SgBWIterator it; it; ++it) { SgBlackWhite color(*it); for (SgVectorIteratorOf<GoRegion> it(AllRegions(color)); it; ++it) { GoRegion* r1 = *it; if (! r1->IsValid()) r1->ComputeBasicFlags(); } } } }
std::vector< std::vector<SgMove> > GoAutoBook::ParseWorkList(std::istream& in) { std::vector< std::vector<SgMove> > ret; while (in) { std::string line; std::getline(in, line); if (line == "") continue; std::vector<SgMove> var; std::istringstream in2(line); while (true) { std::string s; in2 >> s; if (! in2 || s == "|") break; std::istringstream in3(s); SgPoint p; in3 >> SgReadPoint(p); if (! in3) throw SgException("Invalid point"); var.push_back(p); } ret.push_back(var); } SgDebug() << "GoAutoBook::ParseWorkList: Read " << ret.size() << " variations.\n"; return ret; }
SgEmptyBlackWhite DfpnSolver::StartSearch(DfpnHashTable& hashTable, PointSequence& pv, const DfpnBounds& maxBounds) { m_aborted = false; m_hashTable = &hashTable; m_numTerminal = 0; m_numMIDcalls = 0; m_generateMoves = 0; m_totalWastedWork = 0; m_prunedSiblingStats.Clear(); m_moveOrderingPercent.Clear(); m_moveOrderingIndex.Clear(); m_deltaIncrease.Clear(); m_checkTimerAbortCalls = 0; // Skip search if already solved DfpnData data; if (TTRead(data) && data.m_bounds.IsSolved()) { SgDebug() << "Already solved!\n"; const SgEmptyBlackWhite toPlay = GetColorToMove(); SgEmptyBlackWhite w = Winner(data.m_bounds.IsWinning(), toPlay); GetPVFromHash(pv); SgDebug() << SgEBW(w) << " wins!\n"; WriteMoveSequence(SgDebug(), pv); return w; } m_timer.Start(); DfpnHistory history; MID(maxBounds, history); m_timer.Stop(); GetPVFromHash(pv); SgEmptyBlackWhite winner = SG_EMPTY; if (TTRead(data) && data.m_bounds.IsSolved()) { const SgEmptyBlackWhite toPlay = GetColorToMove(); winner = Winner(data.m_bounds.IsWinning(), toPlay); } PrintStatistics(winner, pv); if (m_aborted) SgWarning() << "Search aborted.\n"; return winner; }
SgMove GoAutoBook::FindBestChild(GoAutoBookState& state) const { std::size_t bestCount = 0; SgMove bestMove = SG_NULLMOVE; float bestScore = 100.0f; SgBookNode node; 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() && 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; } } } state.Undo(); } } return bestMove; }
void GoRegionBoard::ExecuteMovePrologue() { if (! UpToDate()) // full recomputation { if (DEBUG_REGION_BOARD) SgDebug() << "recompute everything\n"; GenBlocksRegions(); } }
void GoUctFeatureKnowledgeFactory::ReadWeights() { m_weights = FeFeatureWeights::ReadDefaultWeights(); SgDebug() << "GoUctFeatureKnowledgeFactory Read weights for " << m_weights.m_nuFeatures << " features with k = " << m_weights.m_k << ", minID = " << m_weights.m_minID << ", maxID = " << m_weights.m_maxID << '\n'; }
GoBlock* GoRegionBoard::GetBlock(const SgPointSet& boundary, SgBlackWhite color) const { SG_ASSERT(UpToDate()); for (SgVectorIteratorOf<GoBlock> it(AllBlocks(color)); it; ++it) { if (boundary.SubsetOf((*it)->Stones())) /* */ return *it; /* */ } SgDebug() << "ERROR: no block on set"; const int size = Board().Size(); boundary.Write(SgDebug(), size); SgDebug() << "blocks:"; for (SgVectorIteratorOf<GoBlock> it(AllBlocks(color));it; ++it) (*it)->Stones().Write(SgDebug(), size); SG_ASSERT(false); return 0; }
void GoSafetySolver::Merge(GoChain* c1, GoChain* c2, GoRegion* r, bool bySearch) { SG_ASSERT(! r->GetFlag(GO_REGION_USED_FOR_MERGE)); r->SetFlag(GO_REGION_USED_FOR_MERGE, true); GoChainCondition* c = 0; if (bySearch) c = new GoChainCondition(GO_CHAIN_BY_SEARCH); else { SgPoint lib1, lib2; r->Find2FreeLibs(c1, c2, &lib1, &lib2); c = new GoChainCondition(GO_CHAIN_TWO_LIBERTIES_IN_REGION, lib1, lib2); } GoChain* m = new GoChain(c1, c2, c); SgBlackWhite color = c1->Color(); bool found = Regions()->AllChains(color).Exclude(c1); SG_DEBUG_ONLY(found); SG_ASSERT(found); found = Regions()->AllChains(color).Exclude(c2); SG_ASSERT(found); Regions()->AllChains(color).Include(m); SG_ASSERT(Regions()->AllChains(color).UniqueElements()); for (SgVectorIteratorOf<GoRegion> it(Regions()->AllRegions(color)); it; ++it) { GoRegion* r = *it; bool replace1 = r->ReplaceChain(c1, m); bool replace2 = r->ReplaceChain(c2, m); if (replace1 || replace2) { r->ReInitialize(); r->ComputeFlag(GO_REGION_STATIC_1VITAL); } } if (DEBUG_MERGE_CHAINS) { SgDebug() << "\nmerge:"; c1->WriteID(SgDebug()); SgDebug() << " + "; c2->WriteID(SgDebug()); SgDebug() << " = "; m->WriteID(SgDebug()); SgDebug() << '\n'; } delete c1; delete c2; }
void GoUctSearch::DisplayGfx() { SgDebug() << "gogui-gfx:\n"; switch (m_liveGfx) { case GOUCT_LIVEGFX_COUNTS: GoUctUtil::GfxBestMove(*this, m_toPlay, SgDebug()); GoUctUtil::GfxMoveValues(*this, m_toPlay, SgDebug()); GoUctUtil::GfxCounts(Tree(), SgDebug()); GoUctUtil::GfxStatus(*this, SgDebug()); break; case GOUCT_LIVEGFX_SEQUENCE: GoUctUtil::GfxSequence(*this, m_toPlay, SgDebug()); GoUctUtil::GfxStatus(*this, SgDebug()); break; case GOUCT_LIVEGFX_NONE: SG_ASSERT(false); // Should only be called when LiveGfx is enabled break; } SgDebug() << '\n'; }
void GoRegionBoard::Clear() { if (DEBUG_REGION_BOARD) SgDebug() << "Clear\n"; for (SgBWIterator it; it; ++it) { SgBlackWhite color(*it); for (SgVectorIteratorOf<GoBlock> it1(AllBlocks(color)); it1; ++it1) delete *it1; for (SgVectorIteratorOf<GoRegion> it2(AllRegions(color)); it2; ++it2) delete *it2; for (SgVectorIteratorOf<GoChain> it3(AllChains(color)); it3; ++it3) delete *it3; } m_allBlocks[SG_BLACK].Clear(); m_allBlocks[SG_WHITE].Clear(); m_allRegions[SG_BLACK].Clear(); m_allRegions[SG_WHITE].Clear(); m_allChains[SG_BLACK].Clear(); m_allChains[SG_WHITE].Clear(); m_stack.Clear(); m_code.Invalidate(); m_invalid = true; m_computedHealthy = false; m_boardSize = m_board.Size(); for (SgBWIterator it; it; ++it) { SgBlackWhite color(*it); for (SgPoint p = SgPointUtil::Pt(1, 1); p <= SgPointUtil::Pt(SG_MAX_SIZE, SG_MAX_SIZE); ++p) { m_region[color][p] = 0; } } for (SgPoint p = SgPointUtil::Pt(1, 1); p <= SgPointUtil::Pt(SG_MAX_SIZE, SG_MAX_SIZE); ++p) m_block[p] = 0; }
void GoRegionBoard::OnUndoneMove() // Called after a move has been undone. The board is guaranteed to be in // a legal state. { //SG_ASSERT(false); // incremental code is incomplete, do not call if (DEBUG_REGION_BOARD) SgDebug() << "OnUndoneMove " << '\n'; const bool IS_UNDO = false; SgVectorOf<GoRegion> changed; for (int val = m_stack.PopEvent(); val != SG_NEXTMOVE; val = m_stack.PopEvent()) { switch (val) { case REGION_REMOVE: { GoRegion* r = static_cast<GoRegion*>(m_stack.PopPtr()); AddRegion(r, IS_UNDO); changed.Insert(r); } break; case REGION_ADD: { GoRegion* r = static_cast<GoRegion*>(m_stack.PopPtr()); RemoveRegion(r, IS_UNDO); } break; case REGION_REMOVE_BLOCK: { GoBlock* b = static_cast<GoBlock*>(m_stack.PopPtr()); AddBlock(b, IS_UNDO); for (int nu = m_stack.PopInt(); nu > 0; --nu) { GoRegion* r = static_cast<GoRegion*>(m_stack.PopPtr()); if (CHECK) SG_ASSERT(! r->Blocks().Contains(b)); r->BlocksNonConst().PushBack(b); changed.Insert(r); } } break; case REGION_ADD_BLOCK: { GoBlock* b = static_cast<GoBlock*>(m_stack.PopPtr()); RemoveBlock(b, IS_UNDO, true); } break; case REGION_ADD_STONE: { GoRegion* r = static_cast<GoRegion*>(m_stack.PopPtr()); SgPoint p = m_stack.PopInt(); r->OnRemoveStone(p); m_region[r->Color()][p] = r; changed.Insert(r); } break; case REGION_ADD_STONE_TO_BLOCK: { GoBlock* b = static_cast<GoBlock*>(m_stack.PopPtr()); SgPoint p = m_stack.PopInt(); b->RemoveStone(p); m_block[p] = 0; } break; default: SG_ASSERT(false); } } for (SgVectorIteratorOf<GoRegion> it(changed); it; ++it) { (*it)->ResetNonBlockFlags(); (*it)->ComputeBasicFlags(); } if (HEAVYCHECK) { for (SgBWIterator it; it; ++it) { SgBlackWhite color(*it); for (SgVectorIteratorOf<GoRegion> it(AllRegions(color)); it; ++it) { const GoRegion* r = *it; SG_UNUSED(r); SG_ASSERT(r->IsValid()); } } } m_code = Board().GetHashCode(); if (HEAVYCHECK) CheckConsistency(); }
void GoUctState::AssertionHandler::Run() { m_state.Dump(SgDebug()); }
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; }