vector<SgPoint> GoBook::LookupAllMoves(const GoBoard& bd) const { vector<SgPoint> result; const GoBook::MapEntry* mapEntry = LookupEntry(bd); if (mapEntry == 0) return result; size_t id = mapEntry->m_id; SG_ASSERT(id < m_entries.size()); const vector<SgPoint>& moves = m_entries[id].m_moves; const int rotation = mapEntry->m_rotation; const int size = mapEntry->m_size; for (vector<SgPoint>::const_iterator it = moves.begin(); it != moves.end(); ++it) { SgPoint p = SgPointUtil::Rotate(rotation, *it, size); if (! bd.IsLegal(p)) { // Should not happen with 64-bit hashes, but not impossible SgWarning() << "illegal book move (hash code collision?)\n"; result.clear(); break; } result.push_back(p); } return result; }
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); } }
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 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 GoUctFeatures:: FindMoveFeaturesUI(const GoBoard& bd, GoUctPlayoutPolicy<GoBoard>& policy, SgPoint move, FeMoveFeatures& features) { SG_ASSERT(move != SG_PASS); if (! bd.IsLegal(move)) return; FeFullBoardFeatures f(bd); FindAllFeatures(bd, policy, f); features = f.Features()[move]; }
/** 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; } }