Skip to content

Instantly share code, notes, and snippets.

@pJotoro
Last active January 13, 2022 10:38
Show Gist options
  • Save pJotoro/296e59d9721df2cf7d2d353390598592 to your computer and use it in GitHub Desktop.
Save pJotoro/296e59d9721df2cf7d2d353390598592 to your computer and use it in GitHub Desktop.
snake demo example for wasm4
// Thanks to Jeroen for giving me his random algorithm code!
package main
import "w4"
Input :: struct #packed {
gamepads: [4]w4.Buttons,
mouse: struct #packed {
pos: [2]i16,
buttons: w4.Mouse_Buttons,
},
}
#assert(size_of(Input) == 4 + 2 + 2 + 1)
input := [2]Input{}
frames_between_new_input: u32
/*
Borrowed from `core:math/rand`
*/
Rand :: struct {
state: u64,
inc: u64,
}
global_rand: Rand
smiley := [8]u8 {
0b11000011,
0b10000001,
0b00100100,
0b00100100,
0b00000000,
0b00100100,
0b10011001,
0b11000011,
}
TILE_SIZE :: 8
ROOM_SIZE :: 20
Vector2 :: distinct [2]int
fruit: Vector2
tail: [ROOM_SIZE*ROOM_SIZE]Vector2
tail_size: uint
direction: Vector2
position: Vector2
@export
start :: proc "c" () {
rand_init(42)
frames_between_new_input = 1 // Used in randomness salt multiplication.
w4.PALETTE[0] = 0xfff6d3
w4.PALETTE[1] = 0xf9a875
w4.PALETTE[2] = 0xeb6b6f
w4.PALETTE[3] = 0x7c3f58
position.x = int(rand_axis())
position.y = int(rand_axis())
direction.x = 1
fruit.x = 10
fruit.y = 10
}
move_delay: uint
previous_gamepad: w4.Buttons
@export
update :: proc "c" () {
/*
Grab this frame's input and process into randomness pool.
*/
update_input_and_randomness_pool()
defer update_input_and_randomness_pool(false)
gamepad := w4.GAMEPAD1^
// Only the buttons that were pressed down this frame
pressed_this_frame := gamepad & (gamepad ~ previous_gamepad)
previous_gamepad = gamepad
if .RIGHT in pressed_this_frame {
if direction.x == -1 && tail_size > 0 do end_game()
direction.x = 1
direction.y = 0
}
if .LEFT in pressed_this_frame {
if direction.x == 1 && tail_size > 0 do end_game()
direction.x = -1
direction.y = 0
}
if .DOWN in pressed_this_frame {
if direction.y == -1 && tail_size > 0 do end_game()
direction.y = 1
direction.x = 0
}
if .UP in pressed_this_frame {
if direction.y == 1 && tail_size > 0 do end_game()
direction.y = -1
direction.x = 0
}
if position == fruit {
if tail_size == 0 {
tail[0] = position - direction
}
else {
tail[tail_size] = tail[tail_size-1] - direction
}
tail_size += 1
fruit_change_position()
}
if move_delay == 0 {
move_delay = 7
for i := len(tail) - 1; i >= 0; i -= 1 {
if i != 0 do tail[i] = tail[i-1]
else do tail[i] = position
}
position += direction
}
move_delay -= 1
if position.x >= ROOM_SIZE || position.y >= ROOM_SIZE || position.x < 0 || position.y < 0 do end_game()
for t, i in tail {
if t == position && uint(i) < tail_size {
end_game()
break
}
if t == fruit do fruit_change_position()
}
w4.blit(&smiley[0], i32(position.x) * TILE_SIZE, i32(position.y) * TILE_SIZE, 8, 8)
for i in 0..<tail_size {
w4.blit(&smiley[0], i32(tail[i].x) * TILE_SIZE, i32(tail[i].y) * TILE_SIZE, 8, 8)
}
w4.blit(&smiley[0], i32(fruit.x) * TILE_SIZE, i32(fruit.y) * TILE_SIZE, 8, 8)
}
end_game :: proc "c" () {
position.x = int(rand_axis())
position.y = int(rand_axis())
direction = Vector2{1, 0}
tail_size = 0
tail = {}
fruit_change_position()
}
fruit_change_position :: proc "c" () {
fruit.x = int(rand_axis())
fruit.y = int(rand_axis())
}
update_input_and_randomness_pool :: proc "contextless" (start := true) {
if start {
/*
Grab new input and mix into randomness state.
*/
input[0] = (^Input)(uintptr(0x16))^
if input[0] == input[1] {
// No new input
frames_between_new_input += 1
} else {
/*
Mix input into randomness. Use FNV-64 to hash input.
*/
input_bytes := transmute([size_of(Input)]u8)input[0]
input_hash := fnv64(input_bytes[:])
/*
Grab 64 bits of randomness from current stream, add frames between input.
*/
salt := u64(rand_u32()) << 32 | u64(rand_u32())
salt += u64(frames_between_new_input)
seed := salt * input_hash
rand_init(seed)
frames_between_new_input = 1
}
} else {
input[1] = input[0]
}
}
/*
Borrowed from `core:math/rand` + `core:hash`.
*/
rand_init :: proc "contextless" (seed: u64) {
global_rand.state = 0
global_rand.inc = (seed << 1) | 1
rand_u32()
global_rand.state += seed
rand_u32()
}
rand_u32 :: proc "contextless" () -> u32 {
old_state := global_rand.state
global_rand.state = old_state * 6364136223846793005 + (global_rand.inc|1)
xor_shifted := u32(((old_state>>18) ~ old_state) >> 27)
rot := u32(old_state >> 59)
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 31))
}
fnv64 :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
h: u64 = seed
for b in data {
h = (h * 0x100000001b3) ~ u64(b)
}
return h
}
COORD_MAX :: 20
COORD_MASK :: 31
rand_axis :: proc "contextless" () -> (axis: u32) {
for {
axis = rand_u32() & COORD_MASK
if axis < COORD_MAX do return
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment