Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

PerfectPlayer.cpp

Go to the documentation of this file.
00001 //----------------------------------------------------------------------------
00002 /** @file PerfectPlayer.cpp
00003  */
00004 //----------------------------------------------------------------------------
00005 
00006 #include "SgSystem.h"
00007 #include "SgRandom.h"
00008 #include "BitsetIterator.hpp"
00009 #include "PerfectPlayer.hpp"
00010 
00011 using namespace benzene;
00012 
00013 //----------------------------------------------------------------------------
00014 
00015 namespace 
00016 {
00017 
00018 HexPoint RandomBit(const bitset_t& bs, SgRandom& random)
00019 {
00020     HexAssert(bs.any());
00021     int index = random.Int(bs.count());
00022     for (BitsetIterator it(bs); it; ++it)
00023     {
00024         if (index == 0)
00025             return *it;
00026         --index;
00027     }
00028     HexAssert(false);
00029     return static_cast<HexPoint>(BitsetUtil::FirstSetBit(bs));
00030 }
00031 
00032 }
00033 
00034 //----------------------------------------------------------------------------
00035 
00036 PerfectPlayer::PerfectPlayer(DfpnSolver& solver, DfpnStates& positions)
00037     : BenzenePlayer(),
00038       m_solver(solver),
00039       m_positions(positions),
00040       m_maxTime(60.0),
00041       m_propagateBackwards(true)
00042 {
00043 }
00044 
00045 PerfectPlayer::~PerfectPlayer()
00046 {
00047 }
00048 
00049 //----------------------------------------------------------------------------
00050 
00051 HexPoint PerfectPlayer::Search(const HexState& state, const Game& game,
00052                                HexBoard& brd, const bitset_t& consider, 
00053                                double maxTime, double& score)
00054 {
00055     SG_UNUSED(consider);
00056     SG_UNUSED(score);
00057     LogInfo() << "PerfectPlayer::Search()\n";
00058     LogInfo() << state.Position() << '\n';
00059     if (FillinCausedWin())
00060     {
00061         LogInfo() << "PerfectPlayer: Fillin caused win!\n";
00062         HexColor color = state.ToPlay();
00063         brd.GetPosition().SetPosition(state.Position());
00064         brd.ComputeAll(color);
00065         const InferiorCells& inf = brd.GetInferiorCells();
00066         if (m_fillinWinner == color && inf.Captured(color).any())
00067         {
00068             LogInfo() << "PerfectPlayer: Playing into our fillin...\n";
00069             return RandomBit(inf.Captured(color), SgRandom::Global());
00070         }
00071         else if (m_fillinWinner == !color && inf.Captured(!color).any())
00072         {
00073             LogInfo() << "PerfectPlayer: Playing into opponent fillin...\n";
00074             return RandomBit(inf.Captured(color), SgRandom::Global());
00075         }
00076         LogInfo() << "PerfectPlayer: Playing random empty cell...\n";
00077         return RandomBit(state.Position().GetEmpty(), SgRandom::Global());
00078     }
00079     double timeForMove = std::min(m_maxTime, maxTime);
00080     LogInfo() << "TimeForMove=" << timeForMove << '\n';
00081     double oldTimelimit = m_solver.Timelimit();
00082     m_solver.SetTimelimit(timeForMove);
00083     PointSequence pv;
00084     HexColor winner = m_solver.StartSearch(state, brd, m_positions, pv);
00085     m_solver.SetTimelimit(oldTimelimit);
00086     if (m_propagateBackwards)
00087         m_solver.PropagateBackwards(game, m_positions);
00088     // Return winning/best losing move.
00089     if (winner != EMPTY && !pv.empty())
00090         return pv[0];
00091     // NOTE: This can happen if the current state is a terminal state
00092     // under a rotation, but it is not detected as terminal here
00093     // (there can be slight differences in vcs between rotated
00094     // states). In this case, DFPN does not have a move stored and we
00095     // are stuck if we continue to use the current set of stored
00096     // positions. So we create a new empty DfpnPositions object with a
00097     // small hashtable to use for this (hopefully really small) search
00098     // to find the winning move.
00099     if (winner != EMPTY && pv.empty())
00100     {
00101         boost::scoped_ptr<DfpnHashTable> myTable(new DfpnHashTable(10));
00102         boost::scoped_ptr<DfpnDB> myDB(0);
00103         SolverDBParameters myParam;
00104         DfpnStates myStates(myTable, myDB, myParam);
00105         LogInfo() << "PerfectPlayer: Found win with empty pv at this state:\n";
00106         LogInfo() << brd << '\n';
00107         LogInfo() << "PerfectPlayer: Re-solving with temporary hash table.\n";
00108         winner = m_solver.StartSearch(state, brd, myStates, pv);
00109         HexAssert(winner != EMPTY);
00110         HexAssert(!pv.empty());
00111         return pv[0];
00112     }
00113     // Didn't prove it, find non-losing move with most work.
00114     DfpnData data;
00115     if (!m_positions.Get(state, data))
00116         throw BenzeneException("Root node not in database!?!");
00117     std::size_t maxWork = 0;
00118     HexPoint bestMove = pv[0];
00119     HexState myState(state);
00120     for (std::size_t i = 0; i < data.m_children.Size(); ++i)
00121     {
00122         data.m_children.PlayMove(i, myState);
00123         DfpnData child;
00124         if (m_positions.Get(myState, child))
00125         {
00126             // We're assuming no children are losing (ie, no move is winning),
00127             // so we just need to avoid losing moves (winning children).
00128             if (!child.m_bounds.IsWinning() && child.m_work > maxWork)
00129             {
00130                 bestMove = data.m_children.FirstMove(i);
00131                 maxWork = child.m_work;
00132             }
00133         }
00134         data.m_children.UndoMove(i, myState);
00135     }
00136     LogInfo() << "bestMove=" << bestMove << " (" << maxWork << ")\n";
00137     return bestMove;
00138 }
00139 
00140 //----------------------------------------------------------------------------


6 Jan 2011 Doxygen 1.6.3