// Global function
    public GBThreatSequence FindWinningThreatSeq()
    {
        // First find a number of possibly winning threat trees.
        GBSearchModule gbSearch  = new GBSearchModule(GoBangBoard.boardDim);
        GBSpaceState   rootState = new GBSpaceState((GoBangBoard)gb.Clone());

        rootState.UpdateIsGoal(gbSearch);

        // HEURISTIC: use category reduction (page 140-141)
        gbSearch.categoryReductionHeuristicOn = true;

        DBSearch db = new DBSearch(gbSearch, breadthFirst);

        db.Search(rootState);
        //db.DumpDOTGoalsOnly ();

        // Now, enumerate all the possibly winning threat trees found
        GBThreatSequence[] potentialWinningSeqs =
            GBThreatSequence.BuildAllGoalPathes(gbSearch, db.Root);
        Console.WriteLine("{0} potential winning threat sequences.",
                          potentialWinningSeqs.Length);

        // Check them one by one until a surely winning threat tree is found
        GoBangBoard gbFlipped = (GoBangBoard)gb.Clone();

        gbFlipped.Flip();

        int DEBUGwinningFound         = 0;
        GBThreatSequence DEBUGwinning = null;

        foreach (GBThreatSequence threatSeq in potentialWinningSeqs)
        {
            if (DefenseRefutes(threatSeq,
                               (GoBangBoard)gbFlipped.Clone()) < 0)
            {
                // Found a sure win, return early
                // FIXME: for debugging we count all winning sequences found,
                // but we should return as early as possible.
                DEBUGwinningFound += 1;
                DEBUGwinning       = threatSeq;
                //Console.WriteLine ("WINNING:\n{0}", threatSeq);
                // FIXME
                //return (threatSeq);
            }
        }

        Console.WriteLine("{0} winning of {1} potential winning threat sequences identified",
                          DEBUGwinningFound, potentialWinningSeqs.Length);

        // Found no unrefuted threat sequence
        return(DEBUGwinning);
    }
    /** Try to find a winning threat sequence by dependency based search and
     * on the fly refutation for goal nodes.
     *
     * Return early, as soon as a sure candidate has been found.
     *
     * @param timeoutMS If non-zero, a maximum time spend in db-search is
     * given in milliseconds.  At least 1000 (1s) is meaningful to do
     * something, though.
     *
     * @returns The first winning threat sequence on success, null otherwise.
     */
    public GBThreatSequence FindWinningThreatSeqOTF(int timeoutMS)
    {
        // First find a number of possibly winning threat trees.
        GBSearchModule gbSearch  = new GBSearchModule(GoBangBoard.boardDim);
        GBSpaceState   rootState = new GBSpaceState((GoBangBoard)gb.Clone());

        rootState.UpdateIsGoal(gbSearch);

        // HEURISTIC: use category reduction (page 140-141)
        // FIXME: re-enable as soon as three3 bug is fixed.
        // FIXME: test if this is good in the real ai, otherwise disable again.
        //gbSearch.categoryReductionHeuristicOn = true;

        // Do on-the-fly refutation checking.
        gbSearch.doDefenseRefutationCheck = true;

        if (timeoutMS != 0)
        {
            gbSearch.doExpirationCheck = true;
            gbSearch.expireTime        = DateTime.Now.AddMilliseconds(timeoutMS);
        }

        DBSearch db = new DBSearch(gbSearch, breadthFirst);

        try {
            Console.WriteLine("Board:\n{0}\n", gb);
            db.Search(rootState);
            //db.DumpDOT ();
        } catch (GBSearchModule.GBSearchTimeoutException) {
            // We timed out...
            Console.WriteLine("FindWinningThreatSeqOTF: timeouted...");
        } catch (GBWinningThreatSequenceFoundException gex) {
            //db.DumpDOT ();
            return(gex.seq);
        }

        return(null);
    }
    /** Test a potential winning threat sequence for refutability.
     *
     * @param curBoard The original board the original attacker db-search was
     * started with (root.State.GB).
     * @param root The root node of the search tree.
     * @param goalnode The new goal node identified by db-search.
     *
     * @returns null if the sequence is refutable, otherwise the sequence
     * itself is returned.
     */
    public static GBThreatSequence DefenseRefutable(GoBangBoard curBoard,
                                                    DBNode root, DBNode goalnode)
    {
        // First create the goal path, that is, all nodes that lie on the
        // direct child path to the node.
        ArrayList gp = GBThreatSequence.GoalPath(root, goalnode);

        // Check if there is a clear refutable path node in the path and
        // return early if this is the case.
        //Console.WriteLine ("Path:");
        foreach (DBNode gpN in gp)
        {
            //Console.WriteLine ("   {0}", gpN);
            if (gpN.IsRefutePathRoot)
            {
                //Console.WriteLine ("DEBUG: node in refutepath");
                return(null);
            }
        }

        // Now combine the operators and build a one-by-one threat sequence.
        GBThreatSequence seq = GBThreatSequence.BuildThreatSequence(gp);

        // Clone and flip board
        GoBangBoard gbFlipped = (GoBangBoard)curBoard.Clone();

        gbFlipped.Flip();

        Console.Write("  checking potential {0} pair-move sequence: ", gp.Count);
        Console.Out.Flush();

        int refutes = DefenseRefutes(seq, gbFlipped);

        if (refutes < 0)
        {
            Console.WriteLine("un-refutable");
            return(seq);
        }

        Console.WriteLine("refutable at pair-move {0}", refutes);

        // Mark root of refutation
        if (refutes < (gp.Count - 1))
        {
            DBNode refutePathRoot = (DBNode)gp[refutes + 1];
            refutePathRoot.IsRefutePathRoot = true;
        }

        return(null);
    }
    public void UpdateLegalOperators(int maxCat, bool doCatReduction)
    {
        // If we do category reduction, we shall proceed like this:
        // "... if in a node N of the db-search DAG, the defender has a threat
        // of category c_1, for each descendent of N the attacker is
        // restricted to threats of categories less than c_1".
        //
        // So we first find out the threats of the defender
        if (maxCat >= 1 && doCatReduction)
        {
            // Reverse attacker/defender
            GoBangBoard gbFlip = (GoBangBoard)gb.Clone();
            gbFlip.Flip();

            int lowestCat = GBOperator.LowestOperatorCategory(gbFlip);
            //Console.WriteLine ("lowestCat = {0}, maxCat = {1}", lowestCat, maxCat);

            // If the defender has a five already, there is no need to search.
            if (lowestCat == 0)
            {
                this.legalOperators = null;

                return;
            }

            // Now, we have to add one to the lowest category.  This is
            // because the operators judge what they can create in one move,
            // not what is already there on the board.
            lowestCat += 1;

            // Otherwise, if the defenders lowest category threat is below or
            // equal to the current maximum category, we decrease the maximum
            // category to allow only those attacker threats that can still
            // force.
            if (lowestCat <= maxCat)
            {
                Console.WriteLine("doCatRed: from {0} to {1}", maxCat, lowestCat - 1);
                maxCat = lowestCat - 1;
            }
            else
            {
                Console.WriteLine("maxCat = {0}", maxCat);
            }
        }

        this.legalOperators = GBOperator.LegalOperators(this, maxCat);
    }
    private static int DefenseRefutes(GBThreatSequence seq, GoBangBoard curBoard,
                                      int depth)
    {
        // If either we reached the end of the sequence (seq is null) or we
        // have a class zero threat, we consider the sequence to be
        // un-refutable and return a negative number.
        if (seq == null || seq.attackerThreatClass == 0)
        {
            return(-1);
        }

        // Make the attackers move (with -1 now, as the board is flipped)
        curBoard.board[seq.attacker.Y, seq.attacker.X] = -1;

        /*Console.WriteLine ("move at ({0},{1})",
         *      "abcdefghijklmnopqrstuvwxyz"[seq.attacker.X], seq.attacker.Y);
         *
         * Console.WriteLine ("DEFENSE board is:\n{0}", curBoard);
         * Console.WriteLine ("   attacker threats with {0}", seq.attackerThreatClass);*/

        // Now search for possibly winning threat sequences that cover the
        // goals.  To do this, first build the goalsquares
        int[,] extraGoalSquares = ExtraGoalSquares(seq);

        // TODO
        GBSpaceState rootState =
            new GBSpaceState((GoBangBoard)curBoard.Clone(),
                             seq.attackerThreatClass);

        GBSearchModule gbSearch = new GBSearchModule(GoBangBoard.boardDim);

        // Extra constraints (page 137)
        //
        // 1. "The goal set U_g for player B should be extended with singleton
        // goals for occupying any square in threat a_j or reply d_j with j
        // \geq i."
        //
        // 2. "If B find a potential winning threat sequence, ... this threat
        // sequence is not investigated for counter play of player A.  Instead
        // in such a case we always assume that A's potential winning threat
        // sequence has been refuted."
        //
        // 3. "Thus in a db-search for player B, only threats having replies
        // consisting of a single move are applied."
        gbSearch.goalIfOccupied     = extraGoalSquares;
        gbSearch.OneGoalStopsSearch = true;
        gbSearch.maximumCategory    = seq.attackerThreatClass - 1;
        //Console.WriteLine ("       maxCat = {0}", gbSearch.maximumCategory);

        // Limit the root node's legal operators to only the one with the
        // appropiate category below the maximum one.  Disable the category
        // reduction heuristics, as we do the exhaustive defense search.
        rootState.UpdateLegalOperators(gbSearch.maximumCategory, false);

        // Do the db-search for the defender
        DBSearch db = new DBSearch(gbSearch, false);

        db.Search(rootState);
        if (db.GoalCount > 0)
        {
            /*
             * Console.WriteLine ("Threat below class {0} or goal square occupied.",
             *      gbSearch.maximumCategory);
             * db.DumpDOT ();
             * Console.ReadLine ();*/

            return(depth);
        }

        /*Console.WriteLine ("No class {0} or below threats for the defender found yet",
         *      gbSearch.maximumCategory);*/

        // Make defenders move (now attacker 1, with advantage)
        foreach (GBMove defMove in seq.defender)
        {
            curBoard.board[defMove.Y, defMove.X] = 1;
        }
        //Console.WriteLine ("BOARD:\n{0}", curBoard);

        return(DefenseRefutes(seq.next, curBoard, depth + 1));
    }
    private static int DefenseRefutes(GBThreatSequence seq, GoBangBoard curBoard,
		int depth)
    {
        // If either we reached the end of the sequence (seq is null) or we
        // have a class zero threat, we consider the sequence to be
        // un-refutable and return a negative number.
        if (seq == null || seq.attackerThreatClass == 0)
            return (-1);

        // Make the attackers move (with -1 now, as the board is flipped)
        curBoard.board[seq.attacker.Y, seq.attacker.X] = -1;
        /*Console.WriteLine ("move at ({0},{1})",
            "abcdefghijklmnopqrstuvwxyz"[seq.attacker.X], seq.attacker.Y);

        Console.WriteLine ("DEFENSE board is:\n{0}", curBoard);
        Console.WriteLine ("   attacker threats with {0}", seq.attackerThreatClass);*/

        // Now search for possibly winning threat sequences that cover the
        // goals.  To do this, first build the goalsquares
        int[,] extraGoalSquares = ExtraGoalSquares (seq);

        // TODO
        GBSpaceState rootState =
            new GBSpaceState ((GoBangBoard) curBoard.Clone (),
                seq.attackerThreatClass);

        GBSearchModule gbSearch = new GBSearchModule (GoBangBoard.boardDim);

        // Extra constraints (page 137)
        //
        // 1. "The goal set U_g for player B should be extended with singleton
        // goals for occupying any square in threat a_j or reply d_j with j
        // \geq i."
        //
        // 2. "If B find a potential winning threat sequence, ... this threat
        // sequence is not investigated for counter play of player A.  Instead
        // in such a case we always assume that A's potential winning threat
        // sequence has been refuted."
        //
        // 3. "Thus in a db-search for player B, only threats having replies
        // consisting of a single move are applied."
        gbSearch.goalIfOccupied = extraGoalSquares;
        gbSearch.OneGoalStopsSearch = true;
        gbSearch.maximumCategory = seq.attackerThreatClass - 1;
        //Console.WriteLine ("       maxCat = {0}", gbSearch.maximumCategory);

        // Limit the root node's legal operators to only the one with the
        // appropiate category below the maximum one.  Disable the category
        // reduction heuristics, as we do the exhaustive defense search.
        rootState.UpdateLegalOperators (gbSearch.maximumCategory, false);

        // Do the db-search for the defender
        DBSearch db = new DBSearch (gbSearch, false);
        db.Search (rootState);
        if (db.GoalCount > 0) {
            /*
            Console.WriteLine ("Threat below class {0} or goal square occupied.",
                gbSearch.maximumCategory);
            db.DumpDOT ();
            Console.ReadLine ();*/

            return (depth);
        }

        /*Console.WriteLine ("No class {0} or below threats for the defender found yet",
            gbSearch.maximumCategory);*/

        // Make defenders move (now attacker 1, with advantage)
        foreach (GBMove defMove in seq.defender)
            curBoard.board[defMove.Y, defMove.X] = 1;
        //Console.WriteLine ("BOARD:\n{0}", curBoard);

        return (DefenseRefutes (seq.next, curBoard, depth+1));
    }
    /** Test a potential winning threat sequence for refutability.
     *
     * @param curBoard The original board the original attacker db-search was
     * started with (root.State.GB).
     * @param root The root node of the search tree.
     * @param goalnode The new goal node identified by db-search.
     *
     * @returns null if the sequence is refutable, otherwise the sequence
     * itself is returned.
     */
    public static GBThreatSequence DefenseRefutable(GoBangBoard curBoard,
		DBNode root, DBNode goalnode)
    {
        // First create the goal path, that is, all nodes that lie on the
        // direct child path to the node.
        ArrayList gp = GBThreatSequence.GoalPath (root, goalnode);

        // Check if there is a clear refutable path node in the path and
        // return early if this is the case.
        //Console.WriteLine ("Path:");
        foreach (DBNode gpN in gp) {
            //Console.WriteLine ("   {0}", gpN);
            if (gpN.IsRefutePathRoot) {
                //Console.WriteLine ("DEBUG: node in refutepath");
                return (null);
            }
        }

        // Now combine the operators and build a one-by-one threat sequence.
        GBThreatSequence seq = GBThreatSequence.BuildThreatSequence (gp);

        // Clone and flip board
        GoBangBoard gbFlipped = (GoBangBoard) curBoard.Clone ();
        gbFlipped.Flip ();

        Console.Write ("  checking potential {0} pair-move sequence: ", gp.Count);
        Console.Out.Flush ();

        int refutes = DefenseRefutes (seq, gbFlipped);
        if (refutes < 0) {
            Console.WriteLine ("un-refutable");
            return (seq);
        }

        Console.WriteLine ("refutable at pair-move {0}", refutes);

        // Mark root of refutation
        if (refutes < (gp.Count-1)) {
            DBNode refutePathRoot = (DBNode) gp[refutes+1];
            refutePathRoot.IsRefutePathRoot = true;
        }

        return (null);
    }