Writing Mario in sed

Writing Mario in sed

Have you ever wondered if you can write games using sed? No? Anyone? Weird.

Anyway, here is how it works, and this is the link to the repo: https://github.com/chebykinn/sedmario

What is sed

Canonically sed is a tool that is used to transform text line by line using regular expressions. Here's an example of some sensible use of it:

$ echo text | sed 's@\(.*\)@<bold>\1</bold>@g'                                                                                                                                                                                                    ~
<bold>text</bold>

The problem is that this tool can do more than just replace text:

  • It has labels and a goto command

  • There are two buffers for storing data - pattern space and hold space, you can use the second one for storing data in between parsing lines using a special command.

  • As noted in Wikipedia, the sed language is Turing-complete.

Implementation

Game scene

Every game should have a cycle that processes all the logic, movement, and rendering. In sed, everything builds around an input line, and if the user doesn't pass file or pipe data, it will use stdin, so every time you type something and press enter it'll start processing the script.

What do we need in this game cycle?

  • Level rendering

  • Processing of input for movement

  • Enemies movement

The first part of the script is used to print the level:

:print
g
 /^$/ { s/.*/\
+--------------------------------+\
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII\
IIIIIIIIIIIIx00IIIIIIIIIIIIIIIIIII\
|FFFFFFFFFFFFFFFFFFFFFFXFFFFFFFFFFFFFFFFFFFFFFFFF1\
|FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2\
|FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3\
|FFFFFFFFFFFFFFFFXFFFSXSXSFFFFFFFFFFFFFFFFFFFTFFF4\
|FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFTFFFFFFTFFF5\
|FFFFPFFFFFFFFFFFFFFFFFFFFFMFTFFFFFFFFTFFFMFFTFFF6\
|SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS7\
|SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS8\
+--------------------------------+\
 /
 }

Here I replaced the input line buffer with some characters, but this doesn't look really colorful yet because in this buffer I store level map data as characters:

  • P - is player

  • F - air block

  • S - solid block

  • T - pipe

  • M or G - Goomba moving left or right respectively

  • K or O - Koopa

This allows me to write logic based on the character type. Using mentioned labels and the goto command, it's easy to build some logic to process input:

/^a/b left
/^d/b right
/^ /b up
/^s/b down

And then move the player the only way you can do anything in sed - by replacing text:

:left
 g
# replace player block with space block
# collision detection is automatic
 s/FP/PF/
 b end
:right
 g
 # Prevent screen scrolling at the end of level
 /end/b end
 s/PF/FP/

Rendering the level

Okay, so the previous code draws only the first screen, but the game needs to draw new parts of the level as the player goes further to the right. This is done with two tricks:

  1. When the player moves to the right I need to shift the screen to the left, this is done by appending # to every line.

  2. I used the last line for storing metadata - when the player jumps it stores the jump states as 1u, 2u, 3u. Same with the next level segments, once the number of # goes to 16 (I just used that for the round number, and to have fewer segments), I replace those #s with the segment based on zone number in metadata (z1, z2, z3).

Here's how it looks under the hood:

The header is not part of the level data, it's just two constant rows of characters with the score counter.

Coloring the output

After user input is processed and enemies are moved we can use the second, hold buffer to store the level data. Then it's easy to color output, I just need to replace all characters with ANSI escape codes for colors.

Running the game loop

To make the game actually playable I needed to write a simple wrapper shell script that sends enter every second, thus driving the game loop forward.

That's it! There are of course some improvements that are needed to be made, for example instead of sending enter every time it is better to come up with a special sync character to maintain a constant speed when you hold the move buttons, but that's for another time. It was a fun challenge at the university and it was nice to remember what I wrote back then :).

Now that people know how it works, I wonder if someone can continue this challenge, for instance, write Doom or The Legend of Zelda...