I'm coding from scratch a small experimental game on a medium-sized random rectangular square tile map. (Say, a map of a dungeon.)

There are several types of tiles (for example: floor, wall, monster, boss, treasure, player etc.). There are no extra information stored in the map tiles, just tile type.

Most parts of map are randomly generated. However there are several features that are to appear on every generated map in pre-set places. Say, walls around the dungeon, boss locations, player spawn point and the treasure.

Note: Since the game is small, coded for fun, and the only game designer is a programmer (that is, me myself), I do not plan to spend resources on coding a level editor or supporting an existing one. Also, I do not plan to support existing tile map libraries as I want to dig into some of the related coding problems myself (see above about fun), but I be happy happily to look at relevant existing code as a reference.

When I look at my map generation code, I find it rather ugly.

Is there an elegant way to deal with the problem? If that matters, I'm writing this game in JavaScript, but I will be happy to see a relevant reference in any programming language.

Here is a partial pseudocode to illustrate how my current tile generator solution looks like:

Note that I shown a rather simple set of generator rules above. Actual thing is several times longer.

I hate that the code looks imperative — and it is hard to figure out what is going on from the first glance. I think that declarative approach could help. But so far I can't figure out a good solution.

In my opinion, this gives a great visual representation and allows you to use a regular text editor as "tile editor". This character representation is used for generating the actual level data, if you e.g. need coloring, graphical tiles or as I've done, a 3D dungeon mesh.

As for the handling of preset sections, just create similar character representations for each of them and in your generator, loop through them and put them into correct locations in the big dungeon map. Here's some pseudo JavaScript for one possible approach:

I seem to have thought this a bit too high level (as in preset rooms instead of preset walls as you mentioned). However, you could change the preset x and y to be relative to the map size, e.g. x=0.333 would be 1/3 of map width (the loop would multiply x by the width). Null could mean "repeat the pattern through the dimension". Thus the preset for your lava-column example in the comment would be { x:0.333, y:null, map:"L" }.

However, this is rather limited and getting a little complex. For such components as straight, varying size wall sections you are eventually going to have a loop that creates it. The easiest way is to call a function as you did, but if that looks ugly, you can use data-oriented approach as I outlined.

One wild idea I just came up would be to create a small hand-made prototype map which would have all the bosses and treasures in their relative positions, but during the map generation you would multiply all their positions with the actual map dimensions (i.e. you kind of magnify the map, but keep the preset features as one tile. With some constraints, this could be expanded to walls too (if the proto map has adjacent wall tiles, the gaps are filled during expansion in that dimension, but not in the dimension where there is no neighbouring wall). Below is an illustration with only horizontal expansion (S and E are just some preset tiles you want to place relatively but don't want to use explicit function calls).

Nice! But this assumes fixed map dimensions. If map size is not fixed, how would you represent, say, a preset with vertical 1-tile-wide channel of lava across whole map height, at 1/3 of map width? (Or dungeon walls in my original example.)
–
Alexander GladyshJan 31 '13 at 15:18

Thanks. Another use case while I'm digesting about your answer: A square lava pool with a statue in the middle, 1/8 of map height in size, placed in the center of the map.
–
Alexander GladyshJan 31 '13 at 17:03

I'm thinking about extrapolation rules. Something like "#.*#>#.*#", expanding to "#...#>#...#" for map of width 11. But that's a strange road to follow...
–
Alexander GladyshJan 31 '13 at 17:07

As for null as a wildcard (regardless of extrapolation rules above). Probably better idea would be to allow filter functions at x and y fields (i.e. x: function(x) { return x % 2 }, and use certain strings as a shorthand for some of the functions. I.e. y : "*" would be equivalent to y:function(y) { return true }. Not quite declarative, but almost. Note that this is not extrapolation, but something like cloning, and can lead to strange results if preset patterns overlap.
–
Alexander GladyshJan 31 '13 at 19:40