Legacy GM Randomized Generator Always Producing Rows of Straight Lines

G

GamerGuy941

Guest
I have a weird glitch, in GMS v1.4.9999, where I made a randomized generator for level creation. It works just fine, aside from one small issue. Any time I run it, the generator produces results similar to this:
1589167255745.png
The top, bottom right, and bottom left areas are all straight lines. When I run it again, it produces similar results(I am using randomize()). I've even tried making it so that if it is a straight line, it picks another one multiple times, however this didnt work, as if they had to be straight lines. Here is the code for choosing each tile:
Code:
    //Determine possible rooms

    available_tiles="111111111111111";
    null = 0;
    tile = 0;
    switch(north){
        case 1: available_tiles=string_set_byte_at(available_tiles,1,48); 
                available_tiles=string_set_byte_at(available_tiles,4,48);
                available_tiles=string_set_byte_at(available_tiles,5,48);
                available_tiles=string_set_byte_at(available_tiles,11,48);
                available_tiles=string_set_byte_at(available_tiles,13,48);
                available_tiles=string_set_byte_at(available_tiles,14,48);
                available_tiles=string_set_byte_at(available_tiles,15,48); break;
        case 0: available_tiles=string_set_byte_at(available_tiles,2,48); 
                available_tiles=string_set_byte_at(available_tiles,3,48);
                available_tiles=string_set_byte_at(available_tiles,6,48); 
                available_tiles=string_set_byte_at(available_tiles,7,48); 
                available_tiles=string_set_byte_at(available_tiles,8,48); 
                available_tiles=string_set_byte_at(available_tiles,9,48); 
                available_tiles=string_set_byte_at(available_tiles,10,48);
                available_tiles=string_set_byte_at(available_tiles,11,48); break;
    }
    switch(east) {
        case 1: available_tiles=string_set_byte_at(available_tiles,5,48); 
                available_tiles=string_set_byte_at(available_tiles,6,48); 
                available_tiles=string_set_byte_at(available_tiles,7,48); 
                available_tiles=string_set_byte_at(available_tiles,8,48);
                available_tiles=string_set_byte_at(available_tiles,12,48);
                available_tiles=string_set_byte_at(available_tiles,14,48);
                available_tiles=string_set_byte_at(available_tiles,15,48); break;
        case 0: available_tiles=string_set_byte_at(available_tiles,1,48); 
                available_tiles=string_set_byte_at(available_tiles,2,48); 
                available_tiles=string_set_byte_at(available_tiles,3,48); 
                available_tiles=string_set_byte_at(available_tiles,4,48); 
                available_tiles=string_set_byte_at(available_tiles,9,48); 
                available_tiles=string_set_byte_at(available_tiles,10,48); 
                available_tiles=string_set_byte_at(available_tiles,11,48);
                available_tiles=string_set_byte_at(available_tiles,13,48); break;
    }
    switch(south) {
        case 1: available_tiles=string_set_byte_at(available_tiles,1,48); 
                available_tiles=string_set_byte_at(available_tiles,2,48); 
                available_tiles=string_set_byte_at(available_tiles,6,48); 
                available_tiles=string_set_byte_at(available_tiles,10,48);
                available_tiles=string_set_byte_at(available_tiles,12,48);
                available_tiles=string_set_byte_at(available_tiles,13,48);
                available_tiles=string_set_byte_at(available_tiles,15,48); break;
        case 0: available_tiles=string_set_byte_at(available_tiles,3,48); 
                available_tiles=string_set_byte_at(available_tiles,4,48); 
                available_tiles=string_set_byte_at(available_tiles,5,48); 
                available_tiles=string_set_byte_at(available_tiles,7,48); 
                available_tiles=string_set_byte_at(available_tiles,8,48); 
                available_tiles=string_set_byte_at(available_tiles,9,48); 
                available_tiles=string_set_byte_at(available_tiles,11,48);
                available_tiles=string_set_byte_at(available_tiles,14,48); break;
    }
    switch(west) {
        case 1: available_tiles=string_set_byte_at(available_tiles,7,48); 
                available_tiles=string_set_byte_at(available_tiles,9,48); 
                available_tiles=string_set_byte_at(available_tiles,10,48); 
                available_tiles=string_set_byte_at(available_tiles,11,48);
                available_tiles=string_set_byte_at(available_tiles,12,48);
                available_tiles=string_set_byte_at(available_tiles,13,48);
                available_tiles=string_set_byte_at(available_tiles,14,48); break;
        case 0: available_tiles=string_set_byte_at(available_tiles,1,48); 
                available_tiles=string_set_byte_at(available_tiles,2,48); 
                available_tiles=string_set_byte_at(available_tiles,3,48); 
                available_tiles=string_set_byte_at(available_tiles,4,48); 
                available_tiles=string_set_byte_at(available_tiles,5,48); 
                available_tiles=string_set_byte_at(available_tiles,6,48); 
                available_tiles=string_set_byte_at(available_tiles,8,48);
                available_tiles=string_set_byte_at(available_tiles,15,48); break;
    }

    //Pick room

    switch(string_count("1",available_tiles)) {
        case 0: null = 1; break;
        case 1: tile = string_pos("1",available_tiles)-1; break;
        default:
            available_tiles = string_copy(available_tiles,0,11);
            tile = irandom(string_count("1",available_tiles)-1);
            for(i=0; i<tile; i++) {
                available_tiles=string_set_byte_at(available_tiles,string_pos("1",available_tiles),48);
            }
            tile = string_pos("1",available_tiles)-1; break;
    }
    if(null == 0) {
        switch(tile) {
            case 0: currentroom = oGenerateData.r0; break;
            case 1: currentroom = oGenerateData.r1; break;
            case 2: currentroom = oGenerateData.r2; break;
            case 3: currentroom = oGenerateData.r3; break;
            case 4: currentroom = oGenerateData.r4; break;
            case 5: currentroom = oGenerateData.r5; break;
            case 6: currentroom = oGenerateData.r6; break;
            case 7: currentroom = oGenerateData.r7; break;
            case 8: currentroom = oGenerateData.r8; break;
            case 9: currentroom = oGenerateData.r9; break;
            case 10: currentroom = oGenerateData.r10; break;
            case 11: currentroom = oGenerateData.r11; break;
            case 12: currentroom = oGenerateData.r12; break;
            case 13: currentroom = oGenerateData.r13; break;
            case 14: currentroom = oGenerateData.r14; break;
        }
    } else {
        currentroom = oGenerateData.null;
    }
Having determined the surrounding rooms in code that I'm not posting, it picks a room by using a string and then disabling all of the rooms that it cannot be. It then proceeds to pick one of the remaining rooms at random. This has worked at a smaller scale, but I tried increasing it and then this happened. It's using objects already made and starting in the center, then triggering objects on its openings to pick a tile, etc. until it makes this. I have no idea why this is happening, so I'm wondering if maybe there's some sort of seed limit? It seems to be working just fine at the start, but eventually falling apart.
 
S

Sybok

Guest
Hi there,

Did you call randomize() somewhere in your creation code? If not it will produce the same output every time the program is executed.
 
G

GamerGuy941

Guest
Hi there,

Did you call randomize() somewhere in your creation code? If not it will produce the same output every time the program is executed.
Yes, this is mentioned in the post.
 

TheouAegis

Member
I get that you're a beginner, but even the manual says you shouldn't use string_set_byte_at(). You'd be vastly better off just spending 2 or 3 hours learning binary and hexadecimal.

With that out of the way, I'm currently looking over the code trying to find anything that might cause that effect. It will probably take a while because of your methods, though.

First question: What are north, south, east and west in your code? They're booleans, I get that. Can a room have more than one set, or are they mutually exclusive?

Second question: What does each position in the string actually correspond to? You should have probably enumerated those positions so people could actually understand what you're doing.

Third question: Why are there 16 positions utilized? ...This ties in with the second question. lol

Fourth question:
default:
available_tiles = string_copy(available_tiles,0,11);
tile = irandom(string_count("1",available_tiles)-1);
for(i=0; i<tile; i++) {
available_tiles=string_set_byte_at(available_tiles,string_pos("1",available_tiles),48);
}
tile = string_pos("1",available_tiles)-1; break;
Can you elaborate on what this case is actually doing, please?

Last question: What's the code that's actually assigning the random values? Isn't this just the code to process the random values and translate them into paths? It looks to me like it's run from a script inside a loop or at least every step.
 
Last edited:
G

GamerGuy941

Guest
I get that you're a beginner, but even the manual says you shouldn't use string_set_byte_at(). You'd be vastly better off just spending 2 or 3 hours learning binary and hexadecimal.

With that out of the way, I'm currently looking over the code trying to find anything that might cause that effect. It will probably take a while because of your methods, though.

First question: What are north, south, east and west in your code? They're booleans, I get that. Can a room have more than one set, or are they mutually exclusive?

Second question: What does each position in the string actually correspond to? You should have probably enumerated those positions so people could actually understand what you're doing.

Third question: Why are there 16 positions utilized? ...This ties in with the second question. lol

Fourth question:

Can you elaborate on what this case is actually doing, please?

Last question: What's the code that's actually assigning the random values? Isn't this just the code to process the random values and translate them into paths? It looks to me like it's run from a script inside a loop or at least every step.
1. North, south, east, and west return a 1 if there HAS to be an opening in that direction, a 0 if there CAN'T be an opening in that direction, and 'null' if it doesn't matter whether or not there is an opening in that direction.

2. These are the tiles that the 15 positions utilized. There is no 16th.
1589205559368.png

3. This was just the easiest way I could think of storing data of whether or not a room is possible.

4. Since string_pos() returns the first valid string, after randomizing it sets all of the numbers before the chosen number to 0 so that it finds the intended room. I have already made sure this works and a smaller scale.

5. The random values are from irandom(). The rest of it is just taking the result and choosing the correct room. It's being run in an object when a variable returns true. This is to deal with an error I had where GameMaker was giving me a recursion depth failure when I used it at a larger scale. An object is triggered at the start, and then it triggers the variable in the openings of its room. This carries on until the final result. It might be important to mention that this code is being run in a step event, so when it triggers the other code nothing happens until the next step.
 

Nidoking

Member
North, south, east, and west return a 1 if there HAS to be an opening in that direction, a 0 if there CAN'T be an opening in that direction, and 'null' if it doesn't matter whether or not there is an opening in that direction.
Wow. I never thought I would see the extended boolean {TRUE, FALSE, FILE_NOT_FOUND} in the wild. I thought that was a relic of TDWTF lore.

(Psst, this is a good time to create a custom enumeration for these three values)
 
G

GamerGuy941

Guest
I have a weird glitch, in GMS v1.4.9999, where I made a randomized generator for level creation. It works just fine, aside from one small issue. Any time I run it, the generator produces results similar to this:
View attachment 30888
The top, bottom right, and bottom left areas are all straight lines. When I run it again, it produces similar results(I am using randomize()). I've even tried making it so that if it is a straight line, it picks another one multiple times, however this didnt work, as if they had to be straight lines. Here is the code for choosing each tile:
Code:
    //Determine possible rooms

    available_tiles="111111111111111";
    null = 0;
    tile = 0;
    switch(north){
        case 1: available_tiles=string_set_byte_at(available_tiles,1,48);
                available_tiles=string_set_byte_at(available_tiles,4,48);
                available_tiles=string_set_byte_at(available_tiles,5,48);
                available_tiles=string_set_byte_at(available_tiles,11,48);
                available_tiles=string_set_byte_at(available_tiles,13,48);
                available_tiles=string_set_byte_at(available_tiles,14,48);
                available_tiles=string_set_byte_at(available_tiles,15,48); break;
        case 0: available_tiles=string_set_byte_at(available_tiles,2,48);
                available_tiles=string_set_byte_at(available_tiles,3,48);
                available_tiles=string_set_byte_at(available_tiles,6,48);
                available_tiles=string_set_byte_at(available_tiles,7,48);
                available_tiles=string_set_byte_at(available_tiles,8,48);
                available_tiles=string_set_byte_at(available_tiles,9,48);
                available_tiles=string_set_byte_at(available_tiles,10,48);
                available_tiles=string_set_byte_at(available_tiles,11,48); break;
    }
    switch(east) {
        case 1: available_tiles=string_set_byte_at(available_tiles,5,48);
                available_tiles=string_set_byte_at(available_tiles,6,48);
                available_tiles=string_set_byte_at(available_tiles,7,48);
                available_tiles=string_set_byte_at(available_tiles,8,48);
                available_tiles=string_set_byte_at(available_tiles,12,48);
                available_tiles=string_set_byte_at(available_tiles,14,48);
                available_tiles=string_set_byte_at(available_tiles,15,48); break;
        case 0: available_tiles=string_set_byte_at(available_tiles,1,48);
                available_tiles=string_set_byte_at(available_tiles,2,48);
                available_tiles=string_set_byte_at(available_tiles,3,48);
                available_tiles=string_set_byte_at(available_tiles,4,48);
                available_tiles=string_set_byte_at(available_tiles,9,48);
                available_tiles=string_set_byte_at(available_tiles,10,48);
                available_tiles=string_set_byte_at(available_tiles,11,48);
                available_tiles=string_set_byte_at(available_tiles,13,48); break;
    }
    switch(south) {
        case 1: available_tiles=string_set_byte_at(available_tiles,1,48);
                available_tiles=string_set_byte_at(available_tiles,2,48);
                available_tiles=string_set_byte_at(available_tiles,6,48);
                available_tiles=string_set_byte_at(available_tiles,10,48);
                available_tiles=string_set_byte_at(available_tiles,12,48);
                available_tiles=string_set_byte_at(available_tiles,13,48);
                available_tiles=string_set_byte_at(available_tiles,15,48); break;
        case 0: available_tiles=string_set_byte_at(available_tiles,3,48);
                available_tiles=string_set_byte_at(available_tiles,4,48);
                available_tiles=string_set_byte_at(available_tiles,5,48);
                available_tiles=string_set_byte_at(available_tiles,7,48);
                available_tiles=string_set_byte_at(available_tiles,8,48);
                available_tiles=string_set_byte_at(available_tiles,9,48);
                available_tiles=string_set_byte_at(available_tiles,11,48);
                available_tiles=string_set_byte_at(available_tiles,14,48); break;
    }
    switch(west) {
        case 1: available_tiles=string_set_byte_at(available_tiles,7,48);
                available_tiles=string_set_byte_at(available_tiles,9,48);
                available_tiles=string_set_byte_at(available_tiles,10,48);
                available_tiles=string_set_byte_at(available_tiles,11,48);
                available_tiles=string_set_byte_at(available_tiles,12,48);
                available_tiles=string_set_byte_at(available_tiles,13,48);
                available_tiles=string_set_byte_at(available_tiles,14,48); break;
        case 0: available_tiles=string_set_byte_at(available_tiles,1,48);
                available_tiles=string_set_byte_at(available_tiles,2,48);
                available_tiles=string_set_byte_at(available_tiles,3,48);
                available_tiles=string_set_byte_at(available_tiles,4,48);
                available_tiles=string_set_byte_at(available_tiles,5,48);
                available_tiles=string_set_byte_at(available_tiles,6,48);
                available_tiles=string_set_byte_at(available_tiles,8,48);
                available_tiles=string_set_byte_at(available_tiles,15,48); break;
    }

    //Pick room

    switch(string_count("1",available_tiles)) {
        case 0: null = 1; break;
        case 1: tile = string_pos("1",available_tiles)-1; break;
        default:
            available_tiles = string_copy(available_tiles,0,11);
            tile = irandom(string_count("1",available_tiles)-1);
            for(i=0; i<tile; i++) {
                available_tiles=string_set_byte_at(available_tiles,string_pos("1",available_tiles),48);
            }
            tile = string_pos("1",available_tiles)-1; break;
    }
    if(null == 0) {
        switch(tile) {
            case 0: currentroom = oGenerateData.r0; break;
            case 1: currentroom = oGenerateData.r1; break;
            case 2: currentroom = oGenerateData.r2; break;
            case 3: currentroom = oGenerateData.r3; break;
            case 4: currentroom = oGenerateData.r4; break;
            case 5: currentroom = oGenerateData.r5; break;
            case 6: currentroom = oGenerateData.r6; break;
            case 7: currentroom = oGenerateData.r7; break;
            case 8: currentroom = oGenerateData.r8; break;
            case 9: currentroom = oGenerateData.r9; break;
            case 10: currentroom = oGenerateData.r10; break;
            case 11: currentroom = oGenerateData.r11; break;
            case 12: currentroom = oGenerateData.r12; break;
            case 13: currentroom = oGenerateData.r13; break;
            case 14: currentroom = oGenerateData.r14; break;
        }
    } else {
        currentroom = oGenerateData.null;
    }
Having determined the surrounding rooms in code that I'm not posting, it picks a room by using a string and then disabling all of the rooms that it cannot be. It then proceeds to pick one of the remaining rooms at random. This has worked at a smaller scale, but I tried increasing it and then this happened. It's using objects already made and starting in the center, then triggering objects on its openings to pick a tile, etc. until it makes this. I have no idea why this is happening, so I'm wondering if maybe there's some sort of seed limit? It seems to be working just fine at the start, but eventually falling apart.
Still have not been able to figure it out.
 

TheouAegis

Member
1. North, south, east, and west return a 1 if there HAS to be an opening in that direction, a 0 if there CAN'T be an opening in that direction, and 'null' if it doesn't matter whether or not there is an opening in that direction.
null is never; true big typo: You disabled tile 11 instead of 12 for NORTH=0. So if all 4 read as 0, tile 12 is the only one left. We are not seeing a run of tile 12 (culdesac from the north) in the screenshot, so it seems at least one direction is at least getting set per room. Still, it will affect the randomization part (which I still don't quite understand).

If all 4 are true, tile 3 should be left. Correct. If all 4 are false, I'd assume that's what the black box is, which shouldn't be there at all with the typo. *shrug*

2 3 6 7 8 9 10 11 (1 4 5 11 13 14 15)
1 2 3 4 9 10 11 13 (5 6 7 8 12 14 15)
3 4 5 7 8 9 11 14 (1 2 6 10 12 13 15)
1 2 3 4 5 6 8 15 (7 9 10 11 12 13 14)


Point is, your "default" tile condition (multiple tiles possible) should never run. If you added that because of a "bug" you noticed, that should have put up a red flag. If you simply added that as a safeguard from the beginning, then you didn't think this algorithm through properly. Picking a random exit direction means the player has a chance to fall into the void, which your room mapping algorithm would have prevented anyway. If on the other hand you don't actually have a complete room mapping algorithm and this script is run in tandem, then... I guess the default case might be alright, but it's an odd method of map generation.

Post your code that is setting NORTH, EAST, SOUTH, and WEST if fixing the typo doesn't fix the problem.
 
Last edited:

TheouAegis

Member
Make oGenerateData.r# series an actual array.


enum EXITS {
NORTH = $be6,
EAST = $170f,
SOUTH = $25dc,
WEST = $40bf,
ALL = $7fff
}

currentroom = oGenerateData.r[ log2(EXITS.ALL & (EXITS.NORTH ^ EXITS.ALL * north) & (EXITS.EAST ^ EXITS.ALL * east) & (EXITS.SOUTH ^ EXITS.ALL * south) & (EXITS.WEST ^ EXITS.ALL * west)) ];
 
Last edited:
G

GamerGuy941

Guest
null is never; true big typo: You disabled tile 11 instead of 12 for NORTH=0. So if all 4 read as 0, tile 12 is the only one left. We are not seeing a run of tile 12 (culdesac from the north) in the screenshot, so it seems at least one direction is at least getting set per room. Still, it will affect the randomization part (which I still don't quite understand).

If all 4 are true, tile 3 should be left. Correct. If all 4 are false, I'd assume that's what the black box is, which shouldn't be there at all with the typo. *shrug*

2 3 6 7 8 9 10 11 (1 4 5 11 13 14 15)
1 2 3 4 9 10 11 13 (5 6 7 8 12 14 15)
3 4 5 7 8 9 11 14 (1 2 6 10 12 13 15)
1 2 3 4 5 6 8 15 (7 9 10 11 12 13 14)


Point is, your "default" tile condition (multiple tiles possible) should never run. If you added that because of a "bug" you noticed, that should have put up a red flag. If you simply added that as a safeguard from the beginning, then you didn't think this algorithm through properly. Picking a random exit direction means the player has a chance to fall into the void, which your room mapping algorithm would have prevented anyway. If on the other hand you don't actually have a complete room mapping algorithm and this script is run in tandem, then... I guess the default case might be alright, but it's an odd method of map generation.

Post your code that is setting NORTH, EAST, SOUTH, and WEST if fixing the typo doesn't fix the problem.
This did help, however it did not fix the problem.
Here is the code that sets north, east, south, and west.
Code:
    //North
    
    buffer = instance_position(x,y-160,oGenerate);
    if(buffer.start == true) {
        north = buffer.currentroom[# 5,2];
    } else if(buffer.cont == true) {
        north = buffer.currentroom[# 5,2];
    } else if(instance_position(x,y-160,oGenerateBlocker) != noone) {
        north = instance_position(x,y-160,oGenerateBlocker).currentroom[# 5,2];
    } else {
        north = "null";
    }
    
    //South
    
    buffer = instance_position(x,y+160,oGenerate);
    if(buffer.start == true) {
        south = buffer.currentroom[# 5,0];
    } else if(buffer.cont == true) {
        south = buffer.currentroom[# 5,0];
    } else if(instance_position(x,y+160,oGenerateBlocker) != noone) {
        south = instance_position(x,y+160,oGenerateBlocker).currentroom[# 5,0];
    } else {
        south = "null";
    }
    
    //East
    
    buffer = instance_position(x+160,y,oGenerate);
    if(buffer == 100022) {
        buffer = instance_position(x+160,y,oGenerate);
    }
    if(buffer.start == true) {
        east = buffer.currentroom[# 5,3];
    } else if(buffer.cont == true) {
        east = buffer.currentroom[# 5,3];
    } else if(instance_position(x+160,y,oGenerateBlocker) != noone) {
        east = instance_position(x+160,y,oGenerateBlocker).currentroom[# 5,3];
    } else {
        east = "null";
    }
    
    //West
    
    buffer = instance_position(x-160,y,oGenerate);
    if(buffer.start == true) {
        west = buffer.currentroom[# 5,1];
    } else if(buffer.cont == true) {
        west = buffer.currentroom[# 5,1];
    } else if(instance_position(x-160,y,oGenerateBlocker) != noone) {
        west = instance_position(x-160,y,oGenerateBlocker).currentroom[# 5,1];
    } else {
        west = "null";
    }
 
G

GamerGuy941

Guest
Make oGenerateData.r# series an actual array.


enum EXITS {
NORTH = $be6,
EAST = $170f,
SOUTH = $25dc,
WEST = $40bf,
ALL = $7fff
}

currentroom = oGenerateData.r[ log2(EXITS.ALL & (EXITS.NORTH ^ EXITS.ALL * NORTH) & (EXITS.EAST ^ EXITS.ALL * EAST) & (EXITS.SOUTH ^ EXITS.ALL * SOUTH) & (EXITS.WEST ^ EXITS.ALL * WEST)) ];
Sorry, but I have no idea what any of that is. I've never used an enum, and why are you assigning what looks like hexadecimal values to the directions? Also, what does that ridiculously long algorithm mean, and how would it randomize a room? I'm sure it works, but I want to know how it works in case I need to use something similar later.
 

TheouAegis

Member
You can remove this line from //East section.
if(buffer == 100022) { buffer = instance_position(x+160,y,oGenerate); }
And how is each oGenerate and each oGenerateBlocker being assigned a position in the main room and how are their values of currentroom getting assigned? Because your world map generation code should be the only thing left messing the room up.
 

TheouAegis

Member
Sorry, but I have no idea what any of that is. I've never used an enum, and why are you assigning what looks like hexadecimal values to the directions? Also, what does that ridiculously long algorithm mean, and how would it randomize a room? I'm sure it works, but I want to know how it works in case I need to use something similar later.
I messed up the code a tad because only the enums were meant to be capitalized.

I was just condensing your first code down, the one that uses string_replace_at(). The enums are the impossible tiles for each direction (e.g., when north equals 0). 111111111111111 is $7fff, all bits are set, hence EXITS.ALL.

The algorithm is actually pretty short. If I wrote it out longhand, it would be at least 14 lines long. All it does is combined all of the directions, finding the common tile between each of them. ^EXITS.ALL*north (and similar parts) negates the bits, so the impossible tiles become the possible tiles. The log2() around it all takes the result and narrows it down to which tile you need to use.
 
G

GamerGuy941

Guest
I messed up the code a tad because only the enums were meant to be capitalized.

I was just condensing your first code down, the one that uses string_replace_at(). The enums are the impossible tiles for each direction (e.g., when north equals 0). 111111111111111 is $7fff, all bits are set, hence EXITS.ALL.

The algorithm is actually pretty short. If I wrote it out longhand, it would be at least 14 lines long. All it does is combined all of the directions, finding the common tile between each of them. ^EXITS.ALL*north (and similar parts) negates the bits, so the impossible tiles become the possible tiles. The log2() around it all takes the result and narrows it down to which tile you need to use.
Okay, thanks! Going to try it and see what happens.
 
Top