Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

ICEngine.cpp

Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 /** @file ICEngine.cpp
00003  */
00004 //----------------------------------------------------------------------------
00005 
00006 #include "Time.hpp"
00007 #include "BitsetIterator.hpp"
00008 #include "ICEngine.hpp"
00009 #include "BoardUtils.hpp"
00010 
00011 #include "boost/filesystem/path.hpp"
00012 
00013 using namespace benzene;
00014 
00015 //----------------------------------------------------------------------------
00016 
00017 namespace {
00018 
00019 //----------------------------------------------------------------------------
00020    
00021 /** Returns set of cells not reachable from either edge. These areas
00022     are dead, but may not be identified via patterns, etc. Thus we use
00023     a BFS-type algorithm, checking which areas we can reach from an
00024     edge without going through the opposite edge or stones of the
00025     opponent's colour. Note that if the game is already decided, all
00026     remaining empty cells are dead.
00027 */
00028 bitset_t ComputeEdgeUnreachableRegions(const StoneBoard& brd, HexColor c,
00029                                        const bitset_t& stopSet,
00030                                        bool flowFrom1=true,
00031                                        bool flowFrom2=true)
00032 {
00033     bitset_t reachable1, reachable2;
00034     bitset_t flowSet = (brd.GetEmpty() | brd.GetColor(c)) 
00035         & brd.Const().GetCells();
00036     if (flowFrom1) 
00037     {
00038         bitset_t flowSet1 = flowSet;
00039         flowSet1.set(HexPointUtil::colorEdge1(c));
00040         reachable1 
00041             = BoardUtils::ReachableOnBitset(brd.Const(), flowSet1, stopSet,
00042                                             HexPointUtil::colorEdge1(c));
00043     }
00044     if (flowFrom2) 
00045     {
00046         bitset_t flowSet2 = flowSet;
00047         flowSet2.set(HexPointUtil::colorEdge2(c));
00048         reachable2 
00049             = BoardUtils::ReachableOnBitset(brd.Const(), flowSet2, stopSet,
00050                                             HexPointUtil::colorEdge2(c));
00051     }
00052     return brd.GetEmpty() - (reachable1 | reachable2);
00053 }
00054 
00055 /** Computes dead regions on the board created by a single group's
00056     neighbour set. This finds dead regions that cannot be identified
00057     using only local patterns/properties.
00058 */
00059 bitset_t ComputeDeadRegions(const Groups& groups)
00060 {
00061     const StoneBoard& brd = groups.Board();
00062     if (groups.IsGameOver())
00063     return brd.GetEmpty();
00064     
00065     bitset_t dead;
00066     for (GroupIterator i(groups, NOT_EMPTY); i; ++i) 
00067     {
00068         /** @note We believe single stone groups cannot isolate regions by
00069         themselves (i.e. they need to be combined with a non-singleton
00070         group to create a dead region. This should be proven [Phil]. */
00071     if (i->Size() == 1)
00072             continue;
00073     
00074     HexColor c = i->Color();
00075     HexAssert(HexColorUtil::isBlackWhite(c));
00076     
00077     /** Compute which empty cells are reachable from the edges when we
00078         cannot go through this group's empty neighbours (which form a
00079         clique). If the clique covers one edge, we only compute
00080         reachability from the opposite edge. */
00081     bitset_t cliqueCutset = i->Nbs() & brd.GetEmpty();
00082     dead |= ComputeEdgeUnreachableRegions(brd, c, cliqueCutset,
00083                                  i->Captain() != HexPointUtil::colorEdge1(c),
00084                                  i->Captain() != HexPointUtil::colorEdge2(c));
00085     }
00086     
00087     // Areas not reachable due to one or more clique cutsets are dead.
00088     HexAssert(BitsetUtil::IsSubsetOf(dead, brd.GetEmpty()));
00089     return dead;
00090 }
00091 
00092 /** Finds dead regions formed by one group as well as a single cell adjacent
00093     to two of the group's neighbours (but not the group itself).
00094 */
00095 bitset_t FindType1Cliques(const Groups& groups)
00096 {
00097     bitset_t dead;
00098     const StoneBoard& brd = groups.Board();
00099     bitset_t empty = brd.GetEmpty();
00100     
00101     // Find two cells that are adjacent through some group, but not directly.
00102     for (BitsetIterator x(empty); x; ++x) {
00103     for (BitsetIterator y(empty); *y != *x; ++y) {
00104         if (brd.Const().Adjacent(*x, *y)) continue;
00105         bitset_t xyNbs 
00106                 = groups.Nbs(*x, NOT_EMPTY) & groups.Nbs(*y, NOT_EMPTY);
00107         if (xyNbs.none()) continue;
00108         
00109         // Find a 3rd cell directly adjacent to the first two, but not
00110         // adjacent to some group that connects them.
00111         for (BitsetIterator z(empty); z; ++z) {
00112         if (!brd.Const().Adjacent(*x, *z)) continue;
00113         if (!brd.Const().Adjacent(*y, *z)) continue;
00114         HexAssert(*x != *z);
00115         HexAssert(*y != *z);
00116         bitset_t xyExclusiveNbs = xyNbs - groups.Nbs(*z, NOT_EMPTY);
00117         if (xyExclusiveNbs.none()) continue;
00118         
00119         // The 3 cells x,y,z form a clique.
00120         bitset_t clique;
00121         clique.set(*x);
00122         clique.set(*y);
00123         clique.set(*z);
00124         
00125         // The specific group(s) common to x and y do not affect the
00126         // stop set, so we check reachability at most once per color.
00127         if ((xyExclusiveNbs & brd.GetBlack()).any()) {
00128             dead |= ComputeEdgeUnreachableRegions(brd, BLACK, clique);
00129             HexAssert(BitsetUtil::IsSubsetOf(dead, empty));
00130         }
00131         if ((xyExclusiveNbs & brd.GetWhite()).any()) {
00132             dead |= ComputeEdgeUnreachableRegions(brd, WHITE, clique);
00133             HexAssert(BitsetUtil::IsSubsetOf(dead, empty));
00134         }
00135         }
00136     }
00137     }
00138     HexAssert(BitsetUtil::IsSubsetOf(dead, empty));
00139     return dead;
00140 }
00141 
00142 /** Finds dead regions formed by two groups of the same color, using
00143     common empty neighbours and a direct adjacency between two of
00144     their exclusive neighbours.
00145 */
00146 bitset_t FindType2Cliques(const Groups& groups)
00147 {
00148     const StoneBoard& brd = groups.Board();
00149     bitset_t dead;
00150     bitset_t empty = brd.GetEmpty();
00151     
00152     // Find two non-edge groups of the same color with both common
00153     // empty neighbours in common and also exclusive empty neighbours.
00154     for (BWIterator c; c; ++c) {
00155     for (GroupIterator g1(groups, *c); g1; ++g1) {
00156         if (HexPointUtil::isEdge(g1->Captain())) continue;
00157         bitset_t g1_nbs = groups.Nbs(*g1, EMPTY);
00158         
00159         for (GroupIterator g2(groups, *c); &*g2 != &*g1; ++g2) {
00160                 if (HexPointUtil::isEdge(g2->Captain())) continue;
00161         bitset_t g2_nbs = groups.Nbs(*g2, EMPTY);
00162         if ((g1_nbs & g2_nbs).none()) continue;
00163         
00164         bitset_t g1Exclusive = g1_nbs - g2_nbs;
00165         if (g1Exclusive.none()) continue;
00166         bitset_t g2Exclusive = g2_nbs - g1_nbs;
00167         if (g2Exclusive.none()) continue;
00168         
00169         // Now find two cells exclusive neighbours of these two
00170         // groups that are directly adjacent to one another.
00171         for (BitsetIterator x(g1Exclusive); x; ++x) {
00172             for (BitsetIterator y(g2Exclusive); y; ++y) {
00173             if (!brd.Const().Adjacent(*x, *y)) continue;
00174             
00175             // Cells x, y and the common neighbours of
00176             // groups g1, g2 form a clique.
00177             bitset_t clique = g1_nbs & g2_nbs;
00178             clique.set(*x);
00179             clique.set(*y);
00180             dead |= ComputeEdgeUnreachableRegions(brd, *c, clique);
00181             HexAssert(BitsetUtil::IsSubsetOf(dead, empty));
00182             }
00183         }
00184         }
00185     }
00186     }
00187     HexAssert(BitsetUtil::IsSubsetOf(dead, empty));
00188     return dead;
00189 }
00190 
00191 /** Finds dead regions cutoff by cliques created by 3 groups of the
00192     same color.
00193 */
00194 bitset_t FindType3Cliques(const Groups& groups)
00195 {
00196     const StoneBoard& brd = groups.Board();
00197     bitset_t dead;
00198     bitset_t empty = brd.GetEmpty();
00199     
00200     // Find 3 non-edge groups of the same color such that each pair has
00201     // a non-empty intersection of their empty neighbours.
00202     for (BWIterator c; c; ++c) {
00203     for (GroupIterator g1(groups, *c); g1; ++g1) {
00204         if (HexPointUtil::isEdge(g1->Captain())) continue;
00205         bitset_t g1_nbs = groups.Nbs(*g1, EMPTY);
00206         
00207         for (GroupIterator g2(groups, *c); &*g2 != &*g1; ++g2) {
00208         if (HexPointUtil::isEdge(g2->Captain())) continue;
00209         bitset_t g2_nbs = groups.Nbs(*g2, EMPTY);
00210         if ((g1_nbs & g2_nbs).none()) continue;
00211         
00212         for (GroupIterator g3(groups, *c); &*g3 != &*g2; ++g3) {
00213             if (HexPointUtil::isEdge(g3->Captain())) continue;
00214             bitset_t g3_nbs = groups.Nbs(*g3, EMPTY);
00215             if ((g1_nbs & g3_nbs).none()) continue;
00216             if ((g2_nbs & g3_nbs).none()) continue;
00217             
00218             // The union of the pairwise neighbour intersections
00219             // of groups g1, g2, g3 form a clique.
00220             bitset_t clique;
00221             clique = (g1_nbs & g2_nbs) |
00222                  (g1_nbs & g3_nbs) |
00223                  (g2_nbs & g3_nbs);
00224             dead |= ComputeEdgeUnreachableRegions(brd, *c, clique);
00225             HexAssert(BitsetUtil::IsSubsetOf(dead, empty));
00226         }
00227         }
00228     }
00229     }
00230     HexAssert(BitsetUtil::IsSubsetOf(dead, empty));
00231     return dead;
00232 }
00233 
00234 /** Computes dead regions on the board separated via a clique cutset
00235     composed of the intersection of three known maximal
00236     cliques. Returns the union of calls to FindType1Cliques(),
00237     FindType2Cliques(), and FindType3Cliques().
00238     
00239     This finds additional regions not identified via local patterns.
00240 */
00241 bitset_t FindThreeSetCliques(const Groups& groups)
00242 {
00243     if (groups.IsGameOver())
00244     return groups.Board().GetEmpty();
00245 
00246     bitset_t dead1 = FindType1Cliques(groups);
00247     bitset_t dead2 = FindType2Cliques(groups);
00248     bitset_t dead3 = FindType3Cliques(groups);
00249 
00250     return dead1 | dead2 | dead3;
00251 }
00252 
00253 //----------------------------------------------------------------------------
00254 
00255 /** Returns true if the vector of points forms a clique on brd, while
00256     excluding any checks on exclude (to find pre-simplicial cells
00257     exclude should be in vn). */
00258 bool IsClique(const ConstBoard& brd, const std::vector<HexPoint>& vn, 
00259               HexPoint exclude=INVALID_POINT)
00260 {
00261     for (unsigned a=0; a<vn.size(); ++a) {
00262         if (vn[a] == exclude) continue;
00263         for (unsigned b=a+1; b<vn.size(); ++b) {
00264             if (vn[b] == exclude) continue;
00265             if (!brd.Adjacent(static_cast<HexPoint>(vn[a]),
00266                               static_cast<HexPoint>(vn[b])))
00267                 return false;
00268         }
00269     }
00270     return true;
00271 }
00272 
00273 /** Finds dead and vulnerable cells using graph theory; ie, not using
00274     local patterns. The board will have any found dead cells
00275     filled-in. */
00276 void UseGraphTheoryToFindDeadVulnerable(HexColor color, Groups& groups,
00277                                         PatternState& pastate,
00278                                         InferiorCells& inf)
00279 {
00280     StoneBoard& brd = groups.Board();
00281     bitset_t simplicial;
00282     bitset_t adj_to_both_edges = 
00283         groups.Nbs(HexPointUtil::colorEdge1(color), EMPTY) &
00284         groups.Nbs(HexPointUtil::colorEdge2(color), EMPTY);
00285     bitset_t consider = brd.GetEmpty();
00286     consider = consider - adj_to_both_edges;
00287     
00288     // find presimplicial cells and their dominators
00289     for (BitsetIterator p(consider); p; ++p) 
00290     {
00291         std::set<HexPoint> enbs, cnbs;
00292         bitset_t empty_adj_to_group;
00293         bool adj_to_edge = false;
00294     HexPoint edgeNbr = INVALID_POINT;
00295     
00296         // Categorize neighbours as either 'empty' or 'color'. 
00297         for (BoardIterator nb(brd.Const().Nbs(*p)); nb; ++nb) 
00298         {
00299             HexColor ncolor = brd.GetColor(*nb);
00300             if (ncolor == EMPTY) 
00301             {
00302                 enbs.insert(*nb);
00303             } 
00304             else if (ncolor == color) 
00305             {
00306                 HexPoint cap = groups.CaptainOf(*nb);
00307                 bitset_t adj = groups.Nbs(cap, EMPTY);
00308                 adj.reset(*p);
00309         
00310                 // Ignore color groups with no empty neighbours (after
00311                 // removing *p).  If color group has one non-*p
00312                 // neighbour, store it as an empty neighbour.
00313                 // Otherwise, add as a color group (helps us to
00314                 // identify cliques later).  Lastly, edges are a
00315                 // special case - always added as a group.
00316                 if (HexPointUtil::isColorEdge(cap, color)) 
00317                 {
00318             HexAssert(!adj_to_edge || edgeNbr == cap);
00319                     adj_to_edge = true;
00320             edgeNbr = cap;
00321                     cnbs.insert(cap);
00322                     empty_adj_to_group |= adj;
00323                 } 
00324                 else if (adj.count() == 1) 
00325                 {
00326                     enbs.insert(static_cast<HexPoint>
00327                                 (BitsetUtil::FindSetBit(adj)));
00328                 } 
00329                 else if (adj.count() >= 2) 
00330                 {
00331                     cnbs.insert(cap);
00332                     empty_adj_to_group |= adj;
00333                 }
00334             }
00335         }
00336         
00337         // Remove empty neighbours that are adjacent to a color neighbour.
00338         std::set<HexPoint>::iterator it;
00339         for (it = enbs.begin(); it != enbs.end(); ) 
00340         {
00341             if (empty_adj_to_group.test(*it)) 
00342             {
00343                 enbs.erase(it);
00344                 it = enbs.begin();
00345             } 
00346             else 
00347                 ++it;
00348         }
00349     
00350         ////////////////////////////////////////////////////////////
00351         // if adjacent to one empty cell, or a single group of
00352         // your color, then neighbours are a clique, so *p is dead.
00353         if (enbs.size() + cnbs.size() <= 1)
00354             simplicial.set(*p);
00355         // Handle cells adjacent to the edge and those adjacent to
00356         // multiple groups of color (2 or 3). Need to test whether
00357         // the edge/a group's neighbours include all other groups'
00358         // neighbours, possibly omitting one. This, along with at most
00359         // one empty neighbour, makes the cell dead or vulnerable.
00360         else if (adj_to_edge || cnbs.size() >= 2) 
00361         {
00362         if (enbs.size() >= 2) 
00363                 continue;
00364         
00365         if (cnbs.size() == 1) 
00366             {
00367         HexAssert(adj_to_edge && enbs.size() == 1);
00368                 inf.AddVulnerable(*p, *enbs.begin());
00369         } 
00370             else 
00371             {
00372         HexAssert(!adj_to_edge || 
00373                           HexPointUtil::isColorEdge(edgeNbr, color));
00374 
00375         bitset_t killers_bs;
00376         bool isPreSimp = false;
00377         
00378         // Determine if *p is dead (flag if vulnerable)
00379         for (std::set<HexPoint>::iterator i = cnbs.begin();
00380                      i != cnbs.end(); ++i) 
00381                 {
00382             // When adjacent to the edge, only the edge can
00383             // trump other groups' adjacencies.
00384             if (adj_to_edge && *i != edgeNbr) continue;
00385 
00386             bitset_t remainingNbs = 
00387                         empty_adj_to_group - groups.Nbs(*i, EMPTY);
00388             
00389             if (remainingNbs.count() == 0) 
00390                     {
00391             if (enbs.size() == 0)
00392                 simplicial.set(*p);
00393                         else 
00394                         {
00395                 HexAssert(enbs.size() == 1);
00396                 isPreSimp = true;
00397                 killers_bs.set(*enbs.begin());
00398             }
00399             } 
00400                     else if (remainingNbs.count() == 1 && enbs.size() == 0) 
00401                     {
00402             isPreSimp = true;
00403             killers_bs.set(BitsetUtil::FindSetBit(remainingNbs));
00404             }
00405         }
00406         
00407                 if (!simplicial.test(*p) && isPreSimp) 
00408                 {
00409             HexAssert(killers_bs.any());
00410                     for (BitsetIterator k(killers_bs); k; ++k)
00411                         inf.AddVulnerable(*p, *k);
00412         }
00413         }
00414 
00415         }
00416     // If many neighbours and previous cases didn't apply, 
00417         // then most likely *p is not dead or vulnerable.
00418     else if (enbs.size() + cnbs.size() >= 4) 
00419         {
00420             // do nothing
00421     }
00422         // If adjacent to one group and some empty cells, then *p
00423     // cannot be dead, but might be vulnerable.
00424     else if (cnbs.size() == 1) 
00425         {
00426         if (enbs.size() > 1) 
00427                 continue;
00428 
00429         HexAssert(enbs.size() == 1);
00430         HexAssert(empty_adj_to_group.count() >= 2);
00431         
00432         // The single empty neighbour always kills *p
00433             inf.AddVulnerable(*p, *enbs.begin());
00434         
00435         if (empty_adj_to_group.count() == 2) 
00436             {
00437         // If the single group has only two neighbours, it is
00438         // possible that one or both of its neighbours are
00439         // adjacent to the single direct neighbour, causing us
00440         // to have more killers of *p
00441         HexPoint omit = *enbs.begin();
00442                 for (BitsetIterator i(empty_adj_to_group); i; ++i)
00443                     enbs.insert(*i);
00444         
00445         // determine the additional killers of this vulnerable
00446         // cell
00447         std::vector<HexPoint> vn(enbs.begin(), enbs.end());
00448         for (unsigned ex=0; ex<vn.size(); ++ex) 
00449                 {
00450             if (vn[ex] == omit) 
00451                         continue;
00452             if (IsClique(brd.Const(), vn, vn[ex]))
00453                         inf.AddVulnerable(*p, vn[ex]);
00454         }
00455         }
00456     }
00457         else 
00458         {
00459             // If all empty neighbours form a clique, is dead. Otherwise
00460             // check if eliminating one makes the rest a clique.
00461             HexAssert(cnbs.size() == 0);
00462             std::vector<HexPoint> vn(enbs.begin(), enbs.end());
00463 
00464             if (IsClique(brd.Const(), vn))
00465                 simplicial.set(*p);
00466             else 
00467             {
00468                 for (unsigned ex=0; ex<vn.size(); ++ex)
00469                     if (IsClique(brd.Const(), vn, vn[ex]))
00470                         inf.AddVulnerable(*p, vn[ex]);
00471             }
00472         }
00473     }
00474     // Add the simplicial stones to the board
00475     if (simplicial.any())
00476     {
00477         inf.AddDead(simplicial);
00478         brd.AddColor(DEAD_COLOR, simplicial);
00479         pastate.Update(simplicial);
00480         GroupBuilder::Build(brd, groups);
00481     }
00482 }
00483 
00484 //----------------------------------------------------------------------------
00485 
00486 }  // anonymous namespace
00487 
00488 //----------------------------------------------------------------------------
00489 
00490 ICEngine::ICEngine()
00491     : m_find_presimplicial_pairs(true),
00492       m_find_permanently_inferior(true),
00493       m_find_mutual_fillin(false),
00494       m_find_all_pattern_killers(true),
00495       m_find_all_pattern_reversers(false),
00496       m_find_all_pattern_dominators(false),
00497       m_use_handcoded_patterns(true),
00498       m_backup_opponent_dead(false),
00499       m_find_three_sided_dead_regions(false),
00500       m_iterative_dead_regions(false)
00501 {
00502     LoadHandCodedPatterns();
00503     LoadPatterns();
00504 }
00505 
00506 ICEngine::~ICEngine()
00507 {
00508 }
00509 
00510 //----------------------------------------------------------------------------
00511 
00512 /** Creates the set of hand-coded patterns. */
00513 void ICEngine::LoadHandCodedPatterns()
00514 {
00515     HandCodedPattern::CreatePatterns(m_hand_coded);
00516     LogFine() << "ICEngine: " << m_hand_coded.size()
00517               << " hand coded patterns.\n";
00518 }
00519 
00520 /** Loads local patterns from "ice-pattern-file". */
00521 void ICEngine::LoadPatterns()
00522 {
00523 #ifdef ABS_TOP_SRCDIR
00524     m_patterns.LoadPatterns(boost::filesystem::path(ABS_TOP_SRCDIR) 
00525                             / "share" / "ice-patterns.txt");
00526 #else
00527     LogWarning() << "**** NO ICE PATTERNS LOADED ***\n";
00528 #endif
00529 }    
00530 
00531 //----------------------------------------------------------------------------
00532 
00533 std::size_t ICEngine::ComputeDeadCaptured(Groups& groups, PatternState& pastate,
00534                                           InferiorCells& inf, 
00535                                           HexColorSet colors_to_capture) const
00536 {
00537     StoneBoard& brd = groups.Board();
00538     // find dead and captured cells and fill them in. 
00539     std::size_t count = 0;
00540     while (true) 
00541     {
00542         // search for dead; if some are found, fill them in
00543         // and iterate again.
00544         while (true) 
00545         {
00546             /** @todo This can be optimized quite a bit. */
00547             bitset_t dead = FindDead(pastate, brd.GetEmpty());
00548             if (dead.none()) 
00549                 break;
00550             count += dead.count();
00551             inf.AddDead(dead);
00552             brd.AddColor(DEAD_COLOR, dead);
00553             pastate.Update(dead);
00554         }
00555 
00556         // search for black captured cells; if some are found,
00557         // fill them in and go back to look for more dead. 
00558         {
00559             bitset_t black;
00560             if (HexColorSetUtil::InSet(BLACK, colors_to_capture))
00561                 black = FindCaptured(pastate, BLACK, brd.GetEmpty());
00562             if (black.any()) 
00563             {
00564                 count += black.count();
00565                 inf.AddCaptured(BLACK, black);
00566                 brd.AddColor(BLACK, black);
00567                 pastate.Update(black);
00568                 continue;
00569             }
00570         }
00571 
00572         // search for white captured cells; if some are found, fill
00573         // them in and go back to look for more dead/black captured.
00574         {
00575             bitset_t white;
00576             if (HexColorSetUtil::InSet(WHITE, colors_to_capture))
00577                 white = FindCaptured(pastate, WHITE, brd.GetEmpty());
00578             if (white.any()) 
00579             {
00580                 count += white.count();
00581                 inf.AddCaptured(WHITE, white);
00582                 brd.AddColor(WHITE, white);
00583                 pastate.Update(white);
00584                 continue;
00585             }
00586         }
00587         // did not find any fillin, so abort.
00588         break;
00589     }
00590     if (count)
00591         GroupBuilder::Build(brd, groups);
00592     return count;
00593 }
00594 
00595 /** Calls FindPermanentlyInferior() and adds any found to the board
00596     and the set of inferior cells.x */
00597 std::size_t ICEngine::FillinPermanentlyInferior(Groups& groups, 
00598                                          PatternState& pastate,
00599                                          HexColor color, InferiorCells& out, 
00600                                          HexColorSet colors_to_capture) const
00601 {
00602     if (!m_find_permanently_inferior) 
00603         return 0;
00604     if (!HexColorSetUtil::InSet(color, colors_to_capture)) 
00605         return 0;
00606     StoneBoard& brd = groups.Board();
00607     bitset_t carrier;
00608     bitset_t perm = FindPermanentlyInferior(pastate, color, brd.GetEmpty(), 
00609                                             carrier);
00610     if (perm.any())
00611     {
00612         out.AddPermInf(color, perm, carrier);
00613         brd.AddColor(color, perm);
00614         pastate.Update(perm);
00615         GroupBuilder::Build(brd, groups);
00616     }
00617     return perm.count();
00618 }
00619 
00620 /** Calls FindMutualFillin() and adds any found to the board and the
00621     set of inferior cells.x */
00622 std::size_t ICEngine::FillInMutualFillin(Groups& groups, PatternState& pastate,
00623                                          HexColor color, InferiorCells& out, 
00624                                          HexColorSet colors_to_capture) const
00625 {
00626     if (!m_find_mutual_fillin)
00627         return 0;
00628     /** Can only use mutual fillin when both colours can be captured. */
00629     if (!HexColorSetUtil::InSet(BLACK, colors_to_capture) ||
00630         !HexColorSetUtil::InSet(WHITE, colors_to_capture))
00631         return 0;
00632     StoneBoard& brd = groups.Board();
00633     bitset_t carrier;
00634     bitset_t mut[BLACK_AND_WHITE];
00635     FindMutualFillin(pastate, color, brd.GetEmpty(), carrier, mut);
00636     if (mut[BLACK].any())
00637     {
00638         HexAssert(mut[WHITE].any());
00639         HexAssert((mut[BLACK] & mut[WHITE]).none());
00640         /** Note: mutual fillin carrier is same for both colors (* cells). */
00641         out.AddMutualFillin(BLACK, mut[BLACK], carrier);
00642         brd.AddColor(BLACK, mut[BLACK]);
00643         pastate.Update(mut[BLACK]);
00644         out.AddMutualFillin(WHITE, mut[WHITE], carrier);
00645         brd.AddColor(WHITE, mut[WHITE]);
00646         pastate.Update(mut[WHITE]);
00647         GroupBuilder::Build(brd, groups);
00648     }
00649     else
00650         HexAssert(mut[WHITE].none());
00651     return (mut[BLACK] | mut[WHITE]).count();
00652 }
00653 
00654 /** Finds vulnerable cells for color and finds presimplicial pairs
00655     and fills them in for the other color.  Simplicial stones will
00656     be added as dead and played to the board as DEAD_COLOR. */
00657 std::size_t ICEngine::FillInVulnerable(HexColor color, Groups& groups, 
00658                                PatternState& pastate, InferiorCells& inf, 
00659                                HexColorSet colors_to_capture) const
00660 {
00661     std::size_t count = 0;
00662     inf.ClearVulnerable();
00663 
00664     UseGraphTheoryToFindDeadVulnerable(color, groups, pastate, inf);
00665 
00666     // Find vulnerable cells with local patterns--do not ignore the
00667     // presimplicial cells previously found because a pattern
00668     // may encode another dominator.
00669     bitset_t consider = groups.Board().GetEmpty() - inf.Dead();
00670     FindVulnerable(pastate, color, consider, inf);
00671     
00672     // Fill in presimplicial pairs only if we are doing fillin for the
00673     // other player.
00674     if (HexColorSetUtil::InSet(!color, colors_to_capture)) 
00675     {
00676         bitset_t captured = inf.FindPresimplicialPairs();
00677         if (captured.any()) 
00678         {
00679             inf.AddCaptured(!color, captured);
00680             groups.Board().AddColor(!color, captured);
00681             pastate.Update(captured);
00682             GroupBuilder::Build(groups.Board(), groups);
00683         }
00684         count += captured.count();
00685     }
00686     return count;
00687 }
00688 
00689 /** Calls ComputeDeadRegions() and FindThreeSetCliques() and adds
00690     fill-in to board and set of inferior cells. */
00691 std::size_t ICEngine::CliqueCutsetDead(Groups& groups, PatternState& pastate,
00692                                        InferiorCells& out) const
00693 {
00694     bitset_t notReachable = ComputeDeadRegions(groups);
00695     if (m_find_three_sided_dead_regions)
00696         notReachable |= FindThreeSetCliques(groups);
00697     if (notReachable.any()) 
00698     {
00699         out.AddDead(notReachable);
00700         groups.Board().AddColor(DEAD_COLOR, notReachable);
00701         pastate.Update(notReachable);
00702         GroupBuilder::Build(groups.Board(), groups);
00703     }
00704     return notReachable.count();
00705 }
00706 
00707 void ICEngine::ComputeFillin(HexColor color, Groups& groups, 
00708                              PatternState& pastate, InferiorCells& out,
00709                              HexColorSet colors_to_capture) const
00710 {
00711     out.Clear();
00712     bool considerCliqueCutset = true;
00713     while(true)
00714     {
00715         std::size_t count;
00716         int iterations = 0;
00717         for (;; ++iterations)
00718         {
00719             count = 0;
00720             count += ComputeDeadCaptured(groups, pastate, out,
00721                                          colors_to_capture);
00722             count += FillinPermanentlyInferior(groups, pastate, color, out, 
00723                                                colors_to_capture);
00724             count += FillinPermanentlyInferior(groups, pastate, !color, out, 
00725                                                colors_to_capture);
00726             count += FillInMutualFillin(groups, pastate, color, out, 
00727                                         colors_to_capture);
00728             count += FillInMutualFillin(groups, pastate, !color, out, 
00729                                         colors_to_capture);
00730             count += FillInVulnerable(!color, groups, pastate, out, 
00731                                       colors_to_capture);
00732             count += FillInVulnerable(color, groups, pastate, out, 
00733                                       colors_to_capture);
00734             if (0 == count)
00735                 break;
00736             considerCliqueCutset = true;
00737         }
00738         if (m_iterative_dead_regions && considerCliqueCutset)
00739             count = CliqueCutsetDead(groups, pastate, out);
00740         if (0 == count)
00741             break;
00742         considerCliqueCutset = false;
00743     }
00744     if (!m_iterative_dead_regions)
00745         CliqueCutsetDead(groups, pastate, out);
00746 }
00747 
00748 void ICEngine::ComputeInferiorCells(HexColor color, Groups& groups,
00749                                     PatternState& pastate,
00750                                     InferiorCells& out) const
00751 {
00752 #ifndef NDEBUG
00753     HexAssert(groups.Board() == pastate.Board());
00754     StoneBoard oldBoard(groups.Board());
00755 #endif
00756     double startTime = Time::Get();
00757 
00758     ComputeFillin(color, groups, pastate, out);
00759 
00760     {
00761         // Note: We consider vulnerable cells when matching reversible patterns
00762         //       since the captured pattern applies to the entire carrier, not
00763         //       just the centre cell of the pattern.
00764         bitset_t consider = groups.Board().GetEmpty();
00765         FindReversible(pastate, color, consider, out);
00766     }
00767 
00768     {
00769         bitset_t consider = groups.Board().GetEmpty()
00770                             - out.Vulnerable()
00771                             - out.Reversible();
00772         FindDominated(pastate, color, consider, out);
00773     }
00774 
00775     // Play opponent in all empty cells, any dead they created
00776     // are actually vulnerable to the move played. 
00777     if (m_backup_opponent_dead) 
00778     {
00779         int found = BackupOpponentDead(color, groups.Board(), pastate, out);
00780         if (found) {
00781             LogFine() << "Found " << found 
00782                       << " cells vulnerable to opponent moves.\n";
00783         }
00784     }
00785     
00786     double endTime = Time::Get();
00787     LogFine() << "  " << (endTime - startTime) 
00788               << "s to find inferior cells.\n";
00789 #ifndef NDEBUG
00790     HexAssert(groups.Board().Hash() == oldBoard.Hash());
00791 #endif
00792 }
00793 
00794 /** For each empty cell on the board, the move is played with the
00795     opponent's stone (ie, !color) and the fill-in is computed.  Any
00796     dead cells in this state are backed-up as vulnerable cells in the
00797     original state, with the set of captured stones as the
00798     vulnerable-carrier.  This can be moderately expensive.
00799         
00800     @todo Link to the "ice-backup-opp-dead" option, or link it's
00801     documentation here.  
00802 */
00803 std::size_t ICEngine::BackupOpponentDead(HexColor color, 
00804                                          const StoneBoard& board,
00805                                          PatternState& pastate,
00806                                          InferiorCells& out) const
00807 {
00808     StoneBoard brd(board);
00809     PatternState ps(brd);
00810     ps.CopyState(pastate);
00811 
00812     bitset_t reversible = out.Reversible();
00813     bitset_t dominated = out.Dominated();
00814 
00815     std::size_t found = 0;
00816     for (BitsetIterator p(board.GetEmpty()); p; ++p) 
00817     {
00818         brd.StartNewGame();
00819         brd.SetColor(BLACK, board.GetBlack());
00820         brd.SetColor(WHITE, board.GetWhite());
00821         brd.PlayMove(!color, *p);
00822         ps.Update();
00823         Groups groups;
00824         GroupBuilder::Build(brd, groups);
00825 
00826         InferiorCells inf;
00827         ComputeFillin(color, groups, ps, inf);
00828         bitset_t filled = inf.Fillin(BLACK) | inf.Fillin(WHITE);
00829 
00830         for (BitsetIterator d(inf.Dead()); d; ++d) 
00831         {
00832             /** @todo Add if already vulnerable? */
00833             if (!out.Vulnerable().test(*d)
00834                 && !reversible.test(*d)
00835                 && !dominated.test(*d)) 
00836             {
00837                 bitset_t carrier = filled;
00838                 carrier.reset(*d);
00839                 carrier.reset(*p);
00840                 out.AddVulnerable(*d, VulnerableKiller(*p, carrier));
00841                 found++;
00842             }
00843         }
00844     }
00845     return found;
00846 }
00847 
00848 //----------------------------------------------------------------------------
00849 
00850 bitset_t ICEngine::FindDead(const PatternState& pastate,
00851                             const bitset_t& consider) const
00852 {
00853     return pastate.MatchOnBoard(consider, m_patterns.HashedDead());
00854 }
00855 
00856 bitset_t ICEngine::FindCaptured(const PatternState& pastate, HexColor color, 
00857                                 const bitset_t& consider) const
00858 {
00859     bitset_t captured;
00860     for (BitsetIterator p(consider); p; ++p) 
00861     {
00862         if (captured.test(*p)) 
00863             continue;
00864         
00865         PatternHits hits;
00866         pastate.MatchOnCell(m_patterns.HashedCaptured(color), *p,
00867                             PatternState::STOP_AT_FIRST_HIT, hits);
00868 
00869         // Mark carrier as captured if carrier does not intersect the
00870         // set of captured cells found in this pass. 
00871         if (!hits.empty()) 
00872         {
00873             HexAssert(hits.size() == 1);
00874             const std::vector<HexPoint>& moves = hits[0].moves2();
00875 
00876             bitset_t carrier;
00877             for (unsigned i = 0; i < moves.size(); ++i)
00878                 carrier.set(moves[i]);
00879             carrier.set(*p);
00880             if ((carrier & captured).none())
00881                 captured |= carrier;
00882         }
00883     }
00884     return captured;
00885 }
00886 
00887 bitset_t ICEngine::FindPermanentlyInferior(const PatternState& pastate, 
00888                                            HexColor color, 
00889                                            const bitset_t& consider,
00890                                            bitset_t& carrier) const
00891 {
00892     std::vector<PatternHits> hits(FIRST_INVALID);
00893     bitset_t ret = pastate.MatchOnBoard(consider, 
00894                                  m_patterns.HashedPermInf(color), 
00895                                  PatternState::STOP_AT_FIRST_HIT, hits);
00896     for (BitsetIterator p(ret); p; ++p) 
00897     {
00898         HexAssert(hits[*p].size() == 1);
00899         const std::vector<HexPoint>& moves = hits[*p][0].moves2();
00900         for (unsigned i=0; i<moves.size(); ++i)
00901             carrier.set(moves[i]);
00902     }
00903     return ret;
00904 }
00905 
00906 void ICEngine::FindMutualFillin(const PatternState& pastate, 
00907                                 HexColor color, 
00908                                 const bitset_t& consider,
00909                                 bitset_t& carrier,
00910                                 bitset_t *mut) const
00911 {
00912     bitset_t altered;
00913     for (BitsetIterator p(consider); p; ++p) 
00914     {
00915         PatternHits hits;
00916         pastate.MatchOnCell(m_patterns.HashedMutualFillin(color), *p,
00917                             PatternState::STOP_AT_FIRST_HIT, hits);
00918         if (hits.empty())
00919             continue;
00920 
00921         /** Ensure this mutual fillin pattern does not interfere with
00922             any other already-added mutual fillin.
00923          */
00924         HexAssert(hits.size() == 1);
00925         bitset_t willAlter;
00926         willAlter.set(*p);
00927         const std::vector<HexPoint>& moves1 = hits[0].moves1();
00928         for (unsigned i = 0; i < moves1.size(); ++i)
00929                 willAlter.set(moves1[i]);
00930         const std::vector<HexPoint>& moves2 = hits[0].moves2();
00931         for (unsigned i = 0; i < moves2.size(); ++i)
00932                 willAlter.set(moves2[i]);
00933         if ((willAlter & altered).any())
00934             continue;
00935 
00936         /** The mutual fillin can be added. */
00937         altered |= willAlter;
00938         carrier.set(*p);
00939         for (unsigned i = 0; i < moves1.size(); ++i)
00940             mut[color].set(moves1[i]);
00941         for (unsigned i = 0; i < moves2.size(); ++i)
00942             mut[!color].set(moves2[i]);
00943     }
00944 }
00945 
00946 void ICEngine::FindVulnerable(const PatternState& pastate, HexColor color,
00947                               const bitset_t& consider,
00948                               InferiorCells& inf) const
00949 {
00950     PatternState::MatchMode matchmode = PatternState::STOP_AT_FIRST_HIT;
00951     if (m_find_all_pattern_killers)
00952         matchmode = PatternState::MATCH_ALL;
00953 
00954     std::vector<PatternHits> hits(FIRST_INVALID);
00955     bitset_t vul = pastate.MatchOnBoard(consider, 
00956                                      m_patterns.HashedVulnerable(color),
00957                                      matchmode, hits);
00958 
00959     // Add the new vulnerable cells with their killers
00960     for (BitsetIterator p(vul); p; ++p) 
00961     {
00962         for (unsigned j=0; j<hits[*p].size(); ++j) 
00963         {
00964             const std::vector<HexPoint>& moves1 = hits[*p][j].moves1();
00965             HexAssert(moves1.size() == 1);
00966             HexPoint killer = moves1[0];
00967             
00968             bitset_t carrier;
00969             const std::vector<HexPoint>& moves2 = hits[*p][j].moves2();
00970             for (unsigned i=0; i<moves2.size(); ++i) {
00971                 carrier.set(moves2[i]);
00972             }
00973             inf.AddVulnerable(*p, VulnerableKiller(killer, carrier));
00974         }
00975     }
00976 }
00977 
00978 void ICEngine::FindReversible(const PatternState& pastate, HexColor color, 
00979                               const bitset_t& consider,
00980                               InferiorCells& inf) const
00981 {
00982     PatternState::MatchMode matchmode = PatternState::STOP_AT_FIRST_HIT;
00983     if (m_find_all_pattern_reversers)
00984         matchmode = PatternState::MATCH_ALL;
00985 
00986     // Find reversers using patterns
00987     std::vector<PatternHits> hits(FIRST_INVALID);
00988     bitset_t rev = pastate.MatchOnBoard(consider,
00989                                         m_patterns.HashedReversible(color),
00990                                         matchmode, hits);
00991     // Add the new reversible cells with their reversers
00992     for (BitsetIterator p(rev); p; ++p) 
00993     {
00994         for (unsigned j=0; j<hits[*p].size(); ++j) 
00995         {
00996             const std::vector<HexPoint>& moves1 = hits[*p][j].moves1();
00997             HexAssert(moves1.size() == 1);
00998             HexPoint reverser = moves1[0];
00999 
01000             // Carriers are mandatory for reversible patterns;
01001             // otherwise cannot check for independence
01002             HexAssert(hits[*p][j].moves2().size() != 0);
01003             bitset_t carrier;
01004             const std::vector<HexPoint>& moves2 = hits[*p][j].moves2();
01005             for (unsigned i=0; i<moves2.size(); ++i) {
01006                 carrier.set(moves2[i]);
01007             }
01008             inf.AddReversible(*p, carrier, reverser);
01009         }
01010     }
01011 }
01012 
01013 void ICEngine::FindDominated(const PatternState& pastate, HexColor color, 
01014                              const bitset_t& consider,
01015                              InferiorCells& inf) const
01016 {
01017     PatternState::MatchMode matchmode = PatternState::STOP_AT_FIRST_HIT;
01018     if (m_find_all_pattern_dominators)
01019         matchmode = PatternState::MATCH_ALL;
01020 
01021     // Find dominators using patterns
01022     std::vector<PatternHits> hits(FIRST_INVALID);
01023     bitset_t dom = pastate.MatchOnBoard(consider,
01024                                         m_patterns.HashedDominated(color),
01025                                         matchmode, hits);
01026     // Add the new dominated cells with their dominators
01027     for (BitsetIterator p(dom); p; ++p) 
01028     {
01029         for (unsigned j=0; j<hits[*p].size(); ++j) 
01030         {
01031             const std::vector<HexPoint>& moves1 = hits[*p][j].moves1();
01032             HexAssert(moves1.size() == 1);
01033             inf.AddDominated(*p, moves1[0]);
01034 
01035             // For now, no dominated patterns have carriers
01036             // Note: this can change in the future if more complex ICE
01037             // patterns are found
01038             HexAssert(hits[*p][j].moves2().size() == 0);
01039         }
01040     }
01041     // Add dominators found via hand coded patterns
01042     if (m_use_handcoded_patterns)
01043         FindHandCodedDominated(pastate.Board(), color, consider, inf);
01044 }
01045 
01046 void ICEngine::FindDominatedOnCell(const PatternState& pastate,
01047                                    HexColor color, 
01048                                    HexPoint cell,
01049                                    PatternHits& hits) const
01050 {
01051     PatternState::MatchMode matchmode = PatternState::MATCH_ALL;
01052     pastate.MatchOnCell(m_patterns.HashedDominated(color), cell,
01053                         matchmode, hits);
01054 }
01055 
01056 void ICEngine::FindHandCodedDominated(const StoneBoard& board, 
01057                                       HexColor color,
01058                                       const bitset_t& consider, 
01059                                       InferiorCells& inf) const
01060 {
01061     // If board is rectangular, these hand-coded patterns should not
01062     // be used because they need to be mirrored (which is not a valid
01063     // operation on non-square boards).
01064     if (board.Width() != board.Height()) 
01065         return;
01066     for (unsigned i=0; i<m_hand_coded.size(); ++i)
01067         CheckHandCodedDominates(board, color, m_hand_coded[i], 
01068                                 consider, inf);
01069 }
01070 
01071 /** Handles color flipping/rotations for this hand-coded pattern.  If
01072     pattern matches, dominators are added to inf. */
01073 void ICEngine::CheckHandCodedDominates(const StoneBoard& brd,
01074                                        HexColor color,
01075                                        const HandCodedPattern& pattern, 
01076                                        const bitset_t& consider, 
01077                                        InferiorCells& inf) const
01078 {
01079     if (brd.Width() < 4 || brd.Height() < 3)
01080         return;
01081     HandCodedPattern pat(pattern);
01082     // Mirror and flip colors if checking for white
01083     if (color == WHITE) 
01084     {
01085         pat.mirror(brd.Const());
01086         pat.flipColors();
01087     }
01088     // Top corner
01089     if (consider.test(pat.dominatee()) && pat.check(brd))
01090         inf.AddDominated(pat.dominatee(), pat.dominator());
01091     // Bottom corner
01092     pat.rotate(brd.Const());
01093     if (consider.test(pat.dominatee()) && pat.check(brd))
01094         inf.AddDominated(pat.dominatee(), pat.dominator());
01095 }
01096 
01097 //----------------------------------------------------------------------------
01098 
01099 void IceUtil::Update(InferiorCells& out, const InferiorCells& in)
01100 {
01101     // Overwrite old vulnerable/dominated with new vulnerable/dominated 
01102     out.ClearVulnerable();
01103     out.ClearReversible();
01104     out.ClearDominated();
01105     out.AddVulnerableFrom(in);
01106     out.AddReversibleFrom(in);
01107     out.AddDominatedFrom(in);
01108 
01109     // Add the new fillin to the old fillin
01110     for (BWIterator c; c; ++c) 
01111     {
01112         out.AddCaptured(*c, in.Captured(*c));
01113         out.AddPermInfFrom(*c, in);
01114         out.AddMutualFillinFrom(*c, in);
01115     }
01116 
01117     // Add the new dead cells.
01118     out.AddDead(in.Dead());
01119 }
01120 
01121 //----------------------------------------------------------------------------


6 Jan 2011 Doxygen 1.6.3