HWYDT: Donkey Kong
Tags: miniscript, minimicro, programming, howto
Published: 2024-01-11 00:16:31

This is the first in a series of posts where I'll be addressing the question:

## How would you do that?!

In today's post, we'll be looking at the classic arcade game _Donkey Kong_.

![Screen shot of Donkey Kong](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1x19xpoud94enngyfp8a.png)

Like many video games of its day, _Donkey Kong_ consists of sprites (Mario, Donkey Kong, the barrels, the princess, etc.) and tiles (the environment).  Not coincidentally, this is just like [Mini Micro](https://miniscript.org/MiniMicro/)!  But hang on — most of the girders in _Donkey Kong_ are _sloped!_  How would you do _that_?

## The secret of the tiles

If we zoom in on the _Donkey Kong_ screenshot and add grid lines every 8 pixels, we begin to see the secret.

![Zoomed-in Donkey Kong screen shot, with grid lines](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xoclra87t8mj200ky45m.png)

Here you can see that the environment really is a tile display — there's a clear periodicity to it, every 8 pixels.  But where the girders are sloped, we must have other tiles in our tile set, that are shifted versions of the basic girder tile.  We would need, in fact, eight girder-tops, and eight girder-bottoms.  We'll also need versions with ladders above and ladders below.  With those, we could build everything we see here.

A bit of googling for "Donkey Kong arcade tiles" led me to a website called The Spriter's Resource, where someone had already extracted [a Donkey Kong tile set](https://www.spriters-resource.com/fullview/106602/).  But it had a lot of wasted space, so I got out an image editor and boiled it down to this:

![dk_tiles.png, a tile set for this demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3o7dysdnav45vonfufm4.png)

If you're following along at home, save the above image as `dk_tiles.png`.  Then fire up Mini Micro and let's get coding!

## Using the Tile Set

Whenever working with a tile display, the first step is to make sure you know what your tile indexes are!  To save time, I've prepared a blown-up version of our tile set, with indexes overlaid:

![tile indexes for dk_tiles.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zkveah1oh363mv3cczpe.png)

As you can see, the top-left tile is index 0; the one next to it is index 1, and so on.  A basic girder is at index 16 and 32; the next seven after each of these step down by one pixel each.  (The set starting at index 16 have ladders above them, while the set starting at 32 do not).  Girder-bottoms are found at indexes 24 through 31.

I made this tile set 64x128 pixels, because 64x64 wasn't quite big enough, and computers like powers of 2.  So there is plenty of unused space at the bottom for you to add your own tiles, if you decide to pursue this further.

Now that we have an orientation to the tile set, let's start coding!  Launch Mini Micro, `edit`, and put in the following code.

```
clear
display(5).mode = displayMode.tile
td = display(5)
td.tileSet = file.loadImage("dk_tiles.png")
td.tileSetTileSize = 8
td.cellSize = 32
td.extent = [28, 32]
td.clear

makeFlat = function(x0, x1, y)
	for x in range(x0, x1)
		td.setCell x, y, 16
	end for
end function

makeRampUp = function(x0, x1, y0, startLevel)
	level = startLevel
	for x in range(x0, x1)
		td.setCell x, y0, 32 - level
		if level > 0 then td.setCell x, y0+1, 40 - level
		if abs(x-x0) % 2 == 0 then continue
		level += 1
		if level > 7 then
			y0 += 1
			level = 0
		end if
	end for
end function

makeFlat 0, 13, 0
makeRampUp 14, 27, 0, 1
makeRampUp 25, 0, 3, 4
makeRampUp 2, 27, 7, 6
makeRampUp 25, 0, 11, 7
td.setCell 10, 1, 8  // (ladder)
```

The first eight lines or so just set up the TileDisplay.  Note that `tileSetTileSize` is set to 8, because that's how big the tiles are in our tile set.  But given Mini Micro's much higher-resolution display, we set the `cellSize` on screen to 32.  This effectively scales up the tile display by a factor of 4 (32 divided by 8).

Then we define a couple of helper functions for building the level.  `makeFlat` is simple; it just makes a straight row of tile 16 (a basic girder).  The `makeRampUp` function is a little more complex; it keeps a "level" from 0-8 that each girder is shifted relative to the basic girder, and advances this every other column, so we get nice gradual ramps as in the original game.

Finally, the last half-dozen lines use these functions (plus an extra call to `setCell` to build the level.  If you've done all this correctly, running your code should produce this:

![Screen shot of basic level from above code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3l5ljmtw3ej2sfyw2twe.png)

Note that rather than building the level in code, you could use a level editor like /sys/demo/levelEditor to build the level by hand.  That's probably more like what the _Donkey Kong_ developer did; each level is probably just stored in ROM and loaded onto the screen.  But either approach is fine.

## Adding a Player Sprite

A game level isn't much fun if you can't play in it!  So let's add our favorite platformer character, Kip.  We'll make use of the /sys/lib/spriteControllers library to do most of the heavy lifting for us.  `edit` the code again, and add this line to the top of the file:

```
import "spriteControllers"
```

Then scroll to the bottom, and add this code:

```
// Set up our player character, "Kip"
newAnim = @spriteControllers.newAnimation
kip = new spriteControllers.Platformer
display(4).sprites.push kip
kip.x = 400; kip.y = 80
kip.idleAnim = newAnim(file.loadImage("/sys/pics/KP/KP-stand.png"))
kip.runAnim = newAnim([
   file.loadImage("/sys/pics/KP/KP-run1.png"),
   file.loadImage("/sys/pics/KP/KP-run2.png")], 10)
kip.jumpUpAnim = newAnim(file.loadImage("/sys/pics/KP/KP-jump.png"))
kip.fallDownAnim = kip.jumpUpAnim
kip.climbAnim = newAnim([
   file.loadImage("/sys/pics/KP/KP-climb1.png"),
   file.loadImage("/sys/pics/KP/KP-climb2.png")], 10)
kip.curAnim = kip.idleAnim

// Main loop:
while true
	spriteControllers.updateSprites 1/60
	yield
end while
```
Run again, and you should have Kip hanging out on the bottom of the screen.  Using the arrow keys and spacebar, you can make Kip run around and jump — but he passes right through the girders, ignoring them entirely.

## Interacting With the Girders

For our last, and perhaps most crucial, trick, we need to get Kip to walk on the girders — including the sloped ones!

If your last test is still running, press Control-C to break out of it.  Then `edit` again, and insert the following code **before** the main loop:

```
// determine where the ground is below the given position
kip.groundBelow = function(x,y)
	col = floor(x / 32)
	row = floor(y / 32)
	while true
		if row < 0 then return 0
		tile = td.cell(col, row)
		if 16 <= tile <= 23 or 32 <= tile <= 39 then
			return (row+1) * 32 - (tile % 8) * 4
		end if
		row = row - 1
	end while
end function
```

This defines how Kip finds the ground below him.  The given x,y position is in pixel coordinates.  We start by dividing those by 32 — the size of our TileDisplay cells on screen — to find the row and column.  Then comes a little loop, reducing the row by 1 each time until we find a non-null tile in our display.

Then comes the real magic: if the tile we find is in the range 16-23 or 32-39 — which, you might recall, are grid tops in our tile set — then we can calculate the correct Y position of the ground with this math:

```
(row+1) * 32 - (tile % 8) * 4
```

Here, `(row+1) * 32` gives us the top of the tile row; but then we subtract off an additional amount, `(tile % 8) * 4`, to account for how far the girder is shifted down in this particular tile.  `%` is the "mod" operator, and gives us a value from 0-7, based on how far our tile index is beyond 16 or 32 (or any multiple of 8 for that matter).  And then we multiply this by 4, because of the 4-to-1 scaling of our TileDisplay.

So save and run that, and you should be able to walk and jump on the girders perfectly!

![Animation of Kip running around on the sloped girders](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ptnlzgps4trsfwjxcje.gif)

## Conclusion

So now you know the secret of _Donkey Kong_: those apparently sloped girders are just a bunch of shifted girders, carefully arranged side by side, with a bit of math to figure out how Mario should walk on them.

This was the first game to do something like this, and must have caused quite a stir; nobody had seen a tile engine used like this, creating a strong illusion of a non-rectangular environment.  And now that you know how it's done, you can use the same trick in your own Mini Micro games!