1 | import gg |
2 | import gx |
3 | // import sokol.sapp |
4 | import time |
5 | import rand |
6 | |
7 | // constants |
8 | const ( |
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 |
17 | struct Pos { |
18 | x int |
19 | y int |
20 | } |
21 | |
22 | fn (a Pos) + (b Pos) Pos { |
23 | return Pos{a.x + b.x, a.y + b.y} |
24 | } |
25 | |
26 | fn (a Pos) - (b Pos) Pos { |
27 | return Pos{a.x - b.x, a.y - b.y} |
28 | } |
29 | |
30 | enum Direction { |
31 | up |
32 | down |
33 | left |
34 | right |
35 | } |
36 | |
37 | struct App { |
38 | mut: |
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 |
50 | fn (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 | |
65 | fn (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 |
78 | fn 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 | |
104 | fn 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 |
177 | fn 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 | } |