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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use crate::CellGrid;

/// An environment rule uses the neighborhood (up to a certain range as specified) of a cell and applies a function to it.
/// The result of this function is the next value of that cell.
/// Applying this to each cell yields the entire transformation.
///
/// Note that each application of the ```cell_transform``` function will read from the entire untransformed array.
/// Also, the environment will wrap around the grid edges.
/// ```
/// use cellumina::rule::Rule;
/// let rule = cellumina::rule::EnvironmentRule {
///     row_range: 1,
///     col_range: 1,
///     boundaries: (cellumina::rule::BoundaryBehaviour::Periodic, cellumina::rule::BoundaryBehaviour::Periodic),
///     cell_transform: |env: &cellumina::CellGrid| match env
///     // Iterate over neighbors.
///         .iter()
///         .enumerate()
///         .map(|val| match val {
///             // The cell we are transforming does not get counted.
///             (4, 'X') => 0,
///             // Any cell containing an 'X' counts for 1 (alive).
///             (_, 'X') => 1,
///             // Any cell containing any other entry (only ' ' in our initial configuration) counts as 0 (dead).
///             _ => 0,
///         })
///         // Sum over these 9 values...
///         .sum()
///         // ... and map the sum to the new enty of our cell:
///     {
///         // 2 neighbors: The cell keeps its state.
///         2 => env[1][1],
///         // 3 neighbors: The cell gets born.
///         3 => 'X',
///         // 0, 1 or more than 3 neighbors: The cell dies.
///         _ => ' ',
///     },
/// };
/// let mut grid = grid::grid![[' ', ' ', 'X', ' ', ' '][' ', ' ', 'X',' ', ' '][' ', ' ', ' ', ' ', ' '][' ', ' ', 'X', ' ', ' '][' ', ' ', 'X', ' ', ' ']];
/// rule.transform(&mut grid);
/// assert_eq!(
///     grid,
///     grid::grid![[' ', 'X', 'X', 'X', ' '][' ', ' ', ' ',' ', ' '][' ', ' ', ' ', ' ', ' '][' ', ' ', ' ', ' ', ' '][' ', 'X', 'X', 'X', ' ']]
/// );
/// rule.transform(&mut grid);
/// rule.transform(&mut grid);
/// rule.transform(&mut grid);
/// assert_eq!(
///     grid,
///     grid::grid![[' ', 'X', ' ', 'X', ' '][' ', ' ', 'X',' ', ' '][' ', ' ', ' ', ' ', ' '][' ', ' ', 'X', ' ', ' '][' ', 'X', ' ', 'X', ' ']]
/// );
/// ```
#[derive(Clone, Copy)]
pub struct EnvironmentRule {
    /// The vertical range of an environment, extending in both direction from the cell to be transformed.
    ///
    /// Your ```cell_transform``` function will receive a grid of height ```2 * row_range + 1```, centered on the cell that will be replaced by the output.
    pub row_range: usize,
    /// The horizontal range of an environment, extending in both direction from the cell to be transformed.
    ///
    /// Your ```cell_transform``` function will receive a grid of width ```2 * col_range + 1```, centered on the cell that will be replaced by the output.
    pub col_range: usize,
    /// How the rule is supposed to handle cells at the edges of the state space.
    /// The first item describes how to handle trying to access rows out of range, the second columns out of range.
    pub boundaries: (super::BoundaryBehaviour, super::BoundaryBehaviour),
    /// The function that calculates the next state of a single cell based on its environment.
    ///
    /// Receives a grid of size ```2 * row_range + 1``` x ```2 * col_range + 1```. Must return a character.
    /// In the next iteration after applying this rule, the cell in the center of the received grid will contain the return value of this function.
    pub cell_transform: fn(&CellGrid) -> char,
}

impl Default for EnvironmentRule {
    fn default() -> Self {
        Self {
            row_range: Default::default(),
            col_range: Default::default(),
            boundaries: Default::default(),
            cell_transform: |_| ' ',
        }
    }
}

impl std::fmt::Debug for EnvironmentRule {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("EnvironmentRule")
            .field("range_vert", &self.row_range)
            .field("range_hor", &self.col_range)
            .finish()
    }
}

impl super::Rule for EnvironmentRule {
    fn transform(&self, grid: &mut CellGrid) {
        let mut buffer = grid::Grid::new(2 * self.row_range + 1, 2 * self.col_range + 1);
        let (rows, cols) = grid.size();

        // correction factor to make sure no overflowing subtractions happen

        let mut res = CellGrid::new(rows, cols);

        for row in 0..rows {
            for col in 0..cols {
                for row_del in 0..=2 * self.row_range {
                    for col_del in 0..=2 * self.col_range {
                        // Calculate the index we are interested in.
                        let (mut t_row, mut t_col) = (
                            (row + row_del).wrapping_sub(self.row_range),
                            (col + col_del).wrapping_sub(self.col_range),
                        );

                        let mut done = false;

                        // If it is too large check the boundary condition.
                        // The < 0 case is handled because we are performing a wrapping sub.
                        // This might be error-prone if rows is close to the maximum value of a usize.
                        if t_col >= cols {
                            match self.boundaries.1 {
                                // Perdiodic: Take the modulus.
                                super::BoundaryBehaviour::Periodic => t_col %= cols,
                                // Symbol: Set the buffer to a fixed element.
                                super::BoundaryBehaviour::Symbol(symbol) => {
                                    buffer[row_del][col_del] = symbol;
                                    done = true;
                                }
                            }
                        }

                        // Do the same for rows. Doing rows later ensures the boundary symbol of rows takes precedence if need be.
                        if t_row >= rows {
                            match self.boundaries.0 {
                                // Perdiodic: Take the modulus.
                                super::BoundaryBehaviour::Periodic => t_row %= rows,
                                // Symbol: Set the buffer to a fixed element.
                                super::BoundaryBehaviour::Symbol(symbol) => {
                                    buffer[row_del][col_del] = symbol;
                                    done = true;
                                }
                            }
                        }

                        if !done {
                            buffer[row_del][col_del] = grid[t_row][t_col]
                        }
                    }
                }
                res[row][col] = (self.cell_transform)(&buffer);
            }
        }

        *grid = res;
    }
}