• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lloydmeta / gol-rs / 6899756758

17 Nov 2023 04:09AM UTC coverage: 60.435% (-1.2%) from 61.635%
6899756758

Pull #13

github

lloydmeta
Bump version

Signed-off-by: lloydmeta <lloydmeta@gmail.com>
Pull Request #13: Some more build improvements

15 of 42 new or added lines in 3 files covered. (35.71%)

5 existing lines in 2 files now uncovered.

278 of 460 relevant lines covered (60.43%)

89170.27 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.47
/src/data/grid.rs
1
use super::cell::{Cell, Status};
2
use rand;
3
use rand::Rng;
4
use rayon::prelude::*;
5
use std::mem;
6

7
pub const PAR_THRESHOLD_AREA: usize = 250_000;
8

9
/// Used for indexing into the grid
10
#[allow(clippy::module_name_repetitions)]
11
#[derive(Debug, PartialEq, Eq)]
60,000✔
12
pub struct GridIdx(pub usize);
13

14
#[derive(Debug)]
1✔
15
pub struct Grid {
16
    /* Addressed by from-zero (i, j) notation, where i is row number, j is column number
17
     * such that given the following shows coordinates for cells in a 3 x 3 grid:
18
     *
19
     * [ (0,0) (0,1) (0,2) ]
20
     * [ (1,0) (1,1) (1,2) ]
21
     * [ (2,0) (2,1) (2,2) ]
22
     *
23
     * will get flattened into a single vector:
24
     * [ (0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2) ]
25
     */
26
    cells: Vec<Cell>,
27
    scratchpad_cells: Vec<Cell>,
28
    max_i: usize,
29
    max_j: usize,
30
    area: usize,
31
    // Cache of where the neighbours are for each point
32
    neighbours: Vec<[GridIdx; 8]>,
33
}
34

UNCOV
35
#[derive(PartialEq, Eq, Debug, PartialOrd, Ord, Clone)]
×
36
pub struct Coord {
37
    pub i: usize,
38
    pub j: usize,
39
}
40

41
impl Grid {
42
    /// Creates a grid with the given width and height
43
    pub fn new(width: usize, height: usize) -> Self {
6✔
44
        let mut rng = rand::thread_rng();
6✔
45
        // Grid is a matrix with {height} rows and {width} columns, addressed
6✔
46
        // via (i, j) (row, column) convention. Used for finding neightbours because it's
6✔
47
        // just an easier mental model to work with for that problem. It gets flattened later.
6✔
48
        let mut grid = Vec::with_capacity(height);
6✔
49
        for _ in 0..height {
6✔
50
            let mut row = Vec::with_capacity(width);
167✔
51
            for _ in 0..width {
7,589✔
52
                let status = if rng.gen() {
7,589✔
53
                    Status::Alive
3,849✔
54
                } else {
55
                    Status::Dead
3,740✔
56
                };
57
                let cell = Cell(status);
7,589✔
58
                row.push(cell);
7,589✔
59
            }
60
            grid.push(row);
167✔
61
        }
62

63
        let max_i = if height == 0 { 0 } else { height - 1 };
6✔
64
        let max_j = if width == 0 { 0 } else { width - 1 };
6✔
65
        let neighbours = neighbours(max_i, max_j, &grid);
6✔
66
        let cells: Vec<Cell> = grid.into_iter().flatten().collect();
6✔
67
        let scratchpad_cells = cells.clone();
6✔
68
        let area = width * height;
6✔
69
        Self {
6✔
70
            cells,
6✔
71
            scratchpad_cells,
6✔
72
            max_i,
6✔
73
            max_j,
6✔
74
            area,
6✔
75
            neighbours,
6✔
76
        }
6✔
77
    }
6✔
78

79
    /// Returns the i-th Cell in a grid as if the 2 dimensional matrix
80
    /// has been flattened into a 1 dimensional one row-wise
81
    ///
82
    /// TODO: is using iter faster or slower than just doing the checks?
83
    pub fn get_idx(&self, &GridIdx(idx): &GridIdx) -> Option<&Cell> {
9✔
84
        if idx < self.cells.len() {
9✔
85
            Some(&self.cells[idx])
9✔
86
        } else {
87
            None
×
88
        }
89
    }
9✔
90

91
    // TODO delete if not used
92
    pub const fn to_grid_idx(&self, &Coord { i, j }: &Coord) -> Option<GridIdx> {
28✔
93
        if i <= self.max_i && j <= self.max_j {
28✔
94
            Some(GridIdx(self.width() * i + j))
27✔
95
        } else {
96
            None
1✔
97
        }
98
    }
28✔
99

100
    // Returns a slice with references to this grid's cells
101
    pub fn cells(&self) -> Vec<Vec<&Cell>> {
3✔
102
        let mut rows = Vec::with_capacity(self.height());
3✔
103
        let mut i = 0;
3✔
104
        for _ in 0..self.height() {
3✔
105
            let mut columns = Vec::with_capacity(self.width());
13✔
106
            for _ in 0..self.width() {
109✔
107
                columns.push(&self.cells[i]);
109✔
108
                i += 1;
109✔
109
            }
109✔
110
            rows.push(columns);
13✔
111
        }
112
        rows
3✔
113
    }
3✔
114

115
    pub const fn height(&self) -> usize {
6✔
116
        self.max_i + 1
6✔
117
    }
6✔
118

119
    pub const fn width(&self) -> usize {
53✔
120
        self.max_j + 1
53✔
121
    }
53✔
122

123
    pub const fn area(&self) -> usize {
100✔
124
        self.area
100✔
125
    }
100✔
126

127
    pub fn advance(&mut self) {
100✔
128
        {
100✔
129
            let neighbours = &self.neighbours;
100✔
130
            let last_gen = &self.cells;
100✔
131
            let area_requires_par = self.area() >= PAR_THRESHOLD_AREA;
100✔
132
            let cells = &mut self.scratchpad_cells;
100✔
133
            let cell_op = |(i, cell): (usize, &mut Cell)| {
750,000✔
134
                let alives = neighbours[i].iter().fold(0, |acc, &GridIdx(idx)| {
6,000,000✔
135
                    if last_gen[idx].0 == Status::Alive {
6,000,000✔
136
                        acc + 1
788,776✔
137
                    } else {
138
                        acc
5,211,224✔
139
                    }
140
                });
6,000,000✔
141
                let next_status = last_gen[i].next_status(alives);
750,000✔
142
                cell.update(next_status);
750,000✔
143
            };
750,000✔
144
            if area_requires_par {
100✔
145
                cells.par_iter_mut().enumerate().for_each(cell_op);
×
146
            } else {
×
147
                for (i, cell) in cells.iter_mut().enumerate() {
750,000✔
148
                    cell_op((i, cell));
750,000✔
149
                }
750,000✔
150
            }
151
        }
152
        mem::swap(&mut self.cells, &mut self.scratchpad_cells);
100✔
153
    }
100✔
154
}
155

156
fn neighbours(max_i: usize, max_j: usize, cells: &[Vec<Cell>]) -> Vec<[GridIdx; 8]> {
6✔
157
    let mut v = Vec::with_capacity((max_i + 1) * (max_j + 1));
6✔
158
    for (i, row) in cells.iter().enumerate() {
167✔
159
        for (j, _) in row.iter().enumerate() {
7,589✔
160
            let coord = Coord { i, j };
7,589✔
161
            v.push(neighbour_coords(max_i, max_j, &coord));
7,589✔
162
        }
7,589✔
163
    }
164
    v
6✔
165
}
6✔
166

167
fn neighbour_coords(max_i: usize, max_j: usize, coord: &Coord) -> [GridIdx; 8] {
7,592✔
168
    let width = max_j + 1;
7,592✔
169
    let Coord { i, j } = *coord;
7,592✔
170
    let to_grid_idx = |Coord { i, j }: Coord| GridIdx(width * i + j);
60,736✔
171

172
    let i_up = match i {
7,592✔
173
        0 => max_i,
74✔
174
        _ => i - 1,
7,518✔
175
    };
176

177
    let i_down = match i {
7,592✔
178
        _ if i == max_i => 0,
7,592✔
179
        _ => i + 1,
7,518✔
180
    };
181

182
    let j_left = match j {
7,592✔
183
        0 => max_j,
168✔
184
        _ => j - 1,
7,424✔
185
    };
186
    let j_right = match j {
7,592✔
187
        _ if j == max_j => 0,
7,592✔
188
        _ => j + 1,
7,424✔
189
    };
190

191
    let north = Coord { i: i_up, j };
7,592✔
192
    let north_east = Coord {
7,592✔
193
        i: i_up,
7,592✔
194
        j: j_right,
7,592✔
195
    };
7,592✔
196
    let east = Coord { i, j: j_right };
7,592✔
197
    let south_east = Coord {
7,592✔
198
        i: i_down,
7,592✔
199
        j: j_right,
7,592✔
200
    };
7,592✔
201
    let south = Coord { i: i_down, j };
7,592✔
202
    let south_west = Coord {
7,592✔
203
        i: i_down,
7,592✔
204
        j: j_left,
7,592✔
205
    };
7,592✔
206
    let west = Coord { i, j: j_left };
7,592✔
207
    let north_west = Coord { i: i_up, j: j_left };
7,592✔
208
    [
7,592✔
209
        to_grid_idx(north),
7,592✔
210
        to_grid_idx(north_east),
7,592✔
211
        to_grid_idx(east),
7,592✔
212
        to_grid_idx(south_east),
7,592✔
213
        to_grid_idx(south),
7,592✔
214
        to_grid_idx(south_west),
7,592✔
215
        to_grid_idx(west),
7,592✔
216
        to_grid_idx(north_west),
7,592✔
217
    ]
7,592✔
218
}
7,592✔
219

220
#[cfg(test)]
221
mod tests {
222
    use super::*;
223

224
    #[test]
1✔
225
    fn test_grid_new() {
1✔
226
        let grid = Grid::new(10, 5);
1✔
227
        assert_eq!(grid.cells().len(), 5);
1✔
228
        assert_eq!(grid.cells()[0].len(), 10);
1✔
229
    }
1✔
230

231
    #[test]
1✔
232
    fn test_neighbour_coords() {
1✔
233
        let grid = Grid::new(3, 3);
1✔
234
        let max_i = grid.max_i;
1✔
235
        let max_j = grid.max_j;
1✔
236
        /*
1✔
237
         * [ (0,0) (0,1) (0,2) ]
1✔
238
         * [ (1,0) (1,1) (1,2) ]
1✔
239
         * [ (2,0) (2,1) (2,2) ]
1✔
240
         */
1✔
241
        let n0 = neighbour_coords(max_i, max_j, &Coord { i: 0, j: 0 });
1✔
242
        assert_eq!(n0[0], grid.to_grid_idx(&Coord { i: 2, j: 0 }).unwrap()); // N
1✔
243
        assert_eq!(n0[1], grid.to_grid_idx(&Coord { i: 2, j: 1 }).unwrap()); // NE
1✔
244
        assert_eq!(n0[2], grid.to_grid_idx(&Coord { i: 0, j: 1 }).unwrap()); // E
1✔
245
        assert_eq!(n0[3], grid.to_grid_idx(&Coord { i: 1, j: 1 }).unwrap()); // SE
1✔
246
        assert_eq!(n0[4], grid.to_grid_idx(&Coord { i: 1, j: 0 }).unwrap()); // S
1✔
247
        assert_eq!(n0[5], grid.to_grid_idx(&Coord { i: 1, j: 2 }).unwrap()); // SW
1✔
248
        assert_eq!(n0[6], grid.to_grid_idx(&Coord { i: 0, j: 2 }).unwrap()); // W
1✔
249
        assert_eq!(n0[7], grid.to_grid_idx(&Coord { i: 2, j: 2 }).unwrap()); // NW
1✔
250
        let n1 = neighbour_coords(max_i, max_j, &Coord { i: 1, j: 1 });
1✔
251
        assert_eq!(n1[0], grid.to_grid_idx(&Coord { i: 0, j: 1 }).unwrap()); // N
1✔
252
        assert_eq!(n1[1], grid.to_grid_idx(&Coord { i: 0, j: 2 }).unwrap()); // NE
1✔
253
        assert_eq!(n1[2], grid.to_grid_idx(&Coord { i: 1, j: 2 }).unwrap()); // E
1✔
254
        assert_eq!(n1[3], grid.to_grid_idx(&Coord { i: 2, j: 2 }).unwrap()); // SE
1✔
255
        assert_eq!(n1[4], grid.to_grid_idx(&Coord { i: 2, j: 1 }).unwrap()); // S
1✔
256
        assert_eq!(n1[5], grid.to_grid_idx(&Coord { i: 2, j: 0 }).unwrap()); // SW
1✔
257
        assert_eq!(n1[6], grid.to_grid_idx(&Coord { i: 1, j: 0 }).unwrap()); // W
1✔
258
        assert_eq!(n1[7], grid.to_grid_idx(&Coord { i: 0, j: 0 }).unwrap()); // NW
1✔
259
        let n2 = neighbour_coords(max_i, max_j, &Coord { i: 2, j: 2 });
1✔
260
        assert_eq!(n2[0], grid.to_grid_idx(&Coord { i: 1, j: 2 }).unwrap()); // N
1✔
261
        assert_eq!(n2[1], grid.to_grid_idx(&Coord { i: 1, j: 0 }).unwrap()); // NE
1✔
262
        assert_eq!(n2[2], grid.to_grid_idx(&Coord { i: 2, j: 0 }).unwrap()); // E
1✔
263
        assert_eq!(n2[3], grid.to_grid_idx(&Coord { i: 0, j: 0 }).unwrap()); // SE
1✔
264
        assert_eq!(n2[4], grid.to_grid_idx(&Coord { i: 0, j: 2 }).unwrap()); // S
1✔
265
        assert_eq!(n2[5], grid.to_grid_idx(&Coord { i: 0, j: 1 }).unwrap()); // SW
1✔
266
        assert_eq!(n2[6], grid.to_grid_idx(&Coord { i: 2, j: 1 }).unwrap()); // W
1✔
267
        assert_eq!(n2[7], grid.to_grid_idx(&Coord { i: 1, j: 1 }).unwrap()); // NW
1✔
268
    }
1✔
269

270
    // Just a test to make sure advance can run for a large number of iterations
271
    #[test]
1✔
272
    fn test_advance() {
1✔
273
        let mut grid = Grid::new(50, 150);
1✔
274
        print!("{:?}", grid);
1✔
275
        for _ in 0..100 {
101✔
276
            grid.advance();
100✔
277
        }
100✔
278
    }
1✔
279

280
    #[test]
1✔
281
    fn test_alive_count() {
1✔
282
        let mut grid = Grid::new(3, 3);
1✔
283
        let new_cells = vec![
1✔
284
            vec![
1✔
285
                Cell(Status::Alive),
1✔
286
                Cell(Status::Alive),
1✔
287
                Cell(Status::Alive),
1✔
288
            ],
1✔
289
            vec![Cell(Status::Alive), Cell(Status::Dead), Cell(Status::Alive)],
1✔
290
            vec![
1✔
291
                Cell(Status::Alive),
1✔
292
                Cell(Status::Alive),
1✔
293
                Cell(Status::Alive),
1✔
294
            ],
1✔
295
        ]
1✔
296
        .into_iter()
1✔
297
        .flat_map(|v| v)
3✔
298
        .collect();
1✔
299
        grid.cells = new_cells;
1✔
300
        assert_eq!(alive_count(&grid), 8)
1✔
301
    }
1✔
302

303
    #[test]
1✔
304
    fn test_get_idx() {
1✔
305
        let mut grid = Grid::new(3, 3);
1✔
306
        let new_cells: Vec<Cell> = vec![
1✔
307
            vec![
1✔
308
                Cell(Status::Alive),
1✔
309
                Cell(Status::Alive),
1✔
310
                Cell(Status::Alive),
1✔
311
            ],
1✔
312
            vec![Cell(Status::Alive), Cell(Status::Dead), Cell(Status::Alive)],
1✔
313
            vec![
1✔
314
                Cell(Status::Alive),
1✔
315
                Cell(Status::Alive),
1✔
316
                Cell(Status::Alive),
1✔
317
            ],
1✔
318
        ]
1✔
319
        .into_iter()
1✔
320
        .flat_map(|v| v)
3✔
321
        .collect();
1✔
322
        grid.cells = new_cells;
1✔
323
        for idx in 0..9 {
10✔
324
            let cell = grid.get_idx(&GridIdx(idx)).unwrap();
9✔
325
            if idx != 4 {
9✔
326
                assert!(cell.alive())
8✔
327
            } else {
328
                assert!(!cell.alive())
1✔
329
            }
330
        }
331
    }
1✔
332

333
    /// Given
334
    ///
335
    /// [ (0,0) (0,1) (0,2) (0, 3) ]
336
    /// [ (1,0) (1,1) (1,2) (1, 3) ]
337
    /// [ (2,0) (2,1) (2,2) (2, 3) ]
338
    #[test]
1✔
339
    fn test_to_grid_idx() {
1✔
340
        let grid = Grid::new(4, 3);
1✔
341
        assert_eq!(grid.to_grid_idx(&Coord { i: 0, j: 0 }), Some(GridIdx(0)));
1✔
342
        assert_eq!(grid.to_grid_idx(&Coord { i: 1, j: 2 }), Some(GridIdx(6)));
1✔
343
        assert_eq!(grid.to_grid_idx(&Coord { i: 2, j: 3 }), Some(GridIdx(11)));
1✔
344
        assert_eq!(grid.to_grid_idx(&Coord { i: 3, j: 3 }), None);
1✔
345
    }
1✔
346

347
    fn alive_cells(grid: &Grid) -> Vec<Coord> {
1✔
348
        let mut v = vec![];
1✔
349
        for (i, row) in grid.cells().iter().enumerate() {
3✔
350
            for (j, cell) in row.iter().enumerate() {
9✔
351
                if cell.alive() {
9✔
352
                    let coord = Coord { i, j };
8✔
353
                    v.push(coord);
8✔
354
                }
8✔
355
            }
356
        }
357
        v
1✔
358
    }
1✔
359

360
    fn alive_count(grid: &Grid) -> usize {
1✔
361
        alive_cells(grid).len()
1✔
362
    }
1✔
363
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc