Safecracker: A postmortem


What Went Well?

tl;dr

  1. Accessible Gameplay
  2. Choosing a Scalable Project Scope
  3. Mock-ups Helped Choose Layout
  4. Offline Development & Finding Bugs
  5. Efficient Font Packing

1. Accessible Gameplay

Game jams are usually full of arcade-style games inaccessible to players with limitations to their color vision, hearing, or mobility. I wanted my game to stand out by not requiring any of those. In short, it should be a decently entertaining game that allows players to relax while they take their time.

I think my final product meets those goals, and players seem to think so too. Safecracker received favorable reactions in itch.io comments and ratings, as well as during Darzington's Octojam stream (This link jumps to Safecracker being played).

The game is half-way between Mastermind and the game that inspired it, Bulls and Cows. I'm also satisfied with the final palette. It's reminiscent of the first generation Nintendo Game Boy while still accessible to color-blind audiences.

Color view & Colorblind approximation

Standard view (top) & Colorblind Approximation (bottom)

I based the palette off one available in the Octo web IDE, but with two changes:

  1. Plane 2 draws the third darkest color, used in feedback on choices
  2. Pixels with both planes active display as the near-black of the title text

I wanted to keep the gameplay area's colors similar enough to be read as different from the title. This is one area I didn't succeed in, as it turns out that the chosen colors don't group neatly without hue perception. Despite this, the game succeeds at being playable, even for users with full colorblindness!

Hearing is unnecessary as there's no sound. Complex hand mobility is also unnecessary. It's possible to play the game using only a mouse with an on-screen keypad, even if many clicks are required to move the dials in the current version. I consider this to be a success compared to arcade-style games, even if there are issues with Octo's HTML exports when playing on mobile at the time of publishing.

2. Choosing a Scalable Project Scope

A huge contributor to my success was prototyping to choose a game I could scale up from a minimum viable product.

I started my week by researching puzzle games. I prototyped a few concepts as text-based games in Python, a language I use frequently. These helped eliminate confusing mechanics and presentations very quickly.

For example, Bulls & Cows uses an arbitrary metaphor that might confuse some players. Why is a bull an item that's in the right place? Why is a cow an item out of place? Why farm animals? Visual representations of concepts are generally easier to understand. I'll discuss this more in the next section on mock-ups.

Prototyping in Python also helped me understand the logic I would need to implement for each candidate. Since I initially only allotted the project a few hours per day for a week, I eliminated ideas that required any of the following:

  • Complex data structures, logic, or layout in memory (hashes, trees, sorting, etc)
  • Complex graphics
  • Building new tools beyond simple scripts

This helped me choose a single core game that was not only feasible, but could also be scaled up as time allowed.  I turned my notes into a structured outline of tasks required for translating the Python prototype into Octo, ranked by importance to user experience. When the time to cut features came, I barely had to put any thought into it. My in-depth prioritization had effectively made my choices for me before I even started development. This helped me finish on time, even in the face of serious and unexpected events interrupting my week.

3. Mock-ups Helped Choose Layout

I made multiple sketches for the interface. They made it clear why Mastermind used a 2 x 2 grid of peg slots to show feedback for player guesses.
Some mock-ups of guess feedback layouts

In-line and two rows  can wrongly be read as corresponding to positions in the hidden number. They are also hard to fit into an appealing layout.

Squares of four indicators were the best choice for two reasons:

  1. They discourage players from associating pegs with specific positions in the hidden sequence
  2. They pack neatly into grids

Single pixels were too small to be legible indicators at XO-Chip's 128 x 64 resolution, so I chose 2 x 2 dots with 1 pixel of padding. That gave me 7 x 7 squares for feedback on user guesses. A margin of 1 pixel around the active area gave a 9 x 9 tile size, which also worked very well for tiles containing digits from the font I used, Tom Thumb.

Tom Thumb, the font SAFECRACKER adapts

Tom Thumb is a pixel font with 3 x 5 glyphs. Most of its glyphs are embedded above. I used only the digits and uppercase characters to maximize legibility.

At this point, I was committed to a 9 x 9 tile size for the interface. I decided on a 12 x 5 grid of tiles for the gameplay area: 1 row for feedback, and 4 rows for digits. You can see evidence of the grid layout in this early commit. The chosen layout is forgiving to new players by allowing many attempts while maintaining nearly perfect symmetry. Although there's an extra pixel of vertical padding at the bottom of the screen, most players won't care. The grid layout also translated very well to Octo's :calc statements, allowing much of the layout to be precomputed into constants.

4. Offline Development & Finding Bugs

My offline toolkit consisted of the following:

  1. Git in the terminal for version control
  2. Sublime Text as an editor
  3. c-octo's octo-run for running and debugging
  4. octo-sublime for syntax highlighting
  5. Copies of both the octo and c-octo repos as documentation

Note: I also used NeoVintageous for Vim emulation in Sublime. You only need this if you enjoy using Vim.

This toolkit made it possible to develop on very weak hardware while disconnected from the internet.  It worked so well that I occasionally forgot I wasn't connected! During future jams, I'll unplug for deep work sessions whenever possible. Additionally, both octo-sublime and NeoVintageous seem to work fine with Sublime 4 despite originally being written for version 3.

I also found a number of bugs in c-octo. I've already filed github issues (12) for some and submitted a pull request for one of them during the jam. I'll be submitting more tickets to help improve c-octo as I find the time to thoroughly investigate each problem I encountered.

5. Efficient Font Packing

This is a minor optimization, but I am very happy with it because it's a step toward future projects. 

I got away with 5 bytes per glyph without having to add a separate lookup table for glyphs, and without wasting cycles on offsets for the glyph's ASCII code.

First, I eliminated an addition that would be needed to skip to the digits in the glyph table by placing them at the very start:

: font-5x3
    # Based on Tom Thumb, used under CC1 Public Domain dedication
    # Digits are placed first to simplify math for lookup.
    0x60 0xA0 0xA0 0xA0 0xC0  0x20 0x60 0x20 0x20 0x20  # 0, 1
    0xC0 0x20 0x40 0x80 0xE0  0xC0 0x20 0x40 0x20 0xC0  # 2, 3
    0xA0 0xA0 0xE0 0x20 0x20  0xE0 0x80 0xC0 0x20 0xC0  # 4, 5
    0x60 0x80 0xE0 0xA0 0xE0  0xE0 0x20 0x40 0x80 0x80  # 6, 7
    0xE0 0xA0 0xE0 0xA0 0xE0  0xE0 0xA0 0xE0 0x20 0xC0  # 8, 9
    0x40 0xA0 0xE0 0xA0 0xA0  0xC0 0xA0 0xC0 0xA0 0xC0  # A, B

Then I turned a multiplication by 5 into addition and bit shifting:

        # i := font-5x3 + ( current_digit_value * 5)
        i := font-5x3
        i += current_digit_value
        current_digit_value <<= current_digit_value
        current_digit_value <<= current_digit_value
        i += current_digit_value

These tricks helped fit the compiled ROM into just over 1 kilobyte. I could likely optimize this total even further with more time.

What Went Wrong?

tl;dr

  1. C-octo's (Expected) Limitations & Bugs
  2. Custom Tools
  3. Time Constraints Lead to Cutting Features

1. C-octo's (Expected) Limitations & Bugs

C-octo is great for offline work on underpowered hardware. However, it isn't beginner friendly at time of publication.

First, c-octo's tools are minimalist. The debugger didn't display a position indicator over a view of program source. Instead, it displayed numerical offsets from label addresses. This requires you to know the compiled sizes of each instruction in your Octo source. I hope this changes in the future.

C-octo is also buggy, which is expected for a new tool. As a reminder, I knew what I was getting into. Running into bugs and rough edges was a goal. Despite this, the bugs and limitations sometimes forced me back to the original version of Octo. This only happened when I had to do in-depth debugging or sprite editing. Most people will still want to use Octo rather than c-octo, or at least have it around as an option.

A screenshot of a drawing bug in c-octo

One of the bugs present in c-octo at the time of publishing. It's related to a wait for keypresses that interferes with proper redrawing.

2. Custom Tools

Importing font data from Tom Thumb took far longer than expected.  The font is distributed as a BDF, a raster format unsupported by the Octo-related font import tools I found. I decided to write my own since I had already committed to a 3 x 5 font size.

Although I used Python to write my import script, I wasn't familiar with the library I used to read the BDF file. That led to some issues with partial-size characters such as commas. I manually overrode the output for the affected characters as a quick solution. In turn, the fix caused cosmetic issues with my text. I made a last-minute fix for the issues, resulting in a very clean final layout.

3. Time Constraints Led to Cutting Features

In addition to the issues above, unexpected events arose during the jam, reducing the time available for development. I had to cut the following to make the deadline:

  1. Screen shake on invalid guess attempts
  2. Sound
  3. A vertical layout using Octo's screen rotation to show guesses horizontally
  4. Input with a cursor
  5. A less skewed random number generator
  6. Optimizations for execution speed & binary size
  7. Source code cleanup for style and readability

The game has a lot of room for optimization. The most egregious part is displaying reactions to user feedback. Currently, the entire guess is drawn twice when the player changes a digit in it: once to erase the old guess, and another time to draw the new guess. Instead, it should only redraw the digit in question.

I could also optimize text drawing speed by switching to multi-color sprites for the title and end text. Doing so would halve the number of drawing-related instructions used during the title and end text being drawn. Removing unused parts of the alphabet from the source might cut down final ROM size, even if multicolor sprites are used for text. However, these two optimizations are unimportant since they don't meaningfully affect gameplay, and they might require an additional text drawing routine. They were absolutely not worth it for the jam.

Despite the overall lack of optimization, the released version works fine at 500 cycles per frame.  It's even playable at lower speeds if you're ok with redraw being visible. However, many games by Internet Janitor (Octo's author) manage to redraw changes smoothly at 30 cycles per frame or fewer. That's a worthwhile goal to aim for.

Conclusion

This jam went fairly well. I put a number of ideas into practice while helping polish c-octo. I have 3 takeaways for readers.

1. Use familiar tools whenever a deadline is involved

I'm a fan of Python and use it regularly. Using it for prototyping and creating tools helped me a lot. I made a slightly risky move by using a library I wasn't  familiar with to import font data. This resulted in minor problems. However, there was enough slack in my development plan to release on time. If you use a library you're not familiar with as a core part of your game during a jam, you will likely have a much worse experience. Don't do that.

2. Prototype early to keep scope scalable

Prototyping early helps make sure that your gameplay:

  1. makes sense
  2. can be scaled up from an easy-to-build minimum viable product

Scalability is crucial for jam participants as things almost always go wrong, which leads to cutting features. It helps a lot if the features cut aren't crucial to the game. If somehow nothing goes wrong, players will be pleasantly surprised by a very polished game.

3. The internet and fast hardware are overrated

Making efficient use of low-power hardware will help you get more done, especially with offline tools and good offline documentation.  This is especially important given the chip shortage and global supply chain problems created by the COVID pandemic.

Files

safecracker.ch8 1 kB
Nov 01, 2021
safecracker.gif 62 kB
Nov 01, 2021

Get Safecracker

Leave a comment

Log in with itch.io to leave a comment.