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; }
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()); }
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())]; }
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()); } } }
/** 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); } } }
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; } }