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. }
void GoRegionBoard::OnExecutedUncodedMove(int move, SgBlackWhite moveColor) { if (DEBUG_REGION_BOARD) SgDebug() << "OnExecutedUncodedMove " << SgWritePoint(move) << '\n'; { m_stack.StartMoveInfo(); if (move != SG_PASS) { SG_ASSERT(! Board().LastMoveInfo(GO_MOVEFLAG_SUICIDE)); // can't handle yet, // should be forbidden anyway. @todo allowed in Chinese rules. bool fWasCapture = Board().LastMoveInfo(GO_MOVEFLAG_CAPTURING); UpdateBlock(move, moveColor); { GoRegion* r = PreviousRegionAt(move, moveColor); bool split = GoEyeUtil::IsSplitPt(move, r->Points()); r->OnAddStone(move); PushStone(r, move); SgPointSet points = r->Points(); // needed even after RemoveRegion(r). if (split || points.IsEmpty()) // must remove old region before generating new ones, // because removing clears m_anchor[] RemoveRegion(r); if (split) // find new regions { for (SgConnCompIterator it(points, Board().Size()); it; ++it) GenRegion(*it, moveColor); } } if (fWasCapture) { // FindNewNeighborRegions(move, moveColor); MergeAdjacentAndAddBlock(move, SgOppBW(moveColor)); } m_code = Board().GetHashCode(); if (HEAVYCHECK) CheckConsistency(); } } { for (SgBWIterator it; it; ++it) { SgBlackWhite color(*it); for (SgVectorIteratorOf<GoRegion> it(AllRegions(color)); it; ++it) { GoRegion* r1 = *it; if (! r1->IsValid()) r1->ComputeBasicFlags(); } } } }
/** Find global moves that match a playout pattern or set a block into atari. @param[out] pattern @param[out] atari @param[out] empty As a side effect, this function finds all empty points on the board @return @c true if any such moves was found */ bool GoUctDefaultPriorKnowledge::FindGlobalPatternAndAtariMoves( SgPointSet& pattern, SgPointSet& atari, GoPointList& empty) const { SG_ASSERT(empty.IsEmpty()); const GoUctPatterns<GoBoard>& patterns = m_policy.Patterns(); bool result = false; for (GoBoard::Iterator it(m_bd); it; ++it) if (m_bd.IsEmpty(*it)) { empty.PushBack(*it); if (patterns.MatchAny(*it)) { pattern.Include(*it); result = true; } if (SetsAtari(m_bd, *it)) { atari.Include(*it); result = true; } } return result; }
/** Find global moves that match a playout pattern or set a block into atari. @param[out] pattern @param[out] atari @param[out] empty As a side effect, this function finds all empty points on the board @return @c true if any such moves was found */ bool GoUctDefaultPriorKnowledge::FindGlobalPatternAndAtariMoves( SgPointSet& pattern, SgPointSet& atari, GoPointList& empty) { // Minimum value for pattern gamma to be used. static const float EPSILON = 0.00000000001; const GoBoard& bd = Board(); SG_ASSERT(empty.IsEmpty()); const GoUctPatterns<GoBoard>& patterns = m_policy.GlobalPatterns(); bool result = false; m_maxPatternGamma = -1.f; for (GoBoard::Iterator it(bd); it; ++it) if (bd.IsEmpty(*it)) { empty.PushBack(*it); float gamma = patterns.GetPatternGamma(bd, *it, bd.ToPlay()); if (gamma > EPSILON) { pattern.Include(*it); result = true; m_patternGammas[*it] = gamma; if (gamma > m_maxPatternGamma) m_maxPatternGamma = gamma; } if (SetsAtari(bd, *it)) { atari.Include(*it); result = true; } } return result; }
SgPointSet SpUtil::GetRelevantMoves(GoBoard& bd, SgBlackWhite toPlay, bool useFilter) { SgPointSet legal; for (SgSetIterator it(bd.AllEmpty()); it; ++it) if (bd.IsLegal(*it)) legal.Include(*it); if (! useFilter) return legal; GoSafetySolver safetySolver(bd); SgBWSet safe; safetySolver.FindSafePoints(&safe); SgBlackWhite opp = SgOppBW(toPlay); SgPointSet moves; const GoRules& rules = bd.Rules(); const bool captureDead = rules.CaptureDead(); //const bool japaneseScoring = rules.JapaneseScoring(); for (SgSetIterator it(legal); it; ++it) { SgPoint p = *it; const bool isSafe = safe[toPlay].Contains(p); const bool isSafeOpp = safe[opp].Contains(p); const bool hasOppNeighbors = bd.HasNeighbors(p, opp); if ( (! isSafe && ! isSafeOpp) || (isSafe && captureDead && hasOppNeighbors) // || (isSafeOpp && ! japaneseScoring) ) moves.Include(p); } return moves; }
void GoChain::GetBlocks(const GoRegionBoard* ra, SgVectorOf<GoBlock>* blocks) const { SgBlackWhite color = Color(); SgPointSet chainPts = Stones(); for (SgVectorIteratorOf<GoBlock> it(ra->AllBlocks(color)); it; ++it) if (chainPts.Contains((*it)->Anchor())) blocks->PushBack(*it); }
void GoBlock::CheckConsistency() const { SG_ASSERT(Stones().SubsetOf(m_bd.All(Color()))); SgPoint anchor = Anchor(); SG_ASSERT(m_bd.Anchor(anchor) == Anchor()); SgPointSet stones; for (GoBoard::StoneIterator it(m_bd, anchor); it; ++it) stones.Include(*it); SG_ASSERT(Stones() == stones); }
void GoRegion::Find2FreeLibs(const GoChain* c1, const GoChain* c2, SgPoint* lib1, SgPoint* lib2) const { SgPointSet libs = Points() & c1->FreeLiberties() & c2->FreeLiberties(); if (CHECK) SG_ASSERT(libs.MinSetSize(2)); SgSetIterator it(libs); *lib1 = *it; ++it; *lib2 = *it; }
/** 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); }
bool GoEyeUtil::IsTreeShape(const SgPointSet& area) { for (SgSetIterator it(area); it; ++it) { const SgPoint p(*it); if ( area.Contains(p + SG_NS) && area.Contains(p + SG_WE) && area.Contains(p + SG_NS + SG_WE) ) return false; } return true; }
void updatePointCloudFromRangeSensor() { if(!pointSetFromRangeSensor) return; SgVertexArray& disPoints = *pointSetFromRangeSensor->getOrCreateVertices(); boost::mutex::scoped_lock lock(mtx); disPoints.resize(rangeSensorPoints.size()); copy(rangeSensorPoints.begin(), rangeSensorPoints.end(), disPoints.begin()); lock.unlock(); pointSetFromRangeSensor->notifyUpdate(); }
long GoEyeUtil::DegreeCode8(const SgPointSet& points) { int degrees[9] = {0,0,0,0,0}; for (SgSetIterator it(points); it; ++it) { const SgPoint p(*it); int nbs = 0; for (SgNb8Iterator it(p); it; ++it) { if (points.Contains(*it)) ++nbs; } ++(degrees[nbs]); } return degrees[0] + 10 * degrees[1] + 100 * degrees[2] + 1000 * degrees[3] + 10000 * degrees[4] + 100000 * degrees[5] + 1000000 * degrees[6] + 10000000 * degrees[7] + 100000000 * degrees[8]; }
/** Information about safe points optimized for graphical display in GoGui. This command is compatible with GoGui's analyze command type "gfx". Arguments: benson|static <br> Returns: GoGui gfx commands to display safe points and additional information in the status line - black and white territory: safe points - Color Magenta (#980098): dame points - Color Red: safe for black and white (should not happen) - Circle: unsurroundable - Status line: point counts, percentage of safe points */ void GoSafetyCommands::CmdGfx(GtpCommand& cmd) { cmd.CheckNuArg(1); string type = cmd.Arg(0); int totalRegions = 0; SgBWSet safe = GetSafe(totalRegions, type); SgPointSet dame; SgPointSet unsurroundable; GoSafetyUtil::FindDameAndUnsurroundablePoints(m_bd, m_bd.AllEmpty(), safe, &dame, &unsurroundable); cmd << "BLACK"; for (SgSetIterator it(safe[SG_BLACK]); it; ++it) cmd << ' ' << SgWritePoint(*it); cmd << '\n'; cmd << "WHITE"; for (SgSetIterator it(safe[SG_WHITE]); it; ++it) cmd << ' ' << SgWritePoint(*it); cmd << '\n'; cmd << "COLOR #980098"; for (SgSetIterator it(dame); it; ++it) cmd << ' ' << SgWritePoint(*it); cmd << '\n'; cmd << "CIRCLE"; for (SgSetIterator it(unsurroundable); it; ++it) cmd << ' ' << SgWritePoint(*it); cmd << '\n'; SgPointSet blackAndWhite = safe[SG_WHITE] & safe[SG_BLACK]; if (blackAndWhite.Size() > 0) { // Shouldn't happen cmd << "COLOR red "; for (SgSetIterator it(blackAndWhite); it; ++it) cmd << ' ' << SgWritePoint(*it); cmd << '\n'; } int nuBlack = safe[SG_BLACK].Size(); int nuWhite = safe[SG_WHITE].Size(); int nuPoints = m_bd.AllPoints().Size(); cmd << "TEXT Solver: " << cmd.Arg(0) << " B: " << nuBlack << " (" << (100 * nuBlack / nuPoints) << " %)" << " W: " << nuWhite << " (" << (100 * nuWhite / nuPoints) << " %)" << " Both: " << (nuBlack + nuWhite) << " (" << (100 * (nuBlack + nuWhite) / nuPoints) << " %)" << " Regions: " << totalRegions; }
void updatePointCloudFromRangeCamera() { if(!pointSetFromRangeCamera) return; SgVertexArray& disPoints = *pointSetFromRangeCamera->getOrCreateVertices(); SgColorArray& disColors = *pointSetFromRangeCamera->getOrCreateColors(); boost::mutex::scoped_lock lock(mtx); disPoints.resize(rangeCameraPoints.size()); disColors.resize(rangeCameraColors.size()); copy(rangeCameraPoints.begin(), rangeCameraPoints.end(), disPoints.begin()); copy(rangeCameraColors.begin(), rangeCameraColors.end(), disColors.begin()); lock.unlock(); pointSetFromRangeCamera->notifyUpdate(); }
void clearImage() { if(imageView){ Image image; image.clear(); image.setSize(1,1,3); *image.pixels() = 0; imageView->setImage(image); } if(pointSetFromRangeCamera){ pointSetFromRangeCamera->getOrCreateVertices()->clear(); pointSetFromRangeCamera->getOrCreateColors()->clear(); } if(pointSetFromRangeSensor){ pointSetFromRangeSensor->getOrCreateVertices()->clear(); pointSetFromRangeSensor->getOrCreateColors()->clear(); } }
bool GoEyeUtil::CheckInterior(const GoBoard& bd, const SgPointSet& area, SgBlackWhite opp, bool checkBlocks) { bool hasSingleNbPoint = false; int nuPoints = 0; for (SgSetIterator it(area); it; ++it) { const SgPoint p(*it); if (bd.IsEmpty(p)) { int nuNbs = 0; if (area.Contains(p + SG_NS)) ++nuNbs; if (area.Contains(p - SG_NS)) ++nuNbs; if (area.Contains(p + SG_WE)) ++nuNbs; if (area.Contains(p - SG_WE)) ++nuNbs; if (nuNbs == 1) hasSingleNbPoint = true; else if (nuNbs > 2) return false; } else if (p == bd.Anchor(p)) { if (bd.GetColor(p) != opp) // if own stones on inside: not a tree shape. return false; int nuLibs = bd.NumLiberties(p); if (nuLibs == 1) hasSingleNbPoint = true; else if (checkBlocks && nuLibs > 2) return false; } ++nuPoints; } return nuPoints == 1 || hasSingleNbPoint; }
bool GoEyeUtil::IsNakadeShape(const SgPointSet& area) { switch (area.Size()) { case 1: case 2: case 3: return true; case 4: return IsBulkyFour(area) || IsTShape(area); case 5: return IsBulkyFive(area) || IsCross(area); case 6: return IsRabbitySix(area); default: // too big return false; } }
bool GoEyeUtil::IsLocalSplitPt(SgPoint p, const SgPointSet& set) { int nuNb = 0; for (SgNb4Iterator it(p); it; ++it) { if (set.Contains(*it)) { ++nuNb; if (nuNb >= 2) break; } } if (nuNb >= 2) { if (set[p - SG_NS]) { if (set[p - SG_WE] && TestDiagonal(set, p, -SG_NS, -SG_WE)) return true; if (set[p + SG_NS] && TestOpposite(set, p, SG_NS, SG_WE)) return true; if (set[p + SG_WE] && TestDiagonal(set, p, -SG_NS, +SG_WE)) return true; } if (set[p + SG_NS]) { if (set[p - SG_WE] && TestDiagonal(set, p, +SG_NS, -SG_WE)) return true; if (set[p + SG_WE] && TestDiagonal(set, p, +SG_NS, +SG_WE)) return true; } if (set[p - SG_WE] && set[p + SG_WE] && TestOpposite(set, p, SG_WE, SG_NS)) return true; } return false; // no local split found. }
// 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; }
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; }
void SgNode::SetListProp(SgPropID id, const SgPointSet& value) { SgVector<SgPoint> valueList; value.ToVector(&valueList); SetListProp(id, valueList); }
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; } }