Display some statistics in game_of_life
This commit is contained in:
parent
916a1bfc70
commit
0025b8c3f8
2 changed files with 119 additions and 23 deletions
|
|
@ -26,6 +26,8 @@ struct GameOfLife {
|
|||
next_button: button::State,
|
||||
clear_button: button::State,
|
||||
speed_slider: slider::State,
|
||||
tick_duration: Duration,
|
||||
tick_amount: usize,
|
||||
}
|
||||
|
||||
enum State {
|
||||
|
|
@ -71,7 +73,12 @@ impl Application for GameOfLife {
|
|||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
match message {
|
||||
Message::Grid(message) => {
|
||||
self.grid.update(message);
|
||||
if let Some((tick_duration, tick_amount)) =
|
||||
self.grid.update(message)
|
||||
{
|
||||
self.tick_duration = tick_duration;
|
||||
self.tick_amount = tick_amount;
|
||||
}
|
||||
}
|
||||
Message::Tick(_) | Message::Next => match &mut self.state {
|
||||
State::Paused => {
|
||||
|
|
@ -86,7 +93,7 @@ impl Application for GameOfLife {
|
|||
let needed_ticks =
|
||||
(self.speed as f32 * seconds_elapsed).ceil() as usize;
|
||||
|
||||
if let Some(task) = self.grid.tick(needed_ticks) {
|
||||
if let Some(task) = self.grid.tick(needed_ticks.max(1)) {
|
||||
*last_tick = Instant::now();
|
||||
|
||||
if let Some(speed) = self.next_speed.take() {
|
||||
|
|
@ -154,33 +161,49 @@ impl Application for GameOfLife {
|
|||
|
||||
let selected_speed = self.next_speed.unwrap_or(self.speed);
|
||||
let speed_controls = Row::new()
|
||||
.width(Length::Fill)
|
||||
.align_items(Align::Center)
|
||||
.spacing(10)
|
||||
.push(
|
||||
Slider::new(
|
||||
&mut self.speed_slider,
|
||||
1.0..=100.0,
|
||||
1.0..=1000.0,
|
||||
selected_speed as f32,
|
||||
Message::SpeedChanged,
|
||||
)
|
||||
.width(Length::Units(200))
|
||||
.style(style::Slider),
|
||||
)
|
||||
.push(Text::new(format!("x{}", selected_speed)).size(16))
|
||||
.align_items(Align::Center);
|
||||
.push(Text::new(format!("x{}", selected_speed)).size(16));
|
||||
|
||||
let stats = Column::new()
|
||||
.width(Length::Units(150))
|
||||
.align_items(Align::Center)
|
||||
.spacing(2)
|
||||
.push(
|
||||
Text::new(format!("{} cells", self.grid.cell_count())).size(14),
|
||||
)
|
||||
.push(
|
||||
Text::new(format!(
|
||||
"{:?} ({})",
|
||||
self.tick_duration, self.tick_amount
|
||||
))
|
||||
.size(14),
|
||||
);
|
||||
|
||||
let controls = Row::new()
|
||||
.padding(10)
|
||||
.spacing(20)
|
||||
.align_items(Align::Center)
|
||||
.push(playback_controls)
|
||||
.push(speed_controls)
|
||||
.push(stats)
|
||||
.push(
|
||||
Button::new(&mut self.clear_button, Text::new("Clear"))
|
||||
.on_press(Message::Clear)
|
||||
.style(style::Button),
|
||||
.style(style::Clear),
|
||||
);
|
||||
|
||||
let content = Column::new()
|
||||
.align_items(Align::Center)
|
||||
.push(self.grid.view().map(Message::Grid))
|
||||
.push(controls);
|
||||
|
||||
|
|
@ -199,6 +222,7 @@ mod grid {
|
|||
};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use std::future::Future;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
pub struct Grid {
|
||||
state: State,
|
||||
|
|
@ -214,6 +238,8 @@ mod grid {
|
|||
Populate(Cell),
|
||||
Ticked {
|
||||
result: Result<Life, TickError>,
|
||||
tick_duration: Duration,
|
||||
tick_amount: usize,
|
||||
version: usize,
|
||||
},
|
||||
}
|
||||
|
|
@ -240,16 +266,29 @@ mod grid {
|
|||
const MIN_SCALING: f32 = 0.1;
|
||||
const MAX_SCALING: f32 = 2.0;
|
||||
|
||||
pub fn cell_count(&self) -> usize {
|
||||
self.state.cell_count()
|
||||
}
|
||||
|
||||
pub fn tick(
|
||||
&mut self,
|
||||
amount: usize,
|
||||
) -> Option<impl Future<Output = Message>> {
|
||||
use iced::futures::FutureExt;
|
||||
|
||||
let version = self.version;
|
||||
let tick = self.state.tick(amount)?;
|
||||
|
||||
Some(tick.map(move |result| Message::Ticked { result, version }))
|
||||
Some(async move {
|
||||
let start = Instant::now();
|
||||
let result = tick.await;
|
||||
let tick_duration = start.elapsed() / amount as u32;
|
||||
|
||||
Message::Ticked {
|
||||
result,
|
||||
version,
|
||||
tick_duration,
|
||||
tick_amount: amount,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
|
|
@ -259,25 +298,36 @@ mod grid {
|
|||
self.cache.clear();
|
||||
}
|
||||
|
||||
pub fn update(&mut self, message: Message) {
|
||||
pub fn update(
|
||||
&mut self,
|
||||
message: Message,
|
||||
) -> Option<(Duration, usize)> {
|
||||
match message {
|
||||
Message::Populate(cell) => {
|
||||
self.state.populate(cell);
|
||||
self.cache.clear()
|
||||
self.cache.clear();
|
||||
|
||||
None
|
||||
}
|
||||
Message::Ticked {
|
||||
result: Ok(life),
|
||||
version,
|
||||
tick_duration,
|
||||
tick_amount,
|
||||
} if version == self.version => {
|
||||
self.state.update(life);
|
||||
self.cache.clear()
|
||||
self.cache.clear();
|
||||
|
||||
Some((tick_duration, tick_amount))
|
||||
}
|
||||
Message::Ticked {
|
||||
result: Err(error), ..
|
||||
} => {
|
||||
dbg!(error);
|
||||
|
||||
None
|
||||
}
|
||||
Message::Ticked { .. } => {}
|
||||
Message::Ticked { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -478,6 +528,10 @@ mod grid {
|
|||
}
|
||||
|
||||
impl State {
|
||||
fn cell_count(&self) -> usize {
|
||||
self.life.len() + self.births.len()
|
||||
}
|
||||
|
||||
fn contains(&self, cell: &Cell) -> bool {
|
||||
self.life.contains(cell) || self.births.contains(cell)
|
||||
}
|
||||
|
|
@ -533,6 +587,18 @@ mod grid {
|
|||
}
|
||||
|
||||
impl Life {
|
||||
fn len(&self) -> usize {
|
||||
self.cells.len()
|
||||
}
|
||||
|
||||
fn contains(&self, cell: &Cell) -> bool {
|
||||
self.cells.contains(cell)
|
||||
}
|
||||
|
||||
fn populate(&mut self, cell: Cell) {
|
||||
self.cells.insert(cell);
|
||||
}
|
||||
|
||||
fn tick(&mut self) {
|
||||
let mut adjacent_life = FxHashMap::default();
|
||||
|
||||
|
|
@ -559,14 +625,6 @@ mod grid {
|
|||
}
|
||||
}
|
||||
|
||||
fn contains(&self, cell: &Cell) -> bool {
|
||||
self.cells.contains(cell)
|
||||
}
|
||||
|
||||
fn populate(&mut self, cell: Cell) {
|
||||
self.cells.insert(cell);
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Cell> {
|
||||
self.cells.iter()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,12 @@ const ACTIVE: Color = Color::from_rgb(
|
|||
0xDA as f32 / 255.0,
|
||||
);
|
||||
|
||||
const DESTRUCTIVE: Color = Color::from_rgb(
|
||||
0xC0 as f32 / 255.0,
|
||||
0x47 as f32 / 255.0,
|
||||
0x47 as f32 / 255.0,
|
||||
);
|
||||
|
||||
const HOVERED: Color = Color::from_rgb(
|
||||
0x67 as f32 / 255.0,
|
||||
0x7B as f32 / 255.0,
|
||||
|
|
@ -55,6 +61,38 @@ impl button::StyleSheet for Button {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Clear;
|
||||
|
||||
impl button::StyleSheet for Clear {
|
||||
fn active(&self) -> button::Style {
|
||||
button::Style {
|
||||
background: Some(Background::Color(DESTRUCTIVE)),
|
||||
border_radius: 3,
|
||||
text_color: Color::WHITE,
|
||||
..button::Style::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn hovered(&self) -> button::Style {
|
||||
button::Style {
|
||||
background: Some(Background::Color(Color {
|
||||
a: 0.5,
|
||||
..DESTRUCTIVE
|
||||
})),
|
||||
text_color: Color::WHITE,
|
||||
..self.active()
|
||||
}
|
||||
}
|
||||
|
||||
fn pressed(&self) -> button::Style {
|
||||
button::Style {
|
||||
border_width: 1,
|
||||
border_color: Color::WHITE,
|
||||
..self.hovered()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Slider;
|
||||
|
||||
impl slider::StyleSheet for Slider {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue