game_of_life/
game-of-life.rs1use js_canvas_rendering_context_2d::*;
2
3
4
5fn main(){
6 draw();
7}
8
9
10#[no_mangle]
11pub extern "C" fn draw(){
12 UNIVERSE.with(|universe|{
13 let borrow = universe.borrow();
14 borrow.draw();
15 });
16}
17
18
19#[no_mangle]
20pub extern "C" fn tick(){
21 UNIVERSE.with(|universe|{
22 let mut borrow = universe.borrow_mut();
23 borrow.tick();
24 });
25}
26
27thread_local! {
28 pub static UNIVERSE: std::cell::RefCell<Box<Universe>> = std::cell::RefCell::new(Box::new(Universe{..Default::default()}));
29}
30
31#[repr(u8)]
32#[derive(Clone, Copy, Debug, PartialEq, Eq)]
33pub enum Cell {
34 Dead = 0,
35 Alive = 1,
36}
37
38pub struct Universe {
39 width: u32,
40 height: u32,
41 cells: Vec<Cell>,
42}
43
44impl Universe {
45
46 fn get_index(&self, row: u32, column: u32) -> usize {
47 (row * self.width + column) as usize
48 }
49
50 fn live_neighbor_count(&self, row: u32, column: u32) -> u8 {
51 let mut count = 0;
52 for delta_row in [self.height - 1, 0, 1].iter().cloned() {
53 for delta_col in [self.width - 1, 0, 1].iter().cloned() {
54 if delta_row == 0 && delta_col == 0 {
55 continue;
56 }
57
58 let neighbor_row = (row + delta_row) % self.height;
59 let neighbor_col = (column + delta_col) % self.width;
60 let idx = self.get_index(neighbor_row, neighbor_col);
61 count += self.cells[idx] as u8;
62 }
63 }
64 count
65 }
66
67 pub fn tick(&mut self) {
68 let mut next = self.cells.clone();
69
70 for row in 0..self.height {
71 for col in 0..self.width {
72 let idx = self.get_index(row, col);
73 let cell = self.cells[idx];
74 let live_neighbors = self.live_neighbor_count(row, col);
75
76 let next_cell = match (cell, live_neighbors) {
77 (Cell::Alive, x) if x < 2 => Cell::Dead,
80 (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive,
83 (Cell::Alive, x) if x > 3 => Cell::Dead,
86 (Cell::Dead, 3) => Cell::Alive,
89 (otherwise, _) => otherwise,
91 };
92
93 next[idx] = next_cell;
94 }
95 }
96
97 self.cells = next;
98 }
99
100 pub fn draw(&self){
101 let (canvas_w,canvas_h) = get_canvas_dimensions();
102 let (offset_x, offset_y) = get_offsets(canvas_w, canvas_h);
103 let coeff = get_coeff(canvas_w, canvas_h);
104
105
106 CanvasRenderingContext2D::clear_rect(0., 0., canvas_w, canvas_h);
107
108 let cell_width = 100.0 / self.width as f32;
109 let cell_height = 100.0 / self.height as f32;
110 let mut x_i = 0;
111 let mut y_i= 0;
112
113 for line in self.cells.as_slice().chunks(self.width as usize) {
114 for &cell in line {
115 if cell == Cell::Alive{
116 let start_x = apply_transformation(x_i as f32 * cell_width, coeff, offset_x);
117 let start_y = apply_transformation(y_i as f32 * cell_height, coeff, offset_y);
118
119
120 CanvasRenderingContext2D::begin_path();
121 CanvasRenderingContext2D::set_fill_style_rgba(200, 200, 200,255);
122 CanvasRenderingContext2D::fill_rect(start_x, start_y,cell_width, cell_height);
123 }
124
125 x_i += 1;
126 }
127 x_i = 0;
128 y_i += 1;
129 }
130 }
131}
132
133impl Default for Universe {
134 fn default() -> Self {
135 let width = 16;
136 let height = 16;
137
138 let cells = (0..width * height)
139 .map(|i| {
140 if i % 2 == 0 || i % 7 == 0 {
141 Cell::Alive
142 } else {
143 Cell::Dead
144 }
145 })
146 .collect();
147
148 Universe {
149 width,
150 height,
151 cells,
152 }
153 }
154}
155
156
157fn apply_transformation(source: f32, coeff: f32, offset: f32) -> f32 {
158 offset + (coeff * source)
159 }
160
161 fn get_offsets(canvas_w:f32, canvas_h:f32) -> (f32,f32){
162
163 let square_side = canvas_w.min(canvas_h);
164 let offset_x = (canvas_w - square_side) / 2.;
165 let offset_y = (canvas_h - square_side) / 2.;
166 (offset_x,offset_y)
167 }
168
169 fn get_coeff(canvas_w:f32, canvas_h:f32) -> f32{
170 let square_side = canvas_w.min(canvas_h);
171
172 square_side / 100.0
173 }
174
175 fn get_canvas_dimensions() -> (f32, f32){
176 (CanvasRenderingContext2D::get_canvas_width(),CanvasRenderingContext2D::get_canvas_height())
177 }