Skip to content

Commit 741849d

Browse files
grid logic 2 conway.rs (#3598)
Co-authored-by: Mattuwu <syan4@ualberta.ca>
1 parent 0f05f02 commit 741849d

File tree

3 files changed

+108
-180
lines changed

3 files changed

+108
-180
lines changed

examples/game_of_life/src/cell.rs

Lines changed: 0 additions & 52 deletions
This file was deleted.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use rand::Rng;
2+
3+
pub struct Conway {
4+
pub cellules: Vec<bool>,
5+
pub width: usize,
6+
pub height: usize,
7+
}
8+
9+
impl Conway {
10+
pub fn new(width: usize, height: usize) -> Self {
11+
Self {
12+
cellules: vec![false; width * height],
13+
width,
14+
height,
15+
}
16+
}
17+
18+
pub fn alive(&self, row: usize, col: usize) -> bool {
19+
self.cellules[row * self.width + col]
20+
}
21+
22+
pub fn toggle(&mut self, row: usize, col: usize) {
23+
let i = row * self.width + col;
24+
self.cellules[i] = !self.cellules[i];
25+
}
26+
27+
pub fn random_mutate(&mut self) {
28+
let mut rng = rand::rng();
29+
self.cellules.iter_mut().for_each(|c| *c = rng.random());
30+
}
31+
32+
pub fn reset(&mut self) {
33+
self.cellules.iter_mut().for_each(|c| *c = false);
34+
}
35+
36+
pub fn step(&mut self) {
37+
let mut to_toggle = Vec::new();
38+
for row in 0..self.height {
39+
for col in 0..self.width {
40+
let n = self.live_neighbours(row as isize, col as isize);
41+
if (self.alive(row, col) && (n <= 1 || n > 3)) || (!self.alive(row, col) && n == 3)
42+
{
43+
to_toggle.push((row, col));
44+
}
45+
}
46+
}
47+
to_toggle
48+
.iter()
49+
.for_each(|(row, col)| self.toggle(*row, *col));
50+
}
51+
52+
fn live_neighbours(&self, row: isize, col: isize) -> usize {
53+
(-1..=1)
54+
.flat_map(|r| (-1..=1).map(move |c| (r, c)))
55+
.filter(|&(r, c)| (r, c) != (0, 0))
56+
.filter(|&(r, c)| self.cellules[self.row_col_as_idx(row + r, col + c)])
57+
.count()
58+
}
59+
60+
fn row_col_as_idx(&self, row: isize, col: isize) -> usize {
61+
let row = wrap(row, self.height as isize);
62+
let col = wrap(col, self.width as isize);
63+
row * self.width + col
64+
}
65+
}
66+
67+
fn wrap(idx: isize, range: isize) -> usize {
68+
((idx % range + range) % range) as usize // because % has sign of dividend
69+
}

examples/game_of_life/src/main.rs

Lines changed: 39 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,185 +1,107 @@
1-
use cell::Cellule;
21
use gloo::timers::callback::Interval;
3-
use rand::Rng;
42
use yew::html::Scope;
53
use yew::{classes, html, Component, Context, Html};
64

7-
mod cell;
5+
mod conway;
86

97
pub enum Msg {
108
Random,
119
Start,
1210
Step,
1311
Reset,
1412
Stop,
15-
ToggleCellule(usize),
13+
ToggleCellule((usize, usize)),
1614
Tick,
1715
}
1816

1917
pub struct App {
2018
active: bool,
21-
cellules: Vec<Cellule>,
22-
cellules_width: usize,
23-
cellules_height: usize,
19+
conway: conway::Conway,
2420
_interval: Interval,
2521
}
2622

2723
impl App {
28-
pub fn random_mutate(&mut self) {
29-
for cellule in self.cellules.iter_mut() {
30-
if rand::rng().random() {
31-
cellule.set_alive();
32-
} else {
33-
cellule.set_dead();
34-
}
35-
}
36-
}
37-
38-
fn reset(&mut self) {
39-
for cellule in self.cellules.iter_mut() {
40-
cellule.set_dead();
41-
}
42-
}
43-
44-
fn step(&mut self) {
45-
let mut to_dead = Vec::new();
46-
let mut to_live = Vec::new();
47-
for row in 0..self.cellules_height {
48-
for col in 0..self.cellules_width {
49-
let neighbors = self.neighbors(row as isize, col as isize);
50-
51-
let current_idx = self.row_col_as_idx(row as isize, col as isize);
52-
if self.cellules[current_idx].is_alive() {
53-
if Cellule::alone(&neighbors) || Cellule::overpopulated(&neighbors) {
54-
to_dead.push(current_idx);
55-
}
56-
} else if Cellule::can_be_revived(&neighbors) {
57-
to_live.push(current_idx);
58-
}
59-
}
60-
}
61-
to_dead
62-
.iter()
63-
.for_each(|idx| self.cellules[*idx].set_dead());
64-
to_live
65-
.iter()
66-
.for_each(|idx| self.cellules[*idx].set_alive());
67-
}
68-
69-
fn neighbors(&self, row: isize, col: isize) -> [Cellule; 8] {
70-
[
71-
self.cellules[self.row_col_as_idx(row + 1, col)],
72-
self.cellules[self.row_col_as_idx(row + 1, col + 1)],
73-
self.cellules[self.row_col_as_idx(row + 1, col - 1)],
74-
self.cellules[self.row_col_as_idx(row - 1, col)],
75-
self.cellules[self.row_col_as_idx(row - 1, col + 1)],
76-
self.cellules[self.row_col_as_idx(row - 1, col - 1)],
77-
self.cellules[self.row_col_as_idx(row, col - 1)],
78-
self.cellules[self.row_col_as_idx(row, col + 1)],
79-
]
80-
}
81-
82-
fn row_col_as_idx(&self, row: isize, col: isize) -> usize {
83-
let row = wrap(row, self.cellules_height as isize);
84-
let col = wrap(col, self.cellules_width as isize);
85-
86-
row * self.cellules_width + col
87-
}
88-
89-
fn view_cellule(&self, idx: usize, cellule: &Cellule, link: &Scope<Self>) -> Html {
90-
let cellule_status = {
91-
if cellule.is_alive() {
92-
"cellule-live"
93-
} else {
94-
"cellule-dead"
95-
}
24+
fn view_cellule(&self, row: usize, col: usize, link: &Scope<Self>) -> Html {
25+
let status = if self.conway.alive(row, col) {
26+
"cellule-live"
27+
} else {
28+
"cellule-dead"
9629
};
9730
html! {
98-
<div key={idx} class={classes!("game-cellule", cellule_status)}
99-
onclick={link.callback(move |_| Msg::ToggleCellule(idx))}>
31+
<div class={classes!("game-cellule", status)}
32+
onclick={link.callback(move |_| Msg::ToggleCellule((row,col)))}>
10033
</div>
10134
}
10235
}
10336
}
37+
10438
impl Component for App {
10539
type Message = Msg;
10640
type Properties = ();
10741

10842
fn create(ctx: &Context<Self>) -> Self {
10943
let callback = ctx.link().callback(|_| Msg::Tick);
110-
let interval = Interval::new(200, move || callback.emit(()));
111-
112-
let (cellules_width, cellules_height) = (53, 40);
11344

11445
Self {
11546
active: false,
116-
cellules: vec![Cellule::new_dead(); cellules_width * cellules_height],
117-
cellules_width,
118-
cellules_height,
119-
_interval: interval,
47+
conway: conway::Conway::new(53, 40),
48+
_interval: Interval::new(200, move || callback.emit(())),
12049
}
12150
}
12251

12352
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
53+
let mut render = true;
12454
match msg {
12555
Msg::Random => {
126-
self.random_mutate();
56+
self.conway.random_mutate();
12757
log::info!("Random");
128-
true
12958
}
13059
Msg::Start => {
13160
self.active = true;
13261
log::info!("Start");
133-
false
62+
render = false;
13463
}
13564
Msg::Step => {
136-
self.step();
137-
true
65+
self.conway.step();
13866
}
13967
Msg::Reset => {
140-
self.reset();
68+
self.conway.reset();
14169
log::info!("Reset");
142-
true
14370
}
14471
Msg::Stop => {
14572
self.active = false;
14673
log::info!("Stop");
147-
false
148-
}
149-
Msg::ToggleCellule(idx) => {
150-
let cellule = self.cellules.get_mut(idx).unwrap();
151-
cellule.toggle();
152-
true
74+
render = false;
15375
}
76+
Msg::ToggleCellule((row, col)) => self.conway.toggle(row, col),
15477
Msg::Tick => {
15578
if self.active {
156-
self.step();
157-
true
79+
self.conway.step();
15880
} else {
159-
false
81+
render = false;
16082
}
16183
}
16284
}
85+
render
16386
}
16487

16588
fn view(&self, ctx: &Context<Self>) -> Html {
166-
let cell_rows =
167-
self.cellules
168-
.chunks(self.cellules_width)
169-
.enumerate()
170-
.map(|(y, cellules)| {
171-
let idx_offset = y * self.cellules_width;
172-
173-
let cells = cellules
174-
.iter()
175-
.enumerate()
176-
.map(|(x, cell)| self.view_cellule(idx_offset + x, cell, ctx.link()));
177-
html! {
178-
<div key={y} class="game-row">
179-
{ for cells }
180-
</div>
181-
}
182-
});
89+
let cell_rows = self
90+
.conway
91+
.cellules
92+
.chunks(self.conway.width)
93+
.enumerate()
94+
.map(|(row, cellules)| {
95+
let cells = cellules
96+
.iter()
97+
.enumerate()
98+
.map(|(col, _)| self.view_cellule(row, col, ctx.link()));
99+
html! {
100+
<div class="game-row">
101+
{ for cells }
102+
</div>
103+
}
104+
});
183105

184106
html! {
185107
<div>
@@ -212,17 +134,6 @@ impl Component for App {
212134
}
213135
}
214136

215-
fn wrap(coord: isize, range: isize) -> usize {
216-
let result = if coord < 0 {
217-
coord + range
218-
} else if coord >= range {
219-
coord - range
220-
} else {
221-
coord
222-
};
223-
result as usize
224-
}
225-
226137
fn main() {
227138
wasm_logger::init(wasm_logger::Config::default());
228139
log::trace!("Initializing yew...");

0 commit comments

Comments
 (0)