game_of_life/
game-of-life.rs

1use 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                    // Rule 1: Any live cell with fewer than two live neighbours
78                    // dies, as if caused by underpopulation.
79                    (Cell::Alive, x) if x < 2 => Cell::Dead,
80                    // Rule 2: Any live cell with two or three live neighbours
81                    // lives on to the next generation.
82                    (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive,
83                    // Rule 3: Any live cell with more than three live
84                    // neighbours dies, as if by overpopulation.
85                    (Cell::Alive, x) if x > 3 => Cell::Dead,
86                    // Rule 4: Any dead cell with exactly three live neighbours
87                    // becomes a live cell, as if by reproduction.
88                    (Cell::Dead, 3) => Cell::Alive,
89                    // All other cells remain in the same state.
90                    (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 }