Now Reading
Lisp Sport Jam – “Wireworld” – Hoot’s low stage WASM tooling in motion –

Lisp Sport Jam – “Wireworld” – Hoot’s low stage WASM tooling in motion –

2023-06-12 14:25:01

Wireworld + splash screen

This blogpost focuses on our second Spring Lisp Game Jam 2023 entry,
wasm4-wireworld,
an implementation of
Wireworld on prime of
Hoot‘s lower-level meeting
instruments that are part of our Guile → WASM venture to carry Spritely Goblins-powered distributed purposes to the widespread internet browser.

For our entry, we focused WASM-4, a “fantasy console” which makes use of low-level WebAssembly constructs.

Actually, if you happen to see the animated “wireworld” splash display screen on the prime
of this text, that is working wasm-wireworld itself… you’ll be able to even
run it live in your browser!

A quick intro to Wireworld

The true level of this blogpost is not Wireworld itself, however how
Hoot enabled making this Wireworld demo.
So we will stroll into how Hoot turned out to be an unbelievable
toolkit to construct this recreation jam entry, however first let’s speak about
Wireworld to offer some extra context!

Wireworld is a robust mobile automata, categorically just like
Conway’s Game of Life.
Not like Life, Wireworld’s paths are mounted: “electrons” circulate upon copper
wires.
The principles of Wireworld are quite simple:

  • Clean tiles stay clean
  • Electron heads at all times develop into electron tails
  • Electron tails at all times develop into copper
  • Copper stays copper, except there are one or two electron heads in any cardinal path, wherein case it turns into an electron head

That is it! Regardless of that simplicity, this results in highly effective emergent
habits.
Electron head and tail pairs seem to “circulate” fluidly upon copper
wires and may department simply.
However we will additionally make “turbines” and diodes that solely allow
electrons to circulate in a single path and never one other:

Wireworld + intro

(Try it live!)

These guidelines end in habits which allows visually beautiful examples,
a lot of which even look like computational circuitry.

Beneath is a
“binary adder” circuit
working our implementation of Wireworld.

Wireworld + binary adder

Encoded in little endian, 3 (011) + 6 (110) = 9 (1001)!
(And this is the live version
press the “x” button to start out the simulation if you’re prepared!)

If you happen to discover this type of factor pleasant, see
The Wireworld Computer
for a tutorial that makes use of Wireworld to construct from the only parts
all the best way as much as a pc that may calculate prime numbers.

Strive Wireworld your self!

Whereas constructing a cool model of Wireworld wasn’t the actual aim
of our participation within the recreation jam, it was plenty of enjoyable to construct…
and we hope it is enjoyable to make use of and play with, too!
And because you’re studying this in an internet browser and we’re utilizing
Webassembly anyway, why not strive taking part in with Wireworld proper right here
within the browser?

You’ll be able to click on to position wires and electron heads/tails and press X
to start out/cease the emulation. Get pleasure from!

Insights from utilizing Hoot to construct WASM-4 Wireworld

Our intent on this recreation jam was to indicate off how the “decrease stage layers”
of Hoot (i.e. the assembler, disassembler, and so forth) are themselves helpful
and highly effective and might be used to compile one thing interactive and
compelling.
WASM-4’s minimalist homage to 80s online game consoles helped us ship
on this focus by encouraging “coding in direction of the naked metallic”.

As traditional, recreation jams power you to pair down options. For example,
4-colored sprites had been deliberate from the start, and we thought of
some uncommon additions to wireworld, comparable to tiles which may make
noise, which we thought possibly would enable Wireworld for use as a
customized music synthesizer.

Mockup sprites for wireworld

We had been glad to have the ability to
deliver a completed and working Wireworld demo
by the top of the sport jam, however most of those options had been
eliminated, and notably we didn’t but even have time to correctly get
within the sprites we had designed… as an alternative we shipped with placeholder
graphics utilizing the “hiya world” smiley from the WASM-4 tutorial!

Release artwork for wireworld

We had been shocked to get up the subsequent morning to search out that Spritely
group member Vivi had taken our program and
not solely made a cool world with it, however had modified this system
itself:

Thus it was Vivi who first put a model of the binary adder (the
general design taken from
the wireworld computer)
inside wasm4-wireworld.
With no help from Spritely’s inside crew, Vivi modified the
grid dimension to render at 40×40 to have enough house to characterize
the binary adder (the sprite information was not modified for this grid
redesign in Vivi’s preliminary hack, resulting in an entertaining impact
resembling renderings from a corrupted Gameboy cartridge).
The sport jam submitted entry had a 20×20 grid dimension, however as soon as we noticed
the binary adder, we knew we needed to have it, so we resized the
official implementation to 40×40 and redrew the sprites accordingly.

However the largest takeaways from the jam had been that Hoot, whereas nonetheless in
early growth, is already a really promising and highly effective
atmosphere for doing low-level Webassembly programming.
This deserves some additional rationalization!

How we used Hoot to construct wasm4-wireworld

The final time we talked about Hoot
(our Scheme->WASM venture)
we talked about
directly compiling Scheme to WebAssembly.
That is after all the upper stage aim of Hoot:
since Spritely’s tooling is written in
Guile Scheme,
we would like Spritely to be within the browser, and compiling Scheme packages
themselves to WebAssembly is a good way to perform that aim.

However what if we needed to play with WebAssembly on a decrease stage?
It seems Hoot is a good alternative for this: since Hoot makes use of Guile’s
compiler tower
and has a number of steps of transformation, the decrease stage assembler,
disassembler, and so forth instruments of WebAssembly are additionally accessible to the
impressed Guile hacker.

Although Hoot might be an entire Scheme→WebAssembly compiler… it
already accommodates a nascent highly effective and normal WebAssembly toolkit!


Getting Technical: Making a minimal WASM-4 cart utilizing Hoot

WASM-4 is deliberately minimal and sparse.
No rubbish collector extension, and no room for increased stage constructs.
You have received 64kb of reminiscence, 4 colours, and particular person directions rely.

WASM-4 runs totally compiled WebAssembly packages referred to as “carts”.
A really minimal WASM-4 cart (in reality a reduce down model of WASM-4’s
“hiya world” cart) might be outlined like so:

(outline $smiley$ '(i32.const #x19a0))   (outline smiley-data
  #vu8(#b11000011             #b10000001             #b00100100             #b00100100             #b00000000
       #b00100100
       #b10011001
       #b11000011))

(outline (our-game)
  `(module
        (import "env" "blit" (func $blit (param i32 i32 i32 i32 i32 i32)))

        (information ,$smiley$ ,smiley-data)

    (func (export "replace")
      (name $blit ,$smiley$
            (i32.const 76) (i32.const 76)              (i32.const 8) (i32.const 8)                (i32.const 0)))))              

Right here we’re utilizing a particular lisp/scheme function referred to as “quasiquote”.
Quasiquote is enabled with the “back-tick” character showing earlier than
module, switching into read-only information.
On this manner, inside our-game, within the locations we’ve written
,$smiley$ we are literally substituting within the $smiley$ definition
from the highest of this system, and the place we are saying ,smiley-data we’re
inserting the bytevector describing the smiley sprite.
This seems to be a robust templating system for producing
WebAssembly, and we are going to use it in better element later.

Now for instance we need to do that cart.
Since we’re in a lisp and lisp tradition encourages “reside growth”,
let’s make a helper utility to compile and run our recreation:

(use-modules (wasm assemble))

(outline* (try-game #:non-obligatory (recreation (our-game)))
  (call-with-output-file "our-game.wasm"
    (lambda (op)
      (put-bytevector op (wat->wasm recreation))))
  (system* "wasm4" "our-game.wasm"))

Now giving the sport a strive is so simple as working try-game on the REPL:

scheme@(guile-user)> (try-game)

Here is a model of the cart we simply constructed above working reside:

Programmatically producing WebAssembly

Sadly, WebAssembly may be very verbose.
Take into account how messy our “replace” process was already trying:

See Also

  `(func (export "replace")
     (name $blit ,$smiley$
           (i32.const 76) (i32.const 76)             (i32.const 8) (i32.const 8)               (i32.const 0)))                

That is very noisy! All of these i32.const operations are
getting in the best way of us putting our smiley and defining its width
and peak.
To not point out that the ultimate argument defines varied flags specified
by particular person bits that are very onerous to recollect.
If we had been writing many of those, this might get very onerous to learn certainly.
Time for an abstraction!

(outline (maybe-i32.const x)
  (if (quantity? x)
      `(i32.const ,x)
      x))

(outline* (call-blit sprite-ptr x y width peak
                    #:key 2bpp? rotate? flip-x? flip-y?)
  (outline flags
    (logior #b0                                        (if 2bpp?   #b00000001 #b0)                (if flip-x? #b00000010 #b0)                (if flip-y? #b00000100 #b0)                (if rotate? #b00001000 #b0)))    `(name $blit ,sprite-ptr
         ,(maybe-i32.const x) ,(maybe-i32.const y)
         ,(maybe-i32.const width) ,(maybe-i32.const peak)
         ,(maybe-i32.const flags)))

Now we will replace our replace process to rotate and place
the smiley a number of occasions:

(func (export "replace")
  ,(call-blit $smiley$ 76 76 8 8)
  ,(call-blit $smiley$ 42 42 8 8
              #:flip-y? #t #:rotate? #t))

Regardless of now blitting the smiley twice, and rotating and flipping it
on the second model, that is now dramatically simpler to learn than
the primary model.

Scheme procedures to generate information and entire packages

Whereas we’re not translating Scheme to WebAssembly straight on this
specific utilization of Hoot, we’re utilizing Scheme to generate
WebAssembly… and meaning we’ve the complete energy of Scheme at our
fingertips!

This turned out to be massively helpful through the recreation jam. For
occasion, whereas the “smiley” sprite we confirmed earlier was outlined in 1
bit per pixel (“1BPP”) and thus might be “seen whereas squinting” in
its binary illustration:

(outline smiley-data
  #vu8(#b11000011             #b10000001             #b00100100             #b00100100             #b00000000
       #b00100100
       #b10011001
       #b11000011))

Nonetheless, for Wireworld we needed to make the most of WASM-4’s assist
for a 4-color palette to attract prettier sprites.
These are usually not readable as textual content in pure binary information in the identical manner.
Nonetheless, for the sake of quick iteration and straightforward capability to “play with”
sprite look, we needed to get again the defined-in-ascii-art
strategy.

So we did simply that. Listed below are the pinnacle and tail sprite definitions:

(outline head-text
  "
X##X
#~.#
#~~#
X##X")

(outline tail-text
  "
XXXX
X#~X
X##X
XXXX")

Right here X represents a darkish blue pixel, # is darkish purple,
~ is gentle purple, and . is off-white.
These sprites are compiled into their binary illustration from
inside Scheme itself:

scheme@(guile-user)> (text->2bpp-sprite-bv head-text)
$7 = #vu8(235 146 150 235 0)

We are able to then straight insert the binary illustration into the
meeting of this system!
(Understanding how
text->2bpp-sprite-bv
works is left as an train for the reader.)

When setting up this blogpost, we additionally needed a option to generate
a number of Wireworld WASM-4 carts which every began with an
initial world state.
As soon as once more, we needed to outline these in plaintext for quick
iteration and experimentation whereas preserving readability:

  *#                                #@
 @  ################################  *
  #                                  #
 #                                    #
  #                                  #
 #                                    #
  ######     ####### ####   ####     #
 #     #     #   #   #   # #          #
  #    #  #  #   #   ####   ###      #
 #     #  #  #   #   #  #  #          #
  #    #  #  #   #   #   # #         #
 #      ## ##  #######   ## ####      #
  #                             ######
 #                                    #
  #                                  #
 #                                    #
  #  #     #   ##   ####  #    ###   #
 #   #     #  #  #  #   # #    #  #   #
  #  #  #  # *    @ ####  #    #  #  #
 #   #  #  # @    * #  #  #    #  #   #
  #  #  #  #  #  #  #   # #    #  #  #
 #    ## ##    ##   #   # #### ###    #
  #                                  #
 #                                    #
  #                                  #
 #                                    #
  #                                  #
 *  ################################  @
  @#                                #*

Writing a textual content parser interpreted by a WASM program for studying
textual wireworld descriptions would have been an excessive amount of work
(each for us and for the spirit of an old-school fantasy console).
We took the identical approach as with the textual illustration of
sprites: our Scheme program
loaded and translated worlds
from the textual illustration to the very in-memory illustration
our WASM-4 recreation would use, then because of the ability of quasiquote,
we merely inserted the sport into the generated program.

The actually cool factor right here is that
the technology of carts is itself a process.
Producing a customized cart is so simple as:

(wat->wasm (make-wireworld-game
            #:world (load-world-file world-filename)))

Thus our make file may merely spit out customized recordsdata with customized
preliminary world states, “baking” the preliminary stage descriptions into
reminiscence:

$ make
Constructed construct/wireworld-adder.wasm
Constructed construct/wireworld-splash.wasm
Constructed construct/wireworld-intro.wasm
Constructed construct/wireworld-blank.wasm

If you happen to’ve ever heard lisp programmers speak about “packages that write
packages”, take into account this a pleasant instance!

Classes realized and meta-observations

Our largest successes had been after we started embracing the abstraction powers
offered by Hoot.
Initially we merely hand-coded utilizing WebAssembly’s textual format and
compiled such recordsdata.
As soon as we moved to the scheme-as-code-generator abstractions proven in
this text there was a marked uptick in productiveness and
correctness.
Code turned simpler to jot down and perceive and iteration turned sooner.
Instruments such because the textual representations of sprites and ranges turned
simple to straight combine.
And considerably, by embracing the “packages that write packages”
philosophy, producing the customized carts proven off on this article turned
so simple as passing within the related arguments to the process which
“baked” the carts with the related ranges.
This latter half was significantly satisfying however was merely a pure
outgrowth of the type of programming we took.

Hoot’s major aim is to get Spritely’s tooling accessible within the
browser by
directly compiling Scheme to WebAssembly.
Nonetheless it seems that Hoot’s decrease stage layers of abstractions
are highly effective tooling in their very own proper!
Sport jams are a terrific alternative nice alternative to place your tooling
to the check, and we’re delighted with the result.

Source Link

What's Your Reaction?
Excited
0
Happy
0
In Love
0
Not Sure
0
Silly
0
View Comments (0)

Leave a Reply

Your email address will not be published.

2022 Blinking Robots.
WordPress by Doejo

Scroll To Top