<!-- Maze Generator -->
<!-- (c) Copyright 2004, SillySot Software, All Rights Reserved -->
<!-- This software is distributed without warranty of any kind -->


function maze(elementId,size)
{

    var           preloads = new Array();

    var           pics = ["SNN.gif","SNY.gif","SYN.gif","SYY.gif",
                          "TNN01.gif","TNN02.gif","TNN03.gif","TNN12.gif","TNN13.gif",
                          "TNN23.gif","TNY12.gif","TNY13.gif","TNY23.gif","TYN02.gif","TYN03.gif","TYN23.gif",
                          "TYY23.gif","WNN.gif","WNY.gif","WYN.gif","WYY.gif"];

    this.cell = function(x, y)
    {
        this.cellid = y *width+x;
        this.neighbors = [(height == (y+1))?-1:(this.cellid+width), // bot
            (width == (x+1))?-1:(this.cellid+1),           // right
                    (y == 0)?-1:(this.cellid-width),       // up
                            (x == 0)?-1:(this.cellid-1)];  // left

        this.head = this;
        this.tail = this;
        this.walls = new Array('Y', 'Y');
        this.active = 0;
        this.kickWall = function(r)
        {
            var           wall,h,th,t,neighbor;

            neighbor = (wall = 1 == r%2)?this.neighbors[1]:this.neighbors[0];

            if (neighbor != -1)
            {
                neighbor = sets[neighbor];
                if (this.getHead() != neighbor.getHead())
                {                                          // join color sets using head as color indicator
                    h = neighbor.getHead();
                    th = this.getHead();
                    t = h.tail;
                    h.tail = th.tail;
                    th.head = h;
                    vectorCount--;
                    this.walls[wall?0:1] = 'N';
                }
            }
        }
        ;

        this.explore = function(dir)                       // dir=0/down 1/right 2/up 3/left
        {
            var           a,nextIndex,check,img,image,i,found;
            var           localDir = dir;

            found = 0;
            this.active = 1;
            localDir = (localDir+2)%4;                     // start going backwards. Next will be 90 deg.

            if (this == traversalPoints[1])
                found = 1;
            for (a = 0; a < 4 && !found; a++)
            {
                localDir = (localDir+1)%4;
                nextIndex = this.neighbors[localDir];

                if (nextIndex != -1)
                {
                    switch (localDir)
                    {
                        case 0 :
                            check = this.walls[1];
                            break;
                        case 1 :
                            check = this.walls[0];
                            break;
                        case 2 :
                            check = sets[nextIndex].walls[1];
                            break;
                        case 3 :
                            check = sets[nextIndex].walls[0];
                            break;
                    }
                    if ('N' == check && sets[nextIndex].active == 0)
                        found = sets[nextIndex].explore(localDir);
                }
                if (found)
                {
                    img = document.getElementById(elementId+'c'+this.cellid);
                    image = img.src;
                    image = image.replace(/^.*\// , '');
                        if (image.substring(0, 1) == 'W')
                    {
                        i = [(dir+2)%4,localDir];
                        i.sort();
                        image = 'T'+image.substring(1, 3)+i[0]+i[1]+'.gif';
                        img.src = image;
                    }
                    markedCells[markedCells.length] = img;
                }
            }
            this.active = 0;
            return  found;
        }
        ;
        this.getHead = function()
        {
            var           p,q;

            for (p = this.head; p.head != p; p = q)
            {
                q = p.head;
                p.head = q.head;
            }
            this.head = p;
            return  p;
        }
        ;
    }

    this.newMaze = function()
    {
        var           x,y,indicies,len,r,n;

        pointCount = 0;
        traversalPoints = new Array();
        width = Math.max(2, parseInt((element.clientWidth+size-1)/size));
        height = Math.max(2, parseInt((element.clientHeight+size-1)/size));

        element.style.width = width *(size);
        element.style.height = height *(size);
        element.innerHTML = '';

        vectorCount = width *height;
        sets = new Array();
        indicies = new Array();
        n = 0;
        for (y = 0; y < height; y++)
        {
            for (x = 0; x < width; x++)
            {
                indicies[indicies.length] = indicies.length;
                indicies[indicies.length] = indicies.length;
                sets[n++] = new this.cell(x, y);
            }
        }
        len = indicies.length;
        r = 0;
        while (vectorCount > 1)
        {
            r = parseInt(Math.random()*len);
            sets[parseInt(indicies[r]/2)].kickWall(indicies[r]);
            indicies[r] = indicies[(len--)-1];             // much faster than splice(r.1)
        }
        this.drawMaze();
    }

    this.drawMaze = function()
    {
        this.set('');
        var           n = 0;
        var           cell,x,y;
        var           constsz = ') height='+size+' width='+size+' src="W';

        for (y = 0; y < height; y++)
        {
            for (x = 0; x < width; x++)
            {
                cell = sets[n++];
                this.add('<div style="position:absolute;margin:none;padding;0;top:');
                this.add(y *size+';left:'+x *size);
                this.add('"><img id="'+elementId+'c'+cell.cellid+'" onclick=clk("'+elementId+'",'+cell.cellid);
                this.add(constsz);
                this.add(cell.walls[0]+cell.walls[1]+'.gif"></div>');
            }
        }

        element.innerHTML = this.fin();
    }

    this.clk = function(id)
    {
        var           img,image;

        if (pointCount == 0)
            markedCells = new Array();
        if (pointCount == 2)
        {
            this.reDrawMaze();
            pointCount = 0;
            markedCells = new Array();
        }

        img = document.getElementById(elementId+'c'+id);
        image = img.src;
        image = image.replace(/^.*\/W/, 'S');
        img.src = image;
        markedCells[markedCells.length] = img;
        traversalPoints[pointCount++] = sets[id];
        if (pointCount == 2)
            this.solveMaze();

    }

    this.reDrawMaze = function()
    {
        var           a,img,image;

        for (a = 0; a < markedCells.length; a++)
        {
            img = markedCells[a];
            image = img.src;
            img.src = image.replace(/^.*\/./, 'W').substring(0, 3)+'.gif';
        }
    }

    this.solveMaze = function()
    {
        traversalPoints[0].explore(0);
    }

    this.set = function(line)
    {
        lineoutArray = new Array();
        lineoutArray[0] = line;
    }

    this.add = function(line)
    {
        lineoutArray[1+lineoutArray.length] = line;
    }

    this.fin = function()
    {
        return  lineoutArray.join('');
    }
    var           element = document.getElementById(elementId);
    var           a,width,height;
    var           sets = new Array();
    var           traversalPoints = new Array();
    var           pointCount = 0;
    var lineoutArray;
    var           vectorCount = null;
    var           markedCells = new Array();
    var           ie = navigator.userAgent.indexOf('MSIE') != -1;

    element.maze=this; // set this as a property on dom element to avoid global var
    for (a = 0; a < pics.length; a++)
    {
        preloads[preloads.length] = new Image();
        preloads[a].src = pics[a];
    }
    this.newMaze();
}
function clk(elementId,id)
    {
    window.status= elementId+" : " + id
      document.getElementById(elementId).maze.clk(id);

    }
function createMaze(id,sz) { return new maze(id,sz);}
function createRMaze(id,sz) { return new maze(id,parseInt(Math.random()*30)+8);}