Index   Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

SgSearch.cpp

Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 /** @file SgSearch.cpp
00003     See SgSearch.h. */
00004 //----------------------------------------------------------------------------
00005 
00006 #include "SgSystem.h"
00007 #include "SgSearch.h"
00008 
00009 #include <algorithm>
00010 #include <iomanip>
00011 #include <limits>
00012 #include <sstream>
00013 #include <math.h>
00014 #include "SgDebug.h"
00015 #include "SgHashTable.h"
00016 #include "SgMath.h"
00017 #include "SgNode.h"
00018 #include "SgProbCut.h"
00019 #include "SgSearchControl.h"
00020 #include "SgSearchValue.h"
00021 #include "SgTime.h"
00022 #include "SgVector.h"
00023 #include "SgWrite.h"
00024 
00025 using namespace std;
00026 
00027 //----------------------------------------------------------------------------
00028 
00029 namespace{
00030     const bool DEBUG_SEARCH = false;
00031     const bool DEBUG_SEARCH_ITERATIONS = false;
00032 }
00033 //----------------------------------------------------------------------------
00034 
00035 void SgKiller::MarkKiller(SgMove killer)
00036 {
00037     if (killer == m_killer1)
00038         ++m_count1;
00039     else if (killer == m_killer2)
00040     {
00041         ++m_count2;
00042         if (m_count1 < m_count2)
00043         {
00044             swap(m_killer1, m_killer2);
00045             swap(m_count1, m_count2);
00046         }
00047     }
00048     else if (m_killer1 == SG_NULLMOVE)
00049     {
00050         m_killer1 = killer;
00051         m_count1 = 1;
00052     }
00053     else
00054     {
00055         m_killer2 = killer;
00056         m_count2 = 1;
00057     }
00058 }
00059 
00060 void SgKiller::Clear()
00061 {
00062     m_killer1 = SG_NULLMOVE;
00063     m_count1 = 0;
00064     m_killer2 = SG_NULLMOVE;
00065     m_count2 = 0;
00066 }
00067 
00068 //----------------------------------------------------------------------------
00069 
00070 bool SgSearchHashData::IsBetterThan(const SgSearchHashData& data) const
00071 {
00072     if (m_depth > data.m_depth)
00073         return true;
00074     if (m_depth < data.m_depth)
00075         return false;
00076     return (! m_isUpperBound && ! m_isLowerBound)
00077         || (m_isLowerBound && data.m_isLowerBound && m_value > data.m_value)
00078         || (m_isUpperBound && data.m_isUpperBound && m_value < data.m_value);
00079 }
00080 
00081 //----------------------------------------------------------------------------
00082 
00083 namespace {
00084 
00085 /** copy stack onto sequence, starting with Top() */
00086 void ReverseCopyStack(const SgSearchStack& moveStack, SgVector<SgMove>& sequence)
00087 {
00088     sequence.Clear();
00089     for (int i = moveStack.Size() - 1; i >= 0; --i)
00090         sequence.PushBack(moveStack[i]);
00091 }
00092 
00093 void WriteSgSearchHashData(std::ostream& str, const SgSearch& search, 
00094                            const SgSearchHashData& data)
00095 {
00096     str << "move = " << search.MoveString(data.BestMove()) 
00097         << " exact = " << data.IsExactValue()
00098         << " value = " << data.Value() 
00099         << '\n';
00100 }
00101 
00102 
00103 void WriteMoves(const SgSearch& search, const SgVector<SgMove>& sequence)
00104 {
00105     for (SgVectorIterator<SgMove> it(sequence); it; ++it)
00106         SgDebug() << ' ' << search.MoveString(*it);
00107 }
00108 
00109 void PrintPV(const SgSearch& search, int depth, int value,
00110              const SgVector<SgMove>& sequence,
00111              bool isExactValue)
00112 {
00113     SgDebug() << "Iteration d = " << depth
00114         << ", value = " << value
00115         << ", exact = " << isExactValue
00116         << ", sequence = ";
00117     WriteMoves(search, sequence);
00118     SgDebug() << '\n';
00119 }
00120 
00121 } // namespace
00122 
00123 //----------------------------------------------------------------------------
00124 
00125 const int SgSearch::SG_INFINITY = numeric_limits<int>::max();
00126 
00127 SgSearch::SgSearch(SgSearchHashTable* hash)
00128     : m_hash(hash),
00129       m_tracer(0),
00130       m_currentDepth(0),
00131       m_useScout(false),
00132       m_useKillers(false),
00133       m_useOpponentBest(0),
00134       m_useNullMove(0),
00135       m_nullMoveDepth(2),
00136       m_aborted(false),
00137       m_foundNewBest(false),
00138       m_reachedDepthLimit(false),
00139       m_stat(),
00140       m_timerLevel(0),
00141       m_control(0),
00142       m_probcut(0),
00143       m_abortFrequency(1)
00144 {
00145     InitSearch();
00146 }
00147 
00148 SgSearch::~SgSearch()
00149 {
00150 }
00151 
00152 void SgSearch::CallGenerate(SgVector<SgMove>* moves, int depth)
00153 {
00154     Generate(moves, depth);
00155     if (DEBUG_SEARCH)
00156     {
00157         SgDebug() << "SgSearch::CallGenerate: d=" << depth;
00158         WriteMoves(*this, *moves);
00159         SgDebug() << '\n';
00160     }
00161 }
00162 
00163 void SgSearch::InitSearch(int startDepth)
00164 {
00165     m_currentDepth = startDepth;
00166     m_moveStack.Clear();
00167     m_moveStack.PushBack(SG_NULLMOVE);
00168     m_moveStack.PushBack(SG_NULLMOVE);
00169     if (m_useKillers)
00170     {
00171         for (int i = 0; i <= MAX_KILLER_DEPTH; ++i)
00172             m_killers[i].Clear();
00173     }
00174 }
00175 
00176 bool SgSearch::LookupHash(SgSearchHashData& data) const
00177 {
00178     SG_ASSERT(! data.IsValid());
00179     if (m_hash == 0 || ! m_hash->Lookup(GetHashCode(), &data))
00180         return false;
00181     if (DEBUG_SEARCH)
00182     {
00183         SgDebug() << "SgSearch::LookupHash: " << GetHashCode() << ' ';
00184         WriteSgSearchHashData(SgDebug(), *this, data);
00185     }
00186     return true;
00187 }
00188 
00189 void SgSearch::OnStartSearch()
00190 {
00191     // Default implementation does nothing
00192 }
00193 
00194 void SgSearch::SetSearchControl(SgSearchControl* control)
00195 {
00196     m_control = control;
00197 }
00198 
00199 void SgSearch::SetProbCut(SgProbCut* probcut)
00200 {
00201     m_probcut = probcut;
00202 }
00203 
00204 void SgSearch::StoreHash(int depth, int value, SgMove move,
00205                          bool isUpperBound, bool isLowerBound, bool isExact)
00206 {
00207     SG_ASSERT(m_hash);
00208     SgSearchHashData data(depth, value, move, isUpperBound, isLowerBound,
00209                           isExact);
00210     if (DEBUG_SEARCH)
00211     {
00212         SgDebug() << "SgSearch::StoreHash: " << GetHashCode() 
00213                   << ": ";
00214         WriteSgSearchHashData(SgDebug(), *this, data);
00215     }
00216     m_hash->Store(GetHashCode(), data);
00217 }
00218 
00219 bool SgSearch::TraceIsOn() const
00220 {
00221     return m_tracer && m_tracer->TraceIsOn();
00222 }
00223 
00224 bool SgSearch::AbortSearch()
00225 {
00226     if (! m_aborted)
00227     {
00228         // Checking abort is potentially expensive, involves system call.
00229         // Only check every m_abortFrequency nodes
00230         if (m_stat.NumNodes() % m_abortFrequency != 0)
00231             return false;
00232         m_aborted =
00233                m_control 
00234             && m_control->Abort(m_timer.GetTime(), m_stat.NumNodes());
00235         if (! m_aborted && SgUserAbort())
00236             m_aborted = true;
00237         if (m_aborted && TraceIsOn())
00238             m_tracer->TraceComment("aborted");
00239     }
00240     return m_aborted;
00241 }
00242 
00243 bool SgSearch::NullMovePrune(int depth, int delta, int beta)
00244 {
00245     SgSearchStack ignoreStack;
00246     bool childIsExact = true;
00247     if (beta >= SG_INFINITY - 1)
00248         return false;
00249     if (CallExecute(SG_PASS, &delta, depth))
00250     {
00251         float nullvalue =
00252             float(-SearchEngine(depth - delta, -beta, -beta + 1, ignoreStack,
00253                                 &childIsExact, true));
00254         CallTakeBack();
00255         if (nullvalue >= beta)
00256         {
00257             if (TraceIsOn())
00258                 m_tracer->TraceComment("null-move-cut");
00259             return true;
00260         }
00261     }
00262     return false;
00263 }
00264 
00265 void SgSearch::GetStatistics(SgSearchStatistics* stat)
00266 {
00267     m_stat.SetTimeUsed(m_timer.GetTime());
00268     *stat = m_stat;
00269 }
00270 
00271 void SgSearch::StartTime()
00272 {
00273     if (++m_timerLevel == 1)
00274     {
00275         m_stat.Clear();
00276         m_timer.Start();
00277     }
00278 }
00279 
00280 void SgSearch::StopTime()
00281 {
00282     if (--m_timerLevel == 0 && ! m_timer.IsStopped())
00283         m_timer.Stop();
00284 }
00285 
00286 int SgSearch::CallEvaluate(int depth, bool* isExact)
00287 {
00288     int v = Evaluate(isExact, depth);
00289     if (DEBUG_SEARCH)
00290         SgDebug() << "SgSearch::CallEvaluate d = " << depth 
00291                   << ", v = " << v
00292                   << '\n';
00293     return v;
00294 }
00295 
00296 bool SgSearch::CallExecute(SgMove move, int* delta, int depth)
00297 {
00298     const SgBlackWhite toPlay = GetToPlay();
00299     if (DEBUG_SEARCH)
00300         SgDebug() << "SgSearch::CallExecute: d = " << depth << ' '
00301                   << SgBW(toPlay) << ' ' << MoveString(move) << '\n';
00302     if (Execute(move, delta, depth))
00303     {
00304         m_stat.IncNumMoves();
00305         if (move == SG_PASS)
00306             m_stat.IncNumPassMoves();
00307         m_moveStack.PushBack(move);
00308         ++m_currentDepth;
00309         if (TraceIsOn())
00310             m_tracer->AddTraceNode(move, toPlay);
00311         return true;
00312     }
00313     return false;
00314 }
00315 
00316 void SgSearch::CallTakeBack()
00317 {
00318     if (DEBUG_SEARCH)
00319         SgDebug() << "SgSearch::CallTakeBack\n";
00320     TakeBack();
00321     if (TraceIsOn())
00322         m_tracer->TakeBackTraceNode();
00323     m_moveStack.PopBack();
00324     --m_currentDepth;
00325 }
00326 
00327 void SgSearch::CreateTracer()
00328 {
00329     m_tracer = new SgSearchTracer(0);
00330 }
00331 
00332 void SgSearch::AddSequenceToHash(const SgVector<SgMove>& sequence, int depth)
00333 {
00334     if (! m_hash)
00335         return;
00336     int numMovesToUndo = 0;
00337     for (SgVectorIterator<SgMove> iter(sequence); iter; ++iter)
00338     {
00339         SgMove move = *iter;
00340         int delta = DEPTH_UNIT;
00341         if (CallExecute(move, &delta, depth))
00342         {
00343             // Store move with position before the move is played.
00344             CallTakeBack();
00345 
00346             // Move is the only relevant data for seeding the hash table.
00347             SgSearchHashData data(0, 0, move);
00348             SG_ASSERT(move != SG_NULLMOVE);
00349             m_hash->Store(GetHashCode(), data);
00350             if (DEBUG_SEARCH)
00351                 SgDebug() << "SgSearch::AddSequenceToHash: "
00352                           << MoveString(move) << '\n';
00353             // Execute move again.
00354             int delta = DEPTH_UNIT;
00355             if (CallExecute(move, &delta, depth))
00356                 ++numMovesToUndo;
00357             else // it just worked, should not fail now.
00358                 SG_ASSERT(false);
00359         }
00360         else
00361             break;
00362     }
00363 
00364     // Restore the original position.
00365     while (--numMovesToUndo >= 0)
00366         CallTakeBack();
00367 }
00368 
00369 int SgSearch::DFS(int startDepth, int depthLimit,
00370                 int boundLo, int boundHi,
00371                 SgVector<SgMove>* sequence, bool* isExactValue)
00372 {
00373     InitSearch(startDepth);
00374     SG_ASSERT(m_currentDepth == startDepth);
00375     SG_ASSERT(sequence);
00376     m_aborted = false;
00377     m_foundNewBest = false;
00378     SgSearchStack moveStack;
00379     int value = SearchEngine(depthLimit * DEPTH_UNIT, 
00380                              boundLo, boundHi, moveStack,
00381                               isExactValue);
00382     ReverseCopyStack(moveStack, *sequence);
00383     return value;
00384 }
00385 
00386 
00387 int SgSearch::DepthFirstSearch(int depthLimit, int boundLo, int boundHi,
00388                                SgVector<SgMove>* sequence, bool clearHash,
00389                                SgNode* traceNode)
00390 {
00391     SG_ASSERT(sequence);
00392     OnStartSearch();
00393     if (m_tracer && traceNode)
00394     {
00395         SG_ASSERT(m_tracer->TraceNode() == 0);
00396         SG_ASSERT(m_tracer->TraceIsOn());
00397         m_tracer->InitTracing("DepthFirstSearch");
00398     }
00399     StartTime();
00400     if (clearHash && m_hash)
00401     {
00402         m_hash->Clear();
00403         AddSequenceToHash(*sequence, 0);
00404     }
00405     m_depthLimit = 0;
00406     bool isExactValue = true;
00407     int value = DFS(0, depthLimit, boundLo, boundHi, sequence, &isExactValue);
00408     StopTime();
00409     if (m_tracer && traceNode)
00410         m_tracer->AppendTrace(traceNode);
00411     return value;
00412 }
00413 
00414 int SgSearch::IteratedSearch(int depthMin, int depthMax, int boundLo,
00415                              int boundHi, SgVector<SgMove>* sequence,
00416                              bool clearHash, SgNode* traceNode)
00417 {
00418     SG_ASSERT(sequence);
00419     OnStartSearch();
00420     if (m_tracer && traceNode)
00421     {
00422         SG_ASSERT(m_tracer->TraceNode() == 0);
00423         SG_ASSERT(m_tracer->TraceIsOn());
00424         m_tracer->InitTracing("IteratedSearch");
00425     }
00426     StartTime();
00427     if (clearHash && m_hash)
00428     {
00429         m_hash->Clear();
00430         AddSequenceToHash(*sequence, 0);
00431     }
00432 
00433     int value = 0;
00434     m_depthLimit = depthMin;
00435     // done in DFS, but that's too late, is tested after StartOfDepth
00436     m_aborted = false;
00437 
00438     // Keep track of value and sequence of previous iteration in case search
00439     // gets aborted (since value and sequence are then ill-defined).
00440     m_prevValue = 0;
00441     m_prevSequence.Clear();
00442     bool isExactValue = true;
00443 
00444     do
00445     {
00446         if (m_control != 0
00447             && ! m_control->StartNextIteration(m_depthLimit,
00448                                                m_timer.GetTime(), 
00449                                                m_stat.NumNodes()))
00450             SetAbortSearch();
00451         if (m_aborted)
00452             break;
00453         StartOfDepth(m_depthLimit);
00454 
00455         // Record depth limit of depths where we actually do some search.
00456         m_stat.SetDepthReached(m_depthLimit);
00457 
00458         // Remember whether we actually reach the depth limit. If not, no
00459         // sense in increasing the depth limit, won't find anything new.
00460         m_reachedDepthLimit = false;
00461         isExactValue = true;
00462         m_foundNewBest = false;
00463 
00464         // Fixed-depth search.
00465         value = DFS(0, m_depthLimit, boundLo, boundHi, sequence,
00466                     &isExactValue);
00467 
00468         // Restore result of previous iteration if aborted.
00469         if (m_aborted)
00470         {
00471             if (m_prevSequence.NonEmpty() && ! m_foundNewBest)
00472             {
00473                 value = m_prevValue;
00474                 *sequence = m_prevSequence;
00475             }
00476             break;
00477         }
00478         else
00479         {
00480             if (DEBUG_SEARCH_ITERATIONS)
00481                 PrintPV(*this, m_depthLimit, value, *sequence, isExactValue);
00482             m_prevValue = value;
00483             m_prevSequence = *sequence;
00484         }
00485 
00486         // Stop iteration as soon as exact result or a bounding value found.
00487         if (isExactValue || value <= boundLo || boundHi <= value)
00488             break;
00489 
00490         ++m_depthLimit;
00491 
00492     } while (  m_depthLimit <= depthMax
00493             && ! isExactValue
00494             && ! m_aborted
00495             && (! CheckDepthLimitReached() || m_reachedDepthLimit)
00496             );
00497 
00498     StopTime();
00499     if (m_tracer && traceNode)
00500         m_tracer->AppendTrace(traceNode);
00501     return value;
00502 }
00503 
00504 bool SgSearch::TryMove(SgMove move, const SgVector<SgMove>& specialMoves,
00505                        const int depth,
00506                        const int alpha, const int beta,
00507                        int& loValue, int& hiValue,
00508                        SgSearchStack& stack,
00509                        bool& allExact,
00510                        bool& isCutoff)
00511 {
00512     if (specialMoves.Contains(move)) // already tried move before
00513         return false;
00514 
00515     int delta = DEPTH_UNIT;
00516     if (! CallExecute(move, &delta, depth))
00517         return false;
00518 
00519     bool childIsExact = true;
00520     SgSearchStack newStack;
00521     int merit = -SearchEngine(depth - delta, -hiValue,
00522                               -max(loValue, alpha), newStack,
00523                               &childIsExact);
00524     if (loValue < merit && ! m_aborted) // new best move
00525     {
00526         loValue = merit;
00527         if (m_useScout)
00528         {
00529             // If getting a move that's better than what we have
00530             // so far, not good enough to cause a cutoff, was
00531             // searched with a narrow window, and doesn't
00532             // immediately lead to a terminal node, then search
00533             // again with a wide window to get a more precise
00534             // value.
00535             if (  alpha < merit
00536                && merit < beta
00537                && delta < depth
00538                )
00539             {
00540                 childIsExact = true;
00541                 loValue = -SearchEngine(depth-delta, -beta,
00542                                         -merit, newStack,
00543                                         &childIsExact);
00544             }
00545             hiValue = max(loValue, alpha) + 1;
00546         }
00547         stack.CopyFrom(newStack);
00548         stack.Push(move);
00549         SG_ASSERT(move != SG_NULLMOVE);
00550         if (m_currentDepth == 1 && ! m_aborted)
00551             m_foundNewBest = true;
00552     }
00553     if (! childIsExact)
00554         allExact = false;
00555     CallTakeBack();
00556     if (loValue >= beta)
00557     {
00558         // Move generated a cutoff: add this move to the list of
00559         // killers.
00560         if (m_useKillers && m_currentDepth <= MAX_KILLER_DEPTH)
00561             m_killers[m_currentDepth].MarkKiller(move);
00562         if (TraceIsOn())
00563             m_tracer->TraceComment("b-cut");
00564         isCutoff = true;
00565     }
00566     return true;
00567 }
00568 
00569 bool SgSearch::TrySpecialMove(SgMove move, SgVector<SgMove>& specialMoves,
00570                        const int depth,
00571                        const int alpha, const int beta,
00572                        int& loValue, int& hiValue,
00573                        SgSearchStack& stack,
00574                        bool& allExact,
00575                        bool& isCutoff)
00576 
00577 {
00578     if (specialMoves.Contains(move))
00579         return false;
00580     bool executed = TryMove(move, specialMoves,
00581                              depth, alpha, beta,
00582                                 loValue, hiValue, stack,
00583                              allExact, isCutoff);
00584     specialMoves.PushBack(move);
00585     return executed;
00586 }
00587 
00588 int SgSearch::SearchEngine(int depth, int alpha, int beta,
00589                            SgSearchStack& stack, bool* isExactValue,
00590                            bool lastNullMove)
00591 {
00592     SG_ASSERT(stack.IsEmpty() || stack.Top() != SG_NULLMOVE);
00593     SG_ASSERT(alpha < beta);
00594 
00595     // Only place we check whether the search has been newly aborted. In all
00596     // other places, just check whether search was aborted before.
00597     // AR: what to return here?
00598     // if - (SG_INFINITY-1), then will be positive on next level?
00599     if (AbortSearch())
00600     {
00601         *isExactValue = false;
00602         return alpha;
00603     }
00604 
00605     // Null move pruning
00606     if (  m_useNullMove
00607        && depth > 0
00608        && ! lastNullMove
00609        && NullMovePrune(depth, DEPTH_UNIT * (1 + m_nullMoveDepth), beta)
00610        )
00611     {
00612         *isExactValue = false;
00613         return beta;
00614     }
00615 
00616     // ProbCut
00617     if (m_probcut && m_probcut->IsEnabled())
00618     {
00619         int probCutValue;
00620         if (m_probcut->ProbCut(*this, depth, alpha, beta, stack, 
00621                                isExactValue, &probCutValue)
00622            )
00623             return probCutValue;
00624     }
00625 
00626     m_stat.IncNumNodes();
00627     bool hasMove = false; // true if a move has been executed at this level
00628     int loValue = -(SG_INFINITY - 1);
00629     m_reachedDepthLimit = m_reachedDepthLimit || (depth <= 0);
00630 
00631     // check whether position is solved from hash table.
00632     SgSearchHashData data; // initialized to ! data.IsValid()
00633     if (LookupHash(data))
00634     {
00635         if (data.IsExactValue()) // exact value: stop search
00636         {
00637             *isExactValue = true;
00638             stack.Clear();
00639             if (data.BestMove() != SG_NULLMOVE)
00640                 stack.Push(data.BestMove());
00641             if (TraceIsOn())
00642                 m_tracer->TraceValue(data.Value(), GetToPlay(),
00643                                      "exact-hash", true);
00644             return data.Value();
00645         }
00646     }
00647 
00648     bool allExact = true; // Do all moves have exact evaluation?
00649     if (depth > 0 && ! EndOfGame())
00650     {
00651         // Check whether current position has already been encountered.
00652         SgMove tryFirst = SG_NULLMOVE;
00653         SgMove opponentBest = SG_NULLMOVE;
00654         if (data.IsValid())
00655         {
00656             if (data.Depth() > 0)
00657             {
00658                 tryFirst = data.BestMove();
00659                 SG_ASSERT(tryFirst != SG_NULLMOVE);
00660             }
00661 
00662             // If data returned from hash table is based on equal or deeper 
00663             // search than what we plan to do right now, just use that data. 
00664             // The hash table may have deeper data for the current position
00665             // since the same number of moves may result in more 'depth'
00666             // left if the 'delta' for the moves has been smaller, which
00667             // will happen when most moves came from the cache.
00668             if (depth <= data.Depth())
00669             {
00670                 // Rely on value returned from hash table to be for the
00671                 // current position. In Go, it can happen that the move is
00672                 // not legal (ko recapture)
00673                 int delta = DEPTH_UNIT;
00674                 bool canExecute = CallExecute(tryFirst, &delta, depth);
00675                 if (canExecute)
00676                     CallTakeBack();
00677                 else
00678                     tryFirst = SG_NULLMOVE;
00679                 if (tryFirst != SG_NULLMOVE || data.IsExactValue())
00680                 {
00681                     // getting a deep enough hash hit or an exact value
00682                     // is as good as reaching the depth limit by search.
00683                     m_reachedDepthLimit = true;
00684                     // Update bounds with data from cache.
00685                     data.AdjustBounds(&alpha, &beta);
00686 
00687                     if (alpha >= beta)
00688                     {
00689                         *isExactValue = data.IsExactValue();
00690                         stack.Clear();
00691                         if (tryFirst != SG_NULLMOVE)
00692                             stack.Push(tryFirst);
00693                         if (TraceIsOn())
00694                             m_tracer->TraceValue(data.Value(), GetToPlay(),
00695                                            "Hash hit", *isExactValue);
00696                         return data.Value();
00697                     }
00698                 }
00699             }
00700 
00701             int delta = DEPTH_UNIT;
00702             if (  tryFirst != SG_NULLMOVE
00703                && CallExecute(tryFirst, &delta, depth)
00704                )
00705             {
00706                 bool childIsExact = true;
00707                 loValue = -SearchEngine(depth-delta, -beta, -alpha, stack,
00708                                         &childIsExact);
00709                 if (TraceIsOn())
00710                     m_tracer->TraceComment("tryFirst");
00711                 CallTakeBack();
00712                 hasMove = true;
00713                 if (m_aborted)
00714                 {
00715                     if (TraceIsOn())
00716                         m_tracer->TraceComment("aborted");
00717                     *isExactValue = false;
00718                     return (1 < m_currentDepth) ? alpha : loValue;
00719                 }
00720                 if (stack.NonEmpty())
00721                 {
00722                     opponentBest = stack.Top();
00723                     SG_ASSERT(opponentBest != SG_NULLMOVE);
00724                 }
00725                 stack.Push(tryFirst);
00726                 if (! childIsExact)
00727                    allExact = false;
00728                 if (loValue >= beta)
00729                 {
00730                     if (TraceIsOn())
00731                         m_tracer->TraceValue(loValue, GetToPlay());
00732                     // store in hash table. Known to be exact only if
00733                     // solved for one player.
00734                     bool isExact = SgSearchValue::IsSolved(loValue);
00735                     StoreHash(depth, loValue, tryFirst,
00736                               false /*isUpperBound*/,
00737                               true /*isLowerBound*/, isExact);
00738                     *isExactValue = isExact;
00739                     if (TraceIsOn())
00740                         m_tracer->TraceValue(loValue, GetToPlay(),
00741                                              "b-cut", isExact);
00742                     return loValue;
00743                 }
00744             }
00745         }
00746 
00747         // 'hiValue' is equal to 'beta' for alpha-beta algorithm, and gets set
00748         // to alpha+1 for Scout, except for the first move.
00749         int hiValue =
00750             (hasMove && m_useScout) ? max(loValue, alpha) + 1 : beta;
00751         bool foundCutoff = false;
00752         SgVector<SgMove> specialMoves;
00753          // Don't execute 'tryFirst' again.
00754         if (tryFirst != SG_NULLMOVE)
00755             specialMoves.PushBack(tryFirst);
00756  
00757         // Heuristic: "a good move for my opponent is a good move for me"
00758         if (  ! foundCutoff
00759            && m_useOpponentBest
00760            && opponentBest != SG_NULLMOVE
00761            && TrySpecialMove(opponentBest, specialMoves,
00762                                 depth, alpha, beta,
00763                                 loValue, hiValue, stack,
00764                              allExact, foundCutoff)
00765            )
00766             hasMove = true;
00767            
00768         if (  ! foundCutoff 
00769            && m_useKillers
00770            && m_currentDepth <= MAX_KILLER_DEPTH
00771            )
00772         {
00773             SgMove killer1 = m_killers[m_currentDepth].GetKiller1();
00774             if (  killer1 != SG_NULLMOVE
00775                && TrySpecialMove(killer1, specialMoves,
00776                                 depth, alpha, beta,
00777                                 loValue, hiValue, stack,
00778                              allExact, foundCutoff)
00779                )
00780                 hasMove = true;
00781             SgMove killer2 = m_killers[m_currentDepth].GetKiller2();
00782             if (  ! foundCutoff 
00783                && killer2 != SG_NULLMOVE
00784                && TrySpecialMove(killer2, specialMoves,
00785                                 depth, alpha, beta,
00786                                 loValue, hiValue, stack,
00787                              allExact, foundCutoff)
00788                )
00789                 hasMove = true;
00790         }
00791 
00792         // Generate the moves for this position.
00793         SgVector<SgMove> moves;
00794         if (! foundCutoff && ! m_aborted)
00795         {
00796             CallGenerate(&moves, depth);
00797             // Iterate through all the moves to find the best move and
00798             // correct value for this position.
00799             for (SgVectorIterator<SgMove> it(moves); it && ! foundCutoff; ++it)
00800             {
00801                 if (TryMove(*it, specialMoves,
00802                                 depth, alpha, beta,
00803                                 loValue, hiValue, stack,
00804                              allExact, foundCutoff)
00805                    )
00806                      hasMove = true;
00807                 if (! foundCutoff && m_aborted)
00808                 {
00809                     if (TraceIsOn())
00810                         m_tracer->TraceComment("ABORTED");
00811                     *isExactValue = false;
00812                     return (1 < m_currentDepth) ? alpha : loValue;
00813                 }
00814             }
00815         }
00816 
00817         // Make sure the move added to the hash table really got generated.
00818 #ifndef NDEBUG
00819         if (hasMove && stack.NonEmpty() && ! m_aborted)
00820         {
00821             SgMove bestMove = stack.Top();
00822             SG_ASSERT(bestMove != SG_NULLMOVE);
00823             SG_ASSERT(  specialMoves.Contains(bestMove)
00824                      || moves.Contains(bestMove)
00825                      );
00826         }
00827 #endif
00828     }
00829 
00830     bool isSolved = ! m_aborted;
00831     if (! m_aborted)
00832     {
00833         // Evaluate position if terminal node (either no moves generated, or
00834         // none of the generated moves were legal).
00835         bool solvedByEval = false;
00836         if (! hasMove)
00837         {
00838             m_stat.IncNumEvals();
00839             stack.Clear();
00840             loValue = CallEvaluate(depth, &solvedByEval);
00841         }
00842 
00843         // Save data about current position in the hash table.
00844         isSolved = solvedByEval
00845                 || SgSearchValue::IsSolved(loValue)
00846                 || (hasMove && allExact);
00847         // || EndOfGame(); bug: cannot store exact score after two passes.
00848         if (  m_hash
00849            && ! m_aborted
00850            && (isSolved || stack.NonEmpty())
00851            )
00852         {
00853             SgMove bestMove = SG_NULLMOVE;
00854             if (stack.NonEmpty())
00855             {
00856                 bestMove = stack.Top();
00857                 SG_ASSERT(bestMove != SG_NULLMOVE);
00858             }
00859             SG_ASSERT(alpha <= beta);
00860             StoreHash(depth, loValue, bestMove,
00861                       (loValue <= alpha) /* upper */,
00862                       (beta <= loValue) /* lower*/, isSolved);
00863         }
00864     }
00865 
00866     // If aborted search and didn't find any values, just return alpha.
00867     // Can't return best found so far, since may not have tried the optimal
00868     // counter-move yet. However, return best value found so far on top
00869     // level, since assuming hash move will have been tried first.
00870     if (m_aborted && (1 < m_currentDepth || loValue < alpha))
00871         loValue = alpha;
00872 
00873     *isExactValue = isSolved;
00874     if (TraceIsOn())
00875         m_tracer->TraceValue(loValue, GetToPlay(), 0, isSolved);
00876     SG_ASSERT(stack.IsEmpty() || stack.Top() != SG_NULLMOVE);
00877     return loValue;
00878 }
00879 
00880 void SgSearch::StartOfDepth(int depth)
00881 {
00882     if (DEBUG_SEARCH)
00883         SgDebug() << "SgSearch::StartOfDepth: " << depth << '\n';
00884     // add another separate search tree. We are either at the root of the
00885     // previous depth tree or, if depth==0, one level higher at the
00886     // linking node.
00887     if (m_tracer && ! m_aborted)
00888         m_tracer->StartOfDepth(depth);
00889 }
00890 
00891 //----------------------------------------------------------------------------
00892 


Sun Mar 13 2011 Doxygen 1.7.1