void GoBook::Add(const GoBoard& bd, SgPoint move)
{
    if (move != SG_PASS && bd.Occupied(move))
        throw SgException("point is not empty");
    if (! bd.IsLegal(move))
        throw SgException("move is not legal");
    const GoBook::MapEntry* mapEntry = LookupEntry(bd);
    if (mapEntry == 0)
    {
        vector<SgPoint> moves;
        moves.push_back(move);
        GoBoard tempBoard;
        InsertEntry(GetSequence(bd), moves, bd.Size(), tempBoard, 0);
    }
    else
    {
        size_t id = mapEntry->m_id;
        SG_ASSERT(id < m_entries.size());
        Entry& entry = m_entries[id];
        int invRotation = SgPointUtil::InvRotation(mapEntry->m_rotation);
        SgPoint rotMove = SgPointUtil::Rotate(invRotation, move, bd.Size());
        if (! Contains(entry.m_moves, rotMove))
            entry.m_moves.push_back(rotMove);
    }
}
int LibertyAveragex10(const GoBoard& board, SgBlackWhite color)
{
    int nuLibs = 0, nuBlocks = 0;
    const int size = board.Size();
    for (SgConnCompIterator it(board.All(color), board.Size()); it; ++it)
    {
        ++nuBlocks;
        nuLibs += ((*it).Border(size) & board.AllEmpty()).Size();
    }
    return (nuBlocks == 0) ? 0 : 10 * nuLibs / nuBlocks;
}
bool GoRegion::ProtectedCuts(const GoBoard& board) const
{
    if (! GetFlag(GO_REGION_CORRIDOR))
        return false;
    if (m_blocks.IsLength(2))
        /* */ return Safe2Cuts(board); /* */ // easy case of only 2 blocks

    bool prot = true;
    SgPointSet allCuts;
    const int size = board.Size();
    GoBlock* block1, *block2;
    for (SgVectorPairIteratorOf<GoBlock> it(m_blocks);
         it.NextPair(block1, block2);)
    {
        SgPointSet lib1(block1->Stones().Border(size));
        SgPointSet lib2(block2->Stones().Border(size));
        SgPointSet cuts(lib1 & lib2 & Points());
        if (! cuts.SubsetOf(board.AllEmpty()))
        // cut occupied by opponent. Bad for us.
            return false;
        else
            allCuts |= cuts;
    }
    // no eye space left ? hard to distinguish false eyes from ok
    // AR why must this be checked??? Should not matter for flat regions.
    // Try to take it out.
    //if (Points().SubsetOf(allCuts | allCuts.Border()))
    //  prot = false;

    return prot;
}
Beispiel #4
0
void GoInfluence::FindInfluence(const GoBoard& board,
                    int nuExpand,
                    int nuShrink,
                    SgBWSet* influence)
{   
    SgBWSet result = SgBWSet(board.All(SG_BLACK), board.All(SG_WHITE));
    SgBWSet next;
    const int size = board.Size();
    for (int i = 1; i <= nuExpand; ++i)
    {
        for (SgBlackWhite c = SG_BLACK; c <= SG_WHITE; ++c)
        {
            SgBlackWhite opp = SgOppBW(c);
            next[c] = result[c].Border(size) - result[opp];
        }
        result[SG_BLACK] |= (next[SG_BLACK] - next[SG_WHITE]);
        result[SG_WHITE] |= (next[SG_WHITE] - next[SG_BLACK]);
    }

    for (int i = 1; i <= nuShrink; ++i)
    {
        result[SG_BLACK] = result[SG_BLACK].Kernel(size);
        result[SG_WHITE] = result[SG_WHITE].Kernel(size);
    }
    
    *influence = result;
}
GoPlayer::GoPlayer(const GoBoard& bd)
    : GoBoardSynchronizer(bd),
      m_currentNode(0),
      m_bd(bd.Size(), GoSetup(), bd.Rules()),
      m_variant(0)
{
    SetSubscriber(m_bd);
    ClearSearchTraces();
}
void GoBoardHistory::SetFromBoard(const GoBoard& bd)
{
    m_boardSize = bd.Size();
    m_rules = bd.Rules();
    m_setup = bd.Setup();
    m_moves.Clear();
    for (int i = 0; i < bd.MoveNumber(); ++i)
        m_moves.PushBack(bd.Move(i));
    m_toPlay = bd.ToPlay();
}
void GoBook::Entry::ApplyTo(GoBoard& bd) const
{
    if (bd.Size() != m_size)
        bd.Init(m_size);
    GoBoardUtil::UndoAll(bd);
    for (vector<SgPoint>::const_iterator it = m_sequence.begin();
         it != m_sequence.end(); ++it)
    {
        SG_ASSERT(bd.IsLegal(*it));
        bd.Play(*it);
    }
}
void GoBook::Delete(const GoBoard& bd, SgPoint move)
{
    const GoBook::MapEntry* mapEntry = LookupEntry(bd);
    if (mapEntry == 0)
        return;
    size_t id = mapEntry->m_id;
    SG_ASSERT(id < m_entries.size());
    Entry& entry = m_entries[id];
    int invRotation = SgPointUtil::InvRotation(mapEntry->m_rotation);
    SgPoint rotMove = SgPointUtil::Rotate(invRotation, move, bd.Size());
    if (! Erase(entry.m_moves, rotMove))
        throw SgException("GoBook::Delete: move not found");
}
GoRegionBoard::GoRegionBoard(const GoBoard& board)
    : m_board(board),
      m_region(SgPointArray<GoRegion*>(0)),
      m_block(0),
      m_invalid(true),
      m_computedHealthy(false),
      m_boardSize(board.Size())
{
    m_code.Invalidate();
    m_chainsCode.Invalidate();
    GenBlocksRegions();
    ++s_alloc;
}
bool GoRegion::Safe2Cuts(const GoBoard& board) const
{
    SG_ASSERT(m_blocks.IsLength(2));
    const int size = board.Size();
    GoBlock* block1 = m_blocks.Front();
    GoBlock* block2 = m_blocks.Back();
    SgPointSet cuts(Points());
    cuts -= board.AllEmpty();
    if (cuts.IsEmpty())
        /* */ return true; /* */
    cuts &= block1->Stones().Border(size);
    cuts &= block2->Stones().Border(size);
    return cuts.IsEmpty();
}
void GoGtpCommandUtil::RespondNumberArray(GtpCommand& cmd,
                                          const SgPointArray<int>& array,
                                          int scale, const GoBoard& board)
{
    SgPointArray<string> result("\"\"");
    for (GoBoard::Iterator it(board); it; ++it)
    {
        SgPoint p(*it);
        if (array[p] != numeric_limits<int>::min())
        {
            ostringstream out;
            out << (array[p] / scale);
            result[p] = out.str();
        }
    }
    cmd << '\n' << SgWritePointArray<string>(result, board.Size());
}
Beispiel #12
0
SgPoint GoUctUtil::GenForcedOpeningMove(const GoBoard& bd)
{
    int sz = bd.Size();
    if (sz < 13 || bd.TotalNumStones(SG_BLACK) > 5
        || bd.TotalNumStones(SG_WHITE) > 5)
        return SG_NULLMOVE;
    SgSList<SgPoint,4> moves;
    if (IsRectEmpty(bd, 1, 5, 1, 5))
        moves.PushBack(Pt(4, 4));
    if (IsRectEmpty(bd, 1, 5, sz - 4, sz))
        moves.PushBack(Pt(4, sz - 3));
    if (IsRectEmpty(bd, sz - 4, sz, 1, 5))
        moves.PushBack(Pt(sz - 3, 4));
    if (IsRectEmpty(bd, sz - 4, sz, sz - 4, sz))
        moves.PushBack(Pt(sz - 3, sz - 3));
    if (moves.IsEmpty())
        return SG_NULLMOVE;
    return moves[SgRandom::Global().Int(moves.Length())];
}
Beispiel #13
0
void GoBoardUpdater::Update(const SgNode* node, GoBoard& bd)
{
    ////SG_ASSERT(node != 0);
    m_nodes.clear();
    while (node != 0)
    {
        m_nodes.push_back(node);
        node = node->Father();
    }
    const SgNode* root = m_nodes[m_nodes.size() - 1];
    int size = GO_DEFAULT_SIZE;
    SgPropInt* boardSize = static_cast<SgPropInt*>(root->Get(SG_PROP_SIZE));
    if (boardSize)
    {
        size = boardSize->Value();
        ////SG_ASSERT(SgUtil::InRange(size, SG_MIN_SIZE, SG_MAX_SIZE));
    }
    bd.Init(size);
    for (vector<const SgNode*>::reverse_iterator it = m_nodes.rbegin();
         it != m_nodes.rend(); ++it)
    {
        const SgNode* node = *it;
        SgEmptyBlackWhite player = GetPlayer(node);
        if (node->HasProp(SG_PROP_ADD_EMPTY)
            || node->HasProp(SG_PROP_ADD_BLACK)
            || node->HasProp(SG_PROP_ADD_WHITE))
        {
            // Compute the new initial setup position to re-initialize the
            // board with
            GoSetup setup = GoSetupUtil::CurrentPosSetup(bd);
            if (player != SG_EMPTY)
                setup.m_player = player;
            if (node->HasProp(SG_PROP_ADD_BLACK))
            {
                SgPropAddStone* prop =
                  dynamic_cast<SgPropAddStone*>(node->Get(SG_PROP_ADD_BLACK));
                const SgVector<SgPoint>& addBlack = prop->Value();
                for (SgVectorIterator<SgPoint> it2(addBlack); it2; ++it2)
                {
                    SgPoint p = *it2;
                    setup.m_stones[SG_WHITE].Exclude(p);
                    if (! setup.m_stones[SG_BLACK].Contains(p))
                        setup.AddBlack(p);
                }
            }
            if (node->HasProp(SG_PROP_ADD_WHITE))
            {
                SgPropAddStone* prop =
                  dynamic_cast<SgPropAddStone*>(node->Get(SG_PROP_ADD_WHITE));
                const SgVector<SgPoint>& addWhite = prop->Value();
                for (SgVectorIterator<SgPoint> it2(addWhite); it2; ++it2)
                {
                    SgPoint p = *it2;
                    setup.m_stones[SG_BLACK].Exclude(p);
                    if (! setup.m_stones[SG_WHITE].Contains(p))
                        setup.AddWhite(p);
                }
            }
            if (node->HasProp(SG_PROP_ADD_EMPTY))
            {
                SgPropAddStone* prop =
                  dynamic_cast<SgPropAddStone*>(node->Get(SG_PROP_ADD_EMPTY));
                const SgVector<SgPoint>& addEmpty = prop->Value();
                for (SgVectorIterator<SgPoint> it2(addEmpty); it2; ++it2)
                {
                    SgPoint p = *it2;
                    setup.m_stones[SG_BLACK].Exclude(p);
                    setup.m_stones[SG_WHITE].Exclude(p);
                }
            }
            bd.Init(bd.Size(), setup);
        }
        else if (player != SG_EMPTY)
            bd.SetToPlay(player);
        if (node->HasProp(SG_PROP_MOVE))
        {
            SgPropMove* prop =
                dynamic_cast<SgPropMove*>(node->Get(SG_PROP_MOVE));
            SgPoint p = prop->Value();
            if (p == SG_PASS || ! bd.Occupied(p))
                bd.Play(p, prop->Player());
        }
    }
}
Beispiel #14
0
/** Insert a new position entry and all its transformations
    @param sequence A move sequence that leads to the position
    @param moves The moves to play in this position
    @param size
    @param tempBoard A local temporary work board, reused for efficiency
    (does not have to be initialized)
    @param line Line number of entry from the file (0 if unknown or entry is
    not from a file) */
void GoBook::InsertEntry(const vector<SgPoint>& sequence,
                         const vector<SgPoint>& moves, int size,
                         GoBoard& tempBoard, int line)
{
    if (moves.size() == 0)
        ThrowError("Line contains no moves");
    if (tempBoard.Size() != size)
        tempBoard.Init(size);
    Entry entry;
    entry.m_size = size;
    entry.m_line = line;
    entry.m_sequence = sequence;
    entry.m_moves = moves;
    m_entries.push_back(entry);
    size_t id = m_entries.size() - 1;
    vector<Map::iterator> newEntries;
    for (int rot = 0; rot < 8; ++rot)
    {
        GoBoardUtil::UndoAll(tempBoard);
        for (vector<SgPoint>::const_iterator it = sequence.begin();
             it != sequence.end(); ++it)
        {
            SgPoint p = SgPointUtil::Rotate(rot, *it, size);
            if (! tempBoard.IsLegal(p))
                ThrowError("Illegal move in variation");
            tempBoard.Play(p);
        }
        // It is enough to check the moves for legality for one rotation
        if (rot == 0)
            for (vector<SgPoint>::const_iterator it = moves.begin();
                 it != moves.end(); ++it)
                if (! tempBoard.IsLegal(SgPointUtil::Rotate(rot, *it, size)))
                    ThrowError("Illegal move in move list");
        MapEntry mapEntry;
        mapEntry.m_size = size;
        mapEntry.m_id = id;
        mapEntry.m_rotation = rot;
        SgHashCode hashCode = tempBoard.GetHashCodeInclToPlay();
        pair<Map::iterator,Map::iterator> e = m_map.equal_range(hashCode);
        bool isInNewEntries = false;
        for (Map::iterator it = e.first; it != e.second; ++it)
            if (it->second.m_size == size)
            {
                if (! Contains(newEntries, it))
                {
                    ostringstream o;
                    o << "Entry duplicates line "
                      << m_entries[it->second.m_id].m_line;
                    ThrowError(o.str());
                }
                isInNewEntries = true;
                break;
            }
        if (! isInNewEntries)
        {
            Map::iterator newIt =
                m_map.insert(Map::value_type(hashCode, mapEntry));
            newEntries.push_back(newIt);
        }
    }
}
Beispiel #15
0
void GoEyeUtil::TestNakade(const SgPointSet& points,
                           const GoBoard& bd,
                           SgBlackWhite color,
                           bool isFullyEnclosed,
                           bool& isNakade,
                           bool& makeNakade,
                           bool& makeFalse,
                           bool& maybeSeki,
                           bool& sureSeki,
                           SgPoint* vital)
{   // handles case
    // of more than 1 point passing vital point test.
    // passes back vital point if exactly one is found.
    // @todo handle case where vital is illegal or suicide (eye within eye?).
    // also test bigger, would-be-alive shapes in atari.
    // @todo handle seki.
    
    SG_UNUSED(makeFalse);
    isNakade = makeNakade = maybeSeki = sureSeki = false;
    SgPoint vitalP(SG_NULLPOINT);
    const SgBlackWhite opp = SgOppBW(color);
    const int nuPoints = points.Size();
    
    SG_ASSERT(nuPoints >= 3); // don't call for smaller areas, no need,
    // and results in isNakade = false which is confusing.
    
    if (nuPoints == 4) // special case 4 point areas
    {
        if (IsBulkyFour(points))
        {
            if (   isFullyEnclosed
                && TwoDiagonalStonesInBulkyFour(bd, points, opp)
               )
                makeNakade = true;
            else
                isNakade = true;
            /* */ return; /* */
        }
        else if (   isFullyEnclosed
                 && points.SubsetOf(bd.AllEmpty())
                 && IsBentFour(points, bd.Size(), vital)
                )
        {
            makeNakade = true;
            /* */ return; /* */
        }
    }
    else if (isFullyEnclosed && nuPoints == 5) // special case 5 point areas
    {
        const GoEyeStatus status = BulkyFiveNakade(bd, points, opp);
        if (ProcessStatus(status, isNakade, makeNakade))
            /* */ return; /* */
    }
    else if (isFullyEnclosed && nuPoints == 6) // special case 6 point areas
    {
        if (Is2x3Area (points))
        {
            GoEyeStatus status = Special2x3Cases(bd, points, opp);
            if (ProcessStatus(status, isNakade, makeNakade))
                /* */ return; /* */
        }   
    }
    if (   isFullyEnclosed
        && AlmostFilledByNakade(bd, points, opp)
       )
    {
        isNakade = true;
        /* */ return; /* */
    }
        
    if (   isFullyEnclosed
        && (   AlmostFilledByLivingShape(bd, points, opp)
            || ContainsLivingShape(bd, points, opp)
           )
       )
    {   // not nakade, 2 eyes
        /* */ return; /* */
    }
        
    int nuMakeNakade = 0;
    int nuVitalOccupied = 0;
    bool hasDivider = false;
    // counting number of nakade fixes bug with stretched 5 pt eye 
    // that had 3 vital pts,
    // one of them occupied. was classified as 'isNakade7 = 1 eye.
    // see sgf/ld200,#20
    for (SgSetIterator it(points); it; ++it)
    {
        SgPoint p(*it);
        if (IsVitalPt(points, p, opp, bd))
        {
            if (bd.IsEmpty(p))
            {
                if (bd.IsLegal(p, opp))
                {
                    ++nuMakeNakade;
                    vitalP = p;
                }
                else
                    hasDivider = true;
            }
            else
                ++nuVitalOccupied;
        }
    }
    
    if (hasDivider)
    { // alive 
    }
    else if (nuMakeNakade == 1) // exactly one way to make nakade here.
    {
        makeNakade = true;
        *vital = vitalP;
    }
    else if (nuMakeNakade > 0) 
        isNakade = false;
    else if (nuVitalOccupied < 3)
        isNakade = true;
    else
    {
        maybeSeki = true;
        // @todo if (IsSureSekiShape(...)) sureSeki = true;
    }
}