Last active
January 13, 2022 10:38
-
-
Save pJotoro/296e59d9721df2cf7d2d353390598592 to your computer and use it in GitHub Desktop.
snake demo example for wasm4
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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