0

I made a maze generation command line tool before. It uses a form of Growing Tree (prim's). And its link is here.

I tried to rewrite it in JS. Using HTML5 canvas. I checked the code line by line. Almost all lines are identical.

Python one fills image buffer entirely, JS doesn't fill its canvas. I tried to debug several times. Couldn't find a difference.

Here is the Python Version.

from PIL import Image
from random import choice, randrange
from argparse import ArgumentParser


parser = ArgumentParser()
parser.add_argument('WIDTH', type=int)
parser.add_argument('HEIGHT', type=int)
parser.add_argument('OUTPUT', type=str)

args = parser.parse_args()
WIDTH = args.WIDTH + (args.WIDTH + 1) % 2
HEIGHT = args.HEIGHT + (args.HEIGHT + 1) % 2
OUTPUT = args.OUTPUT


def checked(p):
    return 0 < p[0] < WIDTH and 0 < p[1] < HEIGHT


def mid_point(p1, p2):
    return (p1[0]+p2[0])/2, (p1[1]+p2[1])/2


current_point = (1, 1)
visited = {current_point: 1}
active = [current_point]
mapper = {}


while active:
    left = (current_point[0] - 2, current_point[1])
    right = (current_point[0] + 2, current_point[1])
    up = (current_point[0], current_point[1] + 2)
    down = (current_point[0], current_point[1] - 2)

    neighbours = [n for n in (left, right, up, down)
                  if checked(n) and n not in visited]
    if not neighbours:
        index = randrange(0, len(active))
        current_point = active[index]
        active.pop(index)
    else:
        picked = choice(neighbours)
        visited[picked] = 1
        active.append(picked)
        m = mid_point(current_point, picked)
        mapper[m] = 1
        mapper[current_point] = 1
        mapper[picked] = 1
        current_point = picked


data = []

for h in range(HEIGHT):
    for w in range(WIDTH):
        data.append(int((w, h) in mapper))

img = Image.new('1', size=(WIDTH, HEIGHT), color=0)
img.putdata(data)
img.save(OUTPUT, format='png')

Here is the JS Version.

canvas = document.getElementById('lab');
WIDTH = canvas.width;
HEIGHT = canvas.height;
ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = false;

function drawPixel (x, y, r, g, b, a) {
    var imgIndex = (x + y * WIDTH) * 4;
    imagedata.data[imgIndex + 0] = r;
    imagedata.data[imgIndex + 1] = g;
    imagedata.data[imgIndex + 2] = b;
    imagedata.data[imgIndex + 3] = a;
}

function checked(p){
    return (0 < p[0] && p[0]< WIDTH) && (0 < p[1] && p[1] < HEIGHT);
}

function midPoint(p0, p1) {
    return [Math.floor((p0[0] + p1[0]) / 2),
            Math.floor((p0[1] + p1[1]) / 2)];
}

currentPoint = [1, 1];
visited = {}
visited[currentPoint] = true;
active = [currentPoint]
mapper = {}


while(active.length > 0) {
    left = [currentPoint[0] - 2, currentPoint[1]];
    right = [currentPoint[0] +2, currentPoint[1]];
    up = [currentPoint[0], currentPoint[1] + 2];
    down = [currentPoint[0], currentPoint[1] -2];

    let neighbourCandidates = [left, right, up, down];
    let neighbours = []
    for (var i = 0; i < neighbourCandidates.length; i++) {
        if(checked(neighbourCandidates[i]) && !visited[neighbourCandidates[i]]) {
            neighbours.push(neighbourCandidates[i]);
        }
    }

    if(neighbours.length == 0) {
        index = Math.floor(Math.random() * active.length);
        currentPoint = active[index];
        active.pop(index);
    } else {
        index = Math.floor(Math.random() * neighbours.length);
        picked = neighbours[index];
        visited[picked] = 1;
        active.push(picked);
        m = midPoint(currentPoint, picked);
        mapper[m] = 1;
        mapper[currentPoint] = 1;
        mapper[picked] = 1;
        currentPoint = picked;
    }
}


imagedata = ctx.getImageData(0, 0, WIDTH, HEIGHT);
for (var h = 0; h < HEIGHT; h++) {
    for (var w = 0; w < WIDTH; w++) {
        if(mapper[[w, h]]){
            drawPixel(w, h, 255, 255, 255, 255);
        }
    }
}

ctx.putImageData(imagedata, 0, 0);

Just create an HTML document with a canvas that has id lab and import the file. I couldn't find the reason of different behaviour. Any guesses?

nejdetckenobi
  • 564
  • 2
  • 8
  • 24
  • I think you need to study up on how javascript handles arrays and objects. – Blindman67 Feb 07 '17 at 16:00
  • if(mapper[[h,w]]){ though a valid statement is incorrect. First mapper is not an array but an object. I think you want it as an array. To access a flat array you need something like if(mapper[w + h * WIDTH]) to find the item at column w row h and the modulo of WIDTH – Blindman67 Feb 07 '17 at 16:12
  • @Blindman67 `mapper` just keeps the record of pixels that belongs to tree. I use it because of hashing. if `d` is an object and `e` is an array, if I type `d[e]`, `e` will be converted to string so I can use the benefits of hashing. You can see that from this [link](http://stackoverflow.com/questions/194846/is-there-any-kind-of-hash-code-function-in-javascript). So, I think I have no confusion in keys (because I have a delimiter) I can use `mapper[[h,w]]` notation and I think it's working just fine. Instead of using calculations. – nejdetckenobi Feb 08 '17 at 08:42
  • Unusual, and a horribly inefficient way to save a bit (boolean) value. It is about 32words per entry in an object. Using a byte array you would have much quicker lookups, and the memory penalty would be negated when you use over 1/64th of possible entries. Also you dont need a hash w * h * width will return a unique value. and the final loop you only need to increment the index. `var ind = 0;` befor loop, and in loop `if(mapper[ind++]){` – Blindman67 Feb 08 '17 at 14:16
  • @Blindman67 ok I will update the code. Let's see if it's a solution for error or not. – nejdetckenobi Feb 08 '17 at 15:22
  • Oh don't know is it is a solution. If I had an answer I would have put one down. I have never needed to learn python so not 100% sure what it is you are doing. I was just making some idle remarks.. – Blindman67 Feb 08 '17 at 16:15

0 Answers0