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