Skip to main content

Placing the Fruit

A freely moving snake is nice. But it get's a bit dull if that's all there is. To make it a bit more of a challenge, you'd need to add something to change the snake. The classic approach is to let the snake "eat" fruits. That's a good place to start.

To place (and eat) a fruit, you first need to make a variable for this. Since it's simply a point on the grid, a point will do.
;; snake.direction   = 0x19a0;; snake.body_length = 0x19a8;; snake.body        = 0x19ac;; frame_count       = 0x262c;; fruit             = 0x2630

Random Numbers#

The WebAssembly text format doesn't provide any random number generator, so we'll have to write our own. Xorshift is a reasonable choice.

;; Initialize the random state to 1234.(global $random-state (mut i32) (i32.const 1234))
(func $rnd (param $n i32) (result i32)  (local $x i32)
  ;; x = random-state  ;; x ^= x << 13  ;; x ^= x >> 17  ;; x ^= x << 5  ;; random-state = x  (global.set $random-state    (local.tee $x      (i32.xor        (local.tee $x          (i32.xor            (local.tee $x              (i32.xor                (local.tee $x (global.get $random-state))                (i32.shl                  (local.get $x)                  (i32.const 13))))            (i32.shr_u              (local.get $x)              (i32.const 17))))        (i32.shl          (local.get $x)          (i32.const 5)))))
  ;; convert a random i32 in the range [0, 2**32) to a random f32 in the range  ;; [0, 1). Then multiply by `n` to convert it to a f32 in the range [0, n).  ;; Finally convert it back to an i32.  (i32.trunc_f32_u    (f32.mul      (f32.mul        (f32.convert_i32_u (i32.shr_u (local.get $x) (i32.const 8)))        (f32.const 0x1p-24))      (f32.convert_i32_u        (local.get $n)))))

This allows you to call rnd(20) to get a number between 0 and 19. Now you can randomly initialize the fruit position:

(func (export "start")  (i32.store (global.get $PALETTE0) (i32.const 0xfbf7f3))  (i32.store (global.get $PALETTE1) (i32.const 0xe5b083))  (i32.store (global.get $PALETTE2) (i32.const 0x426e5d))  (i32.store (global.get $PALETTE3) (i32.const 0x20283d))
  ;; fruit.x = rnd(20)  (i32.store (i32.const 0x2630) (call $rnd (i32.const 20)))
  ;; fruit.y = rnd(20)  (i32.store (i32.const 0x2634) (call $rnd (i32.const 20))))

Importing PNG Files#

Importing images in WASM-4 works a bit different compared to other game engines and Fantasy Consoles. Images have to meet certain criteria:

  • PNG only
  • Index only
  • 4 colors max

Indexed PNG files can be created by several image apps like Aseprite or GIMP.

The image we import is a 8x8 PNG file with exactly 4 colors:

Zoomed Fruit This image is zoomed by 800%.

Zoomed Fruit This is the original image. You can download it to proceed.

Now you need to import the image. For this, the WASM-4 CLI tool w4 comes with another tool: png2src. You can use it like this:

w4 png2src --wat fruit.png

This will output the following content in the terminal:

;; fruit;; fruit_width = 8;;; fruit_height = 8;;; fruit_flags = 1; // BLIT_2BPP
(data  (i32.const ???)  "\00\a0\02\00\0e\f0\36\5c\d6\57\d5\57\35\5c\0f\f0")

To get it into a an existing file, use the >> operator. Like this:

w4 png2src --wat fruit.png >> main.wat

This will add the previous lines to your main.wat and causes an error because it doesn't know where to put the data. Let's put it at 0x2638, after the fruit position:

(data  (i32.const 0x2638)  "\00\a0\02\00\0e\f0\36\5c\d6\57\d5\57\35\5c\0f\f0")

With that out of the way, it's time to actually render the newly imported sprite.

Rendering a PNG File#

Rendering the sprite is rather simple. Just call the blit function of w4:

;; Copies pixels to the framebuffer.(import "env" "blit" (func $blit (param i32 i32 i32 i32 i32 i32)))

In practice it looks like this:

(func (export "update")  ...
  ;; Draw fruit.  (call $blit    (i32.const 0x2638)    (i32.mul (i32.load (i32.const 0x2630)) (i32.const 8))    (i32.mul (i32.load (i32.const 0x2634)) (i32.const 8))    (i32.const 8)    (i32.const 8)    (i32.const 1)))

But since you set the drawing colors, you need to change the drawing colors too:

(func (export "update")  ...
  ;; Set fruit colors.  (i32.store16 (global.get $DRAW_COLORS) (i32.const 0x4320))
  ;; Draw fruit.  (call $blit    (i32.const 0x2638)    (i32.mul (i32.load (i32.const 0x2630)) (i32.const 8))    (i32.mul (i32.load (i32.const 0x2634)) (i32.const 8))    (i32.const 8)    (i32.const 8)    (i32.const 1)))

This way, w4 uses the color palette in it's default configuration. Except for one thing: The background will be transparent.