summaryrefslogtreecommitdiff
path: root/wasm-game-of-life/src/lib.rs
blob: fe82d2c114d01df87e6a5c9ba2a85d0ecb78fc08 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
mod utils;

use wasm_bindgen::prelude::*;
use wasm_bindgen::__rt::core::fmt::Alignment::Center;
use wasm_bindgen::__rt::core::fmt::Formatter;

// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

fn main() {
    #[wasm_bindgen]
    #[repr(u8)]
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    pub enum Cell {
        Dead = 0,
        Alive = 1,
    }

    #[wasm_bindgen]
    pub struct Universe {
        width: u32,
        height: u32,
        cells: Vec<Cell>,
    }

    #[wasm_bindgen]
    impl Universe {
        fn get_index(&self, row: u32, column: u32) -> usize {
            (row * self.width + column) as usize
        }

        fn live_neighbor_count(&self, row: u32, column: u32) -> u8 {
            let mut count = 0;
            for delta_row in [self.height - 1, 0, 1].iter().cloned() {
                for delta_column in [self.width - 1, 0, 1].iter().cloned() {
                    if delta_row == 0 && delta_column == 0 {
                        continue;
                    }

                    let neighbor_row = (row + delta_row) % self.height;
                    let neighbor_column = (column + delta_column) % self.width;
                    let idx = self.get_index(neighbor_row, neighbor_column);
                    count += self.cells[idx] as u8;
                }
            }
            count
        }
        pub fn new() -> Universe {
            let width = 64;
            let height = 64;

            let cells = (0..width * height)
                .map(|x| {
                    if x % 2 == 0 || x % 7 == 0 {
                        Cell::Alive
                    } else {
                        Cell::Dead
                    }
                })
                .collect();

            Universe {
                width,
                height,
                cells,
            }
        }

        pub fn render(&self) -> String {
            self.to_string()
        }

        pub fn tick(&mut self) {
            let mut next = self.cells.clone();

            for row in 0..self.height {
                for col in 0..self.width {
                    let idx = self.get_index(row, col);
                    let cell = self.cells[idx];
                    let live_neighbors = self.live_neighbor_count(row, col);

                    let next_cell = match (cell, live_neighbors) {
                        // 1. Underpopulation
                        (Cell::Alive, x) if x < 2 => Cell::Dead,
                        // 2. OK
                        (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive,
                        // 3. Overpopulation
                        (Cell::Alive, x) if x > 3 => Cell::Dead,
                        // 4. Reproduction
                        (Cell::Dead, 3) => Cell::Alive,
                        (otherwise, _) => otherwise,
                    };

                    next[idx] = next_cell;
                }
            }

            self.cells = next;
        }

        pub fn width(&self) -> u32 {
            self.width
        }

        pub fn height(&self) -> u32 {
            self.height
        }

        pub fn cells(&self) -> *const Cell {
            self.cells.as_ptr()
        }
    }

    use std::fmt;

    impl fmt::Display for Universe {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            for line in self.cells.as_slice().chunks(self.width as usize) {
                for &cell in line {
                    let symbol = if cell == Cell::Dead { '◻' } else { '◼' };
                    write!(f, "{}", symbol)?;
                }
                write!(f, "\n")?;
            }

            Ok(())
        }
    }
}