1
- import datatypes
2
1
import gg
3
2
import gx
4
3
import os
5
4
import rand
6
5
import time
6
+ import math.vec { Vec2 }
7
7
8
8
// constants
9
+ const font = $embed_file ('../assets/fonts/RobotoMono-Regular.ttf' )
9
10
const top_height = 100
10
11
const canvas_size = 700
11
12
const game_size = 17
@@ -14,20 +15,8 @@ const tick_rate_ms = 100
14
15
const high_score_file_path = os.join_path (os.cache_dir (), 'v' , 'examples' , 'snek' )
15
16
16
17
// types
17
- struct Vec {
18
- x int
19
- y int
20
- }
21
-
22
- fn (a Vec) + (b Vec) Vec {
23
- return Vec{a.x + b.x, a.y + b.y}
24
- }
25
-
26
- fn (a Vec) - (b Vec) Vec {
27
- return Vec{a.x - b.x, a.y - b.y}
28
- }
29
-
30
18
type HighScore = int
19
+ type Vec = Vec2 [int ]
31
20
32
21
fn (mut h HighScore) save () {
33
22
os.mkdir_all (os.dir (high_score_file_path)) or { return }
45
34
best HighScore
46
35
snake []Vec
47
36
dir Vec
48
- dir_queue datatypes.Queue[Vec]
37
+ dir_queue []Vec
49
38
food Vec
50
39
last_tick i64
51
40
}
52
41
53
42
// utility
54
43
fn (mut app App) reset_game () {
55
44
app.score = 0
56
- app.snake = [
57
- Vec{3 , 8 },
58
- Vec{2 , 8 },
59
- Vec{1 , 8 },
60
- Vec{0 , 8 },
61
- ]
45
+ app.snake = [Vec{3 , 8 }, Vec{2 , 8 }, Vec{1 , 8 }, Vec{0 , 8 }]
62
46
app.dir = Vec{1 , 0 }
63
- app.dir_queue = datatypes.Queue[Vec]{}
47
+ app.dir_queue = []
64
48
app.food = Vec{10 , 8 }
65
49
app.last_tick = time.ticks ()
66
50
}
@@ -70,35 +54,12 @@ fn (mut app App) move_food() {
70
54
x := rand.intn (game_size) or { 0 }
71
55
y := rand.intn (game_size) or { 0 }
72
56
app.food = Vec{x, y}
73
-
74
57
if app.food ! in app.snake {
75
58
return
76
59
}
77
60
}
78
61
}
79
62
80
- // events
81
- fn on_keydown (key gg.KeyCode, mod gg.Modifier, mut app App) {
82
- dir := match key {
83
- .w, .up {
84
- Vec{0 , - 1 }
85
- }
86
- .s, .down {
87
- Vec{0 , 1 }
88
- }
89
- .a, .left {
90
- Vec{- 1 , 0 }
91
- }
92
- .d, .right {
93
- Vec{1 , 0 }
94
- }
95
- else {
96
- return
97
- }
98
- }
99
- app.dir_queue.push (dir)
100
- }
101
-
102
63
fn on_frame (mut app App) {
103
64
// check if snake bit itself
104
65
if app.snake[0 ] in app.snake[1 ..] {
@@ -110,10 +71,10 @@ fn on_frame(mut app App) {
110
71
|| app.snake[0 ].y > = game_size {
111
72
app.reset_game ()
112
73
}
113
-
114
74
progress := f32_min (1 , f32 (time.ticks () - app.last_tick) / f32 (tick_rate_ms))
115
- app.gg.begin ()
116
75
76
+ // draw everything:
77
+ app.gg.begin ()
117
78
// draw food
118
79
app.gg.draw_rect_filled (tile_size * app.food.x, tile_size * app.food.y + top_height,
119
80
tile_size, tile_size, gx.red)
@@ -126,16 +87,14 @@ fn on_frame(mut app App) {
126
87
127
88
// draw partial head
128
89
head := app.snake[0 ]
129
- app.gg.draw_rect_filled (int (tile_size * (head.x + app.dir.x * progress)),
130
- int (tile_size * (head.y + app.dir.y * progress)) + top_height, tile_size, tile_size,
131
- gx.blue)
90
+ app.gg.draw_rect_filled (tile_size * (head.x + app.dir.x * progress), tile_size * (head.y +
91
+ app.dir.y * progress) + top_height, tile_size, tile_size, gx.blue)
132
92
133
93
// draw partial tail
134
94
tail := app.snake.last ()
135
95
tail_dir := app.snake[app.snake.len - 2 ] - tail
136
- app.gg.draw_rect_filled (int (tile_size * (tail.x + tail_dir.x * progress)),
137
- int (tile_size * (tail.y + tail_dir.y * progress)) + top_height, tile_size, tile_size,
138
- gx.blue)
96
+ app.gg.draw_rect_filled (tile_size * (tail.x + tail_dir.x * progress), tile_size * (tail.y +
97
+ tail_dir.y * progress) + top_height, tile_size, tile_size, gx.blue)
139
98
140
99
// draw score bar
141
100
app.gg.draw_rect_filled (0 , 0 , canvas_size, top_height, gx.black)
@@ -156,11 +115,8 @@ fn on_frame(mut app App) {
156
115
// "snake" along
157
116
mut prev := app.snake[0 ]
158
117
app.snake[0 ] = app.snake[0 ] + app.dir
159
-
160
118
for i in 1 .. app.snake.len {
161
- tmp := app.snake[i]
162
- app.snake[i] = prev
163
- prev = tmp
119
+ app.snake[i], prev = prev, app.snake[i]
164
120
}
165
121
166
122
// add tail segment if food has been eaten
@@ -174,7 +130,8 @@ fn on_frame(mut app App) {
174
130
app.move_food ()
175
131
}
176
132
177
- if dir := app.dir_queue.pop () {
133
+ if app.dir_queue.len > 0 {
134
+ dir := app.dir_queue.pop ()
178
135
if dir.x != - app.dir.x || dir.y != - app.dir.y {
179
136
app.dir = dir
180
137
}
@@ -186,31 +143,40 @@ fn on_frame(mut app App) {
186
143
app.gg.end ()
187
144
}
188
145
189
- const font = $embed_file ('../assets/fonts/RobotoMono-Regular.ttf' )
190
-
191
- // setup
192
- fn main () {
193
- mut app := App{}
194
- app.reset_game ()
195
- app.best.load ()
196
-
197
- mut font_copy := font
198
- font_bytes := unsafe {
199
- font_copy.data ().vbytes (font_copy.len)
146
+ // events
147
+ fn on_keydown (key gg.KeyCode, mod gg.Modifier, mut app App) {
148
+ app.dir_queue << match key {
149
+ .w, .up {
150
+ Vec{0 , - 1 }
151
+ }
152
+ .s, .down {
153
+ Vec{0 , 1 }
154
+ }
155
+ .a, .left {
156
+ Vec{- 1 , 0 }
157
+ }
158
+ .d, .right {
159
+ Vec{1 , 0 }
160
+ }
161
+ else {
162
+ return
163
+ }
200
164
}
201
-
202
- app.gg = gg.new_context (
203
- bg_color: gx.white
204
- frame_fn: on_frame
205
- keydown_fn: on_keydown
206
- user_data: & app
207
- width: canvas_size
208
- height: top_height + canvas_size
209
- create_window: true
210
- resizable: false
211
- window_title: 'snek'
212
- font_bytes_normal: font_bytes
213
- )
214
-
215
- app.gg.run ()
216
165
}
166
+
167
+ mut app := App{}
168
+ app.reset_game ()
169
+ app.best.load ()
170
+
171
+ mut font_copy := font
172
+ app.gg = gg.new_context (
173
+ bg_color: gx.white
174
+ frame_fn: on_frame
175
+ keydown_fn: on_keydown
176
+ user_data: & app
177
+ width: canvas_size
178
+ height: top_height + canvas_size
179
+ window_title: 'snek'
180
+ font_bytes_normal: unsafe { font_copy.data ().vbytes (font_copy.len) }
181
+ )
182
+ app.gg.run ()
0 commit comments