diff --git a/examples/snek/snek.v b/examples/snek/snek.v index 8779df0974..371f0bf3c5 100644 --- a/examples/snek/snek.v +++ b/examples/snek/snek.v @@ -1,9 +1,9 @@ -import os +import datatypes import gg import gx -// import sokol.sapp -import time +import os import rand +import time // constants const top_height = 100 @@ -11,28 +11,20 @@ const canvas_size = 700 const game_size = 17 const tile_size = canvas_size / game_size const tick_rate_ms = 100 - const high_score_file_path = os.join_path(os.cache_dir(), 'v', 'examples', 'snek') // types -struct Pos { +struct Vec { x int y int } -fn (a Pos) + (b Pos) Pos { - return Pos{a.x + b.x, a.y + b.y} +fn (a Vec) + (b Vec) Vec { + return Vec{a.x + b.x, a.y + b.y} } -fn (a Pos) - (b Pos) Pos { - return Pos{a.x - b.x, a.y - b.y} -} - -enum Direction { - up - down - left - right +fn (a Vec) - (b Vec) Vec { + return Vec{a.x - b.x, a.y - b.y} } type HighScore = int @@ -48,30 +40,28 @@ fn (mut h HighScore) load() { struct App { mut: - gg &gg.Context = unsafe { nil } - score int - best HighScore - snake []Pos - dir Direction - last_dir Direction - food Pos - start_time i64 - last_tick i64 + gg &gg.Context = unsafe { nil } + score int + best HighScore + snake []Vec + dir Vec + dir_queue datatypes.Queue[Vec] + food Vec + last_tick i64 } // utility fn (mut app App) reset_game() { app.score = 0 app.snake = [ - Pos{3, 8}, - Pos{2, 8}, - Pos{1, 8}, - Pos{0, 8}, + Vec{3, 8}, + Vec{2, 8}, + Vec{1, 8}, + Vec{0, 8}, ] - app.dir = .right - app.last_dir = app.dir - app.food = Pos{10, 8} - app.start_time = time.ticks() + app.dir = Vec{1, 0} + app.dir_queue = datatypes.Queue[Vec]{} + app.food = Vec{10, 8} app.last_tick = time.ticks() } @@ -79,7 +69,7 @@ fn (mut app App) move_food() { for { x := rand.intn(game_size) or { 0 } y := rand.intn(game_size) or { 0 } - app.food = Pos{x, y} + app.food = Vec{x, y} if app.food !in app.snake { return @@ -89,81 +79,65 @@ fn (mut app App) move_food() { // events fn on_keydown(key gg.KeyCode, mod gg.Modifier, mut app App) { - match key { + dir := match key { .w, .up { - if app.last_dir != .down { - app.dir = .up - } + Vec{0, -1} } .s, .down { - if app.last_dir != .up { - app.dir = .down - } + Vec{0, 1} } .a, .left { - if app.last_dir != .right { - app.dir = .left - } + Vec{-1, 0} } .d, .right { - if app.last_dir != .left { - app.dir = .right - } + Vec{1, 0} + } + else { + return } - else {} } + app.dir_queue.push(dir) } fn on_frame(mut app App) { + // check if snake bit itself + if app.snake[0] in app.snake[1..] { + app.reset_game() + } + + // check if snake hit a wall + if app.snake[0].x < 0 || app.snake[0].x >= game_size || app.snake[0].y < 0 + || app.snake[0].y >= game_size { + app.reset_game() + } + + progress := f32_min(1, f32(time.ticks() - app.last_tick) / f32(tick_rate_ms)) app.gg.begin() - now := time.ticks() + // draw food + app.gg.draw_rect_filled(tile_size * app.food.x, tile_size * app.food.y + top_height, + tile_size, tile_size, gx.red) - if now - app.last_tick >= tick_rate_ms { - app.last_tick = now - - // finding delta direction - delta_dir := match app.dir { - .up { Pos{0, -1} } - .down { Pos{0, 1} } - .left { Pos{-1, 0} } - .right { Pos{1, 0} } - } - - // "snaking" along - mut prev := app.snake[0] - app.snake[0] = app.snake[0] + delta_dir - - for i in 1 .. app.snake.len { - tmp := app.snake[i] - app.snake[i] = prev - prev = tmp - } - - // adding last segment - if app.snake[0] == app.food { - app.move_food() - app.score++ - if app.score > app.best { - app.best = app.score - app.best.save() - } - app.snake << app.snake.last() + app.snake.last() - app.snake[app.snake.len - 2] - } - - app.last_dir = app.dir - } - // drawing snake - for pos in app.snake { + // draw snake + for pos in app.snake[..app.snake.len - 1] { app.gg.draw_rect_filled(tile_size * pos.x, tile_size * pos.y + top_height, tile_size, tile_size, gx.blue) } - // drawing food - app.gg.draw_rect_filled(tile_size * app.food.x, tile_size * app.food.y + top_height, - tile_size, tile_size, gx.red) + // draw partial head + head := app.snake[0] + app.gg.draw_rect_filled(int(tile_size * (head.x + app.dir.x * progress)), + int(tile_size * (head.y + app.dir.y * progress)) + top_height, tile_size, tile_size, + gx.blue) - // drawing top + // draw partial tail + tail := app.snake.last() + tail_dir := app.snake[app.snake.len - 2] - tail + app.gg.draw_rect_filled(int(tile_size * (tail.x + tail_dir.x * progress)), + int(tile_size * (tail.y + tail_dir.y * progress)) + top_height, tile_size, tile_size, + gx.blue) + + // draw score bar app.gg.draw_rect_filled(0, 0, canvas_size, top_height, gx.black) app.gg.draw_text(150, top_height / 2, 'Score: ${app.score}', gx.TextCfg{ color: gx.white @@ -178,14 +152,35 @@ fn on_frame(mut app App) { size: 65 }) - // checking if snake bit itself - if app.snake[0] in app.snake[1..] { - app.reset_game() - } - // checking if snake hit a wall - if app.snake[0].x < 0 || app.snake[0].x >= game_size || app.snake[0].y < 0 - || app.snake[0].y >= game_size { - app.reset_game() + if progress == 1 { + // "snake" along + mut prev := app.snake[0] + app.snake[0] = app.snake[0] + app.dir + + for i in 1 .. app.snake.len { + tmp := app.snake[i] + app.snake[i] = prev + prev = tmp + } + + // add tail segment if food has been eaten + if app.snake[0] == app.food { + app.score++ + if app.score > app.best { + app.best = app.score + app.best.save() + } + app.snake << app.snake.last() + app.snake.last() - app.snake[app.snake.len - 2] + app.move_food() + } + + if dir := app.dir_queue.pop() { + if dir.x != -app.dir.x || dir.y != -app.dir.y { + app.dir = dir + } + } + + app.last_tick = time.ticks() } app.gg.end()