SgBWSet GoSafetyCommands::GetSafe(int& totalRegions, const string& type)
{
    GoModBoard modBoard(m_bd);
    GoBoard& bd = modBoard.Board();
    GoRegionBoard regionAttachment(bd);
    SgBWSet safe;
    if (type == "benson")
    {
        GoBensonSolver solver(bd, &regionAttachment);
        solver.FindSafePoints(&safe);
    }
    else if (type == "static")
    {
        GoSafetySolver solver(bd, &regionAttachment);
        solver.FindSafePoints(&safe);
    }
    else
        throw GtpFailure() << "invalid safety solver: " << type;
    SgPointSet proved = safe.Both();
    for (SgBWIterator it; it; ++it)
    {
        SgBlackWhite c = *it;
        for (SgVectorIteratorOf<GoRegion> it(regionAttachment.AllRegions(c));
             it; ++it)
            if ((*it)->Points().SubsetOf(proved))
                ++totalRegions;
    }
    return safe;
}
/** List of safe points.
    If no color is given, safe points of both colors are listed.
    Arguments: benson|static [black|white]<br>
    Returns: number of point followed bu list of points in one line.
*/
void GoSafetyCommands::CmdSafe(GtpCommand& cmd)
{
    cmd.CheckNuArgLessEqual(2);
    string type = cmd.Arg(0);
    int totalRegions = 0;
    SgBWSet safe = GetSafe(totalRegions, type);
    SgPointSet set =
        (cmd.NuArg() == 2 ? safe[BlackWhiteArg(cmd, 1)] : safe.Both());
    cmd << set.Size();
    for (SgSetIterator it(set); it; ++it)
        cmd << ' ' << SgWritePoint(*it);
}
vector<SgPoint> GoUctDefaultMoveFilter::Get()
{
    vector<SgPoint> rootFilter;
    SgBlackWhite toPlay = m_bd.ToPlay();
    SgBlackWhite opp = SgOppBW(toPlay);

    // Safe territory
    if (m_param.m_checkSafety)
    {
        GoModBoard modBoard(m_bd);
        GoBoard& bd = modBoard.Board();
        SgBWSet alternateSafe;
        bool isAllAlternateSafe = false;
        // Alternate safety is used to prune moves only in opponent territory
        // and only if everything is alive under alternate play. This ensures that
        // capturing moves that are not liberties of dead blocks and ko threats
        // will not be pruned. This alternate safety pruning is not going to
        // improve or worsen playing strength, but may cause earlier passes,
        // which is nice in games against humans
        GoSafetySolver safetySolver(bd);
        safetySolver.FindSafePoints(&alternateSafe);
        isAllAlternateSafe = (alternateSafe.Both() == bd.AllPoints());

        // Benson solver guarantees that capturing moves of dead blocks are
        // liberties of the dead blocks and that no move in Benson safe territory
        // is a ko threat
        GoBensonSolver bensonSolver(bd);
        SgBWSet unconditionalSafe;
        bensonSolver.FindSafePoints(&unconditionalSafe);

        for (GoBoard::Iterator it(bd); it; ++it)
        {
            SgPoint p = *it;
            if (m_bd.IsLegal(p))
            {
                bool isUnconditionalSafe = unconditionalSafe[toPlay].Contains(p);
                bool isUnconditionalSafeOpp = unconditionalSafe[opp].Contains(p);
                bool isAlternateSafeOpp = alternateSafe[opp].Contains(p);
                bool hasOppNeighbors = bd.HasNeighbors(p, opp);
                // Always generate capturing moves in own safe territory, even
                // if current rules do no use CaptureDead(), because the UCT
                // player always scores with Tromp-Taylor after two passes in the
                // in-tree phase
                if (  (isAllAlternateSafe && isAlternateSafeOpp)
                   || isUnconditionalSafeOpp
                   || (isUnconditionalSafe && ! hasOppNeighbors)
                   )
                    rootFilter.push_back(p);
            }
        }
    }

    // Loosing ladder defense moves
    if (m_param.m_checkLadders)
    {
        for (GoBlockIterator it(m_bd); it; ++it)
        {
            SgPoint p = *it;
            if (m_bd.GetStone(p) == toPlay && m_bd.InAtari(p))
            {
                if (m_ladder.Ladder(m_bd, p, toPlay, &m_ladderSequence,
                                    false/*twoLibIsEscape*/) < 0)
                {
                    if (m_ladderSequence.Length() >= m_param.m_minLadderLength)
                        rootFilter.push_back(m_bd.TheLiberty(p));
                }
            }

        }
    }

    if (m_param.m_checkOffensiveLadders)
    {
        for (GoBlockIterator it(m_bd); it; ++it)
        {
            SgPoint p = *it;
            if (m_bd.GetStone(p) == opp && m_bd.NumStones(p) >= 5 && m_bd.NumLiberties(p) == 2)
            {
                if (m_ladder.Ladder(m_bd, p, toPlay, &m_ladderSequence,
                                    false/*twoLibIsEscape*/) > 0)
                {
                    if (m_ladderSequence.Length() >= m_param.m_minLadderLength) 
                        rootFilter.push_back(m_ladderSequence[0]);
                }
            }
        }
    }

    if (m_param.m_filterFirstLine)
    {
        // Moves on edge of board, if no stone is near
        const SgBoardConst& bc = m_bd.BoardConst();
        for (SgLineIterator it(bc, 1); it; ++it)
        {
            const SgPoint p = *it;
            if (m_bd.IsEmpty(p) && IsEmptyEdge(m_bd, p))
                rootFilter.push_back(p);
        }
    }

    return rootFilter;
}