/** Finds inferior cells, builds vcs. Sets moves to consider to all
    empty cells. If fillin causes terminal state, sets
    m_fillinCausedWin to true and recomputes fillin/vcs with ice
    temporarily turned off (so it can pass the players a non-empty
    consider set).
*/
HexPoint BenzenePlayer::InitSearch(HexBoard& brd, HexColor color, 
                                   bitset_t& consider, double& score)
{
    // Resign if the game is already over
    Groups groups;
    GroupBuilder::Build(brd.GetPosition(), groups);
    if (groups.IsGameOver()) 
    {
        score = IMMEDIATE_LOSS;
        return RESIGN;
    }
    StoneBoard original(brd.GetPosition());
    brd.ComputeAll(color);
    m_fillinCausedWin = false;
    m_fillinWinner = EMPTY;
    if (brd.GetGroups().IsGameOver()) 
    {
        // Fillin caused win, remove and re-compute without ice.
        m_fillinCausedWin = true;
        m_fillinWinner = brd.GetGroups().GetWinner();
        LogInfo() << "Captured cells caused win! Removing...\n";
        brd.GetPosition().SetPosition(original);
        bool oldUseIce = brd.UseICE();
        brd.SetUseICE(false);
        brd.ComputeAll(color);
        brd.SetUseICE(oldUseIce);
        BenzeneAssert(!brd.GetGroups().IsGameOver());
    } 
    consider = brd.GetPosition().GetEmpty();
    score = 0;
    return INVALID_POINT;
}
bitset_t ProofUtil::InitialProofForOpponent(const HexBoard& brd, 
                                            HexColor toPlay)
{
    // Add opponent played stones and deduction set.
    const InferiorCells& inf = brd.GetInferiorCells();
    bitset_t proof = brd.GetPosition().GetPlayed(!toPlay);
    proof |= inf.DeductionSet(!toPlay);

    // Add all semi-connections from the mustplay.
    const VCList& lst = brd.Cons(!toPlay).GetList(VC::SEMI, 
                                          HexPointUtil::colorEdge1(!toPlay),
                                          HexPointUtil::colorEdge2(!toPlay));
    const bool useGreedy = brd.Builder().Parameters().use_greedy_union;
    proof |= useGreedy ? lst.getGreedyUnion() : lst.getUnion();

    // Add reversable reversers. 
    // The carriers do NOT need to be included in the proof, since
    // they are captured by the (losing) player, not his opponent (for
    // whom we are building the proof set).
    // TODO: Currently, we just add the first reverser: we should see
    // if any reverser is already in the proof, since then we wouldn't
    // need to add one.
    for (BitsetIterator p(inf.Reversible()); p; ++p) 
    {
        const std::set<HexPoint>& reversers = inf.Reversers(*p);
        proof.set(*reversers.begin());
    }
    // Add vulnerable killers and their carriers.
    // TODO: Currently, we just add the first killer: we should see if
    // any killer is already in the proof, since then we wouldn't need
    // to add one.
    for (BitsetIterator p(inf.Vulnerable()); p; ++p) 
    {
        const std::set<VulnerableKiller>& killers = inf.Killers(*p);
        proof.set((*killers.begin()).killer());
        proof |= ((*killers.begin()).carrier());
    }
    return proof;
}
bool EndgameUtil::IsLostGame(const HexBoard& brd, HexColor color, 
                             bitset_t& proof)
{
    if (brd.GetGroups().GetWinner() == !color)
    {
        proof = StonesInProof(brd, !color);
        return true;
    }
    VC vc;
    if (brd.Cons(!color).SmallestVC(HexPointUtil::colorEdge1(!color),
                                    HexPointUtil::colorEdge2(!color),
                                    VC::FULL, vc))
    {
        proof = vc.Carrier() | StonesInProof(brd, !color);
	return true;
    }
    if (ComputeConsiderSet(brd, color).none())
    {
        proof = brd.GetPosition().GetEmpty() | StonesInProof(brd, !color);
        return true;
    }
    return false;
}
bitset_t ProofUtil::MaximumProofSet(const HexBoard& brd, HexColor toPlay)
{
    return brd.GetPosition().GetEmpty()
        | brd.GetPosition().GetPlayed(toPlay)
        | brd.GetInferiorCells().DeductionSet(toPlay);
}
bool Decompositions::Find(const HexBoard& brd, HexColor color,
                          bitset_t& captured)
{
    // If game is over or decided, don't do any work.
    HexPoint edge1 = HexPointUtil::colorEdge1(color);
    HexPoint edge2 = HexPointUtil::colorEdge2(color);
    const VCSet& cons = brd.Cons(color);
    if (brd.GetGroups().IsGameOver() || cons.Exists(edge1, edge2, VC::FULL)) 
    {
	captured.reset();
	return false;
    }
    
    /** Compute neighbouring groups of opposite color.

        NOTE: Assumes that edges that touch are adjacent. See
        ConstBoard for more details. 
    */
    PointToBitset adjTo;
    PointToBitset adjByMiai;
    ComputeAdjacentByMiai(brd, adjByMiai);
    for (GroupIterator g(brd.GetGroups(), color); g; ++g) 
    {
	bitset_t opptNbs = adjByMiai[g->Captain()] 
            | (g->Nbs() & brd.GetPosition().GetColor(!color));
	if (opptNbs.count() >= 2)
	    adjTo[g->Captain()] = opptNbs;
    }
    // The two color edges are in the list. If no other groups are, then quit.
    BenzeneAssert(adjTo.size() >= 2);
    if (adjTo.size() == 2) 
    {
	captured.reset();
	return false;
    }
    
    // Compute graph representing board from color's perspective.
    PointToBitset graphNbs;
    GraphUtil::ComputeDigraph(brd.GetGroups(), color, graphNbs);
    
    // Find (ordered) pairs of color groups that are VC-connected and have at
    // least two adjacent opponent groups in common.
    PointToBitset::iterator g1, g2;
    for (g1 = adjTo.begin(); g1 != adjTo.end(); ++g1) {
	for (g2 = adjTo.begin(); g2 != g1; ++g2) {
	    if ((g1->second & g2->second).count() < 2) continue;
	    if (!cons.Exists(g1->first, g2->first, VC::FULL)) continue;
	    
	    // This is such a pair, so at least one of the two is not an edge.
	    // Find which color edges are not equal to either of these groups.
	    BenzeneAssert(!HexPointUtil::isEdge(g1->first) ||
                          !HexPointUtil::isEdge(g2->first));
	    bool edge1Free = (g1->first != edge1 && g2->first != edge1);
	    bool edge2Free = (g1->first != edge2 && g2->first != edge2);
	    
	    // Find the set of empty cells bounded by these two groups.
	    bitset_t stopSet = graphNbs[g1->first] | graphNbs[g2->first];
	    bitset_t decompArea;
	    decompArea.reset();
	    if (edge1Free)
		decompArea |= GraphUtil::BFS(edge1, graphNbs, stopSet);
	    if (edge2Free)
		decompArea |= GraphUtil::BFS(edge2, graphNbs, stopSet);
	    decompArea.flip();
	    decompArea &= brd.GetPosition().GetEmpty();
	    
	    // If the pair has a VC confined to these cells, then we have
	    // a decomposition - return it.
	    const VCList& vl = cons.GetList(VC::FULL, g1->first, g2->first);
	    for (VCListConstIterator it(vl); it; ++it)
            {
		if (BitsetUtil::IsSubsetOf(it->Carrier(), decompArea)) 
                {
		    captured = it->Carrier();
		    return true;
		}
	    }
	}
    }
    
    // No combinatorial decomposition with a VC was found.
    captured.reset();
    return false;
}
HexPoint MoHexPlayer::Search(const HexState& state, const Game& game,
                             HexBoard& brd, const bitset_t& given_to_consider,
                             double maxTime, double& score)
{
    BenzeneAssert(!brd.GetGroups().IsGameOver());
    HexColor color = state.ToPlay();   
   
    SgTimer totalElapsed;
    PrintParameters(color, maxTime);

    // Do presearch and abort if win found. Allow it to take 20% of
    // total time.
    SgTimer timer;
    bitset_t consider = given_to_consider;
    PointSequence winningSequence;
    if (m_performPreSearch && PerformPreSearch(brd, color, consider, 
                                               maxTime * 0.2, winningSequence))
    {
	LogInfo() << "Winning sequence found before UCT search!\n"
		  << "Sequence: " << winningSequence[0] << '\n';
        score = IMMEDIATE_WIN;
	return winningSequence[0];
    }
    timer.Stop();
    LogInfo() << "Time for PreSearch: " << timer.GetTime() << "s\n";

    maxTime -= timer.GetTime();
    maxTime = std::max(1.0, maxTime);
        
    // Create the initial state data
    MoHexSharedData data(m_search.FillinMapBits());
    data.gameSequence = game.History();
    data.rootState = HexState(brd.GetPosition(), color);
    data.rootConsider = consider;
    
    // Reuse the old subtree
    SgUctTree* initTree = 0;
    if (m_reuse_subtree)
    {
        MoHexSharedData oldData(m_search.SharedData());
        initTree = TryReuseSubtree(oldData, data);
        if (!initTree)
            LogInfo() << "No subtree to reuse.\n";
    }
    m_search.SetSharedData(data);

    brd.GetPatternState().ClearPatternCheckStats();
    int old_radius = brd.GetPatternState().UpdateRadius();
    brd.GetPatternState().SetUpdateRadius(m_search.TreeUpdateRadius());

    // Do the search
    std::vector<SgMove> sequence;
    std::vector<SgMove> rootFilter;
    m_search.SetBoard(brd);
    score = m_search.Search(m_max_games, maxTime, sequence,
                            rootFilter, initTree, 0);

    brd.GetPatternState().SetUpdateRadius(old_radius);

    // Output stats
    std::ostringstream os;
    os << '\n';
#if COLLECT_PATTERN_STATISTICS
    {
        MoHexState& thread0 
            = dynamic_cast<MoHexState&>(m_search.ThreadState(0));
        MoHexPolicy* policy = dynamic_cast<MoHexPolicy*>(thread0.Policy());
        if (policy)
            os << policy->DumpStatistics() << '\n';
    }
#endif
    os << "Elapsed Time   " << totalElapsed.GetTime() << "s\n";
    m_search.WriteStatistics(os);
    os << "Score          " << std::setprecision(2) << score << "\n"
       << "Sequence      ";
    for (std::size_t i = 0; i < sequence.size(); i++)
        os << " " << MoHexUtil::MoveString(sequence[i]);
    os << '\n';
    LogInfo() << os.str() << '\n';

#if 0
    if (m_save_games) 
    {
        std::string filename = "uct-games.sgf";
        uct.SaveGames(filename);
        LogInfo() << "Games saved in '" << filename << "'.\n";
    }
#endif

    // Return move recommended by MoHexSearch
    if (sequence.size() > 0) 
        return static_cast<HexPoint>(sequence[0]);

    // It is possible that MoHexSearch did only 1 simulation (probably
    // because it ran out of time to do more); in this case, the move
    // sequence is empty and so we give a warning and return a random
    // move.
    LogWarning() << "**** MoHexSearch returned empty sequence!\n"
		 << "**** Returning random move!\n";
    return BoardUtil::RandomEmptyCell(brd.GetPosition());
}