1
- import os
1
+ import datatypes
2
2
import gg
3
3
import gx
4
- // import sokol.sapp
5
- import time
4
+ import os
6
5
import rand
6
+ import time
7
7
8
8
// constants
9
9
const top_height = 100
10
10
const canvas_size = 700
11
11
const game_size = 17
12
12
const tile_size = canvas_size / game_size
13
13
const tick_rate_ms = 100
14
-
15
14
const high_score_file_path = os.join_path (os.cache_dir (), 'v' , 'examples' , 'snek' )
16
15
17
16
// types
18
- struct Pos {
17
+ struct Vec {
19
18
x int
20
19
y int
21
20
}
22
21
23
- fn (a Pos ) + (b Pos) Pos {
24
- return Pos {a.x + b.x, a.y + b.y}
22
+ fn (a Vec ) + (b Vec) Vec {
23
+ return Vec {a.x + b.x, a.y + b.y}
25
24
}
26
25
27
- fn (a Pos) - (b Pos) Pos {
28
- return Pos{a.x - b.x, a.y - b.y}
29
- }
30
-
31
- enum Direction {
32
- up
33
- down
34
- left
35
- right
26
+ fn (a Vec) - (b Vec) Vec {
27
+ return Vec{a.x - b.x, a.y - b.y}
36
28
}
37
29
38
30
type HighScore = int
@@ -48,38 +40,36 @@ fn (mut h HighScore) load() {
48
40
49
41
struct App {
50
42
mut :
51
- gg & gg.Context = unsafe { nil }
52
- score int
53
- best HighScore
54
- snake []Pos
55
- dir Direction
56
- last_dir Direction
57
- food Pos
58
- start_time i64
59
- last_tick i64
43
+ gg & gg.Context = unsafe { nil }
44
+ score int
45
+ best HighScore
46
+ snake []Vec
47
+ dir Vec
48
+ dir_queue datatypes.Queue[Vec]
49
+ food Vec
50
+ last_tick i64
60
51
}
61
52
62
53
// utility
63
54
fn (mut app App) reset_game () {
64
55
app.score = 0
65
56
app.snake = [
66
- Pos {3 , 8 },
67
- Pos {2 , 8 },
68
- Pos {1 , 8 },
69
- Pos {0 , 8 },
57
+ Vec {3 , 8 },
58
+ Vec {2 , 8 },
59
+ Vec {1 , 8 },
60
+ Vec {0 , 8 },
70
61
]
71
- app.dir = .right
72
- app.last_dir = app.dir
73
- app.food = Pos{10 , 8 }
74
- app.start_time = time.ticks ()
62
+ app.dir = Vec{1 , 0 }
63
+ app.dir_queue = datatypes.Queue[Vec]{}
64
+ app.food = Vec{10 , 8 }
75
65
app.last_tick = time.ticks ()
76
66
}
77
67
78
68
fn (mut app App) move_food () {
79
69
for {
80
70
x := rand.intn (game_size) or { 0 }
81
71
y := rand.intn (game_size) or { 0 }
82
- app.food = Pos {x, y}
72
+ app.food = Vec {x, y}
83
73
84
74
if app.food ! in app.snake {
85
75
return
@@ -89,81 +79,65 @@ fn (mut app App) move_food() {
89
79
90
80
// events
91
81
fn on_keydown (key gg.KeyCode, mod gg.Modifier, mut app App) {
92
- match key {
82
+ dir := match key {
93
83
.w, .up {
94
- if app.last_dir != .down {
95
- app.dir = .up
96
- }
84
+ Vec{0 , - 1 }
97
85
}
98
86
.s, .down {
99
- if app.last_dir != .up {
100
- app.dir = .down
101
- }
87
+ Vec{0 , 1 }
102
88
}
103
89
.a, .left {
104
- if app.last_dir != .right {
105
- app.dir = .left
106
- }
90
+ Vec{- 1 , 0 }
107
91
}
108
92
.d, .right {
109
- if app.last_dir != .left {
110
- app.dir = .right
111
- }
93
+ Vec{1 , 0 }
94
+ }
95
+ else {
96
+ return
112
97
}
113
- else {}
114
98
}
99
+ app.dir_queue.push (dir)
115
100
}
116
101
117
102
fn on_frame (mut app App) {
118
- app.gg.begin ()
119
-
120
- now := time.ticks ()
121
-
122
- if now - app.last_tick > = tick_rate_ms {
123
- app.last_tick = now
124
-
125
- // finding delta direction
126
- delta_dir := match app.dir {
127
- .up { Pos{0 , - 1 } }
128
- .down { Pos{0 , 1 } }
129
- .left { Pos{- 1 , 0 } }
130
- .right { Pos{1 , 0 } }
131
- }
103
+ // check if snake bit itself
104
+ if app.snake[0 ] in app.snake[1 ..] {
105
+ app.reset_game ()
106
+ }
132
107
133
- // "snaking" along
134
- mut prev := app.snake[0 ]
135
- app.snake[0 ] = app.snake[0 ] + delta_dir
108
+ // check if snake hit a wall
109
+ if app.snake[0 ].x < 0 || app.snake[0 ].x > = game_size || app.snake[0 ].y < 0
110
+ || app.snake[0 ].y > = game_size {
111
+ app.reset_game ()
112
+ }
136
113
137
- for i in 1 .. app.snake.len {
138
- tmp := app.snake[i]
139
- app.snake[i] = prev
140
- prev = tmp
141
- }
114
+ progress := f32_min (1 , f32 (time.ticks () - app.last_tick) / f32 (tick_rate_ms))
115
+ app.gg.begin ()
142
116
143
- // adding last segment
144
- if app.snake[0 ] == app.food {
145
- app.move_food ()
146
- app.score++
147
- if app.score > app.best {
148
- app.best = app.score
149
- app.best.save ()
150
- }
151
- app.snake << app.snake.last () + app.snake.last () - app.snake[app.snake.len - 2 ]
152
- }
117
+ // draw food
118
+ app.gg.draw_rect_filled (tile_size * app.food.x, tile_size * app.food.y + top_height,
119
+ tile_size, tile_size, gx.red)
153
120
154
- app.last_dir = app.dir
155
- }
156
- // drawing snake
157
- for pos in app.snake {
121
+ // draw snake
122
+ for pos in app.snake[..app.snake.len - 1 ] {
158
123
app.gg.draw_rect_filled (tile_size * pos.x, tile_size * pos.y + top_height, tile_size,
159
124
tile_size, gx.blue)
160
125
}
161
126
162
- // drawing food
163
- app.gg.draw_rect_filled (tile_size * app.food.x, tile_size * app.food.y + top_height,
164
- tile_size, tile_size, gx.red)
127
+ // draw partial head
128
+ 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)
132
+
133
+ // draw partial tail
134
+ tail := app.snake.last ()
135
+ 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)
165
139
166
- // drawing top
140
+ // draw score bar
167
141
app.gg.draw_rect_filled (0 , 0 , canvas_size, top_height, gx.black)
168
142
app.gg.draw_text (150 , top_height / 2 , 'Score: ${app.score} ' , gx.TextCfg{
169
143
color: gx.white
@@ -178,14 +152,35 @@ fn on_frame(mut app App) {
178
152
size: 65
179
153
})
180
154
181
- // checking if snake bit itself
182
- if app.snake[0 ] in app.snake[1 ..] {
183
- app.reset_game ()
184
- }
185
- // checking if snake hit a wall
186
- if app.snake[0 ].x < 0 || app.snake[0 ].x > = game_size || app.snake[0 ].y < 0
187
- || app.snake[0 ].y > = game_size {
188
- app.reset_game ()
155
+ if progress == 1 {
156
+ // "snake" along
157
+ mut prev := app.snake[0 ]
158
+ app.snake[0 ] = app.snake[0 ] + app.dir
159
+
160
+ for i in 1 .. app.snake.len {
161
+ tmp := app.snake[i]
162
+ app.snake[i] = prev
163
+ prev = tmp
164
+ }
165
+
166
+ // add tail segment if food has been eaten
167
+ if app.snake[0 ] == app.food {
168
+ app.score++
169
+ if app.score > app.best {
170
+ app.best = app.score
171
+ app.best.save ()
172
+ }
173
+ app.snake << app.snake.last () + app.snake.last () - app.snake[app.snake.len - 2 ]
174
+ app.move_food ()
175
+ }
176
+
177
+ if dir := app.dir_queue.pop () {
178
+ if dir.x != - app.dir.x || dir.y != - app.dir.y {
179
+ app.dir = dir
180
+ }
181
+ }
182
+
183
+ app.last_tick = time.ticks ()
189
184
}
190
185
191
186
app.gg.end ()
0 commit comments