Example #1
0
bool GoEyeUtil::IsSinglePointEye2(const GoBoard& board, SgPoint p, 
                                  SgBlackWhite color, SgVector<SgPoint>& eyes)
{
    // Must be an empty point
    if (! board.IsColor(p, SG_EMPTY))
        return false;
    // All adjacent neighbours must be friendly
    SgBoardColor opp = SgOppBW(color);
    for (SgNb4Iterator adj(p); adj; ++adj)
    {
        SgBoardColor adjColor = board.GetColor(*adj);
        if (adjColor == opp || adjColor == SG_EMPTY)
            return false;
    }
    // All diagonals (with up to one exception) must be friendly or an eye
    int baddiags = 0;
    int maxbaddiags = (board.Line(p) == 1 ? 0 : 1);
    for (SgNb4DiagIterator it(p); it; ++it)
    {
        if (board.IsColor(*it, opp))
            ++baddiags;
        if (board.IsColor(*it, SG_EMPTY) && ! eyes.Contains(*it))
        {
            // Assume this point is an eye and recurse
            eyes.PushBack(p);
            if (! IsSinglePointEye2(board, *it, color, eyes))
                ++baddiags;
            eyes.PopBack();
        }
        if (baddiags > maxbaddiags)
            return false;
    }
    return true;
}
bool GoRegion::AdjacentToSomeBlock(const SgVector<SgPoint>& anchors) const
{
    for (SgVectorIteratorOf<GoBlock> it(m_blocks); it; ++it)
    {
        if (anchors.Contains((*it)->Anchor()))
            /* */ return true; /* */
    }
    return false;
}
void SgVectorUtil::Intersection(SgVector<int>* vector,
                                const SgVector<int>& vector2)
{
     SgVector<int> newVector;
     for (SgVectorIterator<int> it(*vector); it; ++it)
     {
         // @todo speed up by hash tags, if used in time-critical code
         if (vector2.Contains(*it))
             newVector.PushBack(*it);
     }
     newVector.SwapWith(vector);
}
SgPoint SgEvaluatedMovesArray::SelectNextBest(SgVector<SgPoint>& bestSoFar)
    const
{
    int bestValue = s_minValue; SgPoint best = 0;
    for (SgPoint p = 0; p < SG_MAXPOINT; ++p)
    {
        if ((m_value[p] > bestValue) && ! bestSoFar.Contains(p))
        {
            bestValue = m_value[p];
            best = p;
        }
    }
    return best;
}
Example #5
0
bool SgSearch::TrySpecialMove(SgMove move, SgVector<SgMove>& specialMoves,
                              const int depth,
                              const int alpha, const int beta,
                              int& loValue, int& hiValue,
                              SgSearchStack& stack,
                              bool& allExact,
                              bool& isCutoff)

{
    if (specialMoves.Contains(move))
        return false;
    bool executed = TryMove(move, specialMoves,
                            depth, alpha, beta,
                            loValue, hiValue, stack,
                            allExact, isCutoff);
    specialMoves.PushBack(move);
    return executed;
}
bool GoRegion::Has2IntersectionPoints(const SgVector<SgPoint>& usedLibs) const
{
    SgVector<SgPoint> jointLibs;
    JointLibs(&jointLibs);
    if (jointLibs.MinLength(2))
    {
        // check if libs are intersection pts
        int nuIPs = 0;
        // doesn't have to adjacent to all interior points! 2005/08
        for (SgVectorIterator<SgPoint> it(jointLibs); it; ++it)
        {
            if (IsSplitPt(*it, Points()) && ! usedLibs.Contains(*it))
            {
                if (++nuIPs >= 2)
                    return true;
            }
        }
    }
    return false;
}
Example #7
0
void GoLadderUtil::FindLadderEscapeMoves(const GoBoard& bd, SgPoint prey, 
                           SgVector<SgPoint>& escapeMoves)
{
    SG_ASSERT(bd.NumLiberties(prey) == 1);
    SG_ASSERT(escapeMoves.IsEmpty());
    
    SgPoint p = bd.TheLiberty(prey);
    SgVector<SgPoint> candidates;
    candidates.PushBack(p);
    if (IsLadderEscapeMove(bd, prey, p))
    	escapeMoves.PushBack(p);
    for (GoAdjBlockIterator<GoBoard> it(bd, prey, 1); it; ++it)
    {
        // check if prey can escape by capturing *it on p.
        SgPoint p = bd.TheLiberty(*it);
        if (! candidates.Contains(p))
        {
		    candidates.PushBack(p);
        	if (IsLadderEscapeMove(bd, prey, p))
            	escapeMoves.PushBack(p);
        }
    }
}                           
// improved by using recursive extension to find 2-conn paths.
bool GoRegion::Find2ConnForAllInterior(SgMiaiStrategy* miaiStrategy,
                                       SgVector<SgPoint>& usedLibs) const
{
    SgVector<SgMiaiPair> myStrategy;
    const int size = m_bd.Size();
    SgPointSet interior = AllInsideLibs();
    if (interior.IsEmpty())
    {
        return true;
    }
    //if (GetFlag(GO_REGION_SINGLE_BLOCK_BOUNDARY))
    {
        SgPointSet testSet = interior;
        SgPointSet originalLibs = testSet.Border(size) & Dep().Border(size)
                                & m_bd.AllEmpty() & Points();
        SgPointSet updateLibs = originalLibs;

        // now try to find miai-paths to remaining interior points recursively
        bool changed = true;
        while (changed)
        {
            changed = false;
            if (testSet.IsEmpty())
            {
                SgVector<SgPoint> jlibs;
                JointLibs(&jlibs);
                SgVector<SgPoint> ips;
                GetIPs(&ips);
                SgVector<SgMiaiPair> updateStrg;

                for (SgSetIterator it(interior); it; ++it)
                {
                    SgPoint p = *it;
                    SgPointSet s1;
                    s1.Include(p);
                    SgPointSet rest = s1.Border(size) & updateLibs;
                    if (! rest.IsEmpty())
                    {
                        for (SgVectorIterator<SgMiaiPair> it2(myStrategy);
                             it2; ++it2)
                        {
                            SgMiaiPair x = *it2;
                            if (   SgPointUtil::AreAdjacent(p, x.first)
                                && SgPointUtil::AreAdjacent(p, x.second)
                               )
                            {
                                if (ips.Contains(x.first))
                                {
                                    updateLibs.Include(x.first);
                                    usedLibs.Exclude(x.first);
                                    SgPoint t = rest.PointOf();
                                    x.first = t;
                                    updateLibs.Exclude(t);
                                    rest.Exclude(t);
                                    usedLibs.Include(t);
                                }
                                if (  ips.Contains(x.second)
                                   && ! rest.IsEmpty()
                                   )
                                {
                                    updateLibs.Include(x.second);
                                    usedLibs.Exclude(x.second);
                                    SgPoint t = rest.PointOf();
                                    x.second = t;
                                    updateLibs.Exclude(t);
                                    rest.Exclude(t);
                                    usedLibs.Include(t);
                                }
                                updateStrg.Include(x);
                            }
                        }
                    }
                }
                miaiStrategy->SetStrategy(updateStrg);
                /* */ return true; /* */
            }
            for (SgSetIterator it(interior); it; ++it)
            {
                SgMiaiPair miaiPair;
                if (Find2BestLibs(*it, updateLibs, testSet, &miaiPair))
                {
                    if (miaiPair.first == miaiPair.second)
                    {
                        SgDebug() <<"\nmiaipair are same: "
                                  << SgWritePoint(miaiPair.first)
                                  << SgWritePoint(miaiPair.second);
                        SgDebug() <<"\ncurrent region is:\n";
                        Points().Write(SgDebug(), size);
                        SG_ASSERT(false);
                    }
                    myStrategy.PushBack(miaiPair);
                    usedLibs.PushBack(miaiPair.first);
                    usedLibs.PushBack(miaiPair.second);
                    updateLibs.Exclude(miaiPair.first);
                    updateLibs.Exclude(miaiPair.second);
                    updateLibs.Include(*it);
                    testSet.Exclude(*it);
                    changed = true;
                }
            }
        } // while  loop for recursive finding
    }
    miaiStrategy->Clear();
    return false;
}
Example #9
0
int SgSearch::SearchEngine(int depth, int alpha, int beta,
                           SgSearchStack& stack, bool* isExactValue,
                           bool lastNullMove)
{
    SG_ASSERT(stack.IsEmpty() || stack.Top() != SG_NULLMOVE);
    SG_ASSERT(alpha < beta);

    // Only place we check whether the search has been newly aborted. In all
    // other places, just check whether search was aborted before.
    // AR: what to return here?
    // if - (SG_INFINITY-1), then will be positive on next level?
    if (AbortSearch())
    {
        *isExactValue = false;
        return alpha;
    }

    // Null move pruning
    if (  m_useNullMove
            && depth > 0
            && ! lastNullMove
            && NullMovePrune(depth, DEPTH_UNIT * (1 + m_nullMoveDepth), beta)
       )
    {
        *isExactValue = false;
        return beta;
    }

    // ProbCut
    if (m_probcut && m_probcut->IsEnabled())
    {
        int probCutValue;
        if (m_probcut->ProbCut(*this, depth, alpha, beta, stack,
                               isExactValue, &probCutValue)
           )
            return probCutValue;
    }

    m_stat.IncNumNodes();
    bool hasMove = false; // true if a move has been executed at this level
    int loValue = -(SG_INFINITY - 1);
    m_reachedDepthLimit = m_reachedDepthLimit || (depth <= 0);

    // check whether position is solved from hash table.
    SgSearchHashData data; // initialized to ! data.IsValid()
    if (LookupHash(data))
    {
        if (data.IsExactValue()) // exact value: stop search
        {
            *isExactValue = true;
            stack.Clear();
            if (data.BestMove() != SG_NULLMOVE)
                stack.Push(data.BestMove());
            if (TraceIsOn())
                m_tracer->TraceValue(data.Value(), GetToPlay(),
                                     "exact-hash", true);
            return data.Value();
        }
    }

    bool allExact = true; // Do all moves have exact evaluation?
    if (depth > 0 && ! EndOfGame())
    {
        // Check whether current position has already been encountered.
        SgMove tryFirst = SG_NULLMOVE;
        SgMove opponentBest = SG_NULLMOVE;
        if (data.IsValid())
        {
            if (data.Depth() > 0)
            {
                tryFirst = data.BestMove();
                SG_ASSERT(tryFirst != SG_NULLMOVE);
            }

            // If data returned from hash table is based on equal or deeper
            // search than what we plan to do right now, just use that data.
            // The hash table may have deeper data for the current position
            // since the same number of moves may result in more 'depth'
            // left if the 'delta' for the moves has been smaller, which
            // will happen when most moves came from the cache.
            if (depth <= data.Depth())
            {
                // Rely on value returned from hash table to be for the
                // current position. In Go, it can happen that the move is
                // not legal (ko recapture)
                int delta = DEPTH_UNIT;
                bool canExecute = CallExecute(tryFirst, &delta, depth);
                if (canExecute)
                    CallTakeBack();
                else
                    tryFirst = SG_NULLMOVE;
                if (tryFirst != SG_NULLMOVE || data.IsExactValue())
                {
                    // getting a deep enough hash hit or an exact value
                    // is as good as reaching the depth limit by search.
                    m_reachedDepthLimit = true;
                    // Update bounds with data from cache.
                    data.AdjustBounds(&alpha, &beta);

                    if (alpha >= beta)
                    {
                        *isExactValue = data.IsExactValue();
                        stack.Clear();
                        if (tryFirst != SG_NULLMOVE)
                            stack.Push(tryFirst);
                        if (TraceIsOn())
                            m_tracer->TraceValue(data.Value(), GetToPlay(),
                                                 "Hash hit", *isExactValue);
                        return data.Value();
                    }
                }
            }

            int delta = DEPTH_UNIT;
            if (  tryFirst != SG_NULLMOVE
                    && CallExecute(tryFirst, &delta, depth)
               )
            {
                bool childIsExact = true;
                loValue = -SearchEngine(depth-delta, -beta, -alpha, stack,
                                        &childIsExact);
                if (TraceIsOn())
                    m_tracer->TraceComment("tryFirst");
                CallTakeBack();
                hasMove = true;
                if (m_aborted)
                {
                    if (TraceIsOn())
                        m_tracer->TraceComment("aborted");
                    *isExactValue = false;
                    return (1 < m_currentDepth) ? alpha : loValue;
                }
                if (stack.NonEmpty())
                {
                    opponentBest = stack.Top();
                    SG_ASSERT(opponentBest != SG_NULLMOVE);
                }
                stack.Push(tryFirst);
                if (! childIsExact)
                    allExact = false;
                if (loValue >= beta)
                {
                    if (TraceIsOn())
                        m_tracer->TraceValue(loValue, GetToPlay());
                    // store in hash table. Known to be exact only if
                    // solved for one player.
                    bool isExact = SgSearchValue::IsSolved(loValue);
                    StoreHash(depth, loValue, tryFirst,
                              false /*isUpperBound*/,
                              true /*isLowerBound*/, isExact);
                    *isExactValue = isExact;
                    if (TraceIsOn())
                        m_tracer->TraceValue(loValue, GetToPlay(),
                                             "b-cut", isExact);
                    return loValue;
                }
            }
        }

        // 'hiValue' is equal to 'beta' for alpha-beta algorithm, and gets set
        // to alpha+1 for Scout, except for the first move.
        int hiValue =
            (hasMove && m_useScout) ? max(loValue, alpha) + 1 : beta;
        bool foundCutoff = false;
        SgVector<SgMove> specialMoves;
        // Don't execute 'tryFirst' again.
        if (tryFirst != SG_NULLMOVE)
            specialMoves.PushBack(tryFirst);

        // Heuristic: "a good move for my opponent is a good move for me"
        if (  ! foundCutoff
                && m_useOpponentBest
                && opponentBest != SG_NULLMOVE
                && TrySpecialMove(opponentBest, specialMoves,
                                  depth, alpha, beta,
                                  loValue, hiValue, stack,
                                  allExact, foundCutoff)
           )
            hasMove = true;

        if (  ! foundCutoff
                && m_useKillers
                && m_currentDepth <= MAX_KILLER_DEPTH
           )
        {
            SgMove killer1 = m_killers[m_currentDepth].GetKiller1();
            if (  killer1 != SG_NULLMOVE
                    && TrySpecialMove(killer1, specialMoves,
                                      depth, alpha, beta,
                                      loValue, hiValue, stack,
                                      allExact, foundCutoff)
               )
                hasMove = true;
            SgMove killer2 = m_killers[m_currentDepth].GetKiller2();
            if (  ! foundCutoff
                    && killer2 != SG_NULLMOVE
                    && TrySpecialMove(killer2, specialMoves,
                                      depth, alpha, beta,
                                      loValue, hiValue, stack,
                                      allExact, foundCutoff)
               )
                hasMove = true;
        }

        // Generate the moves for this position.
        SgVector<SgMove> moves;
        if (! foundCutoff && ! m_aborted)
        {
            CallGenerate(&moves, depth);
            // Iterate through all the moves to find the best move and
            // correct value for this position.
            for (SgVectorIterator<SgMove> it(moves); it && ! foundCutoff; ++it)
            {
                if (TryMove(*it, specialMoves,
                            depth, alpha, beta,
                            loValue, hiValue, stack,
                            allExact, foundCutoff)
                   )
                    hasMove = true;
                if (! foundCutoff && m_aborted)
                {
                    if (TraceIsOn())
                        m_tracer->TraceComment("ABORTED");
                    *isExactValue = false;
                    return (1 < m_currentDepth) ? alpha : loValue;
                }
            }
        }

        // Make sure the move added to the hash table really got generated.
#ifndef NDEBUG
        if (hasMove && stack.NonEmpty() && ! m_aborted)
        {
            SgMove bestMove = stack.Top();
            SG_ASSERT(bestMove != SG_NULLMOVE);
            SG_ASSERT(  specialMoves.Contains(bestMove)
                        || moves.Contains(bestMove)
                     );
        }
#endif
    }

    bool isSolved = ! m_aborted;
    if (! m_aborted)
    {
        // Evaluate position if terminal node (either no moves generated, or
        // none of the generated moves were legal).
        bool solvedByEval = false;
        if (! hasMove)
        {
            m_stat.IncNumEvals();
            stack.Clear();
            loValue = CallEvaluate(depth, &solvedByEval);
        }

        // Save data about current position in the hash table.
        isSolved = solvedByEval
                   || SgSearchValue::IsSolved(loValue)
                   || (hasMove && allExact);
        // || EndOfGame(); bug: cannot store exact score after two passes.
        if (  m_hash
                && ! m_aborted
                && (isSolved || stack.NonEmpty())
           )
        {
            SgMove bestMove = SG_NULLMOVE;
            if (stack.NonEmpty())
            {
                bestMove = stack.Top();
                SG_ASSERT(bestMove != SG_NULLMOVE);
            }
            SG_ASSERT(alpha <= beta);
            StoreHash(depth, loValue, bestMove,
                      (loValue <= alpha) /* upper */,
                      (beta <= loValue) /* lower*/, isSolved);
        }
    }

    // If aborted search and didn't find any values, just return alpha.
    // Can't return best found so far, since may not have tried the optimal
    // counter-move yet. However, return best value found so far on top
    // level, since assuming hash move will have been tried first.
    if (m_aborted && (1 < m_currentDepth || loValue < alpha))
        loValue = alpha;

    *isExactValue = isSolved;
    if (TraceIsOn())
        m_tracer->TraceValue(loValue, GetToPlay(), 0, isSolved);
    SG_ASSERT(stack.IsEmpty() || stack.Top() != SG_NULLMOVE);
    return loValue;
}
Example #10
0
bool SgSearch::TryMove(SgMove move, const SgVector<SgMove>& specialMoves,
                       const int depth,
                       const int alpha, const int beta,
                       int& loValue, int& hiValue,
                       SgSearchStack& stack,
                       bool& allExact,
                       bool& isCutoff)
{
    if (specialMoves.Contains(move)) // already tried move before
        return false;

    int delta = DEPTH_UNIT;
    if (! CallExecute(move, &delta, depth))
        return false;

    bool childIsExact = true;
    SgSearchStack newStack;
    int merit = -SearchEngine(depth - delta, -hiValue,
                              -max(loValue, alpha), newStack,
                              &childIsExact);
    if (loValue < merit && ! m_aborted) // new best move
    {
        loValue = merit;
        if (m_useScout)
        {
            // If getting a move that's better than what we have
            // so far, not good enough to cause a cutoff, was
            // searched with a narrow window, and doesn't
            // immediately lead to a terminal node, then search
            // again with a wide window to get a more precise
            // value.
            if (  alpha < merit
                    && merit < beta
                    && delta < depth
               )
            {
                childIsExact = true;
                loValue = -SearchEngine(depth-delta, -beta,
                                        -merit, newStack,
                                        &childIsExact);
            }
            hiValue = max(loValue, alpha) + 1;
        }
        stack.CopyFrom(newStack);
        stack.Push(move);
        SG_ASSERT(move != SG_NULLMOVE);
        if (m_currentDepth == 1 && ! m_aborted)
            m_foundNewBest = true;
    }
    if (! childIsExact)
        allExact = false;
    CallTakeBack();
    if (loValue >= beta)
    {
        // Move generated a cutoff: add this move to the list of
        // killers.
        if (m_useKillers && m_currentDepth <= MAX_KILLER_DEPTH)
            m_killers[m_currentDepth].MarkKiller(move);
        if (TraceIsOn())
            m_tracer->TraceComment("b-cut");
        isCutoff = true;
    }
    return true;
}