void GoRegionBoard::MergeAdjacentAndAddBlock(SgPoint move,
                                             SgBlackWhite capturedColor)
{
    SgVector<SgPoint> nb;
    for (GoNbIterator it(Board(), move); it; ++it)
        if (Board().IsEmpty(*it))
            nb.PushBack(*it);

    SgVectorOf<GoBlock> captures;
    PreviousBlocksAt(nb, capturedColor, &captures);
    SG_ASSERT(captures.NonEmpty());

    SgPointSet captured;
    {for (SgVectorIteratorOf<GoBlock> it(captures); it; ++it)
        captured |= (*it)->Stones();
    }
    SgVectorOf<GoRegion> adj;
    const int size = Board().Size();
    RegionsAt(captured.Border(size), capturedColor, &adj);
    SG_ASSERT(adj.NonEmpty());
    GoRegion* r = MergeAll(adj, captured, capturedColor);
    SG_UNUSED(r);

    for (SgVectorIteratorOf<GoBlock> it(captures); it; ++it)
        RemoveBlock(*it, true, false);
        // don't remove from regions; already gone.
}
Exemple #2
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;
}
SgVector<SgPoint> GoGtpCommandUtil::PointListArg(const GtpCommand& cmd,
                                               std::size_t number,
                                               const GoBoard& board)
{
    SgVector<SgPoint> result;
    for (size_t i = number; i < cmd.NuArg(); ++i)
        result.PushBack(PointArg(cmd, i, board));
    return result;
}
SgVector<SgPoint> GoGtpCommandUtil::GetHandicapStones(int size, int n)
{
    SgVector<SgPoint> stones;
    if (n == 0)
        return stones;
    // GTP locations are defined up to size 25, but SG_MAX_SIZE could be
    // smaller
    if (size > SG_MAX_SIZE || size > 25)
        throw GtpFailure("no standard handicap locations defined");
    int line1 = -1;
    int line2 = -1;
    int line3 = -1;
    if (size >= 13)
    {
        line1 = 4;
        line3 = size - 3;
    }
    else if (size >= 7)
    {
        line1 = 3;
        line3 = size - 2;
    }
    if (size >= 9 && size % 2 != 0)
        line2 = size / 2 + 1;
    if (line1 < 0 || n == 1 || n > 9 || (n > 4 && line2 < 0))
        throw GtpFailure("no standard handicap locations defined");
    if (n >= 1)
        stones.PushBack(Pt(line1, line1));
    if (n >= 2)
        stones.PushBack(Pt(line3, line3));
    if (n >= 3)
        stones.PushBack(Pt(line1, line3));
    if (n >= 4)
        stones.PushBack(Pt(line3, line1));
    if (n >= 5 && n % 2 != 0)
    {
        stones.PushBack(Pt(line2, line2));
        --n;
    }
    if (n >= 5)
        stones.PushBack(Pt(line1, line2));
    if (n >= 6)
        stones.PushBack(Pt(line3, line2));
    if (n >= 7)
        stones.PushBack(Pt(line2, line1));
    if (n >= 8)
        stones.PushBack(Pt(line2, line3));
    return stones;
}
void SgEvaluatedMovesArray::BestMoves(SgVector<SgPoint>& best, int nuMoves)
    const
{
    best.Clear();
    while (--nuMoves >= 0)
    {
        int nextBest = SelectNextBest(best);
        best.PushBack(nextBest);
    }
}
void GoGtpCommandUtil::ParseMultiStoneArgument(GtpCommand& cmd,
                                               const GoBoard& board,
                                               SgBlackWhite& toPlay,
                                               SgBlackWhite& defender,
                                               SgVector<SgPoint>& crucial)
{
    toPlay = GoGtpCommandUtil::BlackWhiteArg(cmd, 0);
    SgDebug() << "Set " << SgBW(toPlay) << " to play\n";
    SgPoint point = GoGtpCommandUtil::StoneArg(cmd, 1, board);
    defender = board.GetColor(point);
    SG_ASSERT(defender == SG_BLACK || defender == SG_WHITE);
    crucial.PushBack(point);
    for (size_t i = 2; i < cmd.NuArg(); ++i)
    {
        SgPoint p = GoGtpCommandUtil::StoneArg(cmd, i, board);
        if (board.GetColor(p) != defender)
            throw GtpFailure("Crucial stones must be same color");
        else
            crucial.PushBack(p);
    }
}
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);
}
Exemple #8
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);
        }
    }
}                           
void GoRegion::GetDivideMiaiPairs(SgVector<SgMiaiPair>& pairs) const
{
    SgVector<SgPoint> divPs;

    for (SgVectorIteratorOf<GoBlock> it(Blocks()); it; ++it)
    {
        SgVector<SgPoint> libs, temp;
        InsideLibs(*it, &libs);
        SgMiaiPair p1;
        SgPoint a = -1;

        // only find miaipairs from libs (empty points)
        for (SgVectorIterator<SgPoint> it2(libs); it2; ++it2)
        {
            if (IsSplitPt(*it2, Points()))
                temp.PushBack(*it2);
        }
        temp.Sort();
        divPs.PushBackList(temp);

        for (SgVectorIterator<SgPoint> it2(temp); it2; ++it2)
        {
            if (a == -1)
                a = (*it2);
            else
            {
                if (SgPointUtil::AreAdjacent(a, *it2))
                {
                    p1.first = a;
                    p1.second = *it2;
                    pairs.PushBack(p1);
                }
                a = *it2;
            }
        }

    } // found miaipairs for each block

    if (WRITEDEBUG)
    {
        SgDebug() << SgWritePointList(divPs, "divPs: ", true);
        for (SgVectorIterator<SgMiaiPair> it(pairs); it; ++it)
        {
            SgDebug() << "Pair(1: " << SgWritePoint((*it).first)
            << " 2: " << SgWritePoint((*it).second) << ")\n";
        }
    }
}
void GoRegionBoard::FindNewNeighborRegions(SgPoint move,
                                                BlackWhite moveColor)
{ // move was capture -> new region for each captured block.

    SgVector<SgPoint> nb;
    for (Nb4Iterator it(move); it; ++it)
        if (Board().IsEmpty(*it))
            nb.PushBack(*it);

    SgVectorOf<GoBlock> captures;
    PreviousBlocksAt(nb, OppBW(moveColor), &captures);
    SG_ASSERT(captures.NonEmpty());

    for (SgVectorIteratorOf<GoBlock> it(captures); it; ++it)
        BlockToRegion(*it);
}
Exemple #11
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;
}
Exemple #12
0
void GoGame::InitHandicap(const GoRules& rules, SgNode* root)
{
    // TODO: Use PlaceHandicap() in implementation of InitHandicap() to
    // avoid redundancy

    // Add handicap properties.
    if (2 <= rules.Handicap())
    {
        SgPropInt* handicap =
            new SgPropInt(SG_PROP_HANDICAP, rules.Handicap());
        root->Add(handicap);
        if (rules.JapaneseHandicap())
        {
            if (9 <= m_board.Size())
            {
                int h = rules.Handicap();
                int half = (m_board.Size()+1) / 2;
                SgVector<SgPoint> stones;
                if ((4 < h) && (h % 2 != 0))
                {
                    stones.PushBack(SgPointUtil::Pt(half, half));
                    --h;
                }
                if (13 <= m_board.Size())
                {
                    AddHandicap(m_board.Size(), 4, 4, &h, &stones);
                    if (0 < h)
                        AddHandicap(m_board.Size(), half, 4, &h, &stones);
                    if (0 < h)
                        AddHandicap(m_board.Size(), 3, 3, &h, &stones);
                    if (0 < h)
                        AddHandicap(m_board.Size(), 7, 7, &h, &stones);
                    if (0 < h)
                        AddHandicap(m_board.Size(), half, 3, &h, &stones);
                    if (0 < h)
                        AddHandicap(m_board.Size(), half - (half - 4) / 2,
                                    4, &h, &stones);
                    if (0 < h)
                        AddHandicap(m_board.Size(), half + (half - 4) / 2,
                                    4, &h, &stones);
                }
                else
                {
                    AddHandicap(m_board.Size(), 3, 3, &h, &stones);
                    if (0 < h)
                        AddHandicap(m_board.Size(), half, 3, &h, &stones);
                    if (0 < h)
                        AddHandicap(m_board.Size(), 4, 4, &h, &stones);
                }
                SgPropAddStone* addBlack =
                    new SgPropAddStone(SG_PROP_ADD_BLACK, stones);
                root->Add(addBlack);

                // White to play.
                SgPropPlayer* player =
                    new SgPropPlayer(SG_PROP_PLAYER, SG_WHITE);
                root->Add(player);
            }
        }
        else
        {
            // Chinese handicap.
            SgPropInt* chinese =
                new SgPropInt(SG_PROP_CHINESE, rules.Handicap());
            root->Add(chinese);
        }
    }
}
// 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;
}
Exemple #14
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;
}
Exemple #15
0
bool GoEyeUtil::NumberOfMoveToEye2(const GoBoard& board, SgBlackWhite color,
                                   SgPoint p, int& nummoves)
{
    nummoves = 0;
    bool capturing = false;
    SgVector<SgPoint> usedpoints;
    usedpoints.PushBack(p);
    SgPointSet counted;

    // Can never turn own stone into an eye
    if (board.IsColor(p, color))
        return false;
    
    // If opponent stone then it must be captured to make eye
    if (board.IsColor(p, SgOppBW(color)))
    {
        capturing = true;
    
        // If it is obviously safe then it can never be an eye
        if (SinglePointSafe2(board, p)) // Quick, naive safety test
            return false;

        for (GoBoard::LibertyIterator libit(board, p); libit; ++libit)
            counted.Include(*libit);
    }

    // Count immediate adjacencies
    for (SgNb4Iterator nb(p); nb; ++nb)
    {
        SgPoint adj = *nb;
        
        // Empty points must be filled
        if (board.IsColor(adj, SG_EMPTY))
        {
            counted.Include(adj);
        }
        
        // If adjacent opponent then can never be an eye
        else if (board.IsColor(adj, SgOppBW(color)))
        {
            if (capturing)
                counted.Include(adj); // must capture and then fill
            else
                return false;
        }
    }

    // Up to one diagonal can be ignored: estimate most costly
    SgPoint toignore = SG_NULLPOINT;
    int maxcost = 0;
    int infcost = 1000;
    if (board.Line(p) > 1)
    {
        for (SgNb4DiagIterator nbd(p); nbd; ++nbd)
        {
            SgPoint diag = *nbd;
            int cost = 0;

            if (  board.IsColor(diag, SG_EMPTY)
               && ! IsSinglePointEye2(board, diag, color, usedpoints))
            {
                cost = 1;
            }
            
            else if (board.IsColor(diag, SgOppBW(color)))
            {
                // quick safety test
                if (SinglePointSafe2(board, diag))
                    cost = infcost;
                else
                    cost = board.NumLiberties(diag);
            }

            if (cost > maxcost)
            {
                maxcost = cost;
                toignore = diag;
            }
        }
    }

    // Now mark points that must be played to secure diagonals
    for (SgNb4DiagIterator nbd(p); nbd; ++nbd)
    {
        SgPoint diag = *nbd;
        if (diag == toignore)
            continue;
        
        // Empty points must be filled (unless they are eyes)
        if (  board.IsColor(diag, SG_EMPTY)
           && ! IsSinglePointEye2(board, diag, color, usedpoints))
        {
            counted.Include(diag);
        }
        
        // Opponent stones on diagonals must be captured and filled
        else if (board.IsColor(diag, SgOppBW(color)))
        {
            if (SinglePointSafe2(board, diag))
                return false;
            else
            {
                counted.Include(diag);
                for (GoBoard::LibertyIterator libit(board, diag); libit;
                     ++libit)
                    counted.Include(*libit);
            }
        }
    }

    nummoves = counted.Size();
    return true;
}
Exemple #16
0
/** Main ladder routine */
int GoLadder::Ladder(const GoBoard& bd, SgPoint prey, SgBlackWhite toPlay,
                     SgVector<SgPoint>* sequence, bool twoLibIsEscape)
{
    GoModBoard modBoard(bd);
    m_bd = &modBoard.Board();
    InitMaxMoveNumber();
    if (sequence)
        sequence->Clear();
    if (! m_bd->Occupied(prey))
        return 0;
    if (CheckMoveOverflow())
        return GOOD_FOR_PREY;
    int result = 0;
    m_preyColor = m_bd->GetStone(prey);
    m_hunterColor = SgOppBW(m_preyColor);
    int numLib = m_bd->NumLiberties(prey);
    if (2 < numLib)
        result = GOOD_FOR_PREY;
    else
    {
        GoBoard::LibertyIterator libit(*m_bd, prey);
        SgPoint lib1 = *libit;
        m_partOfPrey.Clear();
        MarkStonesAsPrey(prey);
        GoPointList adjBlk = GoBoardUtil::AdjacentStones(*m_bd, prey);
        FilterAdjacent(adjBlk);
        if (toPlay == m_preyColor)
        {
            if (numLib == 1)
                result = PreyLadder(0, lib1, adjBlk, sequence);
            else if (twoLibIsEscape) // prey to play, numLib >= 2
                // For example, Explorer cannot treat this case as a ladder,
                // it messes up the logic
                result = GOOD_FOR_PREY;
            else
            {
                // Prey to play, two liberties. This is usually good, but
                // need to prove that there is some move that really
                //  escapes laddercapture.
                // Try liberties of adjacent blocks with at most two
                // liberties, try lib1 and lib2, and try moves one away
                // from the two liberties.
                // Good example with three blocks that test this case:
                // (;GM[1]SZ[19]FF[3]
                // AB[qa][pa][pb][pd][pc][qe][re][rd][rc][se]
                // AW[pe][pf][qf][qd][qc][rb][qb][sa][sc][rf][rg][sg])
                SgVector<SgPoint> movesToTry;

                // Liberties of adj. blocks with at most two liberties.
                adjBlk = GoBoardUtil::AdjacentStones(*m_bd, prey);
                ReduceToBlocks(adjBlk);
                for (GoPointList::Iterator iterAdj(adjBlk); iterAdj;
                     ++iterAdj)
                {
                    SgPoint block = *iterAdj;
                    SG_ASSERT(m_bd->IsColor(block, m_hunterColor));
                    SG_ASSERT(BlockIsAdjToPrey(block, 1));
                    if (m_bd->NumLiberties(block) <= 2)
                        for (GoBoard::LibertyIterator it(*m_bd, block); it;
                             ++it)
                            movesToTry.PushBack(*it);
                }

                // Liberties of blocks.
                ++libit;
                SgPoint lib2 = *libit;
                movesToTry.PushBack(lib1);
                movesToTry.PushBack(lib2);

                // Moves one away from liberties.
                SgVector<SgPoint> neighbors;
                NeighborsOfColor(*m_bd, lib1, SG_EMPTY, &neighbors);
                movesToTry.Concat(&neighbors);
                NeighborsOfColor(*m_bd, lib2, SG_EMPTY, &neighbors);
                movesToTry.Concat(&neighbors);

                // Try whether any of these moves lead to escape.
                for (SgVectorIterator<SgPoint> it(movesToTry); it; ++it)
                {
                    if (PlayIfLegal(*m_bd, *it, m_preyColor))
                    {
                        if (Ladder(bd, prey, m_hunterColor, 0, twoLibIsEscape)
                            > 0)
                        {
                            if (sequence)
                                sequence->PushBack(*it); 
                            result = GOOD_FOR_PREY;
                        }
                        m_bd->Undo();
                    }
                    if (result != 0)
                        break;
                }

                // If none of those moves worked, prey can't escape.
                // This is a bit pessimistic, there may be other moves
                // that do lead to escape (e.g. approach moves), but
                // ladder algorithm doesn't know about those.
                if (result == 0)
                    result = GOOD_FOR_HUNTER;
            }
        }
        else
        {
            if (IsSnapback(prey))
                result = GOOD_FOR_PREY;
            else
            {
                ++libit;
                if (libit) // two liberties
                    result = HunterLadder(0, lib1, *libit, adjBlk, sequence);
                else // one liberty
                    result = HunterLadder(0, lib1, adjBlk, sequence);
            }
        }
    }
    if (sequence)
        sequence->Reverse(); // built as a stack, with first move at end.
    return result;
}
Exemple #17
0
/** Play prey move and update all the relevant information.
    Extend the prey by playing at its only liberty, or capture a block
    adjacent to the prey. */
int GoLadder::PlayPreyMove(int depth, SgPoint move, SgPoint lib1,
                           const GoPointList& adjBlk,
                           SgVector<SgPoint>* sequence)
{
    int result = 0;
    GoPointList newAdj(adjBlk);
    SgVector<SgPoint> newLib;
    SgVector<SgPoint> newStones;
    SgVector<SgPoint> neighbors;
    if (move == lib1)
    {
        NeighborsOfColor(*m_bd, move, m_preyColor, &neighbors);
        for (SgVectorIterator<SgPoint> iter(neighbors); iter; ++iter)
        {
            SgPoint block = *iter;
            if (! m_partOfPrey[block])
            {
                MarkStonesAsPrey(block, &newStones);
                GoPointList temp =
                    GoBoardUtil::AdjacentStones(*m_bd, block);
                newAdj.PushBackList(temp);
                for (GoBoard::LibertyIterator it(*m_bd, block); it; ++it)
                    newLib.Include(*it);
            }
        }
        m_partOfPrey.Include(move);
    }
    if (PlayIfLegal(*m_bd, move, m_preyColor))
    {
        if (move == lib1)
        {
            NeighborsOfColor(*m_bd, move, SG_EMPTY, &neighbors);
            for (SgVectorIterator<SgPoint> iter(newLib); iter; ++iter)
            {
                SgPoint point = *iter;
                // Test for Empty is necessary because newLib will include
                // the move just played.
                if (m_bd->IsEmpty(point))
                    neighbors.Include(point);
            }
        }
        else
        {
            neighbors.PushBack(lib1);
        }
        if (m_bd->CapturingMove())
        {   // Add the points at the captured stones that are adjacent to the
            // prey to the liberties, at least if exactly one stone captured.
            for (GoPointList::Iterator it(m_bd->CapturedStones()); it;
                 ++it)
            {
                SgPoint stone = *it;
                if (PointIsAdjToPrey(stone))
                    neighbors.Include(stone);
            }
        }
        SG_ASSERT(! neighbors.IsEmpty());
        lib1 = neighbors[0];
        SG_ASSERT(m_bd->IsEmpty(lib1));
        SgArrayList<SgPoint,4> temp =
            NeighborsOfColor(*m_bd, move, m_hunterColor);
        newAdj.PushBackList(temp);
        FilterAdjacent(newAdj);

        if (neighbors.Length() == 1)
            result = HunterLadder(depth + 1, lib1, newAdj, sequence);
        else if (neighbors.Length() == 2)
        {
            SgPoint lib2 = neighbors[1];
            SG_ASSERT(m_bd->IsEmpty(lib2));
            result = HunterLadder(depth + 1, lib1, lib2, newAdj, sequence);
        } 
        else // 3 <= numLib
        {
            if (sequence)
                sequence->Clear();
            result = GOOD_FOR_PREY - (depth + 1);
        }
        if (sequence)
            sequence->PushBack(move);
        m_bd->Undo();
    }
    else
    {
        if (sequence)
            sequence->Clear();
        result = GOOD_FOR_HUNTER + depth;
    }
    m_partOfPrey.Exclude(move);
    m_partOfPrey.Exclude(newStones);

    return result;
}