Tilesheet numbered using binary where the first bit represents North, the second bit is East, the third bit is South and the fourth bit is West. For example, a tile that has neighbours to the North and South would have a binary value of 0101.

function draw(map) for x = 1, map.width do for y = 1, map.height do if map[x][y] > 0 then -- todo: check if map[x][y] is out of bounds local n = 0 n = n + map[x][y - 1] -- North n = n + 2*map[x + 1][y] -- East n = n + 4*map[x][y + 1] -- South n = n + 8*map[x - 1][y] -- West -- todo: draw tile number n at x,y end end end end

Once we find the value of "n" for each cell, we can draw the respective tile for that cell. Please note that we also have to check if map[x][y] remains inside the bounds of our map.

Interactive auto-tiling demonstration

What this example shows is how to pack information about the adjacent cells into an integer. The first bit represents North, the second bit represents West and so forth. The resulting "bit" variable has a value between 0 and 15 representing a tile from the tilesheet.

Once we know the "bit" value of each cell, it's not hard to check if its neighbours are non-empty:

local north = bit%2 >= 1 local east = bit%4 >= 2 local south = bit%8 >= 4 local west = bit%16 >= 8

Example isometric set with 3 possible states for each tile (plains, grassland, water) 3^4 = 81 tiles

What if that we wanted to add two more states: tundra and desert. 5^4 = 625 tiles! As you can see, things can get out of hand quickly as the number of possible states increases. One solution could be to limit the number of transitions. For example, you can make sure that your editor/terrain generation script does not produce maps that contain unlikely transitions like "tundra to desert" or "desert" to "water". Removing unnecessary transitions could greatly reduce the total number of required tiles.

Often, it's necessary to have several layers of tilemaps.
We start with the "terrain" layer containing the plains/grassland/water transitions.
On top of it, we can render another layer with roads.

So far, we only considered 4 adjacent cells for each tile.
If we were to include diagonal neighbour cells, we would need an even bigger tileset.

Example set with 2 possible states for each tile and 8 neighbours 2^8 = 256 tiles

A slightly more generalized version of the previous algorithm:

local dirs = { {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1} } local bit = 0 for i, v in ipairs(dirs) do local x2 = x + v[1] local y2 = y + v[2] if map[x2][y2] == 1 then bit = bit + 2^(i - 1) end end

Using the above technique makes it possible to produce sloping terrain (like in Sim City 2000).
If you are working with a heightmap, one limitation to consider is that each slope variance means another tile state.
So, you may have to "smooth" or "blur" your heightmap to avoid very steep transitions that are not contained in your tilesheet.

Simple sloped isometric tilesheet