/** Apply the operator to the given state, returning a new state.
     *
     * @param state The source state.
     *
     * @returns The newly created destination state.
     */
    public IDBSpaceState Apply(DBSearchModule module, IDBSpaceState stateDB)
    {
        GBSearchModule gmod  = (GBSearchModule)module;
        GBSpaceState   state = (GBSpaceState)stateDB;

        // TODO: remove later, we already checked this previously
        if (Valid(state) == false)
        {
            throw (new ArgumentException("Operator not applicable!"));
        }

        GoBangBoard newBoard = (GoBangBoard)state.GB.Clone();

        // Apply all the f_{add} stones
        for (int n = 0; n < fAdd.GetLength(0); ++n)
        {
            newBoard.board[fAdd[n, 1], fAdd[n, 0]] = fAdd[n, 2];
        }

        GBSpaceState newState = new GBSpaceState(newBoard, this, state,
                                                 gmod.maximumCategory);

        newState.UpdateIsGoal(gmod);

        return(newState);
    }
    public bool Applicable(IDBOperator oper, DBNode node)
    {
        GBOperator   goper  = (GBOperator)oper;
        GBSpaceState gstate = (GBSpaceState)node.State;

        return(goper.Applicable(gstate));
    }
    public void RegisterNewNode(DBNode node, DBNode root)
    {
        DoExpirationCheck();

        if (node.IsGoal == false)
        {
            return;
        }

        // For goal nodes, we check if on-the-fly refutation is wanted, and if
        // so, we try to refute the goal node.
        if (doDefenseRefutationCheck)
        {
            GBSpaceState     gRootState = (GBSpaceState)root.State;
            GBThreatSequence seq        = GBThreatSearch.DefenseRefutable
                                              (gRootState.GB, root, node);

            if (seq != null)
            {
                throw (new GBThreatSearch.GBWinningThreatSequenceFoundException(seq));
            }
        }

        // This is old code
#if false
        // Now the last node did modify something, but we only want to cache
        // dependency nodes, as combination nodes will not be combined
        // themselves.
        if (node.Type != DBNode.NodeType.Dependency)
        {
            return;
        }

        GBSpaceState state = (GBSpaceState)node.State;
        GBOperator   last  = state.LastOperator;
        if (last == null)
        {
            return;
        }

        // Now, we add this node to the array at all its influencing places,
        // but only where it sets a stone of the attacker (which is the only
        // position that can create new possibilities for attack).
        for (int n = 0; n < last.fAdd.GetLength(0); ++n)
        {
            // Ignore everything except the attacker stones.
            if (last.fAdd[n, 2] != 1)
            {
                continue;
            }

            // Add coordinate
            nodesThatAffectSquares[last.fAdd[n, 1], last.fAdd[n, 0]].Add(node);

            return;
        }
#endif
    }
    public GBSpaceState(GoBangBoard gb, GBOperator lastOperator,
                        GBSpaceState lastState, GBOperator[] legalOperators)
    {
        this.gb           = gb;
        this.lastOperator = lastOperator;
        this.lastState    = lastState;

        this.legalOperators = legalOperators;
    }
    // TODO: maybe remove this, its useless now
    public bool Applicable(GBSpaceState state)
    {
        if (Valid(state) == false)
        {
            return(false);
        }

        return(true);
    }
    public static void Main(string[] args)
    {
        GoBangBoard gb  = new GoBangBoard();
        Random      rnd = new Random();

        // Initialize board randomly
        for (int n = 0; n < 130; ++n)
        {
            gb.board[rnd.Next(0, boardDim), rnd.Next(0, boardDim)] =
                rnd.Next(0, 3) - 1;
        }

        int count = 0;

        foreach (StoneSet ss in gb.G5)
        {
            Console.Write("ss at ({0},{1}) to ({2},{3}), len {4}: ",
                          ss.x, ss.y, ss.ax, ss.ay, ss.stones.Length);
            foreach (int stone in ss.stones)
            {
                Console.Write("{0}", (stone == 0) ? "." :
                              ((stone == 1) ? "O" : "X"));
            }
            Console.WriteLine();

            count += 1;
        }
        Console.WriteLine("|G5| = {0}", count);

        count = 0;
        foreach (StoneSet ss in gb.G6)
        {
            count += 1;
        }
        Console.WriteLine("|G6| = {0}", count);

        count = 0;
        foreach (StoneSet ss in gb.G7)
        {
            count += 1;
        }
        Console.WriteLine("|G7| = {0}", count);

        // Test operators a little
        gb.DumpBoard();

        GBSpaceState state = new GBSpaceState(gb);

        GBOperator[] legalOpers = GBOperator.LegalOperators(state, 2);
        foreach (GBOperator gop in legalOpers)
        {
            Console.WriteLine("oper: {0}", gop);
        }
    }
    public void BuildCombinedOperatorPath(Stack node1Path, Stack node2Path)
    {
        // Root is at the end of the path'es

        /*
         * foreach (DBNode dn in node1Path)
         *      Console.WriteLine ("node1Path: {0}", dn);
         * foreach (DBNode dn in node2Path)
         *      Console.WriteLine ("node2Path: {0}", dn);
         */

        // We need to keep everything in node2Path until we meet a node of
        // node1
        foreach (DBNode pN in node2Path)
        {
            if (node1Path.Contains(pN))
            {
                break;
            }

            // Now the node pN was reached by applying an operator.
            // We have two kinds of operators to stack up: the ones in the
            // 'last' variable, which is the direct last operator applied.
            // And, because the node can have combinedOperPath operators, too,
            // we also need to add them.  The come chronologically first, but
            // we build this list in reverse, so we have to reverse them here,
            // too.
            GBSpaceState state = (GBSpaceState)pN.State;

            if (combinedOperPath == null)
            {
                combinedOperPath = new ArrayList();
            }

            combinedOperPath.Add(state.LastOperator);

            // If there are combined operator in the path already, reverse
            // them.
            if (state.combinedOperPath != null)
            {
                ArrayList combRev = (ArrayList)state.combinedOperPath.Clone();
                combRev.Reverse();

                combinedOperPath.AddRange(combRev);
            }
        }

        // Make it a chronological chain that comes before 'LastOperator'
        if (combinedOperPath != null)
        {
            combinedOperPath.Reverse();
        }
    }
    /** Check if the operator is formally valid.
     *
     * Note: we only check if the squares the operator will occupy are
     * currently free.  The logical check if the operator is applicable must
     * have preceeded this one already.
     *
     * @param state The state the operator will be tested for validity on.
     *
     * @returns True if the operator is valid, false otherwise.
     */
    public bool Valid(GBSpaceState state)
    {
        for (int n = 0; n < fAdd.GetLength(0); ++n)
        {
            if (state.GB.board[fAdd[n, 1], fAdd[n, 0]] != 0)
            {
                return(false);
            }
        }

        return(true);
    }
    // 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);
    }
    // @param gp Goal path.
    public static GBThreatSequence BuildThreatSequence(ArrayList gp)
    {
        GBThreatSequence ts = new GBThreatSequence();

        GBThreatSequence now = ts;

        for (int n = 0; n < gp.Count; ++n)
        {
            DBNode       pathnode = (DBNode)gp[n];
            GBSpaceState state    = (GBSpaceState)pathnode.State;

            // First add the nodes of the path.
            if (state.CombinedOperPath != null)
            {
                /*Console.WriteLine ("DEBUG CombinedOperPath, {0} moves",
                 *      state.CombinedOperPath.Count);*/

                foreach (GBOperator oper in state.CombinedOperPath)
                {
                    //Console.WriteLine ("    oper: {0}", oper);
                    FillInMoves(now, oper);

                    now.next = new GBThreatSequence();
                    now      = now.next;
                }
            }

            //Console.WriteLine ("DEBUG LastOperator");
            // Now the last operator
            GBOperator last = (GBOperator)state.LastOperator;
            if (last == null)
            {
                continue;
            }

            //Console.WriteLine ("    last: {0}", last);
            FillInMoves(now, last);

            // Chain it up, baby.
            now.next = null;
            if (n < (gp.Count - 1))
            {
                now.next = new GBThreatSequence();
                now      = now.next;
            }
        }

        return(ts);
    }
    public bool NotInConflict(GBSpaceState gstate1, GBSpaceState gstate2)
    {
        // We only need to check the last operator chain, as all previous have
        // been checked in the previous stages/levels
        while (gstate2 != null && gstate2.LastOperator != null)
        {
            if (Applicable(gstate2.LastOperator, gstate1) == false)
            {
                return(false);
            }

            gstate2 = gstate2.LastState;
        }
        return(true);
    }
    public static int[,] BuildOperatorMap(GoBangBoard board, out int maxCount)
    {
        // Get legal operators for the state represented by the board
        GBSpaceState state = new GBSpaceState(board);

        GBOperator[] opers = GBOperator.LegalOperators(state, 2);

        // Now build an attack count map and keep track of the maximum number
        // of operators a single attack stone creates.
        maxCount         = 0;
        int[,] attackMap = new int[GoBangBoard.boardDim, GoBangBoard.boardDim];

        foreach (GBOperator op in opers)
        {
            if (op is GBOperatorThree3)
            {
                continue;
            }

            for (int n = 0; n < op.fAdd.GetLength(0); ++n)
            {
                // We are not (yet) interested in the defending stones.
                if (op.fAdd[n, 2] == -1)
                {
                    continue;
                }

                int x = op.fAdd[n, 0];
                int y = op.fAdd[n, 1];
                attackMap[y, x] += 1;
                Console.WriteLine("at ({0}, {1}) oper: {2}", x, y, op);

                if (attackMap[y, x] > maxCount)
                {
                    maxCount = attackMap[y, x];
                }
            }
        }

        return(attackMap);
    }
    /** 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);
    }
    public static GBOperator[] LegalOperators(GBSpaceState state, int maxCat)
    {
        ArrayList opersGoal = new ArrayList ();
        ArrayList opersFours = new ArrayList ();
        ArrayList opersThrees = new ArrayList ();

        // Check G_5 for operators
        foreach (GoBangBoard.StoneSet ss in state.GB.G5) {
            if (IsLastOperatorDependent (ss, state.LastOperator) == false)
                continue;

            GBOperatorFive[] fives = null;
            if (maxCat >= 0)
                fives = GBOperatorFive.GetOperatorsIfValid (ss);

            GBOperatorFour[] fours = null;
            if (maxCat >= 1)
                fours = GBOperatorFour.GetOperatorsIfValid (ss);

            if (fives != null)
                opersGoal.AddRange (fives);
            if (fours != null)
                opersFours.AddRange (fours);
        }

        bool Three2Applicable = false;
        if (maxCat >= 2) {
            // Check G_7 for operators
            foreach (GoBangBoard.StoneSet ss in state.GB.G7) {
                if (IsLastOperatorDependent (ss, state.LastOperator) == false)
                    continue;

                /*
                Console.Write ("7ss: ");
                for (int n = 0 ; n < ss.stones.Length ; ++n)
                    Console.Write ("{0} ", ss.stones[n] == 1 ? "O" :
                        (ss.stones[n] == -1 ? "X" : "."));
                Console.WriteLine ();
                */

                GBOperatorThree2[] three2 =
                    GBOperatorThree2.GetOperatorsIfValid (ss);

                if (three2 != null) {
                    Three2Applicable = true;
                    opersThrees.AddRange (three2);
                }
            }
        }

        // Check G_6 for operators
        if (maxCat >= 1) {
            foreach (GoBangBoard.StoneSet ss in state.GB.G6) {
                if (IsLastOperatorDependent (ss, state.LastOperator) == false)
                    continue;

                GBOperatorStraightFour[] sfours = null;
                if (maxCat >= 1)
                    sfours = GBOperatorStraightFour.GetOperatorsIfValid (ss);

                GBOperatorBrokenThree[] bthrees = null;
                GBOperatorThree3[] three3s = null;
                if (maxCat >= 2) {
                    bthrees = GBOperatorBrokenThree.GetOperatorsIfValid (ss);
                }

                // Heuristic "restricted trees", page 141.
                // HEURISTIC
                // FIXME: re-enable this after testing.
                if (maxCat >= 2 /*&& Three2Applicable == false*/) {
                    three3s = GBOperatorThree3.GetOperatorsIfValid (ss);
                }

                if (sfours != null)
                    opersGoal.AddRange (sfours);
                if (bthrees != null)
                    opersThrees.AddRange (bthrees);
                if (three3s != null)
                    opersThrees.AddRange (three3s);
            }
        }

        // Order: goal, fours, threes
        opersGoal.AddRange (opersFours);
        opersGoal.AddRange (opersThrees);

        if (opersGoal.Count > 0)
            return ((GBOperator[]) opersGoal.ToArray (typeof (GBOperator)));

        return (null);
    }
    public IDBOperator[] LegalOperators(DBNode node)
    {
        GBSpaceState gstate = (GBSpaceState)node.State;

        return(gstate.LegalOperators);
    }
    /** Test the GBSearchModule
     */
    public static void Main(string[] args)
    {
        // Initialize a board randomly
        GoBangBoard gb  = new GoBangBoard();
        Random      rnd = new Random();

        /*
         * for (int n = 0 ; n < 23 ; ++n)
         *      gb.board[rnd.Next (0, gb.boardDim), rnd.Next (0, gb.boardDim)] =
         *              rnd.Next (0, 3) - 1;
         */

        /*
         * gb.board[5,5] = gb.board[5,8] = -1;
         * gb.board[7,4] = gb.board[7,9] = -1;
         *
         * gb.board[5,4] = gb.board[5,6] = gb.board[5,7] = gb.board[5,9] = 1;
         * gb.board[7,6] = gb.board[7,7] = 1;
         */

        gb.board[6, 6] = gb.board[6, 7] = gb.board[6, 8] = 1;
        gb.board[7, 7] = gb.board[8, 6] = gb.board[8, 7] = gb.board[8, 8] = 1;
        gb.board[9, 6] = gb.board[10, 5] = gb.board[10, 6] = gb.board[10, 7] = 1;

        gb.board[6, 5]  = gb.board[7, 5] = gb.board[7, 6] = gb.board[7, 8] = -1;
        gb.board[8, 5]  = gb.board[8, 9] = gb.board[9, 5] = gb.board[9, 7] = gb.board[9, 9] = -1;
        gb.board[10, 4] = gb.board[11, 6] = -1;

        gb.board[5, 5] = 1;
        gb.board[4, 4] = -1;

        gb.board[6, 9]  = 1;
        gb.board[6, 10] = -1;
        gb.board[4, 7]  = 1;
        gb.board[5, 7]  = -1;

        /*
         * gb.board[6,10] = 1;
         * gb.board[6,9] = -1;
         */

        /*
         * gb.board[6,6] = gb.board[6,7] = gb.board[6,8] = 1;
         * gb.board[7,7] = gb.board[8,6] = gb.board[8,7] = gb.board[8,8] = 1;
         * gb.board[9,6] = gb.board[10,5] = gb.board[10,6] = gb.board[10,7] = 1;
         *
         * gb.board[6,5] = gb.board[7,5] = gb.board[7,6] = gb.board[7,8] = -1;
         * gb.board[8,5] = gb.board[8,9] = gb.board[9,5] = gb.board[9,7] = gb.board[9,9] = -1;
         * gb.board[10,4] = gb.board[11,6] = -1;
         *
         * // Move 1/2
         * gb.board[5,5] = 1;
         * gb.board[4,4] = -1;
         *
         * // Move 3/4
         * gb.board[5,7] = 1;
         * gb.board[4,7] = -1;
         *
         * // Move 5/6
         * gb.board[5,9] = 1;
         * gb.board[4,10] = -1;
         *
         * // Move 7/8
         * gb.board[5,8] = 1;
         * gb.board[5,6] = -1;
         *
         * // Move 9/10
         * gb.board[5,11] = 1;
         * gb.board[5,10] = -1;
         *
         * // Move 11/12
         * gb.board[6,10] = 1;
         * gb.board[6,9] = -1;
         *
         * // Move 13/14
         * gb.board[7,9] = 1;
         * gb.board[4,12] = -1;
         *
         * // Move 15/16
         * gb.board[4,6] = 1;
         * gb.board[3,5] = -1;
         */

        /* TODO: check this, ask marco
         * gb.board[4,4] = gb.board[6,6] = gb.board[7,7] = 1;
         */

        GBSearchModule gbSearch  = new GBSearchModule(GoBangBoard.boardDim);
        GBSpaceState   rootState = new GBSpaceState(gb);

        rootState.UpdateIsGoal(gbSearch);

        DBSearch db = new DBSearch(gbSearch, false);

        db.Search(rootState);

        db.DumpDOT();
        //db.DumpDOTGoalsOnly ();
        gbSearch.DEBUGnodeArray();

        /*
         * foreach (DLPSpaceState state in db.GoalStates ()) {
         *      DumpOperatorChain (state);
         * }
         */
    }
    public GBSpaceState(GoBangBoard gb, GBOperator lastOperator,
		GBSpaceState lastState, GBOperator[] legalOperators)
    {
        this.gb = gb;
        this.lastOperator = lastOperator;
        this.lastState = lastState;

        this.legalOperators = legalOperators;
    }
    /** Check if the operator is formally valid.
     *
     * Note: we only check if the squares the operator will occupy are
     * currently free.  The logical check if the operator is applicable must
     * have preceeded this one already.
     *
     * @param state The state the operator will be tested for validity on.
     *
     * @returns True if the operator is valid, false otherwise.
     */
    public bool Valid(GBSpaceState state)
    {
        for (int n = 0 ; n < fAdd.GetLength (0) ; ++n) {
            if (state.GB.board[fAdd[n,1], fAdd[n,0]] != 0)
                return (false);
        }

        return (true);
    }
 public GBSpaceState(GoBangBoard gb, GBOperator lastOperator,
                     GBSpaceState lastState, int maxCat)
     : this(gb, lastOperator, lastState, null)
 {
     this.legalOperators = GBOperator.LegalOperators(this, maxCat);
 }
    // 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);
    }
    // TODO: this needs to be changed fundamentally, including changes in
    // DBSearch.cs.
    //
    // GoMoku will need up to four nodes combined in one combination step, for
    // a situation like this (artificial, but illustrates the point):
    //
    //    O  O  O
    //    O  O  O
    //    O3 O2 O1
    //
    // Lets assume the two rows at the top already existed before, and the
    // states O1, O2 and O3 have been independently explored (as they create a
    // new application of an operator, this will be the case).  However, then
    // the row O1, O2 and O3 create a new threat.  This can only be "seen" by
    // db-search if they are allowed to be combined in one step.  No single
    // combination will create a new dependent operator.
    //
    // We might do up-to-four combination efficiently by exploiting the board
    // geometry.  To do that, we create an array the same dimensions as the
    // board of linked list, storing states.  For each dependency node state,
    // store the state in a number of linked lists.  Store them in that linked
    // lists that are behind the coordinates in the f_{add} set of the last
    // operator of the state.
    //
    // Then, for each coordinate, we do the following: extract all linked
    // lists in a G_7 environment centered at the coordinate into one big
    // linked list.  Only check all the states refered in this big list for
    // 2-, 3- and 4-combinability.  For each coordinate we have to do this
    // four times for the different G_7 environment.
    //
    // The pseudo code for the whole combination stage would look like:
    //
    // foreach (Coordinate coord in boardCoordinates) {
    //   foreach (G_7 g7 centeredIn coord) {
    //     ArrayList states = new ArrayList ();
    //     foreach (Square sq in g7)
    //       states.AddRange (sq.States);
    //
    //     foreach (2-Combination (c1, c2) in states)
    //       CheckCombinability (c1, c2);
    //     foreach (3-Combination (c1, c2, c3) in states)
    //       CheckCombinability (c1, c2, c3);
    //     foreach (4-Combination (c1, c2, c3, c4) in states)
    //       CheckCombinability (c1, c2, c3, c4);
    //   }
    // }
    //
    // The numerical complexity is (n \over k) = \frac{n!}{k! (n - k)!}
    // where n = number of states collected in "states". k = number of
    // elements in the combination (2, 3, 4).
    //
    // So, for n states on the combined G_7 square list this would give
    // (n \over k) k-Combinations.
    //
    // States |  2-C |    3-C |     4-C
    // -------+------+--------+--------
    //     10 |   45 |    120 |     210
    //     20 |  190 |   1140 |    4845
    //    100 | 4950 | 161700 | 3921225
    //
    // So we should keep the number of states influencing a board square well
    // below 100, while <= 20 is certainly ok.
    //
    // XXX: for now, we only test with two-combinability
    public IDBSpaceState CombineIfResultIsNewOperators(DBNode node1,
                                                       Stack node1Path, DBNode node2, Stack node2Path)
    {
        GBSpaceState gstate1 = (GBSpaceState)node1.State;
        GBSpaceState gstate2 = (GBSpaceState)node2.State;
        GBOperator   last2   = gstate2.LastOperator;

        // First check the boards are not incompatible:
        // TODO: check if this is right (or necessary, does not seem to change
        // any results).
        if (gstate2.GB.CompatibleWith(((GBSpaceState)node1.State).GB) == false)
        {
            return(null);
        }

        // Build combined state by applying operator chain.
        ArrayList operatorChain = new ArrayList();

        foreach (DBNode pN in node2Path)
        {
            if (node1Path.Contains(pN))
            {
                break;
            }

            GBSpaceState nstate = (GBSpaceState)pN.State;
            operatorChain.Add(nstate.LastOperator);

            if (nstate.CombinedOperPath != null)
            {
                ArrayList combRev = (ArrayList)nstate.CombinedOperPath.Clone();
                combRev.Reverse();

                operatorChain.AddRange(combRev);
            }
        }
        operatorChain.Reverse();
        operatorChain.Add(last2);

        GBSpaceState gComb = (GBSpaceState)node1.State;

        foreach (GBOperator oper in operatorChain)
        {
            gComb = (GBSpaceState)oper.Apply(this, gComb);
        }

        //GBSpaceState gComb = (GBSpaceState) last2.Apply (this, node1.State);
        //gComb.GB.AddExtraStones (gstate2.GB);
        gComb.UpdateLegalOperators(maximumCategory, categoryReductionHeuristicOn);

        // Now check if the new state results in new operators
        GBOperator[] n1o = gstate1.LegalOperators;
        GBOperator[] n2o = gstate2.LegalOperators;

        GBOperator[] nCo = gComb.LegalOperators;
        if (nCo == null)
        {
            return(null);
        }

        // Check: nCo \setminus (n1o \cup n2o) \neq \emptyset
        foreach (GBOperator gbo in nCo)
        {
            if (n1o != null && Array.IndexOf(n1o, gbo) >= 0)
            {
                return(null);
            }
            if (n2o != null && Array.IndexOf(n2o, gbo) >= 0)
            {
                return(null);
            }
        }

        gComb.UpdateIsGoal(this);

        // Now that the combination succeeded, we still need to copy over all
        // the operators we applied 'at once' when copying the field content.
        // We need to do this for the threat sequence search later, which
        // needs operator-level granularity for the defense search.
        gComb.BuildCombinedOperatorPath(node1Path, node2Path);

        return(gComb);
    }
    public static int[,] BuildOperatorMap(GoBangBoard board, out int maxCount)
    {
        // Get legal operators for the state represented by the board
        GBSpaceState state = new GBSpaceState (board);
        GBOperator[] opers = GBOperator.LegalOperators (state, 2);

        // Now build an attack count map and keep track of the maximum number
        // of operators a single attack stone creates.
        maxCount = 0;
        int[,] attackMap = new int[GoBangBoard.boardDim, GoBangBoard.boardDim];

        foreach (GBOperator op in opers) {
            if (op is GBOperatorThree3)
                continue;

            for (int n = 0 ; n < op.fAdd.GetLength (0) ; ++n) {
                // We are not (yet) interested in the defending stones.
                if (op.fAdd[n,2] == -1)
                    continue;

                int x = op.fAdd[n,0];
                int y = op.fAdd[n,1];
                attackMap[y, x] += 1;
                Console.WriteLine ("at ({0}, {1}) oper: {2}", x, y, op);

                if (attackMap[y, x] > maxCount)
                    maxCount = attackMap[y, x];
            }
        }

        return (attackMap);
    }
    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));
    }
    /** 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);
    }
    public bool Applicable(IDBOperator oper, GBSpaceState state)
    {
        GBOperator goper = (GBOperator)oper;

        return(goper.Applicable(state));
    }
    public int CompareTo(object o2)
    {
        GBSpaceState gbs2 = (GBSpaceState)o2;

        return(gb.CompareTo(gbs2.gb));
    }
    public GBSpaceState(GoBangBoard gb, GBOperator lastOperator,
		GBSpaceState lastState, int maxCat)
        : this(gb, lastOperator, lastState, null)
    {
        this.legalOperators = GBOperator.LegalOperators (this, maxCat);
    }
Пример #28
0
    private void CheckC2(ArrayList[] affecting)
    {
        int[] n = new int[2];

        for (n[0] = 0; n[0] < affecting.Length; ++n[0])
        {
            for (n[1] = (n[0] + 1); n[1] < affecting.Length; ++n[1])
            {
                int[] count = new int[2];
                int   countN;
                if (affecting[n[0]].Count == 0 || affecting[n[1]].Count == 0)
                {
                    continue;
                }

                do
                {
                    // Enumerate all possible combinations at position (n0, n1)
                    DBNode       node1  = (DBNode)affecting[n[0]][count[0]];
                    DBNode       node2  = (DBNode)affecting[n[1]][count[1]];
                    GBSpaceState state1 = (GBSpaceState)node1.State;
                    GBSpaceState state2 = (GBSpaceState)node2.State;

                    // Check that there is a appropiate dependency node in it and
                    // then for combinability for state1/state2
                    if ((thisStageDependencies.Contains(state1) == true ||
                         thisStageDependencies.Contains(state2) == true) &&
                        state2.GB.CompatibleWith(state1.GB) &&
                        gbS.NotInConflict(state1, state2))
                    {
                        GBSpaceState gComb =
                            (GBSpaceState)state2.LastOperator.Apply(gbS, state1);
                        gComb.GB.AddExtraStones(state2.GB);
                        gComb.UpdateLegalOperators(gbS.maximumCategory);

                        // Now check if the new state results in new operators
                        GBOperator[] n1o = state1.LegalOperators;
                        GBOperator[] n2o = state2.LegalOperators;

                        GBOperator[] nCo = gComb.LegalOperators;
                        if (nCo == null)
                        {
                            goto nextComb;
                        }
                        //Console.WriteLine ("nCo.Length = {0}", nCo.Length);

                        // Check: nCo \setminus (n1o \cup n2o) \neq \emptyset
                        bool seenUnique = false;
                        foreach (GBOperator gbo in nCo)
                        {
                            if (n1o != null && Array.IndexOf(n1o, gbo) >= 0)
                            {
                                continue;
                            }
                            if (n2o != null && Array.IndexOf(n2o, gbo) >= 0)
                            {
                                continue;
                            }

                            seenUnique = true;
                            //Console.WriteLine ("unique: {0}", gbo);
                        }
                        if (seenUnique == false)
                        {
                            goto nextComb;
                        }

                        // We have found a new combination

                        /*
                         * Console.WriteLine ("TODO: found found found:");
                         * Console.WriteLine ("From 1: {0}", state1.GB);
                         * Console.WriteLine ("From 2: {0}", state2.GB);
                         * Console.WriteLine ("To: {0}", gComb.GB);
                         * throw (new ArgumentException ("DEBUG"));
                         */

                        DBNode combNode = new DBNode(DBNode.NodeType.Combination,
                                                     gComb, level);
                        combNode.IsGoal = gbS.IsGoal(gComb);

                        /* TODO: have to get this back to db-search somehow,
                         * or throw exception here.
                         * if (combNode.IsGoal)
                         *      goalCount += 1;
                         */

                        dbS.AddCombinationNode(node1, combNode);
                        node2.CombinedChildren.Add(combNode);
                        gbS.RegisterNewNode(combNode);
                    }

nextComb:
                    for (countN = 0; countN < count.Length; ++countN)
                    {
                        count[countN] += 1;

                        if (count[countN] < affecting[n[countN]].Count)
                        {
                            break;
                        }

                        count[countN] = 0;
                    }
                } while (countN < count.Length);
            }
        }
    }
    /** Check the goal conditions on page 135.
     *
     * @param state The space state to be checked for goal'iness.
     *
     * @returns True in case it is a goal state, false otherwise.
     */
    public bool IsGoal(IDBSpaceState stateDB)
    {
        GBSpaceState state = (GBSpaceState)stateDB;

        return(state.IsGoal);
    }
    /** Apply the operator to the given state, returning a new state.
     *
     * @param state The source state.
     *
     * @returns The newly created destination state.
     */
    public IDBSpaceState Apply(DBSearchModule module, IDBSpaceState stateDB)
    {
        GBSearchModule gmod = (GBSearchModule) module;
        GBSpaceState state = (GBSpaceState) stateDB;

        // TODO: remove later, we already checked this previously
        if (Valid (state) == false)
            throw (new ArgumentException ("Operator not applicable!"));

        GoBangBoard newBoard = (GoBangBoard) state.GB.Clone ();

        // Apply all the f_{add} stones
        for (int n = 0 ; n < fAdd.GetLength (0) ; ++n)
            newBoard.board[fAdd[n,1], fAdd[n,0]] = fAdd[n,2];

        GBSpaceState newState = new GBSpaceState (newBoard, this, state,
            gmod.maximumCategory);
        newState.UpdateIsGoal (gmod);

        return (newState);
    }
    public bool Applicable(IDBOperator oper, GBSpaceState state)
    {
        GBOperator goper = (GBOperator) oper;

        return (goper.Applicable (state));
    }
    public bool NotInConflict(GBSpaceState gstate1, GBSpaceState gstate2)
    {
        // We only need to check the last operator chain, as all previous have
        // been checked in the previous stages/levels
        while (gstate2 != null && gstate2.LastOperator != null) {
            if (Applicable (gstate2.LastOperator, gstate1) == false)
                return (false);

            gstate2 = gstate2.LastState;
        }
        return (true);
    }
    /** Test the GBSearchModule
     */
    public static void Main(string[] args)
    {
        // Initialize a board randomly
        GoBangBoard gb = new GoBangBoard ();
        Random rnd = new Random ();

        /*
        for (int n = 0 ; n < 23 ; ++n)
            gb.board[rnd.Next (0, gb.boardDim), rnd.Next (0, gb.boardDim)] =
                rnd.Next (0, 3) - 1;
        */

        /*
        gb.board[5,5] = gb.board[5,8] = -1;
        gb.board[7,4] = gb.board[7,9] = -1;

        gb.board[5,4] = gb.board[5,6] = gb.board[5,7] = gb.board[5,9] = 1;
        gb.board[7,6] = gb.board[7,7] = 1;
        */

        gb.board[6,6] = gb.board[6,7] = gb.board[6,8] = 1;
        gb.board[7,7] = gb.board[8,6] = gb.board[8,7] = gb.board[8,8] = 1;
        gb.board[9,6] = gb.board[10,5] = gb.board[10,6] = gb.board[10,7] = 1;

        gb.board[6,5] = gb.board[7,5] = gb.board[7,6] = gb.board[7,8] = -1;
        gb.board[8,5] = gb.board[8,9] = gb.board[9,5] = gb.board[9,7] = gb.board[9,9] = -1;
        gb.board[10,4] = gb.board[11,6] = -1;

        gb.board[5,5] = 1;
        gb.board[4,4] = -1;

        gb.board[6,9] = 1;
        gb.board[6,10] = -1;
        gb.board[4,7] = 1;
        gb.board[5,7] = -1;

        /*
        gb.board[6,10] = 1;
        gb.board[6,9] = -1;
        */

        /*
        gb.board[6,6] = gb.board[6,7] = gb.board[6,8] = 1;
        gb.board[7,7] = gb.board[8,6] = gb.board[8,7] = gb.board[8,8] = 1;
        gb.board[9,6] = gb.board[10,5] = gb.board[10,6] = gb.board[10,7] = 1;

        gb.board[6,5] = gb.board[7,5] = gb.board[7,6] = gb.board[7,8] = -1;
        gb.board[8,5] = gb.board[8,9] = gb.board[9,5] = gb.board[9,7] = gb.board[9,9] = -1;
        gb.board[10,4] = gb.board[11,6] = -1;

        // Move 1/2
        gb.board[5,5] = 1;
        gb.board[4,4] = -1;

        // Move 3/4
        gb.board[5,7] = 1;
        gb.board[4,7] = -1;

        // Move 5/6
        gb.board[5,9] = 1;
        gb.board[4,10] = -1;

        // Move 7/8
        gb.board[5,8] = 1;
        gb.board[5,6] = -1;

        // Move 9/10
        gb.board[5,11] = 1;
        gb.board[5,10] = -1;

        // Move 11/12
        gb.board[6,10] = 1;
        gb.board[6,9] = -1;

        // Move 13/14
        gb.board[7,9] = 1;
        gb.board[4,12] = -1;

        // Move 15/16
        gb.board[4,6] = 1;
        gb.board[3,5] = -1;
        */

        /* TODO: check this, ask marco
        gb.board[4,4] = gb.board[6,6] = gb.board[7,7] = 1;
        */

        GBSearchModule gbSearch = new GBSearchModule (GoBangBoard.boardDim);
        GBSpaceState rootState = new GBSpaceState (gb);
        rootState.UpdateIsGoal (gbSearch);

        DBSearch db = new DBSearch (gbSearch, false);
        db.Search (rootState);

        db.DumpDOT ();
        //db.DumpDOTGoalsOnly ();
        gbSearch.DEBUGnodeArray ();
        /*
        foreach (DLPSpaceState state in db.GoalStates ()) {
            DumpOperatorChain (state);
        }
        */
    }
    public static GBOperator[] LegalOperators(GBSpaceState state, int maxCat)
    {
        ArrayList opersGoal   = new ArrayList();
        ArrayList opersFours  = new ArrayList();
        ArrayList opersThrees = new ArrayList();

        // Check G_5 for operators
        foreach (GoBangBoard.StoneSet ss in state.GB.G5)
        {
            if (IsLastOperatorDependent(ss, state.LastOperator) == false)
            {
                continue;
            }

            GBOperatorFive[] fives = null;
            if (maxCat >= 0)
            {
                fives = GBOperatorFive.GetOperatorsIfValid(ss);
            }

            GBOperatorFour[] fours = null;
            if (maxCat >= 1)
            {
                fours = GBOperatorFour.GetOperatorsIfValid(ss);
            }

            if (fives != null)
            {
                opersGoal.AddRange(fives);
            }
            if (fours != null)
            {
                opersFours.AddRange(fours);
            }
        }

        bool Three2Applicable = false;

        if (maxCat >= 2)
        {
            // Check G_7 for operators
            foreach (GoBangBoard.StoneSet ss in state.GB.G7)
            {
                if (IsLastOperatorDependent(ss, state.LastOperator) == false)
                {
                    continue;
                }

                /*
                 * Console.Write ("7ss: ");
                 * for (int n = 0 ; n < ss.stones.Length ; ++n)
                 *      Console.Write ("{0} ", ss.stones[n] == 1 ? "O" :
                 *              (ss.stones[n] == -1 ? "X" : "."));
                 * Console.WriteLine ();
                 */

                GBOperatorThree2[] three2 =
                    GBOperatorThree2.GetOperatorsIfValid(ss);

                if (three2 != null)
                {
                    Three2Applicable = true;
                    opersThrees.AddRange(three2);
                }
            }
        }

        // Check G_6 for operators
        if (maxCat >= 1)
        {
            foreach (GoBangBoard.StoneSet ss in state.GB.G6)
            {
                if (IsLastOperatorDependent(ss, state.LastOperator) == false)
                {
                    continue;
                }

                GBOperatorStraightFour[] sfours = null;
                if (maxCat >= 1)
                {
                    sfours = GBOperatorStraightFour.GetOperatorsIfValid(ss);
                }

                GBOperatorBrokenThree[] bthrees = null;
                GBOperatorThree3[]      three3s = null;
                if (maxCat >= 2)
                {
                    bthrees = GBOperatorBrokenThree.GetOperatorsIfValid(ss);
                }

                // Heuristic "restricted trees", page 141.
                // HEURISTIC
                // FIXME: re-enable this after testing.
                if (maxCat >= 2 /*&& Three2Applicable == false*/)
                {
                    three3s = GBOperatorThree3.GetOperatorsIfValid(ss);
                }

                if (sfours != null)
                {
                    opersGoal.AddRange(sfours);
                }
                if (bthrees != null)
                {
                    opersThrees.AddRange(bthrees);
                }
                if (three3s != null)
                {
                    opersThrees.AddRange(three3s);
                }
            }
        }

        // Order: goal, fours, threes
        opersGoal.AddRange(opersFours);
        opersGoal.AddRange(opersThrees);

        if (opersGoal.Count > 0)
        {
            return((GBOperator[])opersGoal.ToArray(typeof(GBOperator)));
        }

        return(null);
    }
    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));
    }
    public static void Main(string[] args)
    {
        GoBangBoard gb = new GoBangBoard ();
        Random rnd = new Random ();

        // Initialize board randomly
        for (int n = 0 ; n < 130 ; ++n)
            gb.board[rnd.Next (0, boardDim), rnd.Next (0, boardDim)] =
                rnd.Next (0, 3) - 1;

        int count = 0;
        foreach (StoneSet ss in gb.G5) {
            Console.Write ("ss at ({0},{1}) to ({2},{3}), len {4}: ",
                ss.x, ss.y, ss.ax, ss.ay, ss.stones.Length);
            foreach (int stone in ss.stones) {
                Console.Write ("{0}", (stone == 0) ? "." :
                    ((stone == 1) ? "O" : "X"));
            }
            Console.WriteLine ();

            count += 1;
        }
        Console.WriteLine ("|G5| = {0}", count);

        count = 0;
        foreach (StoneSet ss in gb.G6)
            count += 1;
        Console.WriteLine ("|G6| = {0}", count);

        count = 0;
        foreach (StoneSet ss in gb.G7)
            count += 1;
        Console.WriteLine ("|G7| = {0}", count);

        // Test operators a little
        gb.DumpBoard ();

        GBSpaceState state = new GBSpaceState (gb);
        GBOperator[] legalOpers = GBOperator.LegalOperators (state, 2);
        foreach (GBOperator gop in legalOpers)
            Console.WriteLine ("oper: {0}", gop);
    }
    public static GBOperator[] LegalOperators(DBNode node, int maxCat)
    {
        GBSpaceState dst = (GBSpaceState)node.State;

        return(LegalOperators(dst, maxCat));
    }
    // TODO: maybe remove this, its useless now
    public bool Applicable(GBSpaceState state)
    {
        if (Valid (state) == false)
            return (false);

        return (true);
    }