Stratagem Devlog, Part 1

State against the machine

thoughts
gamedev
Author

Vincent “VM” Mercator

Published

September 3, 2024

Modified

October 25, 2024

After five years of being abandoned on my computer, Stratagem is finally ready for public release. I’ve rewritten the original code I made in 2019 from scratch using the tools and knowledge that I learned since then.

As of writing this article, you can play the most recent version of Stratagem on your computer, smartphone, or tablet by visiting its Lexaloffle BBS page. You can view its source code directly on its GitHub repository and help me develop it by reporting issues or contributing code.

Note

This article is part of a series called “Stratagem Devlog”.

Logic Updates

As I described before in a previous article, a finite state machine (FSM) is an abstract computing model that can be in one of a finite number of internal states at a time. In hardware design, FSMs are implemented as synchronous sequential circuits: they are synchronous because they update periodically on clock cycles, and sequential because they operate on sequential data like streams of ones and zeros or button presses over time.1

flowchart LR
  input[/sequential input data W/] --> state_logic

  input -- sometimes --> output_logic
  subgraph "Finite State Machine"
    current_state[/current state Q/]
    --> state_logic["state transition logic"]
    --> next_state[/next state Q'/]
    -. "becomes on next clock cycle" .-> current_state
    --> output_logic[output logic]
  end

  output_logic --> output[/sequential output data Z/]

Finite state machines are not limited to just hardware: my video game is also represented as one, too. In this case, The game’s sequential input data is the series of button controls the player presses over time, the sequential output data is the what the game displays over time, the states describe what actions the player can do, and the state transitions describe the rules of the game that link those actions together. The game’s internal state dictates what is being displayed on the screen, and controls what actions the player can or can’t perform to move to a different state.2

Here is a simplified version of Stratagem v0.1.1’s state diagram. I’ve grouped all of the states relating to the actual gameplay loop into its own sub-state machine for clarity.

stateDiagram-v2
  [*] --> title_screen: initialize

  title_screen --> gameplay: player wants to play game
  title_screen --> high_scores: player wants to see high scores

  state gameplay {
    [*] --> game_init

    game_init --> generate_board: game is finished initializing

    generate_board --> game_idle: game is finished generating board

    game_idle --> level_up: player reached level score threshold
    game_idle --> game_over: player ran out of chances
    game_idle --> swap_select: player wants to swap gems

    swap_select --> game_idle: player doesn't want to swap gems
    swap_select --> player_matching: player chose gems to swap

    update_board --> player_matching: board is full and may have matches

    player_matching --> game_idle: board is full and has no matches
    player_matching --> update_board: board has holes from cleared gems

    level_up --> generate_board: player is ready for the next level

    game_over --> [*]: player did not get a high score
    game_over --> enter_high_score: player got a high score

    enter_high_score --> [*]: player finished entering high score
  }

  gameplay --> high_scores
  high_scores --> title_screen: player wants to play again

The PICO-8 engine contains three special functions called _init(), _draw(), and _update() that correspond to initializing the program, drawing on the screen, and updating once every frame. When modeling a PICO-8 program as a state machine, I used the _update() function for state transition logic and the _draw() function defining output logic. Large and/or reusable parts of the program’s code are split off into their own functions for easier readability.

Viewing a video game as a finite state machine helped me make parts of my code work in parallel by only executing parts of code loops that would take too long to complete within a single frame (1/30 of a second). The main example for this is how the interaction between the player_matching state and the update_board state work. These states provide the logic behind clearing matches on the gem grid and dropping new gems down from the top, respectively. In my original code for Stratagem v0.0.3, this equivalent logic was executed continuously inside a match-and-combo-calculating while-loop until the grid was refilled with gems. While this process was functionally identical to how the state diagram worked, it blocked any other operation from happening (e.g., letting the animated background update) since no other code was being executed within the while-loop. To circumvent these issues, the logic for checking state machine transitions was made fast enough to be completed within the span of a single frame.

Multicolored gems drop downward on a six-by-six grid that the player swaps to gain points. The animation is choppy and the sprite artwork is basic.

Animated gameplay of Stratagem v0.1.1. Note that the animated background moves at the same time as the board is updated.

I also added an arcade-like high-score leaderboard using the PICO-8’s persistent data storage that tracks the top ten best (local) games played.

High score screen. The top entry has the initials 'VM' and a high score of 6978.

I’ve been playing a lot of PICO-8 games on my phone. I bet you can’t beat my high scores!

Art Updates

My original code had unused sprite art for both a logo and smaller versions of the gems, but was missing a real title screen. So, I updated the logo to reflect the game’s new name and used the smaller gems to decorate the non-gameplay screens. I took inspiration from another PICO-8 puzzle game called Pushamo to add a simple dynamic background based on a wobbly function.3

Stratagem v0.1.1. O: Start Game. X: High Scores.

Title screen of Stratagem v0.1.1.

Music Updates

The two music tracks in Stratagem’s original version were called “Space Puzzle” and “Breakage”; they were arrangements of homework assignments I made for a college music class. I scrapped both of these songs because I didn’t like their melodies. These songs also used all four of the PICO-8’s sound channels, so they clashed with the game’s sound effects.

I wrote three new songs for Stratagem: “Breakage 2”, “Calm (P8 Cover)”, and “Eight-Four”. “Breakage 2” recycled parts from “Breakage” and added both a new chord progression and a new melody. I’ve already written about “Calm” on my music page; this cover compresses it to fit on only three sound channels. “Eight-Four” is an 8/4 arrangement of an unreleased song I wrote in 7/4 un-creatively named “Seven-Four”.

I still have space to fit one more song in the cartridge, but that’s a task for another day.

Workflow Updates

Even though the PICO-8’s limited resolution is part of its design philosophy as a fantasy console, I absolutely can’t stand reading blocks of text in its 3×5 pixel font for more than ten minutes. Since the .p8 cartridge file format that it uses for describing carts can be viewed as raw text, I can use third-party tools like pico-tool to develop the game art separately from the Lua code. Splitting the code from the art lets me check the code for errors with Selene, add type annotations for Lua LS, and format it with Stylua without having to deal with how the .p8 cartridge format is organized. It wasn’t too difficult to configure these tools to work with PICO-8’s Lua dialect “P8 Lua”.

In Stratagem v0.1.0, I hastily removed pico-tool from my build scripts because I was concerned that it would make the cart-building process less portable. I instead resorted to using shell commands like cat and sed to assemble the cart since I didn’t need to use any of pico-tool’s code-shrinking tools just yet. However, I’d like to add it back to the build process since it can be easily installed in a Python virtual environment.

Conclusion

I’ve been on cloud nine since releasing Stratagem to the public. I’ve said previously that solo game development is super difficult, but I haven’t mentioned how rewarding it is. I can’t help but smile when seeing how people interact with a game I made by myself.

References

Brown, Stephen D., and Zvonko G. Vranesic. Fundamentals of Digital Logic with Verilog Design. Third edition. New York: McGraw-Hill Higher Education, 2014.

Footnotes

  1. Brown and Vranesic, Fundamentals of Digital Logic with Verilog Design, ch. 6.↩︎

  2. If you more information about finite state machines geared towards game developers, I suggest looking at the “State” chapter of the book “Game Programming Patterns” by Robert Nystrom.↩︎

  3. Astute viewers of my site would recognize wobbly functions from prompt 13 of Genuary 2024.↩︎

Reuse

CC BY SA 4.0 International License(View License)