title: "Hex Curler: A Minimalist Webgame" date: August 29, 2019 ---

I just published Hex Curler, a tiny dungeon crawler, based on Jeff Moore's Hex.

You can play it by running the following in a bash shell:

c=x; while [ $c ]; do clear; curl -c k -b k hex.m-chrzan.xyz/$c; read c; done

This was an exercise in minimalism. The game server is implemented in less than a thousand lines of Ruby code. It is completely stateless, requiring no database. The front end "client" is a single line of bash, less than 80 characters long. The only dependency is curl, a CLI tool already available on most Unix-like systems.

The source code is available on my GitLab.

Let's get curling

The whole concept arose from two ideas I had:

I abstracted these ideas away into a Ruby class called Engine and a skeleton Sinatra app.

To create a new "curling" system, you extend Engine and implement four methods:

You also need to define a secret which is a string that is used to validate that a submitted cookie represents a valid state. More on this later.

The Sinatra skeleton instantiates an engine with the received cookie, runs a step, sends back the new state in the returned cookie, and responds with the engine's message. The code for it fits in half of a browser window:

require 'sinatra'
require 'sinatra/cookies'

# exposes `Hex`, which extends `Engine`, implementing a simple dungeon crawler
require './hex_engine'

def secret
  # get secret from environment
  ENV['HEX_SECRET']
end

get '/:command' do |command|
  # `new` uses `hash_to_state` to initialize the engine's state
  engine = Hex.new cookies.to_h
  engine.step command
  # `state_h` uses `state_to_hash` to serialize the engine's new state
  engine.state_h.each_pair do |key, value|
    cookies[key] = value
  end
  engine.message
end

O(1) space webapp

Hex Curler is hosted online but has no session management, no database. It's an O(1) space webapp.

As mentioned before, the game's state is stored in a cookie. The server need only know the contents of that cookie to return a new state back to the user.

To prevent a user from tampering with the cookie, for example by increasing their health to a ridiculous number, becoming invulnerable to enemies in Hex, the cookie also contains a checksum field. This checksum is the hash of the state together with an appended secret only known by the game server. The server will refuse to respond to requests whose cookie does not have a valid checksum.

This introduces some interesting possibilities. For example, let's say Alice wants to boast to her friends about how she just beat Hex, ended with 100 HP remaining, and had upgraded her magic armor to level 5. Her friend Bob doesn't just have to take her word for it, or trust a screenshot that could have easily been photoshopped.

Alice can send Bob her state cookie. If a request to the game server with it succeeds, Bob can be assured that he has cryptographic proof of Alice's claims.