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

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

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

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

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

        newState.UpdateIsGoal(gmod);

        return(newState);
    }
        public DBNode(NodeType type, IDBSpaceState state, int level)
        {
            // FIXME
            this.DebugNN     = DebugNodeNumber;
            DebugNodeNumber += 1;

            this.type = type;
            if (type == NodeType.Dependency)
            {
                combinedChildren = new ArrayList();
            }

            this.state  = state;
            this.isGoal = state.IsGoal;
            this.level  = level;
        }
        // Returns new child
        private DBNode LinkNewChildToGraph(DBNode node, IDBOperator op)
        {
            IDBSpaceState newState = op.Apply(module, node.State);

            newState.UpdateIsGoal(module);

            DBNode newNode = new DBNode(DBNode.NodeType.Dependency,
                                        newState, level);

            if (newNode.IsGoal)
            {
                goalCount += 1;
            }

            TreeSizeIncreased = true;
            nodeCount        += 1;

            // Graph bookkeeping
            node.Children.Add(newNode);

            module.RegisterNewNode(newNode, root);

            return(newNode);
        }
        // partnerPath: path up to and excluding partner
        // nodePath: path up to and excluding node
        private void FindAllCombinationNodes(DBNode partner, Stack partnerPath,
                                             DBNode node, Stack nodePath)
        {
            //Console.WriteLine ("FindAllCombinationNodes");
            if (node == null || node == partner)
            {
                return;
            }

            if (node.IsGoal)
            {
                return;
            }

            if (node.Type == DBNode.NodeType.Root ||
                module.NotInConflict(partner, node))
            {
                // HaveCommonChild checks if we already combined in the
                // reverse direction, but we also need to check whether their
                // path's are independant.  That is, whether tracing node's
                // path back to root we will cross over partner.  Also whether
                // tracing partner's path back will lead us over node.
                if (node.Type == DBNode.NodeType.Dependency &&
                    HaveCommonCombinedChild(partner, node) == false)
                {
                    IDBSpaceState combination =
                        module.CombineIfResultIsNewOperators(partner, partnerPath,
                                                             node, nodePath);

                    if (combination != null)
                    {
                        DBNode combNode = new DBNode(DBNode.NodeType.Combination,
                                                     combination, level);
                        combNode.IsGoal = module.IsGoal(combination);
                        if (combNode.IsGoal)
                        {
                            goalCount += 1;
                        }

                        AddCombinationNode(partner, combNode);

                        // Also make the new node a child ("combined child") of
                        // node.  We do not add it it to the Children array
                        // for two reasons:
                        //   1. We want a strict tree structure in memory, no
                        //      DAG
                        //   2. The only point where this DAG-like
                        //      relationship is used is in this method, when
                        //      its checked we did not already combine them.
                        node.CombinedChildren.Add(combNode);

                        // Notify the implementation of a new node.
                        module.RegisterNewNode(combNode, root);

                        if (combNode.IsGoal)
                        {
                            if (module.OneGoalStopsSearch ||
                                (module.GoalCountThresh != 0 &&
                                 goalCount >= module.GoalCountThresh))
                            {
                                throw (new GoalCountExceededException());
                            }
                        }
                    }
                }

                nodePath.Push(node);
                foreach (DBNode child in node.Children)
                {
                    FindAllCombinationNodes(partner, partnerPath, child, nodePath);
                }

                nodePath.Pop();
            }
        }
        public void Search(IDBSpaceState rootState)
        {
            rootState.UpdateIsGoal(module);
            root = new DBNode(DBNode.NodeType.Root, rootState, 0);
            if (root.IsGoal)
            {
                goalCount += 1;
            }

            module.RegisterNewNode(root, root);

            level = 1;

            TreeSizeIncreased = true;
            try {
                while (TreeSizeIncreased)
                {
                    //Console.WriteLine ("level {0}", level);

                    TreeSizeIncreased = false;

                    if (breadthFirst)
                    {
                        dependencyNodeQueue.Clear();
                    }
                    AddDependencyStage(root);

                    if (breadthFirst)
                    {
                        SolveDependencyStage();
                    }
                    //DumpTree (root);

                    /*Console.WriteLine ("level {0} dependency finished: {1} nodes, {2} goals",
                     *      level, nodeCount, goalCount);*/

                    if (TreeSizeIncreased == false)
                    {
                        break;
                    }
                    if (module.GoalCountThresh != 0 && goalCount >= module.GoalCountThresh)
                    {
                        break;
                    }

                    /*
                     * Console.WriteLine ("COMB TEST");
                     * module.CombinationStage (level, root, this);
                     *
                     * if (module.GoalCountThresh != 0 && goalCount >= module.GoalCountThresh)
                     *      break;
                     */

                    AddCombinationStage(root, new Stack());

                    /*Console.WriteLine ("level {0} combination finished: {1} nodes, {2} goals",
                     *      level, nodeCount, goalCount);*/
                    if (TreeSizeIncreased == false)
                    {
                        break;
                    }
                    if (module.GoalCountThresh != 0 && goalCount >= module.GoalCountThresh)
                    {
                        break;
                    }
                    //DumpTree (root);

                    level += 1;
                }
            } catch (GoalCountExceededException) {
                /*
                 * Console.WriteLine ("GOAL count limit of {0} exceeded.",
                 *      module.GoalCountThresh);*/
            }
        }
    /** Check the goal conditions on page 135.
     *
     * @param state The space state to be checked for goal'iness.
     *
     * @returns True in case it is a goal state, false otherwise.
     */
    public bool IsGoal(IDBSpaceState stateDB)
    {
        GBSpaceState state = (GBSpaceState)stateDB;

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

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

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

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

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

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

        return (newState);
    }