Mimizuku Saga 10
This is my first ever 10-liner written in BASIC for Commodore 64 and the second game released in BASIC. When I was much younger, I used to write in BASIC a lot, but after several years spent in 6502 assembly - with the 10-liners challenge given - it was still a lot of stuff I needed to learn, the "nasty" stuff!
This game has been submitted to the Basic 10liner Contest 2021 in PUR-80 category. Code doesn't use any command abbreviations, just simply the maximum of 80 characters per line.
Inspired by another game from this competition, an amazing, jaw-dropping 10 lines long Sokoban written by Johan Berntsson. Although I started with a space theme, aliens, access keys, bombs and chambers (SPACE10 was the WIP title), somehow I ended up with a Japanese theme, so thanks for many conscious and unconscious influences from Sokoban!
Challenge was great, and I had no idea so much can fit in 10 lines! But there were several refactors, shuffles of code, using math instead of conditions, and some tricks used in order to push more into it. That was real fun! And took me at least ~30 working hours with testing to achieve this result.
Plot
This is an old Japanese legend, which doesn't exist, but perhaps it could as some legends are still alive and some of them were forgotten thousands of years ago. Your dead ancestors brought you to Yomi, the realm of the dead, as they wished to get back to life. But you know that this could only make Izanami, a gatekeeper of this dead realm angry. You've taken the shape of an eared owl - mimizuku, and sneaked into the realm with an idea to greet your ancestors and fill their hearts with sake, so they will regain the equilibrium.
To succeed, you have to greet 25 ancestors on your path and remain alive so you can get back from there. This is a hard task to complete, as with each step, your life force weakens. Also, greeting your ancestors has a price: it drains your life force as well. You can collect sake bottles which will help you deal with your ancestors, so when you're greeting them, they will get some sips of sake first, and only if you don't have any sake left, your life force will be drained.
You are progressing down the realm of the dead by entering gates spread over the area. But if your destiny was to fail, you won't find any further gates and stay there forever.
But stay brave on your quest. Thus this legend wouldn't exist if not for the one who got back from the realm of the dead and passed it along.
Screen
Starting from the top-left of the screen, you see:
- a number of your ancestors left you have to greet on your path
- life energy remaining
- sips of sake, each bottle contains 7 samurai-sized sips of sake
- on greeting your ancestors, drain points are displayed, if you don't have any sips of sake left, this amount of points will be drained from your life force
Action screen contains:
- your owl character starting always from the top-left part of the screen
- white walls you cannot pass through
- purple skulls of your ancestors
- blue bottles of sake
- brown gates to different parts of the realm of the dead
Controls
Use WASD keys on the keyboard to control your owl character in four directions. To greet your ancestors or collect bottles of sake or go through gates simply move there.
When the game is finished, you can play it again just by typing:
RUN:
* note the colon character at the end which separates command from the characters following it
To restart the game, just use RUN STOP key to halt the program execution and run the game again.
The game was tested on real hardware and using WinVice 2.4 emulator.
Code description
Level generation is slow as I squeezed the logic from 3 lines of code down to 2 lines. Because of that, I needed to exchange IF conditions to mathematical equations which have to be executed and nullified when not needed. For this reason, if you play this game on the emulator (e.g. WinVice), it's much more enjoyable in warp mode.
The generator will output a different level on each run, but there are several patterns used in order to make this game playable.
First of all, there are several vertical walls drawn using modulo of 3. Vertically, 3 rows are also used to draw special tiles (bottles of sake, ancestors, and gates). I have found a way of making the math working so a player would never be stuck due to three tiles in a column being a wall. At the same time, I was able to keep some variety and uniqueness in the design of each level.
However, I had no more space to write a logic that guarantees that there is a single gate leading to the next screen. In such a rare case, a game cannot be completed. Anyway, I've found it to be an interesting concept of destiny, which sometimes will lead you to a failure of your quest.
This code was changed several times in order to fit all parts of the math equations in two lines, and still squeeze assigning of `D$` somewhere in this free area!
Line by line code overview
- Line 0: Initialization of constants and variables, allocation of tiles table (`V`), data for life force points, number of ancestors, screen colour location
- Line 1: Level generation (called each time when entering a gate), `Q` contains a part of the equation responsible to display all other tiles than walls, assigning "D" to `D$` used for controls
- Line 2: The second line of level generation, random special tile is generated even though it might be not used but thanks to that a generator fits in two lines, calling tile rendering, closing a FOR loop at the end followed by setting up `X` and `Y` to be used by a player character
- Line 3: The main loop starts here, keyboard controls first, getting a tile under which player wants to move, at the end defining title of the game in data
- Line 4: Displaying top bar with all details like life force and information of how much of energy an ancestor drained, at the end a simple logic to finish the game when succeeded or failed with a different message
- Line 5: Drawing player character on the screen, then a case of stepping on a sake bottle followed by a case of moving to a field with an ancestor. In other cases, moving back to the start of the main loop (line 3). At the end, some data with a value used for level generation followed by a common part of the game-ending message
- Line 6: Clearing the character from the old tile, setting new `X` and `Y` positions, decreasing a life force by one, if a tile is different than a gate: moving back to the start of the main loop (line 3), otherwise clearing the screen and jumping back to the level generation (line 1)
- Line 7: Getting a bottle of sake, jumping to line 6 to redraw player character on a new position. At the end just data string containing the message with two game ending variants followed by all tiles available in the game
- Line 8: Greeting an ancestor, generating a random number of points to be drained and the logic to remove them from life force and collected sips of sake, jumping to line 6 to redraw player on a new position
- Line 9: Setting a tile in the tiles table, rendering a tile on the screen under a given position using `SYS 58640` ($E510) or STUPT, a KERNAL procedure to set a cursor position, tile is selected using a string offset
Variables overview and some tricks used
- L - life force value
- M - number of ancestors to be greeted
- P - screen border and foreground colour
- T$ - the title of the game stored in a string together with clearing of the screen
- U - a value of 10 assigned, used in the level generation code so setting values for `X` and `Y` could fit (variable takes one character)
- E$ - the first part of the screen displayed on the end of the game (when succeeding or failing)
- S$ - a string containing the "DRAIN:" string
- A$ - a concatenation of two things as this way everything fits in the game: first of all the rest of the message when a player will win or fail, followed by all tiles
- X, Y - tile positions `X` and `Y` on the screen
- V(X,Y) - two-dimensional table with all tiles
- D$ - used to store "D" character, otherwise, the title of the game wouldn't have a space character
- K - sips of sake (in BASIC it doesn't need to be defined and it will be zero by default)
And a few other math tricks like merging some conditioning in line 4, e.g. using `L*M<0` instead of two separate conditions, and offsetting a string a similar way.
I did something similar in line 8 in order to decrease the life force. Some sips of sake are taken by the ancestor, but when a drain is higher, it needs to be taken not only from the sake counter (`K`) but also from the life force (`L`). Using conditions could be easier, but it was great to find such a simple alternative!
Also, just have a look at setting D$ in line 1. I had no idea I do not have to close double quotes when assigning some string variables. Well, I think I did something like that when I was young...
Anyway, limitations pushed me into these experimentations with surprising results!
In the last line, I re-used assigning tiles to a two-dimensional table (`V`) between level generation and when a player moves in the game. It was needed due to lack of space, but as several things are manipulated this refactor was an eye-opener. It forced me to call line 9 twice for both states in order to render a tile properly and take an object only once. I was also forced to re-use the same variables for `X` and `Y`, something I would never do in the "normal" code, but I really liked the simplicity I got from all of that. Code-wise though it's not easy to follow though!
Notes
This game was submitted to the competition at 23 February 2021. You can find the game entry here: https://bunsen.itch.io/mimizuku-saga-10-by-bago-zonde
Download
Click download now to get access to the following files:
Leave a comment
Log in with itch.io to leave a comment.