docs(README): readme + cargo fmt/clippy fixes

main
flavien 2022-12-21 16:30:43 +01:00
parent 74e626da69
commit b9efb79fd9
7 changed files with 89 additions and 1430 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
/target
/.vscode
/dist

View File

@ -1,3 +0,0 @@
{
"liveServer.settings.root": "/dist"
}

43
README.md Normal file
View File

@ -0,0 +1,43 @@
# Game of life
Game of life implementation in Rust with egui UI library.
Compilable in WASM with trunk for Web embedding.
<br>
### Currently implemented:
- configurable is size and speed only in code
- no UI
- only print a giant game of life
- work in native and wasm build
<br>
### build and run
**native**
Release mode with egui is really faster so you must run with:
`cargo run --release`
**wasm**
Add wasm32 build target with:
`rustup target add wasm32-unknown-unknown`
Install trunk with:
`cargo install trunk`
Build with:
`trunk build --release`
You can now open the `index.html` file built in the `dist` folder in your browser to run the game.
<br>
## TODO
### Game
toolbar UI with:
- pause/start/restart buttons
- speed/size change selectors
- choose between known interesting seed or totally random one
- only live/dead colors mode or colored birth/death mode
interactions:
- click to kill or create a cell
...
### Embedding
- embed in cyberduck.blog

8
dist/index.html vendored
View File

@ -1,8 +0,0 @@
<html><head>
<link rel="preload" href="/wasm_game_of_life-2b18a98f9a6744b5_bg.wasm" as="fetch" type="application/wasm" crossorigin="">
<link rel="modulepreload" href="/wasm_game_of_life-2b18a98f9a6744b5.js"></head>
<body>
<canvas id="canvas" style="position: absolute; top: 0%; left: 0%; width: 1600px; height: 900px;"></canvas>
<script type="module">import init from '/wasm_game_of_life-2b18a98f9a6744b5.js';init('/wasm_game_of_life-2b18a98f9a6744b5_bg.wasm');</script></body></html>

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,7 +1,7 @@
use std::fmt;
use eframe::epaint::RectShape;
use egui::{pos2, Color32, Rect, Rounding, Sense, Shape, Stroke, Vec2};
use rand::Rng;
use egui::{Sense, Vec2, Color32, Stroke, pos2, Rect, Rounding, Shape};
use std::fmt;
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@ -14,7 +14,7 @@ pub struct Universe {
width: u32,
height: u32,
age: u32,
cells: (Vec<Cell>, Vec<Cell>)
cells: (Vec<Cell>, Vec<Cell>),
}
impl Universe {
@ -51,7 +51,7 @@ impl Universe {
let current = match self.age % 2 {
0 => &self.cells.0,
_ => &self.cells.1
_ => &self.cells.1,
};
let mut width_counter = 0;
let mut width_origin = 0.0;
@ -65,20 +65,34 @@ impl Universe {
width_counter += 1;
} else if width_counter != 0 {
let y = row as f32 * cell_side;
let rect = Rect::from_min_size(pos2(width_origin, y) + canvas_origin, Vec2::new(cell_side*width_counter as f32, cell_side));
rectangles_cache.push(Shape::Rect(RectShape {rect, rounding, fill, stroke}));
let rect = Rect::from_min_size(
pos2(width_origin, y) + canvas_origin,
Vec2::new(cell_side * width_counter as f32, cell_side),
);
rectangles_cache.push(Shape::Rect(RectShape {
rect,
rounding,
fill,
stroke,
}));
width_counter = 0;
}
}
}
return rectangles_cache;
rectangles_cache
}
fn get_index(width: u32, row: u32, column: u32) -> usize {
(row * width + column) as usize
}
fn live_neighbor_count(universe: &Vec<Cell>, height:u32, width: u32, row: u32, column: u32) -> u8 {
fn live_neighbor_count(
universe: &[Cell],
height: u32,
width: u32,
row: u32,
column: u32,
) -> u8 {
let mut count = 0;
for delta_row in [height - 1, 0, 1] {
for delta_col in [width - 1, 0, 1] {
@ -104,7 +118,8 @@ impl Universe {
for col in 0..self.width {
let idx = Universe::get_index(self.width, row, col);
let cell = current[idx];
let live_neighbors = Universe::live_neighbor_count(current, self.height, self.width, row, col);
let live_neighbors =
Universe::live_neighbor_count(current, self.height, self.width, row, col);
let next_cell = match (cell, live_neighbors) {
// Rule 1: Any live cell with fewer than two live neighbours
@ -134,14 +149,14 @@ impl fmt::Display for Universe {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let current = match self.age % 2 {
0 => &self.cells.0,
_ => &self.cells.1
_ => &self.cells.1,
};
for line in current.as_slice().chunks(self.width as usize) {
for &cell in line {
let symbol = if cell == Cell::Dead { '◻' } else { '◼' };
write!(f, "{}", symbol)?;
}
write!(f, "\n")?;
writeln!(f)?;
}
Ok(())
}
@ -180,16 +195,20 @@ fn main() {
resizable: false,
..Default::default()
};
eframe::run_native("My egui App", options, Box::new(|cc| Box::new(MyEguiApp::new(cc))));
eframe::run_native(
"My egui App",
options,
Box::new(|cc| Box::new(MyEguiApp::new(cc))),
);
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Speed {
Slow = 16,
Normal = 8,
Fast = 4,
Fastest = 2
//Slow = 16,
//Normal = 8,
//Fast = 4,
Fastest = 2,
}
impl Default for Speed {
@ -201,8 +220,8 @@ impl Default for Speed {
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Size {
Small = 8,
Medium = 4,
//Small = 8,
//Medium = 4,
Large = 2,
}
@ -216,7 +235,7 @@ struct MyEguiApp {
game: Universe,
frame_counter: u8,
speed: Speed,
size: Size,
//size: Size,
cache: Vec<Shape>,
}
@ -228,10 +247,13 @@ impl MyEguiApp {
// for e.g. egui::PaintCallback.
let size = Size::default();
Self {
game: Universe::new(CANVAS_WIDTH as u32 / size as u32, CANVAS_HEIGHT as u32 / size as u32),
game: Universe::new(
CANVAS_WIDTH as u32 / size as u32,
CANVAS_HEIGHT as u32 / size as u32,
),
frame_counter: 0,
speed: Speed::default(),
size,
//size,
cache: Vec::new(),
}
}
@ -254,4 +276,4 @@ impl eframe::App for MyEguiApp {
self.frame_counter %= self.speed as u8;
ctx.request_repaint();
}
}
}