Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

EndgameUtils.cpp

Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 /** @file EndgameUtils.cpp
00003  */
00004 //----------------------------------------------------------------------------
00005 
00006 #include "EndgameUtils.hpp"
00007 #include "BitsetIterator.hpp"
00008 #include "BoardUtils.hpp"
00009 #include "VCSet.hpp"
00010 #include "VCUtils.hpp"
00011 
00012 using namespace benzene;
00013 
00014 //----------------------------------------------------------------------------
00015 
00016 /** @page playingdeterminedstates Playing in Determined States
00017     
00018     A determined state is defined as a state were one player has
00019     a winning semi/full connection.
00020 
00021     In a winning state, returns key of smallest semi connection,
00022     if one exists.  If no semi connection, plays move that overlaps
00023     the maximum number of full connections.
00024 
00025     In a losing state, returns move overlapping the most SCs (instead
00026     of VCs) since any winning SC still remaining on our opponent's
00027     next turn will allow them to win. Thus, we want to eliminate those
00028     winning SCs that are shortest/easiest to find. It is also possible
00029     that our opponent has winning VCs and yet no winning SCs. In this
00030     case, we just perform the overlap with the VCs.
00031 
00032     @bug It is possible our opponent has winning VCs that are not
00033     derived from the winning SCs in our list. Thus, we may want to
00034     consider overlapping the winning VCs as well.
00035 */
00036 
00037 /** @page computingmovestoconsider Computing the set of moves to consider
00038 
00039     The set of moves to consider is defined as the mustplay minus the
00040     inferior cells minus cells that create states that are mirrors of
00041     themselves (these are losing via the strategy stealing argument)
00042     minus any cells that are rotations of other cells (if the state is
00043     a rotation of itself). This set can never be empty, because
00044     IsLostGame() detects such states and reports them as losing (these
00045     states will be handled by PlayDeterminedState()).
00046 */
00047 
00048 //----------------------------------------------------------------------------
00049 
00050 /** Local functions. */
00051 namespace {
00052 
00053 bitset_t ComputeLossesViaStrategyStealingArgument(const StoneBoard& brd,
00054                                                   HexColor color)
00055 {
00056     bitset_t ret;
00057     if ((brd.Width() == brd.Height())
00058         && (brd.GetPlayed(!color).count() - brd.GetPlayed(color).count() == 1))
00059     {
00060         bitset_t mirror1 
00061             = BoardUtils::Mirror(brd.Const(), brd.GetPlayed(!color))
00062             - brd.GetPlayed(color);
00063         if (mirror1.count() == 1)
00064             ret |= mirror1;
00065         bitset_t mirror2 =
00066             BoardUtils::Mirror(brd.Const(), BoardUtils::Rotate(brd.Const(), 
00067                                                      brd.GetPlayed(!color)))
00068             - brd.GetPlayed(color);
00069         if (mirror2.count() == 1)
00070             ret |= mirror2;
00071         ret &= brd.GetEmpty();
00072     }
00073     return ret;
00074 }
00075 
00076 bitset_t RemoveRotations(const StoneBoard& brd, const bitset_t& consider)
00077 {
00078     bitset_t ret;
00079     for (BitsetIterator it(consider); it; ++it)
00080     {
00081         HexPoint rotated = BoardUtils::Rotate(brd.Const(), *it);
00082         if (!ret.test(rotated))
00083             ret.set(*it);
00084     }
00085     return ret;
00086 }
00087 
00088 bitset_t ComputeConsiderSet(const HexBoard& brd, HexColor color)
00089 {
00090     bitset_t consider = VCUtils::GetMustplay(brd, color);
00091     const InferiorCells& inf = brd.GetInferiorCells();
00092     consider = consider - inf.Vulnerable()
00093                         - inf.Reversible()
00094                         - inf.Dominated()
00095           - ComputeLossesViaStrategyStealingArgument(brd.GetPosition(), color);
00096     if (brd.GetPosition().IsSelfRotation())
00097         consider = RemoveRotations(brd.GetPosition(), consider);
00098     return consider;
00099 }
00100 
00101 bitset_t StonesInProof(const HexBoard& brd, HexColor color)
00102 {
00103     return brd.GetPosition().GetPlayed(color) 
00104         | brd.GetInferiorCells().DeductionSet(color);
00105 }
00106 
00107 /** Priority is given to eliminating the most easily-answered
00108     moves first (i.e. dead cells require no answer, answering
00109     vulnerable plays only requires knowledge of local adjacencies,
00110     etc.) */
00111 void TightenMoveBitset(bitset_t& moveBitset, const InferiorCells& inf)
00112 {
00113     BitsetUtil::SubtractIfLeavesAny(moveBitset, inf.Dead());
00114     BitsetUtil::SubtractIfLeavesAny(moveBitset, inf.Vulnerable());
00115     BitsetUtil::SubtractIfLeavesAny(moveBitset, inf.Captured(BLACK));
00116     BitsetUtil::SubtractIfLeavesAny(moveBitset, inf.Captured(WHITE));
00117     BitsetUtil::SubtractIfLeavesAny(moveBitset, inf.Reversible());
00118     BitsetUtil::SubtractIfLeavesAny(moveBitset, inf.Dominated());
00119     HexAssert(moveBitset.any());
00120 }
00121     
00122 /** Intersects as many of the smallest connections as possible. Then,
00123     subject to that restriction, tries to be a non-inferior move (using
00124     the member variables), and then to overlap as many other connections
00125     as possible. */
00126 HexPoint MostOverlappingMove(const std::vector<VC>& VClist,
00127                              const InferiorCells& inf)
00128 {
00129     bitset_t intersectSmallest;
00130     intersectSmallest.flip();
00131     
00132     // compute intersection of smallest until next one makes it empty
00133     std::vector<VC>::const_iterator it;
00134     for (it = VClist.begin(); it != VClist.end(); ++it) 
00135     {
00136         bitset_t carrier = it->carrier();
00137         if ((carrier & intersectSmallest).none())
00138         break;
00139     intersectSmallest &= carrier;
00140     }
00141     LogFine() << "Intersection of smallest set is:\n"
00142           << HexPointUtil::ToString(intersectSmallest) << '\n';
00143     
00144     // remove as many inferior moves as possible from this intersection
00145     TightenMoveBitset(intersectSmallest, inf);
00146     
00147     LogFine() << "After elimination of inferior cells:\n"
00148           << HexPointUtil::ToString(intersectSmallest) << '\n';
00149     
00150     // determine which of the remaining cells performs best with
00151     // regards to other connections
00152     int numHits[BITSETSIZE];
00153     memset(numHits, 0, sizeof(numHits));
00154     for (it = VClist.begin(); it != VClist.end(); ++it) 
00155     {
00156     bitset_t carrier = it->carrier();
00157     for (int i = 0; i < BITSETSIZE; i++) 
00158         if (intersectSmallest.test(i) && carrier.test(i))
00159         numHits[i]++;
00160     }
00161     
00162     int curBestMove = -1;
00163     int curMostHits = 0;
00164     for (int i = 0; i < BITSETSIZE; i++) 
00165     {
00166     if (numHits[i] > curMostHits) 
00167         {
00168         HexAssert(intersectSmallest.test(i));
00169         curMostHits = numHits[i];
00170         curBestMove = i;
00171     }
00172     }
00173     
00174     if (curMostHits == 0)
00175         LogWarning() << "curMostHits == 0!!\n";
00176     HexAssert(curMostHits > 0);
00177     return (HexPoint)curBestMove;
00178 }
00179     
00180 /** Returns best winning move. */
00181 HexPoint PlayWonGame(const HexBoard& brd, HexColor color)
00182 {
00183     HexAssert(EndgameUtils::IsWonGame(brd, color));
00184 
00185     // If we have a winning SC, then play in the key of the smallest one
00186     VC winningVC;
00187     if (brd.Cons(color).SmallestVC(HexPointUtil::colorEdge1(color), 
00188                                    HexPointUtil::colorEdge2(color), 
00189                                    VC::SEMI, winningVC)) 
00190     {
00191         LogInfo() << "Winning SC.\n";
00192         return winningVC.key();
00193     }
00194     
00195     // If instead we have a winning VC, then play best move in its carrier set
00196     if (brd.Cons(color).Exists(HexPointUtil::colorEdge1(color),
00197                                HexPointUtil::colorEdge2(color),
00198                                VC::FULL))
00199     {
00200         LogFine() << "Winning VC.\n";
00201         std::vector<VC> vcs;
00202         brd.Cons(color).VCs(HexPointUtil::colorEdge1(color),
00203                             HexPointUtil::colorEdge2(color),
00204                             VC::FULL, vcs);
00205     return MostOverlappingMove(vcs, brd.GetInferiorCells());
00206     }
00207     // Should never get here!
00208     HexAssert(false);
00209     return INVALID_POINT;
00210 }
00211 
00212 /** Returns most blocking (ie, the "best") losing move. */
00213 HexPoint PlayLostGame(const HexBoard& brd, HexColor color)
00214 {
00215     HexAssert(EndgameUtils::IsLostGame(brd, color));
00216 
00217     // Determine if color's opponent has guaranteed win
00218     HexColor other = !color;
00219     HexPoint otheredge1 = HexPointUtil::colorEdge1(other);
00220     HexPoint otheredge2 = HexPointUtil::colorEdge2(other);
00221     
00222     LogInfo() << "Opponent has won; playing most blocking move.\n";
00223     
00224     /** Uses semi-connections. 
00225         @see @ref playingdeterminedstates 
00226     */
00227     bool connected = brd.Cons(other).Exists(otheredge1, otheredge2, VC::SEMI);
00228     std::vector<VC> vcs;
00229     brd.Cons(other).VCs(otheredge1, otheredge2, 
00230                         ((connected) ? VC::SEMI : VC::FULL), vcs);
00231 
00232     return MostOverlappingMove(vcs, brd.GetInferiorCells());
00233 }
00234 
00235 } // anonymous namespace
00236 
00237 //----------------------------------------------------------------------------
00238 
00239 bool EndgameUtils::IsWonGame(const HexBoard& brd, HexColor color, 
00240                             bitset_t& proof)
00241 {
00242     if (brd.GetGroups().GetWinner() == color)
00243     {
00244         proof = StonesInProof(brd, color);
00245         return true;
00246     }
00247     VC vc;
00248     const HexPoint edge1 = HexPointUtil::colorEdge1(color);
00249     const HexPoint edge2 = HexPointUtil::colorEdge2(color);
00250     if (brd.Cons(color).SmallestVC(edge1, edge2, VC::SEMI, vc))
00251     {        
00252         proof = vc.carrier() | StonesInProof(brd, color);
00253     return true;
00254     }
00255     if (brd.Cons(color).SmallestVC(edge1, edge2, VC::FULL, vc))
00256     {
00257         proof = vc.carrier() | StonesInProof(brd, color);
00258     return true;
00259     }
00260     return false;
00261 }
00262 
00263 bool EndgameUtils::IsLostGame(const HexBoard& brd, HexColor color, 
00264                              bitset_t& proof)
00265 {
00266     if (brd.GetGroups().GetWinner() == !color)
00267     {
00268         proof = StonesInProof(brd, !color);
00269         return true;
00270     }
00271     VC vc;
00272     if (brd.Cons(!color).SmallestVC(HexPointUtil::colorEdge1(!color),
00273                                     HexPointUtil::colorEdge2(!color),
00274                                     VC::FULL, vc))
00275     {
00276         proof = vc.carrier() | StonesInProof(brd, !color);
00277     return true;
00278     }
00279     if (ComputeConsiderSet(brd, color).none())
00280     {
00281         proof = brd.GetPosition().GetEmpty() | StonesInProof(brd, !color);
00282         return true;
00283     }
00284     return false;
00285 }
00286 
00287 bool EndgameUtils::IsDeterminedState(const HexBoard& brd, HexColor color, 
00288                                     HexEval& score, bitset_t& proof)
00289 {
00290     score = 0;
00291     if (IsWonGame(brd, color, proof))
00292     {
00293         score = IMMEDIATE_WIN;
00294         return true;
00295     }
00296     if (IsLostGame(brd, color, proof))
00297     {
00298         score = IMMEDIATE_LOSS;
00299         return true;
00300     }
00301     return false;
00302 }
00303 
00304 HexPoint EndgameUtils::PlayDeterminedState(const HexBoard& brd, HexColor color)
00305                                           
00306 {
00307     HexAssert(HexColorUtil::isBlackWhite(color));
00308     HexAssert(IsDeterminedState(brd, color));
00309     HexAssert(!brd.GetGroups().IsGameOver());
00310 
00311     if (IsWonGame(brd, color))
00312         return PlayWonGame(brd, color);
00313 
00314     HexAssert(IsLostGame(brd, color));
00315     return PlayLostGame(brd, color);
00316 }
00317 
00318 bitset_t EndgameUtils::MovesToConsider(const HexBoard& brd, HexColor color)
00319 {
00320     HexAssert(HexColorUtil::isBlackWhite(color));
00321     HexAssert(!IsDeterminedState(brd, color));
00322     
00323     bitset_t consider = ComputeConsiderSet(brd, color);
00324     HexAssert(consider.any());
00325 
00326     LogFine() << "Moves to consider for " << color << ":" 
00327               << brd.Write(consider) << '\n';
00328     return consider;
00329 }
00330 
00331 //----------------------------------------------------------------------------


6 Jan 2011 Doxygen 1.6.3