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

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

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

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

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

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

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

        GBSpaceState gComb = (GBSpaceState)node1.State;

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

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

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

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

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

        gComb.UpdateIsGoal(this);

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

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

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

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

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

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

        GBSearchModule gbSearch = new GBSearchModule (GoBangBoard.boardDim);

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

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

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

            return (depth);
        }

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

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

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

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

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

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

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

        GBSearchModule gbSearch = new GBSearchModule(GoBangBoard.boardDim);

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

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

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

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

            return(depth);
        }

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

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

        return(DefenseRefutes(seq.next, curBoard, depth + 1));
    }
Beispiel #4
0
    private void CheckC2(ArrayList[] affecting)
    {
        int[] n = new int[2];

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

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

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

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

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

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

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

                        // We have found a new combination

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

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

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

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

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

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

                        count[countN] = 0;
                    }
                } while (countN < count.Length);
            }
        }
    }