Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,17 @@
|
||||
# snek
|
||||
|
||||
Snake game implemented using `gg` module.
|
||||
|
||||
# Compiling & running
|
||||
|
||||
## Compiling to binary
|
||||
```sh
|
||||
v -prod examples/snek/snek.v
|
||||
./examples/snek/snek # run snek game!
|
||||
```
|
||||
|
||||
## Compiling to JS
|
||||
```sh
|
||||
v -b js_browser examples/snek/snek.js.v
|
||||
```
|
||||
And then open `examples/snek/index.html` in your favourite browser.
|
||||
@@ -0,0 +1,6 @@
|
||||
<body class="main">
|
||||
<title>gg</title>
|
||||
<canvas style="border: 1px solid black;" width="700" height="800" id="canvas"></canvas>
|
||||
<script type="text/javascript" src="snek.js"></script>
|
||||
|
||||
</body>
|
||||
@@ -0,0 +1,194 @@
|
||||
import gg
|
||||
// import sokol.sapp
|
||||
import time
|
||||
import rand
|
||||
|
||||
// constants
|
||||
const top_height = 100
|
||||
const canvas_size = 700
|
||||
const game_size = 17
|
||||
const tile_size = canvas_size / game_size
|
||||
const tick_rate_ms = 100
|
||||
|
||||
// types
|
||||
struct Pos {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
fn (a Pos) + (b Pos) Pos {
|
||||
return Pos{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
|
||||
}
|
||||
|
||||
struct App {
|
||||
mut:
|
||||
gg &gg.Context = unsafe { nil }
|
||||
score int
|
||||
snake []Pos
|
||||
dir Direction
|
||||
last_dir Direction
|
||||
food Pos
|
||||
start_time i64
|
||||
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},
|
||||
]
|
||||
app.dir = .right
|
||||
app.last_dir = app.dir
|
||||
app.food = Pos{10, 8}
|
||||
app.start_time = time.ticks()
|
||||
app.last_tick = time.ticks()
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
if app.food !in app.snake {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// events
|
||||
fn on_keydown(key gg.KeyCode, _ gg.Modifier, mut app App) {
|
||||
match key {
|
||||
.w, .up {
|
||||
if app.last_dir != .down {
|
||||
app.dir = .up
|
||||
}
|
||||
}
|
||||
.s, .down {
|
||||
if app.last_dir != .up {
|
||||
app.dir = .down
|
||||
}
|
||||
}
|
||||
.a, .left {
|
||||
if app.last_dir != .right {
|
||||
app.dir = .left
|
||||
}
|
||||
}
|
||||
.d, .right {
|
||||
if app.last_dir != .left {
|
||||
app.dir = .right
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_frame(mut app App) {
|
||||
app.gg.begin()
|
||||
|
||||
now := time.ticks()
|
||||
|
||||
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 {
|
||||
app.gg.draw_rect(tile_size * pos.x, tile_size * pos.y + top_height, tile_size, tile_size,
|
||||
gg.blue)
|
||||
}
|
||||
|
||||
// drawing food
|
||||
app.gg.draw_rect(tile_size * app.food.x, tile_size * app.food.y + top_height, tile_size,
|
||||
tile_size, gg.red)
|
||||
|
||||
// drawing top
|
||||
app.gg.draw_rect(0, 0, canvas_size, top_height, gg.black)
|
||||
app.gg.draw_text(350, top_height / 2, 'Score: ${app.score}', gg.TextCfg{
|
||||
color: gg.white
|
||||
align: .center
|
||||
vertical_align: .middle
|
||||
size: 80
|
||||
})
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
app.gg.end()
|
||||
}
|
||||
|
||||
// setup
|
||||
fn main() {
|
||||
mut app := App{
|
||||
gg: &gg.Context{}
|
||||
}
|
||||
app.reset_game()
|
||||
|
||||
app.gg = gg.new_context(
|
||||
bg_color: gg.white
|
||||
frame_fn: on_frame
|
||||
keydown_fn: on_keydown
|
||||
user_data: &app
|
||||
width: canvas_size
|
||||
height: top_height + canvas_size
|
||||
create_window: true
|
||||
resizable: false
|
||||
window_title: 'snek'
|
||||
html5_canvas_name: 'canvas'
|
||||
)
|
||||
|
||||
app.gg.run()
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
import gg
|
||||
import os
|
||||
import rand
|
||||
import time
|
||||
import math.vec { Vec2 }
|
||||
|
||||
// constants
|
||||
const font = $embed_file('../assets/fonts/RobotoMono-Regular.ttf')
|
||||
const top_height = 100
|
||||
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
|
||||
type HighScore = int
|
||||
type Vec = Vec2[int]
|
||||
|
||||
fn (mut h HighScore) save() {
|
||||
os.mkdir_all(os.dir(high_score_file_path)) or { return }
|
||||
os.write_file(high_score_file_path, (*h).str()) or { return }
|
||||
}
|
||||
|
||||
fn (mut h HighScore) load() {
|
||||
h = (os.read_file(high_score_file_path) or { '' }).int()
|
||||
}
|
||||
|
||||
struct App {
|
||||
mut:
|
||||
gg &gg.Context = unsafe { nil }
|
||||
score int
|
||||
best HighScore
|
||||
snake []Vec
|
||||
dir Vec
|
||||
dir_queue []Vec
|
||||
food Vec
|
||||
last_tick i64
|
||||
}
|
||||
|
||||
// utility
|
||||
fn (mut app App) reset_game() {
|
||||
app.score = 0
|
||||
app.snake = [Vec{3, 8}, Vec{2, 8}, Vec{1, 8}, Vec{0, 8}]
|
||||
app.dir = Vec{1, 0}
|
||||
app.dir_queue = []
|
||||
app.food = Vec{10, 8}
|
||||
app.last_tick = time.ticks()
|
||||
}
|
||||
|
||||
fn (mut app App) move_food() {
|
||||
for {
|
||||
x := rand.intn(game_size) or { 0 }
|
||||
y := rand.intn(game_size) or { 0 }
|
||||
app.food = Vec{x, y}
|
||||
if app.food !in app.snake {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
// draw everything:
|
||||
app.gg.begin()
|
||||
// draw food
|
||||
app.gg.draw_rect_filled(tile_size * app.food.x, tile_size * app.food.y + top_height, tile_size,
|
||||
tile_size, gg.red)
|
||||
|
||||
// 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, gg.blue)
|
||||
}
|
||||
|
||||
// draw partial head
|
||||
head := app.snake[0]
|
||||
app.gg.draw_rect_filled(tile_size * (head.x + app.dir.x * progress),
|
||||
|
||||
tile_size * (head.y + app.dir.y * progress) + top_height, tile_size, tile_size, gg.blue)
|
||||
|
||||
// draw partial tail
|
||||
tail := app.snake.last()
|
||||
tail_dir := app.snake[app.snake.len - 2] - tail
|
||||
app.gg.draw_rect_filled(tile_size * (tail.x + tail_dir.x * progress),
|
||||
|
||||
tile_size * (tail.y + tail_dir.y * progress) + top_height, tile_size, tile_size, gg.blue)
|
||||
|
||||
// draw score bar
|
||||
app.gg.draw_rect_filled(0, 0, canvas_size, top_height, gg.black)
|
||||
app.gg.draw_text(150, top_height / 2, 'Score: ${app.score}', gg.TextCfg{
|
||||
color: gg.white
|
||||
align: .center
|
||||
vertical_align: .middle
|
||||
size: 65
|
||||
})
|
||||
app.gg.draw_text(canvas_size - 150, top_height / 2, 'Best: ${app.best}', gg.TextCfg{
|
||||
color: gg.white
|
||||
align: .center
|
||||
vertical_align: .middle
|
||||
size: 65
|
||||
})
|
||||
|
||||
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 {
|
||||
app.snake[i], prev = prev, app.snake[i]
|
||||
}
|
||||
|
||||
// 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 app.dir_queue.len > 0 {
|
||||
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()
|
||||
}
|
||||
|
||||
// events
|
||||
fn on_keydown(key gg.KeyCode, _mod gg.Modifier, mut app App) {
|
||||
app.dir_queue << match key {
|
||||
.w, .up {
|
||||
Vec{0, -1}
|
||||
}
|
||||
.s, .down {
|
||||
Vec{0, 1}
|
||||
}
|
||||
.a, .left {
|
||||
Vec{-1, 0}
|
||||
}
|
||||
.d, .right {
|
||||
Vec{1, 0}
|
||||
}
|
||||
else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mut app := App{}
|
||||
app.reset_game()
|
||||
app.best.load()
|
||||
|
||||
mut font_copy := font
|
||||
app.gg = gg.new_context(
|
||||
bg_color: gg.white
|
||||
frame_fn: on_frame
|
||||
keydown_fn: on_keydown
|
||||
user_data: &app
|
||||
width: canvas_size
|
||||
height: top_height + canvas_size
|
||||
window_title: 'snek'
|
||||
font_bytes_normal: unsafe { font_copy.data().vbytes(font_copy.len) }
|
||||
)
|
||||
app.gg.run()
|
||||
Reference in new issue
Block a user