1package game_of_life
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21import time "core:time"
22import rl "vendor:raylib"
23
24
25Window :: struct {
26 name: cstring,
27 width: i32,
28 height: i32,
29 fps: i32,
30 control_flags: rl.ConfigFlags,
31}
32
33Game :: struct {
34 tick_rate: time.Duration,
35 last_tick: time.Time,
36 pause: bool,
37 colors: []rl.Color,
38 width: i32,
39 height: i32,
40}
41
42World :: struct {
43 width: i32,
44 height: i32,
45 alive: []u8,
46}
47
48Cell :: struct {
49 width: f32,
50 height: f32,
51}
52
53User_Input :: struct {
54 left_mouse_clicked: bool,
55 right_mouse_clicked: bool,
56 toggle_pause: bool,
57 mouse_world_position: i32,
58 mouse_tile_x: i32,
59 mouse_tile_y: i32,
60}
61
62
63
64
65
66
67
68
69
70
71update_world :: #force_inline proc(world: ^World, next_world: ^World) {
72 for x: i32 = 0; x < world.width; x += 1 {
73 for y: i32 = 0; y < world.height; y += 1 {
74 neighbors := count_neighbors(world, x, y)
75 index := y * world.width + x
76
77 switch neighbors {
78 case 2: next_world.alive[index] = world.alive[index]
79 case 3: next_world.alive[index] = 1
80 case: next_world.alive[index] = 0
81 }
82 }
83 }
84}
85
86
87
88
89count_neighbors :: #force_inline proc(w: ^World, x: i32, y: i32) -> u8 {
90
91 left := (x - 1) %% w.width
92 right := (x + 1) %% w.width
93 up := (y - 1) %% w.height
94 down := (y + 1) %% w.height
95
96 top_left := w.alive[up * w.width + left ]
97 top := w.alive[up * w.width + x ]
98 top_right := w.alive[up * w.width + right]
99
100 mid_left := w.alive[y * w.width + left ]
101 mid_right := w.alive[y * w.width + right]
102
103 bottom_left := w.alive[down * w.width + left ]
104 bottom := w.alive[down * w.width + x ]
105 bottom_right := w.alive[down * w.width + right]
106
107 top_row := top_left + top + top_right
108 mid_row := mid_left + mid_right
109 bottom_row := bottom_left + bottom + bottom_right
110
111 total := top_row + mid_row + bottom_row
112 return total
113}
114
115
116
117
118draw_world :: #force_inline proc(world: ^World, cell: Cell, colors: []rl.Color) {
119 x, y: i32
120 for y = 0; y < world.height; y += 1 {
121 for x = 0; x < world.width; x += 1 {
122 index := y * world.width + x
123 color := colors[world.alive[index]]
124
125 rect := rl.Rectangle {
126 x = f32(x) * cell.width,
127 y = f32(y) * cell.height,
128 width = cell.width,
129 height = cell.height,
130 }
131 rl.DrawRectangleRec(rect, color)
132 }
133 }
134}
135
136
137
138
139draw_cursor :: proc(user_input: User_Input, cell: Cell) {
140
141 rect := rl.Rectangle {
142 x = f32(user_input.mouse_tile_x) * cell.width,
143 y = f32(user_input.mouse_tile_y) * cell.height,
144 width = cell.width,
145 height = cell.height,
146 }
147 rl.DrawRectangleRec(rect, rl.YELLOW)
148}
149
150
151
152
153
154process_user_input :: proc(user_input: ^User_Input, window: Window, world: World) {
155 m_pos := rl.GetMousePosition()
156 mouse_x := i32((m_pos[0] / f32(window.width)) * f32(world.width))
157 mouse_y := i32((m_pos[1] / f32(window.height)) * f32(world.height))
158
159
160 if user_input.left_mouse_clicked || user_input.right_mouse_clicked {
161 mouse_x %%= world.width
162 mouse_y %%= world.height
163 }
164
165 user_input^ = User_Input {
166 left_mouse_clicked = rl.IsMouseButtonDown(.LEFT),
167 right_mouse_clicked = rl.IsMouseButtonDown(.RIGHT),
168 toggle_pause = rl.IsKeyPressed(.SPACE),
169 mouse_world_position = i32(mouse_y * world.width + mouse_x),
170 mouse_tile_x = mouse_x,
171 mouse_tile_y = mouse_y,
172 }
173}
174
175main :: proc() {
176 window := Window{"Game Of Life", 1024, 1024, 60, rl.ConfigFlags{.WINDOW_RESIZABLE}}
177
178 game := Game {
179 tick_rate = 300 * time.Millisecond,
180 last_tick = time.now(),
181 pause = true,
182 colors = []rl.Color{rl.BLACK, rl.WHITE},
183 width = 64,
184 height = 64,
185 }
186
187 world := World{game.width, game.height, make([]u8, game.width * game.height)}
188 next_world := World{game.width, game.height, make([]u8, game.width * game.height)}
189 defer delete(world.alive)
190 defer delete(next_world.alive)
191
192 cell := Cell {
193 width = f32(window.width) / f32(world.width),
194 height = f32(window.height) / f32(world.width),
195 }
196
197 user_input: User_Input
198
199 rl.InitWindow(window.width, window.height, window.name)
200 rl.SetWindowState(window.control_flags)
201 rl.SetTargetFPS(window.fps)
202
203
204 for !rl.WindowShouldClose() {
205
206
207 if rl.IsWindowResized() {
208 window.width = rl.GetScreenWidth()
209 window.height = rl.GetScreenHeight()
210
211 cell.width = f32(window.width) / f32(world.width)
212 cell.height = f32(window.height) / f32(world.width)
213 }
214
215
216
217
218 process_user_input(&user_input, window, world)
219
220 if user_input.left_mouse_clicked {
221 world.alive[user_input.mouse_world_position] = 1
222 }
223 if user_input.right_mouse_clicked {
224 world.alive[user_input.mouse_world_position] = 0
225 }
226 if user_input.toggle_pause {
227 game.pause = !game.pause
228 }
229
230
231
232
233 if !game.pause && time.since(game.last_tick) > game.tick_rate {
234 game.last_tick = time.now()
235 update_world(&world, &next_world)
236
237
238 world, next_world = next_world, world
239 }
240
241
242
243
244 rl.BeginDrawing()
245 rl.ClearBackground(rl.PINK)
246 draw_world(&world, cell, game.colors)
247 draw_cursor(user_input, cell)
248
249 rl.EndDrawing()
250 }
251
252
253}