Javascript Recursion Unexpectedly Breaking- warning long code

I am having a problem with the following code. It seems to be breaking during the recursion in the maze function within NewMaze. I ran it through firebug and I get the following error:

parent is undefined: line 97 newmaze_generator.js
    [Break On This Error] self.visited = function() {return parent.visited();}; 

And I have no idea why it would be undefined, if that is even the real problem.

I know this is asking alot, so please don't waste your time unless you really feel like helping out a novice javascript programmer. To make things easier, I have the whole code on my website so you can actually run the program, which will do nothing at the moment.

http://level2games.com/games/dungeons/dungeons.html

On a side note this is only my second foray into javascript programming so I could have made a dumb mistake easily.

My MazeGenerator

function NewMaze(_grid) {
    var self = this;
    var stack = new Array();
    var grid = _grid;

    /**
     * A recursive depth-first-search style maze generator
     */
    self.maze = function() {
        var randx = Math.floor(Math.random()*(grid.getWidth()));
        var randy = Math.floor(Math.random()*(grid.getHeight()));
        var startOfMaze = new MyCell(grid.getCell(randx, randy));
        stack.push(startOfMaze);
        mazeHelper(startOfMaze);
    };

    /**
     * Internal recursive method used by maze.
     * A depth-first-search style algorithm.
     * @param mycell current cell
     * @returns {Boolean} true once finished
     */
    function mazeHelper(myCell) {
        var neighbors = myCell.getOpenNeighbors();
        myCell.markVisited();
        if (neighbors.length > 0) {
            var next = Math.floor(Math.random()*(neighbors.length));
            var nextCell = neighbors[next];
            stack.push(nextCell);
            removeWall(myCell, nextCell);
//          alert(myCell.getParent().getArrayLocation().getX()+" , "+myCell.getParent().getArrayLocation().getY());
            mazeHelper(nextCell);
        } else {
            alert("else");
            if (stack.length == 0) return true;
            else mazeHelper(stack.pop());
        };
    };

    /**
     * Removes the wall between the given cells
     * @param fromCell
     * @param toCell
     */
    function removeWall(fromCell, toCell) {
        var xDif = toCell.getParent().getArrayLocation().getX() - fromCell.getParent().getArrayLocation().getX();
        var yDif = toCell.getParent().getArrayLocation().getY() - fromCell.getParent().getArrayLocation().getY();

        if (xDif == -1) toCell.seWall = false;  // if moving up x axis
        if (xDif == 1) fromCell.seWall = false; // if moving down x axis
        if (yDif == -1) toCell.swWall = false;  // if moving up y axis
        if (yDif == 1) fromCell.swWall = false; // if moving down y axis

    };

    /**
     * @returns an instance of 'this' named self.
     */
    self.getSelf = function() {return self;};

    /**
     * Inner class that is an extension of Cell class
     * @param cell the parent cell
     * @returns {MyCell} instance of 'this' child
     */
    function MyCell(cell) {
        var self = this;
        var parent = cell;

        /**
         * Marks the referenced cell as visited using the super class's visited method.
         */
        self.markVisited = function() {parent.markVisited();};

        self.visited = function() {return parent.visited();};

        /**
         * Creates and returns an array of neighbors that have not been visited.
         * @returns {<MyCell>Array} an array of adjacent cells that have not been visited. 
         */
        self.getOpenNeighbors = function() {
            var neighbors = new Array();

            var tc0, tc1, tc3, tc4;
            var parentX = parent.getArrayLocation().getX();
            var parentY = parent.getArrayLocation().getY();

            tc0 = new MyCell(grid.getCell(parentX-1, parentY));
            tc1 = new MyCell(grid.getCell(parentX+1, parentY));
            tc2 = new MyCell(grid.getCell(parentX, parentY-1));
            tc3 = new MyCell(grid.getCell(parentX, parentY+1));

            if (tc0 != undefined && !tc0.visited()) neighbors.push(tc0);
            if (tc1 != undefined && !tc1.visited()) neighbors.push(tc1);
            if (tc2 != undefined && !tc2.visited()) neighbors.push(tc2);
            if (tc3 != undefined && !tc3.visited()) neighbors.push(tc3);

            return neighbors;
        };

        /**
         * @returns an instance of 'this' named self.
         */
        self.getSelf = function() {return self;};

        /**
         * @returns an instance of the 'super' class named parent.
         */
        self.getParent = function() {return parent;};
    };
};

My Maze Class

function NewGrid(context, gwidth, gheight) {
    var self = this;    // stores and instance of 'this'
    var ctx = context;  // local pointer to the context;
    var width = 12;     // 12 is the default number of cells wide
    var height = 12;    // 12 is the default number of cells high

    // Public Variables - needed by NewCell class, umm... please don't overwrite
    NewGrid.FLOOR_COLOR = "#CABCAF"; // default base color - brown
    NewGrid.WALL_COLOR = "#333333"; // default wall color - dark grey
    NewGrid.CELL_WIDTH = FINAL_CELL_WIDTH;      // 200px is the default width
    NewGrid.CELL_HEIGHT = FINAL_CELL_HEIGHT;    // 100px is the default height

    var cells = new Array();    // 2d array of cells, fully instantiated in constructHelper
    contructHelper(gwidth, gheight);

    /**
     * Internal helper method (pseudo-constructor)
     */
    function contructHelper(gwidth, gheight) {
        if (gwidth != undefined && gheight != undefined) {
            width = gwidth;
            height = gheight;
        };

        // Constructs the 2d array of cells i.e. cells[y] = x[]
        cells = new Array(height);
        for (var i = 0; i < cells.length; i++) {
            cells[i] = new Array(height);
        };

        gridBuilder();
    };

    function gridBuilder() {
        var offSet = width * NewGrid.CELL_WIDTH / 2;
        for (var i = 0; i < cells.length; i++) {
            for (var j = cells[i].length - 1; j >= 0; j--) {
                var x = (j * NewGrid.CELL_WIDTH / 2) - (i * NewGrid.CELL_WIDTH / 2) + offSet;
                var y = (i * NewGrid.CELL_HEIGHT / 2) + (j * NewGrid.CELL_HEIGHT / 2);
                cells[i][j] = new NewCell(ctx, new Point(x,y), new Point(i,j));
            };
        };
    };

    /**
     * @returns the width of the grid
     */
    self.getWidth = function() {return width;};

    /**
     * @returns the height of the grid
     */
    self.getHeight = function() {return height;};

    /**
     * draws the grid to the screen
     * @param useViewport true if drawing only a portion
     */
    self.drawMe = function(useViewport) {
        if (!useViewport) {
            // draws the full grid to the canvas
            for (var i = 0; i < cells.length; i++) {
                for (var j = cells[i].length - 1; j >= 0; j--) {
                    cells[i][j].drawMe();
                };
            };
            for (var i = 0; i < cells.length; i++) {
                for (var j = cells[i].length - 1; j >= 0; j--) {
                    cells[i][j].drawWalls();
                };
            };
        } else {
            // draws only the viewable portion of the grid to the canvas.
            // not implemented yet
        }
    };

    self.getCell = function(x,y) {
        if (x >= 0 && x < width) {
            if (y >= 0 && y < height) {
                return cells[x][y].getSelf();
            };
        };
        return undefined;
    };

    /**
     * @returns an instance of 'this' named self.
     */
    self.getSelf = function() {return self;};

    /**
     * Internal class for the viewable portion of the grid
     * @param vpwidth (optional) number of cells to be displayed horizontally
     * @param vpheight (optional) number of cells to be displayed vertically
     */
    function ViewPort(vpwidth, vpheight) {
        var self = this;
        var width = 4;  // 4 is the default number of cells wide
        var height = 3; // 3 is the default number of cells high
        constructHelper(vpwidth, vpheight);

        /**
         * Internal helper method (pseudo-constructor)
         */
        function contructHelper(vpwidth, vpheight) {
            if (vpwidth != undefined && vpheight != undefined) {
                width = vpwidth;
                height = vpheight;
            }; 
        };

        /**
         * @returns the width of the view port
         */
        self.getWidth = function() {return width;};
        /**
         * @returns the height of the view port
         */
        self.getHeight = function() {return height;};

        /**
         * @returns an instance of 'this' named self.
         */
        self.getSelf = function() {return self;};
    };
};

My Cell Class

/**
 * @param context the context on which to be drawn.
 * @param ccorners an array of points representing the 4 corners
 * @param cwidth (optional) the number of pixels wide the cell is.
 * @param cheight (optional) the number of pixels high the cell is.
 */
function NewCell(context, origin, arrLoc) {
    var self = this;            // stores and instance of 'this'
    var ctx = context;          // local pointer to the context;
    var corners = new Array();  // an array of points representing the corners in n-e-s-w order
    var arrayLoc = arrLoc;  // position in the cells array
    var wallHeight = 5; // pixels used to draw the wall

    self.swWall = true; //determines is a wall is present to the south-west
    self.seWall = true; //determines is a wall is present to the south-east

    var visited = false;    // used by maze generator

    contructHelper(origin);

    /**
     * Internal helper method (pseudo-constructor)
     * @param origin is a Point representing the x,y value of the top corner
     */
    function contructHelper(origin) {
        corners[0] = new Point(origin.getX(), origin.getY());                                                       // 0,0
        corners[1] = new Point(origin.getX() + NewGrid.CELL_WIDTH / 2, origin.getY() + NewGrid.CELL_HEIGHT / 2);    // 0+100,0+50
        corners[2] = new Point(origin.getX(), origin.getY() + NewGrid.CELL_HEIGHT);                                 // 0, 0+100
        corners[3] = new Point(origin.getX() - NewGrid.CELL_WIDTH / 2, origin.getY() + NewGrid.CELL_HEIGHT / 2);    // 0-100, 0+50
    };

    /**
     * @returns the x,y value of the top corner as a Point
     */
    self.getOrigin = function() {return corners[0];};

    self.getArrayLocation = function() {return arrayLoc;};

    /**
     * @returns the x,y value of the center of the cell as a Point
     */
    self.getCenter = function() {return new Point(corners[0].getX(), corners[0].getY() + NewGrid.CELL_HEIGHT / 2);};

    /**
     * draws the cell to the canvas using an array of points
     * to from the corners and using (for temporary purposes)
     * a floor color and wall color and wall height.
     */
    self.drawMe = function() {
        ctx.lineWidth = wallHeight;
        ctx.fillStyle = NewGrid.FLOOR_COLOR;
        ctx.beginPath();
        ctx.moveTo(corners[0].getX(),corners[0].getY());
        ctx.lineTo(corners[1].getX(),corners[1].getY());
        ctx.lineTo(corners[2].getX(),corners[2].getY());
        ctx.lineTo(corners[3].getX(),corners[3].getY());
        ctx.closePath();
        ctx.fill();
    };

    self.drawWalls = function() {
        ctx.strokeStyle = NewGrid.WALL_COLOR;
        if (seWall) {
            ctx.beginPath();
            ctx.moveTo(corners[2].getX(),corners[2].getY());
            ctx.lineTo(corners[3].getX(),corners[3].getY());
            ctx.closePath();
            ctx.stroke();
        };

        if (swWall) {
            ctx.beginPath();
            ctx.moveTo(corners[3].getX(),corners[3].getY());
            ctx.lineTo(corners[0].getX(),corners[0].getY());
            ctx.closePath();
            ctx.stroke();
        };
    };

    self.visited = function() {return visited;};

    self.markVisited = function() {visited = true;};

    /**
     * @returns gets the array of the 4 neighboring cells
     */
    self.getNeighbors = function() {
        return neighbors;
    };

    /**
     * @returns an instance of 'this' named self.
     */
    self.getSelf = function() {return self;};
};

Answers


Okay, I figured it out! I was a stupid mistake, whether or not that is a good thing or not I'm not sure.

Here was the problem:

In the getOpenNeighbors function I was doing a check to see if the cell was undefined and if so don't add it to the array of possible neighbors to move to. Well the undefined check was written incorrectly. I had defined the cells as such:

tc0 = new MyCell(grid.getCell(parentX-1, parentY));

The grid.getCell(x,y) method would return undefined if no cell existed in the given location, which is exactly what I wanted it to do.

However in the check I did to make sure it was not added to the array I wrote as such:

if (tc0 != undefined && !tc0.visited()) neighbors.push(tc0);

Well clearly tc0 or any of the tc0-x objects I created in the manner would not be undefined. what I needed to do was check if the object I passed it as a param was undefined. Well I was treating this object as the parent or super class so all I had to do was use my method getParent() and everything would be hunky-doory.

if (tc0.getParent() != undefined && !tc0.visited()) neighbors.push(tc0);

Now everything works! Unfortunately the maze does not 'look' like it should, so I have some trouble shooting to do there, but at least I can move forward now.


Need Your Help

WPF - change the selected Item of one combobox to the selected item of another combobox

c# wpf combobox

I have two combo boxes in my .xaml file. I would call the first combobox "main combo box ". The other combobox also contains the same set of values as in the the first main combobox.

EF 4.1 using code first do not create new database

c# .net asp.net-mvc-3 entity-framework ef-code-first

I create a new web using Entity framework 4.1 code first approach with seperate layers :

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.