09-20-noodling-map-representations.md 9.6 KB


title: Representing Retro Grid Maps tags: maps, goldbox, retro description: Thinking through some requirements around Gold Box style maps. category: coding

date: 2023-09-22

Modern computer RPGs have complicated, detailed, three-dimensional maps, but it was not always thus. Back in the 80s, as they were first crawling from table to monitor, the capabilities of the PCs of the era were better suited to the more spare graph paper maps of old AD&D modules.

I have a fair bit of nostalgia for many of the games from that period, in particular the Gold Box series. If you tease apart the Gold Box file format[^1], there are eleven bits of information associated with each grid square:

  • what wall graphic (if any) for each of the four cardinal directions
  • mobility for each of the four cardinal directions (e.g. if there's a wall, or door, or locked door, or whatnot)
  • the "backdrop"--an image which is used to, for example, distinguish indoor and outdoor locations)
  • the "zone"--what, if any, environmental subdivision of the map the grid square is part of. This is used to have different lists of random encounters, designate places safe for resting, stuff like that.
  • and an event, if any, that occurse in that square. Events cover everything from stores, to NPC interactions, to fixed-location battles.

If I wanted to store a map with information like that, I could do worse than using a JSON representation. Something like:

{
  [
    "x": 12,
    "y": 5,
    "backdrop": "sky",
    "zone": "shopping district",
    "event": "none",
    "wall_north": "none",
    "wall_east": "rock wall",
    "wall_south": "none",
    "wall_west": "storefront door",
    "move_north": "open",
    "move_east": "blocked",
    "move_south": "open",
    "move_west": "unlocked door"
  ],
  ...
}

And that might be fine if you're making something purely machine-written and machine-read. It's essentially a much more verbose form of the old format, with x and y map coordinates explicit rather than position-based. But if you're making something only for machine consumption, then you don't need to be that explicit in the first place.

I'm interested in what a human-centric map file format might look like.

Let's make a few assumptions. First, for example, most walls are going to be walls on both sides (one-way walls or one-way doors are representable based on the above, but it's hard to make sense of a map unless they're in the minority). Further, walls (and their absence) and doors will, in most cases, imply the navigability (that is, if there's a wall, you can't go that way, etc). I also imagine that many maps are going to use one wall and door type primarily. Likewise, I assume that "event" squares are the exception, not the rule. Finally, for the moment, I'm going to ignore "zone" and "background" (don't worry, I'll pick them back up later).

That brings us from eleven down to four bits of information for each cell: in each direction, is there a wall, door, or nothing?

Here's one take[^2]:

+---------+-+-+-+-+-+-----+-----+
|         | D D | | D     |     |
| +D----+ +-+ +-+D+D+ +---+-----+
| |     D   D D     | |   |     |
| | +---+   + +     | +D--+-----+
| | |       D D     |     |     |
| | |       +-+-----+ +D+ +-----+
| | |                 | |       |
| +D+   +D+     +-----+ | +-----+
|       D D     D       | |     |
+--S--+ | +---+ +-------+ +-----+
|     | | D D |     D D         |
+-+ +-+ | +D+-+ +---+-+   +-----+
| | |   | D   | D     |   |     |
+-+ +-+ +D+--D+ +-----+   +-----+
|     D | D   |                 |
+-+ +-+ | +   | +----D--+ +-+ +-+
| | |   | |   | | |     | | | D |
+-+ +-+ | +--D+ | | +---+ +D+ +-+
|     D | D   | D | |           |
+D+ +D+ | |   | +-+-+ +D+D+ +--D+
| | |   | |   | | D D | | | |   |
| +D+   +-+---+ +-+-+ +-+-+ +---+
|               D | | D |       |
+D+ +D+ +D+ +-+ +-+D+ +-+ +-+ +-+
| | | | | | | D       D | | | | |
| +-+ | | | | | +D---D+-+ | | | |
|     | | | | | |   | |   | | | |
+-----+ +-+ +-+ +--D+-+   | | | |
|     D                   | | D |
+-----+-------------------+-+-+-+

That's fairly readable. A D indicates a door, there, while | and - are walls. The + are walls, too. Well, corners.

Well, actually, that's a bit of a thing: the + are used for intersections, but looking back at what we're tracking, there's no mention of intersections. It's a grid, after all--if your grid cell has walls on adjacent cardinal directions, it's an intersection. Otherwise, no. So the + are visually helpful, but convey nothing useful about the map from the machine's perspective.

In fact, there's quite a lot of useless visual help there. Each cell is taking up nine characters across three lines. Sure, there's overlap in the cell information, but that's still bulky for four pieces of information.

As a purely aesthetic aside, theoretically that map is 16x16, but it looks much longer north/south than east/west. That's a side effect of the aspect ratio of our text characters, but it would be nice if the representation looked a bit more proportional. Human-readability is part of the point, after all.

Between that aesthetic concern, and the fact that we have so much visual padding, that sparks a bit of a brainstorm. Let's try something a little different.

_________________________________
| _______ |_| |_|_|_| ____|_____|
| | ____|   | |     | |___|_____|
| | |       |_|_____| ___ |_____|
| |_|   ___     ______| | ______|
|______ | |____ |_______| |_____|
|__ __| | |_|_| ____|_|   ______|
|_| |__ |_|___| |_____|   |_____|
|__ __| | |   | _________ ___ __|
|_| |__ | |___| | | ____| |_| |_|
|__ __| | |   | |_|_| _____ ____|
| |_|   |_|___| |_|_| |_|_| |___|
|__ ___ ___ ___ |_|_| |_| ___ __|
| |_| | | | | | ______|_| | | | |
|_____| |_| |_| |___|_|   | | | |
|_____|___________________|_|_|_|

I'm ignoring doors for the moment, just for this proof of concept, but that looks nice and clean and much closer to square.

There's still redundant information, mind you, but it's much more compact. Each grid square is down to six characters across two lines. And we're making efficient use of the overlap: instead of 33 lines for a 16-length north/south, we only have 17. Our first line is just the northern wall, then each row handles the east, west, and south walls of each grid cell.

We're still using 33 columns, but, again, that ends up looking closer to square.

If we look at an random grid cell, the four bits of information are in one character of one line, then three characters of the following line. That does leave useless extra _. If we eliminated those, it might look like this:

 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|  _ _ _  |_| |_|_|_|  _ _|_ _ _|
| |  _ _|   | |     | |_ _|_ _ _|
| | |       |_|_ _ _|  _  |_ _ _|
| |_|    _       _ _ _| |  _ _ _|
|_ _ _  | |_ _  |_ _ _ _| |_ _ _|
|_   _| | |_|_|  _ _|_|    _ _ _|
|_| |_  |_|_ _| |_ _ _|   |_ _ _|
|_   _| | |   |  _ _ _ _   _   _|
|_| |_  | |_ _| | |  _ _| |_| |_|
|_   _| | |   | |_|_|  _ _   _ _|
| |_|   |_|_ _| |_|_| |_|_| |_ _|
|_   _   _   _  |_|_| |_|  _   _|
| |_| | | | | |  _ _ _|_| | | | |
|_ _ _| |_| |_| |_ _|_|   | | | |
|_ _ _|_ _ _ _ _ _ _ _ _ _|_|_|_|

Most of those extra _ are in spots that might otherwise have east or west walls, so we couldn't just delete them. Instead, we replaced them with spaces. I have to say, it still seems pretty readable to me. This seems like the sort of thing that could come down to personal preference, and since the contents of those spaces aren't going to be used in the map, that's probably fine.

Personally, I prefer the more-connected walls. Let's go back to that, and add doors while we're at it.

Roguelikes have been using + to represent doors for decades, so let's start with that, at least for the doors to the east and west. They're not going to line up as well with the southern walls. I'm going to try . for those.

_________________________________
| _._____ |_+ +_|.|.+ ____|_____|
| | ____+   + +     | |.__+_____|
| | |       +_+_____| _._ |_____|
| |.|   _._     ______| | ______|
|__.___ + +____ +_______| |_____|
|__ __+ | +.+_| ____+_+   ______|
|_| |__ |.|__.| +_____|   |_____|
|__ __+ | +   | _____.___ ___ __|
|_| |__ | |__.| | | ____| |.| +_|
|._ _.+ | +   | +_|_| _._._ ___.|
| |.|   |_|___| |_+_+ |_|_| |___|
|._ _._ _._ ___ +_|.| +_| ___ __|
| |_| | | | | + _.___.+_| | | | |
|_____| |_| |_| |__.|_|   | | | |
|_____+___________________|_|_+_|

Adding the doors definitely makes it a fair bit busier, but I still find it readable. I think the placecs with the most possible confusion are where there are a bunch of doors adjacent to each other--the training hall in the center of the north edge is a good example.

Overall, I like this. It seems reasonable to have a map file consist of a grid like the above, then a bunch of additional data below for all the exceptions.

I promised I'd get to the concepts of "backdrop" and "zone", and I will, but not in this entry. Stay tuned.

[^1]:

Which turns out to be a bit tricky. The file formats can differ
slightly between games, are all stored in a janky compression
format, and don't include information about the file contents.

Bits were expensive way back when, and if you knew that the map of
the civilized quarter was in the third block of the `GEO4.DAX`
archive file, there was no need to label it. Of course, that
starts falling apart when your dev team gets bigger, or if you
want to make the functionality more general.

[^2]: Savvy readers may notice this is the map of the civilized section of Phlan from the first Gold Box game: Pool of Radiance.