// 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);
    }
    /** 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);
    }