summaryrefslogtreecommitdiffstats
path: root/examples/game_of_life
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2020-04-29 23:50:15 +0200
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2020-04-29 23:50:15 +0200
commit5e014a70e864964570e4c1568926b8c647f73c59 (patch)
tree40adf2d376ea0995239fb3292555112bbc517e59 /examples/game_of_life
parent38c4dd5fdbdb299c1c5b0e64f44855e98a0db85f (diff)
downloadiced-5e014a70e864964570e4c1568926b8c647f73c59.tar.gz
iced-5e014a70e864964570e4c1568926b8c647f73c59.tar.bz2
iced-5e014a70e864964570e4c1568926b8c647f73c59.zip
Use sparse grid representation in `game_of_life`
Diffstat (limited to 'examples/game_of_life')
-rw-r--r--examples/game_of_life/src/main.rs137
1 files changed, 69 insertions, 68 deletions
diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs
index a2628594..fb12afa1 100644
--- a/examples/game_of_life/src/main.rs
+++ b/examples/game_of_life/src/main.rs
@@ -14,10 +14,7 @@ use iced::{
use std::time::{Duration, Instant};
pub fn main() {
- GameOfLife::run(Settings {
- antialiasing: true,
- ..Settings::default()
- })
+ GameOfLife::run(Settings::default())
}
#[derive(Default)]
@@ -164,36 +161,55 @@ mod grid {
mouse, ButtonState, Color, Element, Length, MouseCursor, Point,
Rectangle, Size, Vector,
};
+ use std::collections::{HashMap, HashSet};
- const SIZE: usize = 32;
+ const CELL_SIZE: usize = 20;
#[derive(Default)]
pub struct Grid {
- cells: [[Cell; SIZE]; SIZE],
+ alive_cells: HashSet<(usize, usize)>,
mouse_pressed: bool,
cache: canvas::Cache,
}
impl Grid {
- pub fn tick(&mut self) {
- let mut populated_neighbors: [[usize; SIZE]; SIZE] =
- [[0; SIZE]; SIZE];
+ fn with_neighbors(
+ i: usize,
+ j: usize,
+ ) -> impl Iterator<Item = (usize, usize)> {
+ use itertools::Itertools;
- for (i, row) in self.cells.iter().enumerate() {
- for (j, _) in row.iter().enumerate() {
- populated_neighbors[i][j] = self.populated_neighbors(i, j);
- }
- }
+ let rows = i.saturating_sub(1)..=i.saturating_add(1);
+ let columns = j.saturating_sub(1)..=j.saturating_add(1);
- for (i, row) in populated_neighbors.iter().enumerate() {
- for (j, amount) in row.iter().enumerate() {
- let is_populated = self.cells[i][j] == Cell::Populated;
+ rows.cartesian_product(columns)
+ }
- self.cells[i][j] = match amount {
- 2 if is_populated => Cell::Populated,
- 3 => Cell::Populated,
- _ => Cell::Unpopulated,
- };
+ pub fn tick(&mut self) {
+ use itertools::Itertools;
+
+ let populated_neighbors: HashMap<(usize, usize), usize> = self
+ .alive_cells
+ .iter()
+ .flat_map(|&(i, j)| Self::with_neighbors(i, j))
+ .unique()
+ .map(|(i, j)| ((i, j), self.populated_neighbors(i, j)))
+ .collect();
+
+ for (&(i, j), amount) in populated_neighbors.iter() {
+ let is_populated = self.alive_cells.contains(&(i, j));
+
+ match amount {
+ 2 if is_populated => {}
+ 3 => {
+ if !is_populated {
+ self.alive_cells.insert((i, j));
+ }
+ }
+ _ if is_populated => {
+ self.alive_cells.remove(&(i, j));
+ }
+ _ => {}
}
}
@@ -202,8 +218,8 @@ mod grid {
pub fn update(&mut self, message: Message) {
match message {
- Message::Populate { i, j } => {
- self.cells[i][j] = Cell::Populated;
+ Message::Populate { cell } => {
+ self.alive_cells.insert(cell);
self.cache.clear()
}
}
@@ -217,34 +233,28 @@ mod grid {
}
fn populated_neighbors(&self, row: usize, column: usize) -> usize {
- use itertools::Itertools;
+ let with_neighbors = Self::with_neighbors(row, column);
- let rows = row.saturating_sub(1)..=row + 1;
- let columns = column.saturating_sub(1)..=column + 1;
-
- let is_inside_bounds = |i: usize, j: usize| i < SIZE && j < SIZE;
let is_neighbor = |i: usize, j: usize| i != row || j != column;
-
let is_populated =
- |i: usize, j: usize| self.cells[i][j] == Cell::Populated;
+ |i: usize, j: usize| self.alive_cells.contains(&(i, j));
- rows.cartesian_product(columns)
- .filter(|&(i, j)| {
- is_inside_bounds(i, j)
- && is_neighbor(i, j)
- && is_populated(i, j)
- })
+ with_neighbors
+ .filter(|&(i, j)| is_neighbor(i, j) && is_populated(i, j))
.count()
}
fn region(&self, size: Size) -> Rectangle {
- let side = size.width.min(size.height);
+ let width =
+ (size.width / CELL_SIZE as f32).floor() * CELL_SIZE as f32;
+ let height =
+ (size.height / CELL_SIZE as f32).floor() * CELL_SIZE as f32;
Rectangle {
- x: (size.width - side) / 2.0,
- y: (size.height - side) / 2.0,
- width: side,
- height: side,
+ x: (size.width - width) / 2.0,
+ y: (size.height - height) / 2.0,
+ width,
+ height,
}
}
@@ -254,10 +264,10 @@ mod grid {
position: Point,
) -> Option<(usize, usize)> {
if region.contains(position) {
- let cell_size = region.width / SIZE as f32;
-
- let i = ((position.y - region.y) / cell_size).ceil() as usize;
- let j = ((position.x - region.x) / cell_size).ceil() as usize;
+ let i = ((position.y - region.y) / CELL_SIZE as f32).ceil()
+ as usize;
+ let j = ((position.x - region.x) / CELL_SIZE as f32).ceil()
+ as usize;
Some((i.saturating_sub(1), j.saturating_sub(1)))
} else {
@@ -266,21 +276,9 @@ mod grid {
}
}
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
- enum Cell {
- Unpopulated,
- Populated,
- }
-
- impl Default for Cell {
- fn default() -> Cell {
- Cell::Unpopulated
- }
- }
-
#[derive(Debug, Clone, Copy)]
pub enum Message {
- Populate { i: usize, j: usize },
+ Populate { cell: (usize, usize) },
}
impl<'a> canvas::Program<Message> for Grid {
@@ -301,12 +299,12 @@ mod grid {
let cursor_position = cursor.position_in(&bounds)?;
let region = self.region(bounds.size());
- let (i, j) = self.cell_at(region, cursor_position)?;
+ let cell = self.cell_at(region, cursor_position)?;
- let populate = if self.cells[i][j] != Cell::Populated {
- Some(Message::Populate { i, j })
- } else {
+ let populate = if self.alive_cells.contains(&cell) {
None
+ } else {
+ Some(Message::Populate { cell })
};
match event {
@@ -339,14 +337,17 @@ mod grid {
),
);
+ let visible_rows = region.height as usize / CELL_SIZE;
+ let visible_columns = region.width as usize / CELL_SIZE;
+
frame.with_save(|frame| {
frame.translate(Vector::new(region.x, region.y));
- frame.scale(region.width / SIZE as f32);
+ frame.scale(CELL_SIZE as f32);
let cells = Path::new(|p| {
- for (i, row) in self.cells.iter().enumerate() {
- for (j, cell) in row.iter().enumerate() {
- if *cell == Cell::Populated {
+ for i in 0..visible_rows {
+ for j in 0..visible_columns {
+ if self.alive_cells.contains(&(i, j)) {
p.rectangle(
Point::new(j as f32, i as f32),
cell_size,
@@ -363,7 +364,7 @@ mod grid {
let mut frame = Frame::new(bounds.size());
frame.translate(Vector::new(region.x, region.y));
- frame.scale(region.width / SIZE as f32);
+ frame.scale(CELL_SIZE as f32);
if let Some(cursor_position) = cursor.position_in(&bounds) {
if let Some((i, j)) = self.cell_at(region, cursor_position)