From 8f6595c19043585652d5603096b9540cab14efa4 Mon Sep 17 00:00:00 2001 From: Marcin Chrzanowski Date: Thu, 29 Aug 2019 20:03:16 -0700 Subject: Publish Hex Curler blogpost --- src/blog/hex-curler.html | 136 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 src/blog/hex-curler.html (limited to 'src/blog/hex-curler.html') diff --git a/src/blog/hex-curler.html b/src/blog/hex-curler.html new file mode 100644 index 0000000..8f5f1c6 --- /dev/null +++ b/src/blog/hex-curler.html @@ -0,0 +1,136 @@ +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. +

-- cgit v1.2.3