/** 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);
    }
    // Return the operator's category that is the lowest among the applicable
    // operators for state.
    public static int LowestOperatorCategory(GoBangBoard board)
    {
        // A maximum, not important, just must be larger than any real
        // category
        int max = 99;

        // Search category 0 and 1 in G_5
        foreach (GoBangBoard.StoneSet ss in board.G5)
        {
            // There is no category below zero, so we can return early.
            if (GBOperatorFive.Present(ss))
            {
                return(0);
            }

            if (GBOperatorFour.Present(ss))
            {
                max = 1;
            }
        }

        if (max < 99)
        {
            return(max);
        }

        // Search category 1 and 2 in G_6
        foreach (GoBangBoard.StoneSet ss in board.G6)
        {
            // If there was no 0/1 before, this is certainly the minimum.
            if (GBOperatorStraightFour.Present(ss))
            {
                return(1);
            }

            if (max > 2 && GBOperatorBrokenThree.Present(ss))
            {
                max = 2;
            }
            else if (max > 2 && GBOperatorThree3.Present(ss))
            {
                max = 2;
            }
        }

        if (max < 99)
        {
            return(max);
        }

        foreach (GoBangBoard.StoneSet ss in board.G7)
        {
            if (GBOperatorThree2.Present(ss))
            {
                return(2);
            }
        }

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

        this.legalOperators = legalOperators;
    }
    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 void CountStones(GoBangBoard.StoneSet ss,
		out int white, out int hole)
    {
        white = hole = 0;

        for (int n = 0 ; n < ss.stones.Length ; ++n) {
            if (ss.stones[n] == 1)
                white += 1;
            else if (ss.stones[n] == 0)
                hole += 1;
        }
    }
    // 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 void AddExtraStones(GoBangBoard b2)
 {
     for (int y = 0; y < boardDim; ++y)
     {
         for (int x = 0; x < boardDim; ++x)
         {
             if (board[y, x] == 0 && b2.board[y, x] != 0)
             {
                 board[y, x] = b2.board[y, x];
             }
         }
     }
 }
    /** Test a potential winning threat sequence for refutability.
     *
     * @param curBoard The original board the original attacker db-search was
     * started with (root.State.GB).
     * @param root The root node of the search tree.
     * @param goalnode The new goal node identified by db-search.
     *
     * @returns null if the sequence is refutable, otherwise the sequence
     * itself is returned.
     */
    public static GBThreatSequence DefenseRefutable(GoBangBoard curBoard,
                                                    DBNode root, DBNode goalnode)
    {
        // First create the goal path, that is, all nodes that lie on the
        // direct child path to the node.
        ArrayList gp = GBThreatSequence.GoalPath(root, goalnode);

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

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

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

        gbFlipped.Flip();

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

        int refutes = DefenseRefutes(seq, gbFlipped);

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

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

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

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

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

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

                return;
            }

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

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

        this.legalOperators = GBOperator.LegalOperators(this, maxCat);
    }
    // Check if there are no conflicting stones.
    public bool CompatibleWith(GoBangBoard b2)
    {
        for (int y = 0; y < boardDim; ++y)
        {
            for (int x = 0; x < boardDim; ++x)
            {
                if (board[y, x] == 0 || b2.board[y, x] == 0 ||
                    board[y, x] == b2.board[y, x])
                {
                    continue;
                }

                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 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);
    }
    public int CompareTo(object o2)
    {
        GoBangBoard gb2 = (GoBangBoard)o2;

        for (int y = 0; y < boardDim; ++y)
        {
            for (int x = 0; x < boardDim; ++x)
            {
                if (board[y, x] < gb2.board[y, x])
                {
                    return(-1);
                }
                else if (board[y, x] > gb2.board[y, x])
                {
                    return(1);
                }
            }
        }

        return(0);
    }
 public GBSpaceState(GoBangBoard gb)
     : this(gb, null, null, 2)
 {
 }
 public GBThreatSearch(GoBangBoard gb, bool breadthFirst)
 {
     this.gb           = gb;
     this.breadthFirst = breadthFirst;
 }
    public static bool Present(GoBangBoard.StoneSet ss)
    {
        // The pattern is .????. with "." position fixed.
        if (ss.stones[0] != 0 || ss.stones[5] != 0)
            return (false);

        // Now the middle three (index 2, 3, 4, 5) have to be tested, there
        // should only be two white stones.
        int countWhite = 0;
        int countHole = 0;

        CountStones (ss, out countWhite, out countHole);
        if (countWhite != 2 || countHole != 4)
            return (false);

        // Now, the broken three condition: "s_4 neither minimum nor maximum
        // in { s_2, s_3, s_4, s_5 }", where s_4 = s_5 = ., s_2 = s_3 = o.
        //
        // This is a bit ambiguous, because it does not say anything else
        // about the other values s_2, s_3, s_5.

        // Case 1: .o..o.
        if (ss.stones[2] == 0 && ss.stones[3] == 0)
            return (true);

        // Case 2: .o.o..
        if (ss.stones[2] == 0 && ss.stones[4] == 0)
            return (true);

        // Case 3: ..o.o.
        if (ss.stones[1] == 0 && ss.stones[3] == 0)
            return (true);

        // Case 4: .oo... and ...oo.
        if ((ss.stones[1] == 1 && ss.stones[2] == 1) ||
            (ss.stones[3] == 1 && ss.stones[4] == 1))
            return (true);

        return (false);
    }
    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 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);
    }
Beispiel #19
0
    public void CombineCheck(ArrayList[,] affect)
    {
        int         patternG5 = 0;
        GoBangBoard board     = new GoBangBoard();

        ArrayList[] affecting = new ArrayList[5];

        foreach (GoBangBoard.StoneSet ss in board.G5)
        {
            for (int n = 0; n < ss.stones.Length; ++n)
            {
                affecting[n] = affect[ss.y + n * ss.ay, ss.x + n * ss.ax];
            }

            // Now in positions we have 'oneBits' number of positions.
            CheckC2(affecting);
#if false
            // Do a pre-filtering of sensible patterns in this G_5
            bool[] fivePatterns = new bool[1 << 5];

            for (int p = 0; p < fivePatterns.Length; ++p)
            {
                fivePatterns[p] = false;

                for (int n = 0; n < 5; ++n)
                {
                    ss.stones[n] = (p & (1 << n)) == 0 ? 0 : 1;

                    if (ss.stones[n] == 1 && affecting[n].Count == 0)
                    {
                        goto nextPattern;
                    }
                }

                /*
                 * Console.Write ("A pattern {0}: ", p);
                 * for (int n = 0 ; n < 5 ; ++n)
                 *      Console.Write ("{0}", ss.stones[n] == 1 ? "O" : ".");
                 * Console.WriteLine ();
                 */

                GBOperatorFive[] fiveP =
                    GBOperatorFive.GetOperatorsIfValid(ss);
                GBOperatorFour[] fourP =
                    GBOperatorFour.GetOperatorsIfValid(ss);

                /*
                 * Console.WriteLine ("  pattern {0}: {1} fives, {2} fours", p,
                 *      (fiveP == null) ? "-" : String.Format ("{0}", fiveP.Length),
                 *      (fourP == null) ? "-" : String.Format ("{0}", fourP.Length));
                 */

                // Only if we trigger a pattern, we mark it for later action.
                if ((fiveP != null && fiveP.Length > 0) ||
                    (fourP != null && fourP.Length > 0))
                {
                    fivePatterns[p] = true;
                    //Console.WriteLine ("  pattern {0} OK", p);
                }
nextPattern:
                ;
            }

            for (int p = 0; p < fivePatterns.Length; ++p)
            {
                if (fivePatterns[p] == false)
                {
                    continue;
                }

                // Now examine the pattern really.
                Console.WriteLine("pattern {0} success", p);
                Console.WriteLine("    a[0 to 4]: {0}, {1}, {2}, {3}, {4}",
                                  affecting[0].Count,
                                  affecting[1].Count,
                                  affecting[2].Count,
                                  affecting[3].Count,
                                  affecting[4].Count);

                patternG5 += 1;

                // TODO
                // Now we have an array of list of affecting moves plus a
                // pattern they may trigger.  We know that those moves in the
                // same array element are conflicting, so we do not need to
                // check those.  Also, we know the new operators are
                // independent of exchange of operators (commutative), so we
                // must only check each combination once, regardless of the
                // order.
                int oneBits = 0;
                for (int n = 0; n < 5; ++n)
                {
                    if ((p & (1 << n)) != 0)
                    {
                        oneBits += 1;
                    }
                }
                int[] positions = new int[oneBits];

                int oneBitPos = 0;
                for (int n = 0; n < 5; ++n)
                {
                    if ((p & (1 << n)) == 0)
                    {
                        continue;
                    }

                    positions[oneBitPos] = n;
                    oneBitPos           += 1;
                }
            }
#endif
        }
        //Console.WriteLine ("G5 patterns: {0}", patternG5);
    }
    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);
        }
    }
    public static GBOperatorThree2[] GetOperatorsIfValid(GoBangBoard.StoneSet ss)
    {
        if (Present (ss) == false)
            return (null);

        // Operator is valid, lets produce it (only one variant)
        GBOperatorThree2 three2 = new GBOperatorThree2 ();

        three2.fAdd = new int[3,3];
        for (int n = 2 ; n < (ss.stones.Length - 2) ; ++n) {
            if (ss.stones[n] != 0)
                continue;

            three2.fAdd[0,0] = ss.x + n*ss.ax;
            three2.fAdd[0,1] = ss.y + n*ss.ay;
            three2.fAdd[0,2] = 1;
        }

        // Two defending moves (s_2 and s_6, index 1 and 5)
        three2.fAdd[1,0] = ss.x + 1*ss.ax;
        three2.fAdd[1,1] = ss.y + 1*ss.ay;
        three2.fAdd[1,2] = -1;

        three2.fAdd[2,0] = ss.x + 5*ss.ax;
        three2.fAdd[2,1] = ss.y + 5*ss.ay;
        three2.fAdd[2,2] = -1;

        return (new GBOperatorThree2[] { three2 });
    }
    public static bool Present(GoBangBoard.StoneSet ss)
    {
        /*Console.Write ("three ({0},{1}): ", ss.x, ss.y);
        foreach (int a in ss.stones)
            Console.Write ("{0}", a == 1 ? "O" : (a == 0 ? "." : "X"));
        Console.WriteLine ();*/

        // The pattern is ..???.., with "." position fixed, so we can
        // early-check on this now.
        if (ss.stones[0] != 0 || ss.stones[1] != 0 ||
            ss.stones[5] != 0 || ss.stones[6] != 0)
            return (false);

        // Now the middle three (index 2, 3, 4) have to be tested
        int countWhite = 0;
        int countHole = 0;

        CountStones (ss, out countWhite, out countHole);
        if (countWhite != 2 || countHole != 5) {
            return (false);
        }

        return (true);
    }
    public static GBOperatorStraightFour[] GetOperatorsIfValid(GoBangBoard.StoneSet ss)
    {
        if (Present (ss) == false)
            return (null);

        // Operator is valid, lets produce it (only one variant)
        GBOperatorStraightFour sfour = new GBOperatorStraightFour ();

        sfour.fAdd = new int[1,3];
        for (int n = 1 ; n < (ss.stones.Length - 1) ; ++n) {
            if (ss.stones[n] != 0)
                continue;

            sfour.fAdd[0,0] = ss.x + n*ss.ax;
            sfour.fAdd[0,1] = ss.y + n*ss.ay;
            sfour.fAdd[0,2] = 1;
        }

        return (new GBOperatorStraightFour[] { sfour });
    }
    public static bool Present(GoBangBoard.StoneSet ss)
    {
        // The pattern is .????. with "." position fixed.
        if (ss.stones[0] != 0 || ss.stones[5] != 0)
            return (false);

        // Now the middle three (index 2, 3, 4) have to be tested
        int countWhite = 0;
        int countHole = 0;

        CountStones (ss, out countWhite, out countHole);
        if (countWhite != 3 || countHole != 3)
            return (false);

        return (true);
    }
    public static bool Present(GoBangBoard.StoneSet ss)
    {
        int countWhite = 0;
        int countHole = 0;

        CountStones (ss, out countWhite, out countHole);
        if (countWhite != 3 || countHole != 2)
            return (false);

        return (true);
    }
    public static GBOperatorFour[] GetOperatorsIfValid(GoBangBoard.StoneSet ss)
    {
        if (Present (ss) == false)
            return (null);

        // Operator is valid, lets apply two variations
        GBOperatorFour four1 = new GBOperatorFour ();
        GBOperatorFour four2 = new GBOperatorFour ();

        four1.fAdd = new int[2,3];
        four2.fAdd = new int[2,3];
        int hole = 0;
        for (int n = 0 ; n < ss.stones.Length ; ++n) {
            if (ss.stones[n] != 0)
                continue;

            four2.fAdd[hole,0] = four1.fAdd[hole,0] = ss.x + n*ss.ax;
            four2.fAdd[hole,1] = four1.fAdd[hole,1] = ss.y + n*ss.ay;

            if (hole == 0) {
                four1.fAdd[0,2] = 1;
                four2.fAdd[0,2] = -1;

                hole += 1;
            } else {
                four1.fAdd[1,2] = -1;
                four2.fAdd[1,2] = 1;
            }
        }

        return (new GBOperatorFour[] { four1, four2 });
    }
    public static GBOperatorFive[] GetOperatorsIfValid(GoBangBoard.StoneSet ss)
    {
        if (Present (ss) == false)
            return (null);

        // Operator is valid, lets apply it.
        GBOperatorFive five = new GBOperatorFive ();

        five.fAdd = new int[1,3];
        for (int n = 0 ; n < ss.stones.Length ; ++n) {
            if (ss.stones[n] != 0)
                continue;

            five.fAdd[0,0] = ss.x + n*ss.ax;
            five.fAdd[0,1] = ss.y + n*ss.ay;
            five.fAdd[0,2] = 1;
        }

        return (new GBOperatorFive[] { five });
    }
 public GBSpaceState(GoBangBoard gb, GBOperator lastOperator,
                     GBSpaceState lastState, int maxCat)
     : this(gb, lastOperator, lastState, null)
 {
     this.legalOperators = GBOperator.LegalOperators(this, maxCat);
 }
    public static GBOperatorThree3[] GetOperatorsIfValid(GoBangBoard.StoneSet ss)
    {
        if (Present (ss) == false)
            return (null);

        // Case A: ..oo..
        if (ss.stones[1] == 0 && ss.stones[4] == 0) {
            // Operator is valid, lets produce it (two variants)
            GBOperatorThree3 three31 = new GBOperatorThree3 ();
            GBOperatorThree3 three32 = new GBOperatorThree3 ();

            three31.fAdd = new int[4,3];
            three32.fAdd = new int[4,3];

            int hole = 0;
            for (int n = 1 ; n < (ss.stones.Length-1) ; ++n) {
                if (ss.stones[n] != 0)
                    continue;

                three31.fAdd[hole,0] = three32.fAdd[hole,0] = ss.x + n*ss.ax;
                three31.fAdd[hole,1] = three32.fAdd[hole,1] = ss.y + n*ss.ay;

                if (hole == 0) {
                    three31.fAdd[0,2] = 1;
                    three32.fAdd[0,2] = -1;

                    hole += 1;
                } else {
                    three31.fAdd[1,2] = -1;
                    three32.fAdd[1,2] = 1;
                }
            }

            // Two defending moves (s_1 and s_6, index 0 and 5)
            three31.fAdd[2,0] = three32.fAdd[2,0] = ss.x;
            three31.fAdd[2,1] = three32.fAdd[2,1] = ss.y;
            three31.fAdd[2,2] = three32.fAdd[2,2] = -1;

            three31.fAdd[3,0] = three32.fAdd[3,0] = ss.x + 5*ss.ax;
            three31.fAdd[3,1] = three32.fAdd[3,1] = ss.y + 5*ss.ay;
            three31.fAdd[3,2] = three32.fAdd[3,2] = -1;

            return (new GBOperatorThree3[] { three31, three32 });
        } else if ((ss.stones[2] == 0 && ss.stones[4] == 0) ||
            (ss.stones[1] == 0 && ss.stones[3] == 0))
        {
            // Case B & C: .o.o.. and ..o.o.
            // TODO
            GBOperatorThree3 three3 = new GBOperatorThree3 ();

            three3.fAdd = new int[4,3];

            int stoneCount = 0;
            int pCell = 0;
            for (int n = 1 ; n < (ss.stones.Length-1) ; ++n) {
                if (ss.stones[n] != 0) {
                    stoneCount += 1;

                    continue;
                }

                three3.fAdd[pCell,0] = ss.x + n*ss.ax;
                three3.fAdd[pCell,1] = ss.y + n*ss.ay;

                if (stoneCount == 1) {
                    three3.fAdd[pCell,2] = 1;
                } else
                    three3.fAdd[pCell,2] = -1;

                pCell += 1;
            }

            // Two defending moves (s_1 and s_6, index 0 and 5)
            three3.fAdd[2,0] = ss.x;
            three3.fAdd[2,1] = ss.y;
            three3.fAdd[2,2] = -1;

            three3.fAdd[3,0] = ss.x + 5*ss.ax;
            three3.fAdd[3,1] = ss.y + 5*ss.ay;
            three3.fAdd[3,2] = -1;

            return (new GBOperatorThree3[] { three3, });
        } else if (ss.stones[3] == 0 && ss.stones[4] == 0) {
            // Case D: .oo...
            GBOperatorThree3 three3 = new GBOperatorThree3 ();

            three3.fAdd = new int[4,3];

            three3.fAdd[0,0] = ss.x + 3*ss.ax;
            three3.fAdd[0,1] = ss.y + 3*ss.ay;
            three3.fAdd[0,2] = 1;

            three3.fAdd[1,0] = ss.x + 4*ss.ax;
            three3.fAdd[1,1] = ss.y + 4*ss.ay;
            three3.fAdd[1,2] = -1;

            // Two defending moves (s_1 and s_6, index 0 and 5)
            three3.fAdd[2,0] = ss.x;
            three3.fAdd[2,1] = ss.y;
            three3.fAdd[2,2] = -1;

            three3.fAdd[3,0] = ss.x + 5*ss.ax;
            three3.fAdd[3,1] = ss.y + 5*ss.ay;
            three3.fAdd[3,2] = -1;

            return (new GBOperatorThree3[] { three3, });
        } else if (ss.stones[1] == 0 && ss.stones[2] == 0) {
            // Case E: ...oo.
            GBOperatorThree3 three3 = new GBOperatorThree3 ();

            three3.fAdd = new int[4,3];

            three3.fAdd[0,0] = ss.x + 2*ss.ax;
            three3.fAdd[0,1] = ss.y + 2*ss.ay;
            three3.fAdd[0,2] = 1;

            three3.fAdd[1,0] = ss.x + 1*ss.ax;
            three3.fAdd[1,1] = ss.y + 1*ss.ay;
            three3.fAdd[1,2] = -1;

            // Two defending moves (s_1 and s_6, index 0 and 5)
            three3.fAdd[2,0] = ss.x;
            three3.fAdd[2,1] = ss.y;
            three3.fAdd[2,2] = -1;

            three3.fAdd[3,0] = ss.x + 5*ss.ax;
            three3.fAdd[3,1] = ss.y + 5*ss.ay;
            three3.fAdd[3,2] = -1;

            return (new GBOperatorThree3[] { three3, });
        } else {
            throw (new ApplicationException ("BUG in Three3, uncatched case"));
        }
    }
    public static GBOperatorBrokenThree[] GetOperatorsIfValid(GoBangBoard.StoneSet ss)
    {
        if (Present (ss) == false)
            return (null);

        // Case 1
        if (ss.stones[2] == 0 && ss.stones[3] == 0) {
            // Operator is valid, lets produce it (two variants)
            GBOperatorBrokenThree bthree1 = new GBOperatorBrokenThree ();
            GBOperatorBrokenThree bthree2 = new GBOperatorBrokenThree ();

            bthree1.fAdd = new int[4,3];
            bthree2.fAdd = new int[4,3];

            int hole = 0;
            for (int n = 1 ; n < (ss.stones.Length-1) ; ++n) {
                if (ss.stones[n] != 0)
                    continue;

                bthree1.fAdd[hole,0] = bthree2.fAdd[hole,0] = ss.x + n*ss.ax;
                bthree1.fAdd[hole,1] = bthree2.fAdd[hole,1] = ss.y + n*ss.ay;

                if (hole == 0) {
                    bthree1.fAdd[0,2] = 1;
                    bthree2.fAdd[0,2] = -1;

                    hole += 1;
                } else {
                    bthree1.fAdd[1,2] = -1;
                    bthree2.fAdd[1,2] = 1;
                }
            }

            // Two defending moves (s_1 and s_6, index 0 and 5)
            bthree1.fAdd[2,0] = bthree2.fAdd[2,0] = ss.x;
            bthree1.fAdd[2,1] = bthree2.fAdd[2,1] = ss.y;
            bthree1.fAdd[2,2] = bthree2.fAdd[2,2] = -1;

            bthree1.fAdd[3,0] = bthree2.fAdd[3,0] = ss.x + 5*ss.ax;
            bthree1.fAdd[3,1] = bthree2.fAdd[3,1] = ss.y + 5*ss.ay;
            bthree1.fAdd[3,2] = bthree2.fAdd[3,2] = -1;

            return (new GBOperatorBrokenThree[] { bthree1, bthree2 });
        }

        // Case 4:
        // .oo...
        if (ss.stones[1] == 1 && ss.stones[2] == 1) {
            GBOperatorBrokenThree bthree = new GBOperatorBrokenThree ();

            bthree.fAdd = new int[4,3];

            bthree.fAdd[0,0] = ss.x + 0*ss.ax;
            bthree.fAdd[0,1] = ss.y + 0*ss.ay;
            bthree.fAdd[0,2] = -1;

            bthree.fAdd[1,0] = ss.x + 5*ss.ax;
            bthree.fAdd[1,1] = ss.y + 5*ss.ay;
            bthree.fAdd[1,2] = -1;

            bthree.fAdd[2,0] = ss.x + 3*ss.ax;
            bthree.fAdd[2,1] = ss.y + 3*ss.ay;
            bthree.fAdd[2,2] = -1;

            bthree.fAdd[3,0] = ss.x + 4*ss.ax;
            bthree.fAdd[3,1] = ss.y + 4*ss.ay;
            bthree.fAdd[3,2] = 1;

            return (new GBOperatorBrokenThree[] { bthree });
        } else if (ss.stones[3] == 1 && ss.stones[4] == 1) {
            // and ...oo.
            GBOperatorBrokenThree bthree = new GBOperatorBrokenThree ();

            bthree.fAdd = new int[4,3];

            bthree.fAdd[0,0] = ss.x + 0*ss.ax;
            bthree.fAdd[0,1] = ss.y + 0*ss.ay;
            bthree.fAdd[0,2] = -1;

            bthree.fAdd[1,0] = ss.x + 5*ss.ax;
            bthree.fAdd[1,1] = ss.y + 5*ss.ay;
            bthree.fAdd[1,2] = -1;

            bthree.fAdd[2,0] = ss.x + 2*ss.ax;
            bthree.fAdd[2,1] = ss.y + 2*ss.ay;
            bthree.fAdd[2,2] = -1;

            bthree.fAdd[3,0] = ss.x + 1*ss.ax;
            bthree.fAdd[3,1] = ss.y + 1*ss.ay;
            bthree.fAdd[3,2] = 1;

            return (new GBOperatorBrokenThree[] { bthree });
        }

        // Case 2 and 3
        //
        // Only one variation exist:
        // .o.o.. or ..o.o.
        // xoxoox    xooxox
        if ((ss.stones[1] == 1 && ss.stones[3] == 1) ||
            (ss.stones[2] == 1 && ss.stones[4] == 1))
        {
            GBOperatorBrokenThree bthree = new GBOperatorBrokenThree ();
            bthree.fAdd = new int[4,3];

            int posCount = 0;
            int pFillC = 0;
            for (int n = 1 ; n < (ss.stones.Length-1) ; ++n) {
                if (ss.stones[n] == 1) {
                    posCount += 1;

                    continue;
                }

                // Free stone
                // Case A: middle, fill with oponent
                if (posCount == 1) {
                    bthree.fAdd[pFillC,0] = ss.x + n*ss.ax;
                    bthree.fAdd[pFillC,1] = ss.y + n*ss.ay;
                    bthree.fAdd[pFillC,2] = -1;

                    pFillC += 1;
                } else {
                    // Case B: not the middle, fill with our own
                    bthree.fAdd[pFillC,0] = ss.x + n*ss.ax;
                    bthree.fAdd[pFillC,1] = ss.y + n*ss.ay;
                    bthree.fAdd[pFillC,2] = 1;

                    pFillC += 1;
                }
            }

            // The two corner stones: x....x
            bthree.fAdd[2,0] = ss.x;
            bthree.fAdd[2,1] = ss.y;
            bthree.fAdd[2,2] = -1;

            bthree.fAdd[3,0] = ss.x + 5*ss.ax;
            bthree.fAdd[3,1] = ss.y + 5*ss.ay;
            bthree.fAdd[3,2] = -1;

            return (new GBOperatorBrokenThree[] { bthree, });
        }

        throw (new ApplicationException ("Broken three: impossible case"));
    }
    public static bool Present(GoBangBoard.StoneSet ss)
    {
        // The pattern is .????. with "." position fixed.
        if (ss.stones[0] != 0 || ss.stones[5] != 0)
            return (false);

        // Now the middle three (index 2, 3, 4, 5) have to be tested, there
        // should only be two white stones.
        int countWhite = 0;
        int countHole = 0;

        CountStones (ss, out countWhite, out countHole);
        if (countWhite != 2 || countHole != 4)
            return (false);

        // The "three with 3 reply moves" condition: "s_2 either minimum or
        // maximum in { s_2, s_3, s_4, s_5 }".
        //
        // This is a bit ambiguous, because it does not say anything else
        // about the other values s_3, s_4, s_5.
        //
        // This most likely means that there should not be two empty stones in
        // the middle: .o..o., but it must be either .oo..., ...oo., .o.o..,
        // ..o.o. or ..oo..
        if (ss.stones[2] == 0 && ss.stones[3] == 0)
            return (false);

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

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

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

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

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

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

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

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

        return (null);
    }
    public object Clone()
    {
        GoBangBoard gbNew = new GoBangBoard((int[, ])board.Clone());

        return(gbNew);
    }
 /** 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 GCl(GoBangBoard gb, int gSize)
 {
     this.gb = gb;
     this.gSize = gSize;
 }
 public GBThreatSearch(GoBangBoard gb, bool breadthFirst)
 {
     this.gb = gb;
     this.breadthFirst = breadthFirst;
 }
 /** 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 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);
        }
    }
    /** 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 GCl(GoBangBoard gb, int gSize)
 {
     this.gb    = gb;
     this.gSize = gSize;
 }
    public void CombineCheck(ArrayList[,] affect)
    {
        int patternG5 = 0;
        GoBangBoard board = new GoBangBoard ();

        ArrayList[] affecting = new ArrayList[5];

        foreach (GoBangBoard.StoneSet ss in board.G5) {
            for (int n = 0 ; n < ss.stones.Length ; ++n)
                affecting[n] = affect[ss.y + n*ss.ay, ss.x + n*ss.ax];

            // Now in positions we have 'oneBits' number of positions.
            CheckC2 (affecting);
        #if false
            // Do a pre-filtering of sensible patterns in this G_5
            bool[] fivePatterns = new bool[1 << 5];

            for (int p = 0 ; p < fivePatterns.Length ; ++p) {
                fivePatterns[p] = false;

                for (int n = 0 ; n < 5 ; ++n) {
                    ss.stones[n] = (p & (1 << n)) == 0 ? 0 : 1;

                    if (ss.stones[n] == 1 && affecting[n].Count == 0)
                        goto nextPattern;
                }

                /*
                Console.Write ("A pattern {0}: ", p);
                for (int n = 0 ; n < 5 ; ++n)
                    Console.Write ("{0}", ss.stones[n] == 1 ? "O" : ".");
                Console.WriteLine ();
                */

                GBOperatorFive[] fiveP =
                    GBOperatorFive.GetOperatorsIfValid (ss);
                GBOperatorFour[] fourP =
                    GBOperatorFour.GetOperatorsIfValid (ss);
                /*
                Console.WriteLine ("  pattern {0}: {1} fives, {2} fours", p,
                    (fiveP == null) ? "-" : String.Format ("{0}", fiveP.Length),
                    (fourP == null) ? "-" : String.Format ("{0}", fourP.Length));
                    */

                // Only if we trigger a pattern, we mark it for later action.
                if ((fiveP != null && fiveP.Length > 0) ||
                    (fourP != null && fourP.Length > 0))
                {
                    fivePatterns[p] = true;
                    //Console.WriteLine ("  pattern {0} OK", p);
                }
        nextPattern:
                ;
            }

            for (int p = 0 ; p < fivePatterns.Length ; ++p) {
                if (fivePatterns[p] == false)
                    continue;

                // Now examine the pattern really.
                Console.WriteLine ("pattern {0} success", p);
                Console.WriteLine ("    a[0 to 4]: {0}, {1}, {2}, {3}, {4}",
                    affecting[0].Count,
                    affecting[1].Count,
                    affecting[2].Count,
                    affecting[3].Count,
                    affecting[4].Count);

                patternG5 += 1;

                // TODO
                // Now we have an array of list of affecting moves plus a
                // pattern they may trigger.  We know that those moves in the
                // same array element are conflicting, so we do not need to
                // check those.  Also, we know the new operators are
                // independent of exchange of operators (commutative), so we
                // must only check each combination once, regardless of the
                // order.
                int oneBits = 0;
                for (int n = 0 ; n < 5 ; ++n) {
                    if ((p & (1 << n)) != 0)
                        oneBits += 1;
                }
                int[] positions = new int[oneBits];

                int oneBitPos = 0;
                for (int n = 0 ; n < 5 ; ++n) {
                    if ((p & (1 << n)) == 0)
                        continue;

                    positions[oneBitPos] = n;
                    oneBitPos += 1;
                }
            }
        #endif
        }
        //Console.WriteLine ("G5 patterns: {0}", patternG5);
    }
    // Check if there are no conflicting stones.
    public bool CompatibleWith(GoBangBoard b2)
    {
        for (int y = 0 ; y < boardDim ; ++y) {
            for (int x = 0 ; x < boardDim ; ++x) {
                if (board[y,x] == 0 || b2.board[y,x] == 0 ||
                    board[y,x] == b2.board[y,x])
                    continue;

                return (false);
            }
        }

        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);
        }
        */
    }
    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 GBSpaceState(GoBangBoard gb)
     : this(gb, null, null, 2)
 {
 }
    public object Clone()
    {
        GoBangBoard gbNew = new GoBangBoard ((int[,]) board.Clone ());

        return (gbNew);
    }
 public void AddExtraStones(GoBangBoard b2)
 {
     for (int y = 0 ; y < boardDim ; ++y) {
         for (int x = 0 ; x < boardDim ; ++x) {
             if (board[y,x] == 0 && b2.board[y,x] != 0)
                 board[y,x] = b2.board[y,x];
         }
     }
 }
 public GBSpaceState(GoBangBoard gb, int maxCat)
     : this(gb, null, null, maxCat)
 {
 }
    public GBSpaceState(GoBangBoard gb, GBOperator lastOperator,
		GBSpaceState lastState, int maxCat)
        : this(gb, lastOperator, lastState, null)
    {
        this.legalOperators = GBOperator.LegalOperators (this, maxCat);
    }
 public GBSpaceState(GoBangBoard gb, int maxCat)
     : this(gb, null, null, maxCat)
 {
 }
 public GClEnumerator(GoBangBoard gb, int gSize)
 {
     this.gb = gb;
     this.gSize = gSize;
 }
 public GClEnumerator(GoBangBoard gb, int gSize)
 {
     this.gb    = gb;
     this.gSize = gSize;
 }
    public GBSpaceState(GoBangBoard gb, GBOperator lastOperator,
		GBSpaceState lastState, GBOperator[] legalOperators)
    {
        this.gb = gb;
        this.lastOperator = lastOperator;
        this.lastState = lastState;

        this.legalOperators = legalOperators;
    }