00001 //---------------------------------------------------------------------------- 00002 /** @file BoardUtils.cpp 00003 */ 00004 //---------------------------------------------------------------------------- 00005 00006 #include "SgSystem.h" 00007 #include "SgRandom.h" 00008 00009 #include "BoardUtils.hpp" 00010 #include "BitsetIterator.hpp" 00011 #include "VCSet.hpp" 00012 #include "GraphUtils.hpp" 00013 #include "HexBoard.hpp" 00014 #include "Pattern.hpp" 00015 #include "HashedPatternSet.hpp" 00016 00017 using namespace benzene; 00018 00019 //---------------------------------------------------------------------------- 00020 00021 namespace { 00022 00023 //---------------------------------------------------------------------------- 00024 00025 bool g_BoardUtilsDecompsInitialized = false; 00026 std::vector<Pattern> g_oppmiai[BLACK_AND_WHITE]; 00027 HashedPatternSet g_hash_oppmiai[BLACK_AND_WHITE]; 00028 00029 /** Initialize the miai pattern. */ 00030 void InitializeOppMiai() 00031 { 00032 if (g_BoardUtilsDecompsInitialized) return; 00033 00034 LogFine() << "--InitializeOppMiai" << '\n'; 00035 00036 // 00037 // Miai between groups of opposite color. 00038 // W is marked; so if you use this pattern on the black members of 00039 // group, it will tell you the white groups that are adjacent to it. 00040 // Used in the decomposition stuff. 00041 // 00042 // . W 00043 // * . [oppmiai/0] 00044 // m:5,0,4,4,0;1,0,0,0,0;0,0,0,0,0;0,0,0,0,0;0,0,0,0,0;0,0,0,0,0;1 00045 std::string oppmiai = "m:5,0,4,4,0;1,0,0,0,0;0,0,0,0,0;0,0,0,0,0;0,0,0,0,0;0,0,0,0,0;1"; 00046 00047 Pattern pattern; 00048 if (!pattern.unserialize(oppmiai)) { 00049 HexAssert(false); 00050 } 00051 pattern.setName("oppmiai"); 00052 00053 g_oppmiai[BLACK].push_back(pattern); 00054 pattern.flipColors(); 00055 g_oppmiai[WHITE].push_back(pattern); 00056 00057 for (BWIterator c; c; ++c) 00058 g_hash_oppmiai[*c].hash(g_oppmiai[*c]); 00059 } 00060 00061 /** @todo Is it possible to speed this up? */ 00062 void ComputeAdjacentByMiai(const HexBoard& brd, PointToBitset& adjByMiai) 00063 { 00064 HexAssert(g_BoardUtilsDecompsInitialized); 00065 00066 adjByMiai.clear(); 00067 for (BWIterator color; color; ++color) 00068 { 00069 for (BitsetIterator p(brd.GetPosition().GetColor(*color) 00070 & brd.GetPosition().Const().GetCells()); 00071 p; ++p) 00072 { 00073 PatternHits hits; 00074 brd.GetPatternState().MatchOnCell(g_hash_oppmiai[*color], *p, 00075 PatternState::MATCH_ALL, hits); 00076 HexPoint cp = brd.GetGroups().CaptainOf(*p); 00077 for (unsigned j=0; j<hits.size(); ++j) 00078 { 00079 HexPoint cj = brd.GetGroups().CaptainOf(hits[j].moves1()[0]); 00080 adjByMiai[cj].set(cp); 00081 adjByMiai[cp].set(cj); 00082 } 00083 } 00084 } 00085 } 00086 00087 } // namespace 00088 00089 //---------------------------------------------------------------------------- 00090 00091 HexPoint BoardUtils::CoordsToPoint(const ConstBoard& brd, int x, int y) 00092 { 00093 if (x <= -2 || x > brd.Width()) return INVALID_POINT; 00094 if (y <= -2 || y > brd.Height()) return INVALID_POINT; 00095 if ((x == -1 || x == brd.Width()) && 00096 (y == -1 || y == brd.Height())) return INVALID_POINT; 00097 00098 if (y == -1) return NORTH; 00099 if (y == brd.Height()) return SOUTH; 00100 if (x == -1) return WEST; 00101 if (x == brd.Width()) return EAST; 00102 00103 return HexPointUtil::coordsToPoint(x, y); 00104 } 00105 00106 HexPoint BoardUtils::PointInDir(const ConstBoard& brd, 00107 HexPoint point, HexDirection dir) 00108 { 00109 if (HexPointUtil::isEdge(point)) 00110 return point; 00111 00112 int x, y; 00113 HexAssert(HexPointUtil::isInteriorCell(point)); 00114 HexPointUtil::pointToCoords(point, x, y); 00115 x += HexPointUtil::DeltaX(dir); 00116 y += HexPointUtil::DeltaY(dir); 00117 return BoardUtils::CoordsToPoint(brd, x, y); 00118 } 00119 00120 HexPoint BoardUtils::Rotate(const ConstBoard& brd, HexPoint p) 00121 { 00122 HexAssert(brd.IsValid(p)); 00123 00124 if (!brd.IsLocation(p)) return p; 00125 if (HexPointUtil::isEdge(p)) return HexPointUtil::oppositeEdge(p); 00126 00127 int x, y; 00128 HexPointUtil::pointToCoords(p, x, y); 00129 return HexPointUtil::coordsToPoint(brd.Width()-1-x, brd.Height()-1-y); 00130 } 00131 00132 HexPoint BoardUtils::Mirror(const ConstBoard& brd, HexPoint p) 00133 { 00134 HexAssert(brd.IsValid(p)); 00135 HexAssert(brd.Width() == brd.Height()); 00136 00137 if (!brd.IsLocation(p)) return p; 00138 00139 if (HexPointUtil::isEdge(p)) { 00140 if (HexPointUtil::isColorEdge(p, VERTICAL_COLOR)) 00141 return HexPointUtil::rightEdge(p); 00142 else 00143 return HexPointUtil::leftEdge(p); 00144 } 00145 00146 int x, y; 00147 HexPointUtil::pointToCoords(p, x, y); 00148 return HexPointUtil::coordsToPoint(y, x); 00149 } 00150 00151 HexPoint BoardUtils::CenterPoint(const ConstBoard& brd) 00152 { 00153 HexAssert((brd.Width() & 1) && (brd.Height() & 1)); 00154 return CenterPointRight(brd); 00155 } 00156 00157 HexPoint BoardUtils::CenterPointRight(const ConstBoard& brd) 00158 { 00159 int x = brd.Width() / 2; 00160 int y = brd.Height() / 2; 00161 00162 if (!(brd.Width() & 1) && !(brd.Height() & 1)) y--; 00163 00164 return HexPointUtil::coordsToPoint(x, y); 00165 } 00166 00167 HexPoint BoardUtils::CenterPointLeft(const ConstBoard& brd) 00168 { 00169 int x = brd.Width() / 2; 00170 int y = brd.Height() / 2; 00171 00172 if (!(brd.Width() & 1)) x--; 00173 if ((brd.Width() & 1) && !(brd.Height() & 1)) y--; 00174 00175 return HexPointUtil::coordsToPoint(x, y); 00176 } 00177 00178 HexPoint BoardUtils::RandomEmptyCell(const StoneBoard& brd) 00179 { 00180 bitset_t moves = brd.GetEmpty() & brd.Const().GetCells(); 00181 int count = static_cast<int>(moves.count()); 00182 if (count == 0) 00183 return INVALID_POINT; 00184 00185 int randMove = SgRandom::Global().Int(count) + 1; 00186 for (BitsetIterator p(moves); p; ++p) 00187 if (--randMove==0) return *p; 00188 00189 HexAssert(false); 00190 return INVALID_POINT; 00191 } 00192 00193 //---------------------------------------------------------------------------- 00194 00195 bitset_t BoardUtils::PackBitset(const ConstBoard& brd, 00196 const bitset_t& in) 00197 { 00198 int j=0; 00199 bitset_t ret; 00200 for (BoardIterator it(brd.Interior()); it; ++it, ++j) { 00201 if (in.test(*it)) 00202 ret.set(j); 00203 } 00204 return ret; 00205 } 00206 00207 bitset_t BoardUtils::UnpackBitset(const ConstBoard& brd, 00208 const bitset_t& in) 00209 { 00210 int j=0; 00211 bitset_t ret; 00212 for (BoardIterator it(brd.Interior()); it; ++it, ++j) { 00213 if (in.test(j)) 00214 ret.set(*it); 00215 } 00216 return ret; 00217 } 00218 00219 bitset_t BoardUtils::Rotate(const ConstBoard& brd, 00220 const bitset_t& bs) 00221 { 00222 bitset_t ret; 00223 for (BitsetIterator it(bs); it; ++it) { 00224 ret.set(Rotate(brd, *it)); 00225 } 00226 return ret; 00227 } 00228 00229 bitset_t BoardUtils::Mirror(const ConstBoard& brd, 00230 const bitset_t& bs) 00231 { 00232 bitset_t ret; 00233 for (BitsetIterator it(bs); it; ++it) { 00234 ret.set(Mirror(brd, *it)); 00235 } 00236 return ret; 00237 } 00238 00239 bool BoardUtils::ShiftBitset(const ConstBoard& brd, const bitset_t& bs, 00240 HexDirection dir, bitset_t& out) 00241 { 00242 out.reset(); 00243 bool still_inside = true; 00244 for (BitsetIterator p(bs); p; ++p) { 00245 HexPoint s = PointInDir(brd, *p, dir); 00246 if (!HexPointUtil::isEdge(*p) && HexPointUtil::isEdge(s)) 00247 still_inside = false; 00248 out.set(s); 00249 } 00250 return still_inside; 00251 } 00252 00253 bool BoardUtils::ConnectedOnBitset(const ConstBoard& brd, 00254 const bitset_t& carrier, 00255 HexPoint p1, HexPoint p2) 00256 { 00257 HexAssert(carrier.test(p1)); 00258 HexAssert(carrier.test(p2)); 00259 bitset_t seen = ReachableOnBitset(brd, carrier, EMPTY_BITSET, p1); 00260 return seen.test(p2); 00261 } 00262 00263 bitset_t BoardUtils::ReachableOnBitset(const ConstBoard& brd, 00264 const bitset_t& carrier, 00265 const bitset_t& stopset, 00266 HexPoint start) 00267 { 00268 HexAssert(carrier.test(start)); 00269 bitset_t seen; 00270 std::queue<HexPoint> q; 00271 q.push(start); 00272 seen.set(start); 00273 while (!q.empty()) 00274 { 00275 HexPoint p = q.front(); 00276 q.pop(); 00277 if (stopset.test(p)) 00278 continue; 00279 for (BoardIterator nb(brd.Nbs(p)); nb; ++nb) 00280 { 00281 if (carrier.test(*nb) && !seen.test(*nb)) 00282 { 00283 q.push(*nb); 00284 seen.set(*nb); 00285 } 00286 } 00287 } 00288 return seen; 00289 } 00290 00291 //---------------------------------------------------------------------------- 00292 00293 void BoardUtils::InitializeDecompositions() 00294 { 00295 InitializeOppMiai(); 00296 g_BoardUtilsDecompsInitialized = true; 00297 } 00298 00299 bool BoardUtils::FindCombinatorialDecomposition(const HexBoard& brd, 00300 HexColor color, 00301 bitset_t& captured) 00302 { 00303 // If game is over or decided, don't do any work. 00304 HexPoint edge1 = HexPointUtil::colorEdge1(color); 00305 HexPoint edge2 = HexPointUtil::colorEdge2(color); 00306 const VCSet& cons = brd.Cons(color); 00307 if (brd.GetGroups().IsGameOver() || cons.Exists(edge1, edge2, VC::FULL)) 00308 { 00309 captured.reset(); 00310 return false; 00311 } 00312 00313 /** Compute neighbouring groups of opposite color. 00314 00315 @note Assumes that edges that touch are adjacent. See 00316 ConstBoard for more details. 00317 */ 00318 PointToBitset adjTo; 00319 PointToBitset adjByMiai; 00320 ComputeAdjacentByMiai(brd, adjByMiai); 00321 for (GroupIterator g(brd.GetGroups(), color); g; ++g) 00322 { 00323 bitset_t opptNbs = adjByMiai[g->Captain()] 00324 | (g->Nbs() & brd.GetPosition().GetColor(!color)); 00325 if (opptNbs.count() >= 2) 00326 adjTo[g->Captain()] = opptNbs; 00327 } 00328 // The two color edges are in the list. If no other groups are, then quit. 00329 HexAssert(adjTo.size() >= 2); 00330 if (adjTo.size() == 2) 00331 { 00332 captured.reset(); 00333 return false; 00334 } 00335 00336 // Compute graph representing board from color's perspective. 00337 PointToBitset graphNbs; 00338 GraphUtils::ComputeDigraph(brd.GetGroups(), color, graphNbs); 00339 00340 // Find (ordered) pairs of color groups that are VC-connected and have at 00341 // least two adjacent opponent groups in common. 00342 PointToBitset::iterator g1, g2; 00343 for (g1 = adjTo.begin(); g1 != adjTo.end(); ++g1) { 00344 for (g2 = adjTo.begin(); g2 != g1; ++g2) { 00345 if ((g1->second & g2->second).count() < 2) continue; 00346 if (!cons.Exists(g1->first, g2->first, VC::FULL)) continue; 00347 00348 // This is such a pair, so at least one of the two is not an edge. 00349 // Find which color edges are not equal to either of these groups. 00350 HexAssert(!HexPointUtil::isEdge(g1->first) || 00351 !HexPointUtil::isEdge(g2->first)); 00352 bool edge1Free = (g1->first != edge1 && g2->first != edge1); 00353 bool edge2Free = (g1->first != edge2 && g2->first != edge2); 00354 00355 // Find the set of empty cells bounded by these two groups. 00356 bitset_t stopSet = graphNbs[g1->first] | graphNbs[g2->first]; 00357 bitset_t decompArea; 00358 decompArea.reset(); 00359 if (edge1Free) 00360 decompArea |= GraphUtils::BFS(edge1, graphNbs, stopSet); 00361 if (edge2Free) 00362 decompArea |= GraphUtils::BFS(edge2, graphNbs, stopSet); 00363 decompArea.flip(); 00364 decompArea &= brd.GetPosition().GetEmpty(); 00365 00366 // If the pair has a VC confined to these cells, then we have 00367 // a decomposition - return it. 00368 const VCList& vl = cons.GetList(VC::FULL, g1->first, g2->first); 00369 for (VCList::const_iterator vi=vl.begin(); vi!=vl.end(); ++vi) { 00370 if (BitsetUtil::IsSubsetOf(vi->carrier(), decompArea)) { 00371 captured = vi->carrier(); 00372 return true; 00373 } 00374 } 00375 } 00376 } 00377 00378 // No combinatorial decomposition with a VC was found. 00379 captured.reset(); 00380 return false; 00381 } 00382 00383 bool BoardUtils::FindSplittingDecomposition(const HexBoard& brd, 00384 HexColor color, 00385 HexPoint& group) 00386 { 00387 // compute nbrs of color edges 00388 PointToBitset adjByMiai; 00389 ComputeAdjacentByMiai(brd, adjByMiai); 00390 const Groups& groups = brd.GetGroups(); 00391 HexPoint edge1 = HexPointUtil::colorEdge1(!color); 00392 HexPoint edge2 = HexPointUtil::colorEdge2(!color); 00393 bitset_t adjto1 = adjByMiai[edge1] | groups.Nbs(edge1, color); 00394 bitset_t adjto2 = adjByMiai[edge2] | groups.Nbs(edge2, color); 00395 00396 // @note must & with getCells() because we want non-edge groups; 00397 // this assumes that edges are always captains. 00398 bitset_t adjToBothEdges = adjto1 & adjto2 & brd.Const().GetCells(); 00399 00400 // if there is a group adjacent to both opponent edges, return it. 00401 if (adjToBothEdges.any()) 00402 { 00403 group = groups.CaptainOf(static_cast<HexPoint> 00404 (BitsetUtil::FirstSetBit(adjToBothEdges))); 00405 return true; 00406 } 00407 return false; 00408 } 00409 00410 //---------------------------------------------------------------------------- 00411 00412 std::string BoardUtils::GuiDumpOutsideConsiderSet(const StoneBoard& brd, 00413 const bitset_t& consider, 00414 const bitset_t& remove) 00415 { 00416 std::ostringstream os; 00417 bitset_t outside = brd.GetEmpty() - (remove | consider); 00418 for (BitsetIterator p(outside); p; ++p) 00419 os << " " << *p << " x"; 00420 return os.str(); 00421 } 00422 00423 //----------------------------------------------------------------------------