Bokosan – a reverse Sokoban

Bokosan is a reverse Sokoban where boxes are on a goal position and the player needs to pull them away while avoiding deadly spikes and falling into pits.

The game has 20 levels, single player mode, time challenge mode with online leaderboard, customizable player appearance, sound, 4 music tracks (6 in full version) and a few more features.

It is an entry for the js13kgames gamedev competition, where the goal is to create a web browser game using only JavaScript/CSS/HTML, with final size (after ZIP compression) below 13 kB, in a month. It was created for the 2015 jam for the theme “reverse”.

This year I worked together with my brother, Zsolt, he composed the music and created most of the levels.

Play it here.

Graphics

I’ve decided to go with raster graphics, stored in PNG with low color count and replacing colors in code if needed. I’ve tried several other methods to store graphics (putting each sprite in a string, etc.) but the overhead for decoding was just too big.

(The image is scaled down, click here for full version.)

The final PNG contains the Jostix font by Ray Larabie (only uppercase letters, numbers, punctuation marks), 13 tiles, 9 character animation frames and 3 other graphics; it is 700×48 pixels, 20 colors and the final size is 1600 bytes after TinyPNG compression.

It might worth to note that for character animation I used oldschool tricks of mirroring and rotating to reduce the frames. I have only drawn the standing and step with right foot frames and then “stand, step, stand, mirrored step” sequence. Also, I just rotated the “walking up” frames for the “walking right”, as well as the “walking down” for “walking left”. For pulling I did the same but used one frame only and played the sounds of steps.

The colors of customizable parts of the character (hard hat, shirt, pants) are replaced during render (and not on the sprite sheet) as each entry in the challenge leaderboard can be different.

Quick note: when opening an HTML from local disk and accessing its contents in JavaScript, like reading pixels of an image (rather than putting in DOM and showing to user, or using drawImage()) the Cross Origin protection kicks in and prevents the operation. Although when opening from a web server there is no such problem, this feature can be disabled but it’s strongly not recommended. Worth to note that JS engines track the data they handle so reading a canvas where a drawImage() copied a portion of a protected image will result in an error as well.

Sound effects

For the sound effect we used jsfxr to generate them.

I have added a few minor optimizations for jsfxr before compressing the source via Closure Compiler, and after that manually edited the compressed code to be more suitable for ZIP.

Music

The music was created entirely by my brother, Zsolt.

I have created a small tracker that implements the basic features of an XM, handles only one pattern and loops it, but also have a map that describes which channels should be played on which loop (and also an option for intro that will be only played once at the beginning) – this way a song can be made up of a short period of music but can be dynamic and changing on each loop. Also created a converter that reads an XM file and creates the JavaScript arrays for the player, so Zsolt could use MilkyTracker to compose.

The samples were produced using jsfxr just like the sound effects (they are actually in the same array) and are altered on the fly for player needs (changing playback speeds to create the notes).

I have also created a frontend for playback later, check it out.

Level design

The levels were by designed mostly by Zsolt, with a few exceptions.

They are stored as simple strings – ZIP is really good at compressing text.

The levels are locked by default. Only the already solved and the next two unsolved are unlocked, as an attempt to prevent the player from getting stuck on one level.

And as the steps are counted for each level it is just natural that achievement hunters appear, so we added an “A+” badge for them for completing the level with at most the minimal steps we completed them during development, displayed in the level selector.

Challenge mode

There is a challenge mode with online leaderboard showing the name, time and character profile picture for each record.

There are 6 challenges, each consist of 3-4 already completed levels, the player plays them one after the other and the time is recorded and shown on the leaderboard.

Statistics

The game collects statistics about each play: play time, moves made, pulls done, levels started, levels finished.

The statistics is stored on the server side, the games recevies the current snapshot of the statistics and the one from a minute before, and then interpolates them during the next minute – this makes the display much more active.

I loved to see the numbers rolling during the vote period, unfortunately the statistics are no longer available on the jam website. [Although I made a few screenshots, but I could not find them yet.]

Server side

The server keeps track of challenge leaderboards and statistics. It does not do anything more, really.

Compression

I basically go with the idea of writing readable code and leaving the low level optimization and compression to the tools.

Most of the time this works just fine as for a really simplified example…

if (x == 3)
{
    return true;
}

return false;

… is exactly…

return x == 3;

… but the former can be read more easily and the tool will rewrite it to the latter anyways.

For code compression I used Google Closure Compiler in two phases: first is the advanced optimization without whitespace removal, then the whitespace removal. This leaves a chance for (easier) manual/scripted optimization to be more suitable for the next tool (ZIP in this case).

The spritesheet is compressed using TinyPNG.

I used as few files in the compiled version as possible, as each file adds at least 130 bytes to the final ZIP file – this can be a lot and it increases with file name length.

For the final ZIP file the rules dictate to be a standard ZIP file, so no 7zip or other advanced formats are permitted.

To optimize the final ZIP I used AdvanceCOMP’s AdvZIP which basically recompresses an existing ZIP file but keeps compatibility.

About 90% of the build process – starting from readable code to entry ZIP – was automated in a build script. This way I could track the effect of each changes on the final size. The rest was manual editing of code just before Closure Compiler whitespace removal.

Fun fact: the last ZIP before the submitted one was 13313 bytes, 1 byte over the limit. I saved that byte by changing the player name input from “Player name:” to “New name:”.

Final thoughts

I think Bokosan is the most polished game I’ve created until today (alone or in a team), it has the most content and feels the most complete. It also got a lot of positive feedbacks and it was really fun to catch my collagues playing it, trying to get the “A+” badge on all levels.

However, the game has not ranked in the Top 10 in Desktop nor Mobile category, in Server category it finished at 6th place and in Facebook special on 3rd place.

A huge thank you to Andrzej Mazur for organizing js13kgames competition once again, Ray Larabie for the Jostix font, and Markus Neubrand for Jsfxr!

Similar Posts:

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.