private bool GetMoveNormal()
    {
        Console.WriteLine("GetMoveNormal");
        Debug.Assert(threatsequence == null);

        // If only one field, skip sequence search
        ArrayList intFields = fieldagent.ReallyInterestingFields(board, 1);

        if (intFields.Count > 1)
        {
            // Search for winning threat sequence
            threatsequence            = FindWinning(board);
            lastAppliedThreatSequence = null;
            if (threatsequence != null && threatsequence.attacker == null)
            {
                threatsequence = null;
            }

            if (threatsequence != null)
            {
                state = MoveState.Attack;
                statusStr.Append("ATTACK!, ");

                return(true);
            }
        }

        // No threat sequence for us found, so lets proceed
        bestfield = DoABMove();

        return(false);
    }
    private static int[,] ExtraGoalSquares(GBThreatSequence seq)
    {
        ArrayList squares = new ArrayList();

        // Add only the defender moves for the moves just made
        foreach (GBMove defMove in seq.defender)
        {
            squares.Add(new int[] { defMove.X, defMove.Y });
        }

        // Add everything below.
        seq = seq.next;
        while (seq != null)
        {
            squares.Add(new int[] { seq.attacker.X, seq.attacker.Y });

            foreach (GBMove defMove in seq.defender)
            {
                squares.Add(new int[] { defMove.X, defMove.Y });
            }
            seq = seq.next;
        }

        int[,] sqA = new int[squares.Count, 2];
        for (int n = 0; n < squares.Count; ++n)
        {
            sqA[n, 0] = ((int[])squares[n])[0];
            sqA[n, 1] = ((int[])squares[n])[1];
        }

        return(sqA);
    }
    public override string ToString()
    {
        StringBuilder sb = new StringBuilder();

        GBThreatSequence seq = this;

        sb.Append("\n");
        sb.Append("Sequence:\n");

        while (seq != null)
        {
            if (seq.attacker != null)
            {
                sb.AppendFormat("  move: {0}, threat class {1}\n",
                                seq.attacker, seq.attackerThreatClass);

                foreach (GBMove reply in seq.defender)
                {
                    sb.AppendFormat("      def-reply: {0}\n", reply);
                }
            }
            else
            {
                sb.AppendFormat("  already GOAL");
            }

            seq = seq.next;
        }

        return(sb.ToString());
    }
    /** Try to find a winning threat sequence for the opponent.
     *
     * @param board The current game board to be checked.
     * @param dbDefenseTimeLimit The limit in milliseconds to be allowed for
     * the search.
     *
     * @returns The winning threat sequence found null if none has been found.
     */
    private GBThreatSequence GetOpponentSequence(int[,] board, int dbDefenseTimeLimit)
    {
        int[,] boardRev = new int[board.GetLength(1), board.GetLength(0)];
        for (int y = 0; y < boardRev.GetLength(0); ++y)
        {
            for (int x = 0; x < boardRev.GetLength(1); ++x)
            {
                // Reverse, so we check for "us", where us is, in fact, the
                // opponent.
                boardRev[y, x] = -board[x, y];
            }
        }

        Console.WriteLine("Searching for opponent attackability of future board:");
        Console.WriteLine(new GoBangBoard(boardRev));

        GBThreatSequence seq = GBThreatSearch.FindWinning(boardRev,
                                                          dbDefenseTimeLimit * 1000, true);

        if (seq != null)
        {
            Console.WriteLine("Opponent sequence: {0}", seq);
        }

        return(seq);
    }
    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
    }
    // TODO: docs
    public ArrayList ThreatDefensiveInterestingFields(int[,] board,
                                                      int dbDefenseTimeLimit)
    {
        GBThreatSequence seq = GetOpponentSequence(board, dbDefenseTimeLimit);

        if (seq == null)
        {
            return(null);
        }

        // Only sequences which have defending moves (that is, category 1 and
        // above) do count.  The special case STRAIGHT FOUR is a GOAL and does
        // not have any defending moves.
        if (seq.defender == null || seq.defender.Length == 0)
        {
            return(null);
        }

        ArrayList defFields = new ArrayList();

        // Add the attack and the defending moves of the first sequence ply to
        // the "defending interesting fields".
        defFields.Add(new Coordinate(seq.attacker.X, seq.attacker.Y));

        if (seq.defender != null)
        {
            foreach (GBMove gbm in seq.defender)
            {
                defFields.Add(new Coordinate(gbm.X, gbm.Y));
            }
        }


        // Now only keep the most attacking ones, that is the ones that
        // creates the highest number of distinct operators.
        ArrayList fields = GetDefensePriorityFields(board, defFields);

        Console.WriteLine("ThreatDefensiveInterestingFields:");
        Console.Write("  defFields: ");
        foreach (Coordinate c in defFields)
        {
            Console.Write("{0}, ", c);
        }
        Console.WriteLine();
        Console.Write("  to max filtered: ");
        foreach (Coordinate c in fields)
        {
            Console.Write("{0}, ", c);
        }
        Console.WriteLine();

        return(fields);
    }
    // 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);
    }
    public static GBThreatSequence[] BuildAllGoalPathes(GBSearchModule module,
                                                        DBNode root)
    {
        ArrayList goalpathes = GoalPathes(module, root);

        Console.WriteLine("I have {0} goal pathes", goalpathes.Count);

        // Create the threat sequences.
        ArrayList threatSeqs = new ArrayList();

        // Build one individual threat sequence for each goalpath.
        foreach (ArrayList gp in goalpathes)
        {
            GBThreatSequence ts = BuildThreatSequence(gp);
            threatSeqs.Add(ts);
        }

        // DEBUG output
        Console.WriteLine("{0} threat seqs lists", threatSeqs.Count);
        int seqNo = 1;

        foreach (GBThreatSequence seqW in threatSeqs)
        {
            GBThreatSequence seq = seqW;
            Console.WriteLine();

            Console.WriteLine(((GBSpaceState)root.State).GB);
            Console.WriteLine();
            Console.WriteLine("Sequence {0}: ", seqNo);

            int m = 0;
            while (seq != null)
            {
                Console.WriteLine("  move{0}: {1}, threat class {2}", m,
                                  seq.attacker, seq.attackerThreatClass);
                m += 1;

                foreach (GBMove reply in seq.defender)
                {
                    Console.WriteLine("      def-reply{0}: {1}", m, reply);
                    m += 1;
                }

                seq = seq.next;
            }

            seqNo += 1;
        }

        return((GBThreatSequence[])
               threatSeqs.ToArray(typeof(GBThreatSequence)));
    }
    /** 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);
    }
    // @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);
    }
    // timeoutMS: zero or timeout limit in ms
    public static GBThreatSequence FindWinning(int[,] board, int timeoutMS,
                                               bool defOpponentSearch)
    {
        GBThreatSearch gts = new GBThreatSearch(new GoBangBoard(board),
                                                defOpponentSearch);
        GBThreatSequence seq = gts.FindWinningThreatSeqOTF(timeoutMS);

        if (seq != null)
        {
            Console.WriteLine("Board:");
            Console.WriteLine(new GoBangBoard(board));
            Console.WriteLine();

            Console.WriteLine("Winning threat sequence found:");
            Console.WriteLine(seq);
        }
        else
        {
            Console.WriteLine("No winning threat sequence found.");
        }

        return(seq);
    }
    private static void FillInMoves(GBThreatSequence seq, GBOperator oper)
    {
        // Create attacker and defender moves.
        ArrayList defenderMoves = new ArrayList();

        //Console.WriteLine ("DEBUG FillInMoves, {0} moves", oper.fAdd.GetLength (0));

        for (int move = 0; move < oper.fAdd.GetLength(0); ++move)
        {
            // Attacker move
            if (oper.fAdd[move, 2] == 1)
            {
                seq.attacker = new GBMove(oper.fAdd[move, 0],
                                          oper.fAdd[move, 1], oper.fAdd[move, 2]);
                seq.attackerThreatClass = oper.Class;
            }
            else if (oper.fAdd[move, 2] == -1)
            {
                defenderMoves.Add(new GBMove(oper.fAdd[move, 0],
                                             oper.fAdd[move, 1], oper.fAdd[move, 2]));
            }
        }
        seq.defender = (GBMove[])defenderMoves.ToArray(typeof(GBMove));
    }
    private bool GetMoveAttack()
    {
        Console.WriteLine("GetMoveAttack");
        Debug.Assert(threatsequence != null);

        // Check the threat sequence is still valid on the current board.
        // This is the rare magic case that could defeat us (C-3, C-4)
        if (threatsequence.CheckValidRevBoard(board) == false)
        {
            statusStr.Append("magic :-(, ");
            Console.WriteLine("Winning threat sequence not valid anymore, magic case.");

            threatsequence = null;
            state          = MoveState.Normal;

            return(true);
        }

        // Also check if there is still attacker moves left
        if (threatsequence.attacker == null)
        {
            threatsequence = null;
            statusStr.Append("seqEnd reached");

            state = MoveState.Normal;
            return(true);
        }


        // Check the last move of the opponent is one of the expected defender
        // moves in our threat sequence.  If it is not, fall back on
        // alpha-beta search.

        if (lastAppliedThreatSequence != null &&
            lastAppliedThreatSequence.defender != null)
        {
            bool defendedThreat = false;

            foreach (GBMove gbm in lastAppliedThreatSequence.defender)
            {
                if (gbm.X == lastoppmove.X && gbm.Y == lastoppmove.Y)
                {
                    defendedThreat = true;

                    break;
                }
            }

            // If the threat was not defended against, fall back to alpha-beta
            if (defendedThreat == false)
            {
                statusStr.Append("not defended, ");
                threatsequence = null;

                state = MoveState.Normal;
                return(true);
            }
            statusStr.Append("defended, ");
        }

        // Check if the move is in the interesting fields, which basically
        // asserts us there is no opponent threat we have to respond to.
        ArrayList  fields   = fieldagent.ReallyInterestingFields(board, 1);
        Coordinate wantmove = new Coordinate(threatsequence.attacker.X,
                                             threatsequence.attacker.Y);

        if (fields.Contains(wantmove))
        {
            Console.WriteLine("Move is chosen from sequence");
            bestfield = wantmove;
            statusStr.Append("move ({0},{1}) from seq, ", wantmove.X, wantmove.Y);
            sequence_used             = true;
            lastAppliedThreatSequence = threatsequence;

            // Advance to next threat
            threatsequence = threatsequence.next;

            // End of sequence -> fall back to alpha beta, for the _next_
            // move.
            if (threatsequence == null)
            {
                state = MoveState.Normal;
            }
        }
        else
        {
            Console.WriteLine("Move from sequence is not in interesting fields");
            sequence_used = false;
            statusStr.Append("move not from seq, ");

            bestfield = DoABMove();
        }

        // Move made
        return(false);
    }
 /** Check for refutability.
  *
  * @param seq The potential winning threat sequence to be checked.
  * @param curBoard The current board against which the sequence shall be
  * checked.
  *
  * @returns Negative value if the sequence is not refutable, otherwise the
  * value returned is the refutation depth.
  */
 private static int DefenseRefutes(GBThreatSequence seq, GoBangBoard curBoard)
 {
     return (DefenseRefutes (seq, curBoard, 0));
 }
    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[,] ExtraGoalSquares(GBThreatSequence seq)
    {
        ArrayList squares = new ArrayList ();

        // Add only the defender moves for the moves just made
        foreach (GBMove defMove in seq.defender)
            squares.Add (new int[] { defMove.X, defMove.Y });

        // Add everything below.
        seq = seq.next;
        while (seq != null) {
            squares.Add (new int[] { seq.attacker.X, seq.attacker.Y });

            foreach (GBMove defMove in seq.defender)
                squares.Add (new int[] { defMove.X, defMove.Y });
            seq = seq.next;
        }

        int[,] sqA = new int[squares.Count, 2];
        for (int n = 0 ; n < squares.Count ; ++n) {
            sqA[n,0] = ((int[]) squares[n])[0];
            sqA[n,1] = ((int[]) squares[n])[1];
        }

        return (sqA);
    }
    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));
    }
 /** Check for refutability.
  *
  * @param seq The potential winning threat sequence to be checked.
  * @param curBoard The current board against which the sequence shall be
  * checked.
  *
  * @returns Negative value if the sequence is not refutable, otherwise the
  * value returned is the refutation depth.
  */
 private static int DefenseRefutes(GBThreatSequence seq, GoBangBoard curBoard)
 {
     return(DefenseRefutes(seq, curBoard, 0));
 }
 public GBWinningThreatSequenceFoundException(GBThreatSequence seq)
 {
     this.seq = seq;
 }
 public GBWinningThreatSequenceFoundException(GBThreatSequence seq)
 {
     this.seq = seq;
 }
    // @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);
    }
    private bool GetMoveNormal()
    {
        Console.WriteLine ("GetMoveNormal");
        Debug.Assert (threatsequence == null);

        // If only one field, skip sequence search
        ArrayList intFields = fieldagent.ReallyInterestingFields (board, 1);
        if (intFields.Count > 1) {
            // Search for winning threat sequence
            threatsequence = FindWinning(board);
            lastAppliedThreatSequence = null;
            if (threatsequence != null && threatsequence.attacker == null)
                threatsequence = null;

            if (threatsequence != null) {
                state = MoveState.Attack;
                statusStr.Append ("ATTACK!, ");

                return (true);
            }
        }

        // No threat sequence for us found, so lets proceed
        bestfield = DoABMove ();

        return (false);
    }
    private static void FillInMoves(GBThreatSequence seq, GBOperator oper)
    {
        // Create attacker and defender moves.
        ArrayList defenderMoves = new ArrayList ();

        //Console.WriteLine ("DEBUG FillInMoves, {0} moves", oper.fAdd.GetLength (0));

        for (int move = 0 ; move < oper.fAdd.GetLength (0) ; ++move) {
            // Attacker move
            if (oper.fAdd[move, 2] == 1) {
                seq.attacker = new GBMove (oper.fAdd[move, 0],
                    oper.fAdd[move, 1], oper.fAdd[move, 2]);
                seq.attackerThreatClass = oper.Class;
            } else if (oper.fAdd[move, 2] == -1) {
                defenderMoves.Add (new GBMove (oper.fAdd[move, 0],
                    oper.fAdd[move, 1], oper.fAdd[move, 2]));
            }
        }
        seq.defender = (GBMove[]) defenderMoves.ToArray (typeof (GBMove));
    }
    public static void Main(string[] args)
    {
        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;
         */

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

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

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

        /*
         * // FIXME: this sequence is not found as un-refutable, while it is in
         * // fact (magic case, we know exactly what it is caused by and this is
         * // unlikely to appear in a real game and difficult to fix (though
         * // possible))
         * // 138, figure 5.5
         * gb.board[0,0] = gb.board[1,1] = gb.board[2,2] = gb.board[5,13] = -1;
         * gb.board[6,9] = gb.board[7,7] = gb.board[7,8] = -1;
         * gb.board[8,6] = gb.board[8,12] = -1;
         * gb.board[10,7] = gb.board[10,9] = -1;
         * gb.board[10,2] = gb.board[11,1] = gb.board[12,0] = -1;
         *
         * gb.board[2,12] = gb.board[3,12] = 1;
         * gb.board[6,6] = gb.board[6,7] = gb.board[7,6] = gb.board[7,10] = 1;
         * gb.board[8,8] = gb.board[8,11] = gb.board[9,8] = 1;
         */

        // 127b
        gb.board[5, 6] = gb.board[6, 9] = gb.board[7, 7] = gb.board[8, 8] = 1;
        gb.board[8, 9] = 1;

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

        /*
         * // 127a
         * 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;
         */

        Console.WriteLine("Starting winning threat sequence search...");

        GBThreatSearch   gts = new GBThreatSearch(gb, false);
        GBThreatSequence seq = gts.FindWinningThreatSeqOTF();

        if (seq != null)
        {
            Console.WriteLine("Board:");
            Console.WriteLine(gb);
            Console.WriteLine();

            Console.WriteLine("winning threat sequence found:");
            Console.WriteLine(seq);
        }

        GBThreatSearch   gts2 = new GBThreatSearch(gb, true);
        GBThreatSequence seq2 = gts2.FindWinningThreatSeqOTF();

        if (seq2 != null)
        {
            Console.WriteLine("Board:");
            Console.WriteLine(gb);
            Console.WriteLine();

            Console.WriteLine("winning threat sequence found:");
            Console.WriteLine(seq2);
        }
    }
    private bool GetMoveAttack()
    {
        Console.WriteLine ("GetMoveAttack");
        Debug.Assert (threatsequence != null);

        // Check the threat sequence is still valid on the current board.
        // This is the rare magic case that could defeat us (C-3, C-4)
        if (threatsequence.CheckValidRevBoard (board) == false) {
            statusStr.Append ("magic :-(, ");
            Console.WriteLine ("Winning threat sequence not valid anymore, magic case.");

            threatsequence = null;
            state = MoveState.Normal;

            return (true);
        }

        // Also check if there is still attacker moves left
        if (threatsequence.attacker == null) {
            threatsequence = null;
            statusStr.Append ("seqEnd reached");

            state = MoveState.Normal;
            return (true);
        }

        // Check the last move of the opponent is one of the expected defender
        // moves in our threat sequence.  If it is not, fall back on
        // alpha-beta search.

        if (lastAppliedThreatSequence != null &&
            lastAppliedThreatSequence.defender != null)
        {
            bool defendedThreat = false;

            foreach (GBMove gbm in lastAppliedThreatSequence.defender) {
                if (gbm.X == lastoppmove.X && gbm.Y == lastoppmove.Y) {
                    defendedThreat = true;

                    break;
                }
            }

            // If the threat was not defended against, fall back to alpha-beta
            if (defendedThreat == false) {
                statusStr.Append ("not defended, ");
                threatsequence = null;

                state = MoveState.Normal;
                return (true);
            }
            statusStr.Append ("defended, ");
        }

        // Check if the move is in the interesting fields, which basically
        // asserts us there is no opponent threat we have to respond to.
        ArrayList fields = fieldagent.ReallyInterestingFields(board, 1);
        Coordinate wantmove = new Coordinate (threatsequence.attacker.X,
            threatsequence.attacker.Y);

        if (fields.Contains (wantmove)) {
            Console.WriteLine("Move is chosen from sequence");
            bestfield = wantmove;
            statusStr.Append ("move ({0},{1}) from seq, ", wantmove.X, wantmove.Y);
            sequence_used = true;
            lastAppliedThreatSequence = threatsequence;

            // Advance to next threat
            threatsequence = threatsequence.next;

            // End of sequence -> fall back to alpha beta, for the _next_
            // move.
            if (threatsequence == null)
                state = MoveState.Normal;
        } else {
            Console.WriteLine("Move from sequence is not in interesting fields");
            sequence_used = false;
            statusStr.Append ("move not from seq, ");

            bestfield = DoABMove ();
        }

        // Move made
        return (false);
    }