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