3

How do I make an algorithm to zig-zag fill a grid at any size as shown in the image below?

Visual representation. Arrows show which direction it should go to.

Here is my algorithm which doesn't work. (Starting bottom left to top right corner instead):

x1 = 0;
y1 = grid_h-1;
var a = 0;
put(x1,y1);

while(!((x1 = grid_w-1) and (y1 = 0))) { //If it isn't at the top right corner
    if a = 2 {
        x1 += 1;
        put(x1,y1);
        while(x1 != grid_w-1) { //While x1 isn't at the right
            //Go diagonally down
            x1 += 1;
            y1 += 1;
            put(x1,y1);
        }
        y1 -= 1;
        put(x1,y1);
        while(y1 != 0) { //While y1 isn't at the top
            //Go diagonally up
            x1 -= 1;
            y1 -= 1;
            put(x1,y1);
        }
    } else if a = 1 {
        while(x1 != grid_w-1) { //While x1 isn't at the right
            //Go diagonally down
            x1 += 1;
            y1 += 1;
            put(x1,y1);
        }
        y1 -= 1;
        put(x1,y1);
        while(y1 != 0) { //While y1 isn't at the top
            //Go diagonally up
            x1 -= 1;
            y1 -= 1;
            put(x1,y1);
        }
        x1 += 1;
        put(x1,y1);
    } else {
        y1 -= 1;
        if (y1 = 0) { a = 1; } //At top?
        put(x1,y1);
        while(y1 != grid_h-1) { //While y1 isn't at the bottom
            //Go diagonally down
            x1 += 1;
            y1 += 1;
            put(x1,y1);
        }

        x1 += 1;
        put(x1,y1);
        while(x1 != 0) { //While x1 isn't at the left
            //Go diagonally up
            x1 -= 1;
            y1 -= 1;
            put(x1,y1);
            if (y1 = 0) { a = 2; } //At top?
        }
    }
}

Any simpler way to do this?

  • Can you explain why in the fifth example (2 high, by 3 wide) it goes down, up-right, down instead of down, up-right, right as it seems like it would based on the other example? -- Better, can you also describe unambiguously how to fill in with this pattern? – moreON Jan 08 '16 at 05:08
  • Sorry my bad. I want it to fill from top left to bottom right in a zig-zag pattern with the best method for it to do it. What I do know is that it goes one step down then it goes up until it's on top and so on, what I don't know is how to switch when it gets to the corner. – Anastasia Dunbar Jan 08 '16 at 05:26
  • It seems to me that which direction to go is based on the edge that you are on? Bottom: go right. Right: go down. Left: go down. Top: go right. -- evaluating those options, in that order, every time you reach an edge, should work. It covers corners by ensuring that the correct choice is first in those options. – moreON Jan 08 '16 at 05:31
  • Like `while(!((x1 = grid_w-1) and (y1 = grid_h-1))) { /*Change variable a based on where position is.*/ switch(a){ case 0:x1 += 1;break;case 1:y1 += 1;break;case 2:y1 -= 1;break;case 3:x1 += 1;y1 += 1;break;case 4:x1 += 1;y1 -= 1;break;} }` ? – Anastasia Dunbar Jan 08 '16 at 05:41
  • There's a little more than just position, you also need to have some state about which direction you are currently travelling diagonally in and whether you're travelling diagonally or making one of those unit-moves along an edge. – moreON Jan 08 '16 at 05:46
  • Let me look into that. Because I know that in the right top corner. If you moved right you move left-down. If you moved right-up then you move down. – Anastasia Dunbar Jan 08 '16 at 05:51
  • ... Look at that this way. If you moved right, it's because you just did one of your single-unit-edge-moves. This means that you will now be in the diagonal-move state (in this case, down-left, because the last time you were in diagonal-move state you were going the other way). After moving up-right, you've just encountered an edge after a diagonal move, in this case it is the right edge (checked before top), so you go down. Implementing this neatly could still be a challenge. Give it a shot, edit into the question (after your current question) and I'll take a look at it. – moreON Jan 08 '16 at 05:54
  • Also comment again so I get notified if you do that. – moreON Jan 08 '16 at 05:56
  • So many if-statements here. – Anastasia Dunbar Jan 08 '16 at 06:17
  • This doesn't even work for me, and makes me confused. http://pastebin.com/eT5J5fmS Try to read it. – Anastasia Dunbar Jan 08 '16 at 06:28
  • Almost worked. http://pastebin.com/qXj6gfwe – Anastasia Dunbar Jan 08 '16 at 06:42

3 Answers3

2

The key observation here is that you go northeast when the Manhattan distance to the top left square is odd and southwest otherwise.

Of course, you must consider hitting one of the edges. For example, when you walk southwest and hit the bottom or south edge, you move east instead; when you hit the left or west edge, you move south. You can either catch the three cases (south edge, west edge, unrestrained movement) or you can move and correct your position when you have walked out of bounds.

After hitting an adge, your new position should leave you moving the other way. That is, each correction involves an odd number of steps. (Steps here is the Manhattan distance between the point you'd have gone to normally and the point you ended up in.)

If your zigzagging algorithm works correctly, you will end up visiting each cell once. That is you make h × w moves, where h and w are the height and width. You can use this as termination criterion instead of checking whether you are in the last square.

Here's example code for this solution. The additional boolean parameter down specifies whether the first step is down or left.

function zigzag(width, height, down) {
    var x = 0;
    var y = 0;
    var n = width * height;

    if (down === undefined) down = false;

    while (n--) {
        var even = ((x + y) % 2 == 0);

        put(x, y);

        if (even == down) {             // walk southwest
            x--;
            y++;

            if (y == height) {
                y--; x += 2;
            }
            if (x < 0) x = 0;
        } else {                        // walk northeast
            x++;
            y--;

            if (x == width) {
                x--; y += 2;
            }
            if (y < 0) y = 0;
        }
    }

    return res;
}
M Oehm
  • 27,011
  • 3
  • 26
  • 39
0

Here's the solution abusing if-statements.

x1 = 0;
y1 = 0;
put(x1,y1);

var a = 0;
while(!((x1 = grid_w-1) and (y1 = grid_h-1))) {
    switch(a) { //Down, Right-Up, Right, Left-Down
        case 0: y1++; break;
        case 1: x1++;y1--; break;
        case 2: x1++; break;
        case 3: x1--;y1++; break;
    }
    put(x1,y1);
    if (a = 2) { //If moved right.
        if (x1 = grid_w-1) or (y1 = 0) { //If at the right or top edge. Go left-down.
            a = 3
        } else if (y1 = grid_h-1) { //At bottom edge. Go right-up.
            a = 1
        }
    } else if (y1 = 0) { ///At top edge.
        if (x1 = grid_w-1) { //If at the right corner. Go down.
            a = 0;
        } else { //Go right.
            a = 2;
        }
    } else if (a = 3) { ///If moved left-down.
        if (y1 = grid_h-1) { //At bottom n edge. Go right.
            a = 2
        } else if (x1 = 0) { //At left edge and not bottom. Go down.
            a = 0
        }
    } else if (a = 0) { //If moved down.
        if (x1 = 0) { //If at the left corner. Go right-up.
            a = 1
        } else if (x1 = grid_w-1) { //If at the right corner. Go left-down.
            a = 3
        } else { //Go right
            a = 2
        }
    } else if (a = 1) { //If right-up.
        if (x1 = grid_w-1) {  //If at the right corner.
            if (a = 2) { //If moved right. Go left-down.
                a = 3
            } else { //Go down.
                a = 0
            }
        }
    }
}

Doesn't work well if one of the size is 1.

0

Basically we can use state diagram along with recursion to solve this.

permitted_directions = {
"start":["down", "side"], 
"down":["north_east", "south_west"], 
"north_east":["north_east", "side","down"], 
"side":["north_east", "south_west"], 
"south_west":["south_west","down", "side"]
}

def is_possible(x, y, pos):
    if pos == "down":
        if x+1 < row and y >=0 and y < col:
            return (True, x+1, y)
    if pos == "side":
        if x >= 0 and x < row and y+1 >=0 and y+1 < col:
            return (True, x, y+1)
    if pos == "north_east":
        if x-1 >= 0 and x-1 < row and y+1 >= 0 and y+1 < col:
            return (True, x-1, y+1)
    if pos == "south_west":
        if x+1 >= 0 and x+1 < row and y-1 >= 0 and y-1 < col:
            return (True, x+1, y-1)
    return (False, 0, 0)

def fill_the_grid(grid, x, y, position, prev):
    grid[x][y] = prev
    prev = (x, y)
    for pos in permitted_directions[position]:
        possible, p, q = is_possible(x, y, pos)
        if possible:
            return fill_the_grid(grid, p, q, pos, prev)
    return grid
noman pouigt
  • 844
  • 9
  • 24