00001
00002
00003
00004
00005
00006
00007 #include "SgSystem.h"
00008 #include "SgSearch.h"
00009
00010 #include <algorithm>
00011 #include <iomanip>
00012 #include <limits>
00013 #include <sstream>
00014 #include <math.h>
00015 #include "SgDebug.h"
00016 #include "SgHashTable.h"
00017 #include "SgMath.h"
00018 #include "SgNode.h"
00019 #include "SgProbCut.h"
00020 #include "SgSearchControl.h"
00021 #include "SgSearchValue.h"
00022 #include "SgTime.h"
00023 #include "SgVector.h"
00024 #include "SgWrite.h"
00025
00026 using namespace std;
00027
00028
00029
00030 namespace{
00031 const bool DEBUG_SEARCH = false;
00032 const bool DEBUG_SEARCH_ITERATIONS = false;
00033 }
00034
00035
00036 void SgKiller::MarkKiller(SgMove killer)
00037 {
00038 if (killer == m_killer1)
00039 ++m_count1;
00040 else if (killer == m_killer2)
00041 {
00042 ++m_count2;
00043 if (m_count1 < m_count2)
00044 {
00045 swap(m_killer1, m_killer2);
00046 swap(m_count1, m_count2);
00047 }
00048 }
00049 else if (m_killer1 == SG_NULLMOVE)
00050 {
00051 m_killer1 = killer;
00052 m_count1 = 1;
00053 }
00054 else
00055 {
00056 m_killer2 = killer;
00057 m_count2 = 1;
00058 }
00059 }
00060
00061 void SgKiller::Clear()
00062 {
00063 m_killer1 = SG_NULLMOVE;
00064 m_count1 = 0;
00065 m_killer2 = SG_NULLMOVE;
00066 m_count2 = 0;
00067 }
00068
00069
00070
00071 bool SgSearchHashData::IsBetterThan(const SgSearchHashData& data) const
00072 {
00073 if (m_depth > data.m_depth)
00074 return true;
00075 if (m_depth < data.m_depth)
00076 return false;
00077 return (! m_isUpperBound && ! m_isLowerBound)
00078 || (m_isLowerBound && data.m_isLowerBound && m_value > data.m_value)
00079 || (m_isUpperBound && data.m_isUpperBound && m_value < data.m_value);
00080 }
00081
00082
00083
00084 namespace {
00085
00086
00087 void ReverseCopyStack(const SgSearchStack& moveStack, SgVector<SgMove>& sequence)
00088 {
00089 sequence.Clear();
00090 for (int i = moveStack.Size() - 1; i >= 0; --i)
00091 sequence.PushBack(moveStack[i]);
00092 }
00093
00094 void WriteSgSearchHashData(std::ostream& str, const SgSearch& search,
00095 const SgSearchHashData& data)
00096 {
00097 str << "move = " << search.MoveString(data.BestMove())
00098 << " exact = " << data.IsExactValue()
00099 << " value = " << data.Value()
00100 << '\n';
00101 }
00102
00103
00104 void WriteMoves(const SgSearch& search, const SgVector<SgMove>& sequence)
00105 {
00106 for (SgVectorIterator<SgMove> it(sequence); it; ++it)
00107 SgDebug() << ' ' << search.MoveString(*it);
00108 }
00109
00110 void PrintPV(const SgSearch& search, int depth, int value,
00111 const SgVector<SgMove>& sequence,
00112 bool isExactValue)
00113 {
00114 SgDebug() << "Iteration d = " << depth
00115 << ", value = " << value
00116 << ", exact = " << isExactValue
00117 << ", sequence = ";
00118 WriteMoves(search, sequence);
00119 SgDebug() << '\n';
00120 }
00121
00122 }
00123
00124
00125
00126 const int SgSearch::SG_INFINITY = numeric_limits<int>::max();
00127
00128 SgSearch::SgSearch(SgSearchHashTable* hash)
00129 : m_hash(hash),
00130 m_tracer(0),
00131 m_currentDepth(0),
00132 m_useScout(false),
00133 m_useKillers(false),
00134 m_useOpponentBest(0),
00135 m_useNullMove(0),
00136 m_nullMoveDepth(2),
00137 m_aborted(false),
00138 m_foundNewBest(false),
00139 m_reachedDepthLimit(false),
00140 m_stat(),
00141 m_timerLevel(0),
00142 m_control(0),
00143 m_probcut(0),
00144 m_abortFrequency(1)
00145 {
00146 InitSearch();
00147 }
00148
00149 SgSearch::~SgSearch()
00150 {
00151 }
00152
00153 void SgSearch::CallGenerate(SgVector<SgMove>* moves, int depth)
00154 {
00155 Generate(moves, depth);
00156 if (DEBUG_SEARCH)
00157 {
00158 SgDebug() << "SgSearch::CallGenerate: d=" << depth;
00159 WriteMoves(*this, *moves);
00160 SgDebug() << '\n';
00161 }
00162 }
00163
00164 void SgSearch::InitSearch(int startDepth)
00165 {
00166 m_currentDepth = startDepth;
00167 m_moveStack.Clear();
00168 m_moveStack.PushBack(SG_NULLMOVE);
00169 m_moveStack.PushBack(SG_NULLMOVE);
00170 if (m_useKillers)
00171 {
00172 for (int i = 0; i <= MAX_KILLER_DEPTH; ++i)
00173 m_killers[i].Clear();
00174 }
00175 }
00176
00177 bool SgSearch::LookupHash(SgSearchHashData& data) const
00178 {
00179 SG_ASSERT(! data.IsValid());
00180 if (m_hash == 0 || ! m_hash->Lookup(GetHashCode(), &data))
00181 return false;
00182 if (DEBUG_SEARCH)
00183 {
00184 SgDebug() << "SgSearch::LookupHash: " << GetHashCode() << ' ';
00185 WriteSgSearchHashData(SgDebug(), *this, data);
00186 }
00187 return true;
00188 }
00189
00190 void SgSearch::OnStartSearch()
00191 {
00192
00193 }
00194
00195 void SgSearch::SetSearchControl(SgSearchControl* control)
00196 {
00197 m_control = control;
00198 }
00199
00200 void SgSearch::SetProbCut(SgProbCut* probcut)
00201 {
00202 m_probcut = probcut;
00203 }
00204
00205 void SgSearch::StoreHash(int depth, int value, SgMove move,
00206 bool isUpperBound, bool isLowerBound, bool isExact)
00207 {
00208 SG_ASSERT(m_hash);
00209 SgSearchHashData data(depth, value, move, isUpperBound, isLowerBound,
00210 isExact);
00211 if (DEBUG_SEARCH)
00212 {
00213 SgDebug() << "SgSearch::StoreHash: " << GetHashCode()
00214 << ": ";
00215 WriteSgSearchHashData(SgDebug(), *this, data);
00216 }
00217 m_hash->Store(GetHashCode(), data);
00218 }
00219
00220 bool SgSearch::TraceIsOn() const
00221 {
00222 return m_tracer && m_tracer->TraceIsOn();
00223 }
00224
00225 bool SgSearch::AbortSearch()
00226 {
00227 if (! m_aborted)
00228 {
00229
00230
00231 if (m_stat.NumNodes() % m_abortFrequency != 0)
00232 return false;
00233 m_aborted =
00234 m_control
00235 && m_control->Abort(m_timer.GetTime(), m_stat.NumNodes());
00236 if (! m_aborted && SgUserAbort())
00237 m_aborted = true;
00238 if (m_aborted && TraceIsOn())
00239 m_tracer->TraceComment("aborted");
00240 }
00241 return m_aborted;
00242 }
00243
00244 bool SgSearch::NullMovePrune(int depth, int delta, int beta)
00245 {
00246 SgSearchStack ignoreStack;
00247 bool childIsExact = true;
00248 if (beta >= SG_INFINITY - 1)
00249 return false;
00250 if (CallExecute(SG_PASS, &delta, depth))
00251 {
00252 float nullvalue = -SearchEngine(depth - delta,
00253 -beta, -beta + 1, ignoreStack, &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
00344 CallTakeBack();
00345
00346
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
00354 int delta = DEPTH_UNIT;
00355 if (CallExecute(move, &delta, depth))
00356 ++numMovesToUndo;
00357 else
00358 SG_ASSERT(false);
00359 }
00360 else
00361 break;
00362 }
00363
00364
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
00436 m_aborted = false;
00437
00438
00439
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
00456 m_stat.SetDepthReached(m_depthLimit);
00457
00458
00459
00460 m_reachedDepthLimit = false;
00461 isExactValue = true;
00462 m_foundNewBest = false;
00463
00464
00465 value = DFS(0, m_depthLimit, boundLo, boundHi, sequence,
00466 &isExactValue);
00467
00468
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
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))
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)
00525 {
00526 loValue = merit;
00527 if (m_useScout)
00528 {
00529
00530
00531
00532
00533
00534
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
00559
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(const 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
00596
00597
00598
00599 if (AbortSearch())
00600 {
00601 *isExactValue = false;
00602 return alpha;
00603 }
00604
00605
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
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;
00628 int loValue = -(SG_INFINITY - 1);
00629 m_reachedDepthLimit = m_reachedDepthLimit || (depth <= 0);
00630
00631
00632 SgSearchHashData data;
00633 if (LookupHash(data))
00634 {
00635 if (data.IsExactValue())
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;
00649 if (depth > 0 && ! EndOfGame())
00650 {
00651
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
00663
00664
00665
00666
00667
00668 if (depth <= data.Depth())
00669 {
00670
00671
00672
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
00682
00683 m_reachedDepthLimit = true;
00684
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
00733
00734 bool isExact = SgSearchValue::IsSolved(loValue);
00735 StoreHash(depth, loValue, tryFirst,
00736 false ,
00737 true , 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
00748
00749 int hiValue =
00750 (hasMove && m_useScout) ? max(loValue, alpha) + 1 : beta;
00751 bool foundCutoff = false;
00752 SgVector<SgMove> specialMoves;
00753
00754 if (tryFirst != SG_NULLMOVE)
00755 specialMoves.PushBack(tryFirst);
00756
00757
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
00793 SgVector<SgMove> moves;
00794 if (! foundCutoff && ! m_aborted)
00795 {
00796 CallGenerate(&moves, depth);
00797
00798
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
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
00834
00835 bool solvedByEval = false;
00836 if (! hasMove)
00837 {
00838 m_stat.IncNumEvals();
00839 stack.Clear();
00840 loValue = CallEvaluate(depth, &solvedByEval);
00841 }
00842
00843
00844 isSolved = solvedByEval
00845 || SgSearchValue::IsSolved(loValue)
00846 || (hasMove && allExact);
00847
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) ,
00862 (beta <= loValue) , isSolved);
00863 }
00864 }
00865
00866
00867
00868
00869
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
00885
00886
00887 if (m_tracer && ! m_aborted)
00888 m_tracer->StartOfDepth(depth);
00889 }
00890
00891
00892