docs(README): readme + cargo fmt/clippy fixes
parent
74e626da69
commit
b9efb79fd9
|
@ -1 +1,3 @@
|
|||
/target
|
||||
/.vscode
|
||||
/dist
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"liveServer.settings.root": "/dist"
|
||||
}
|
|
@ -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
|
|
@ -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.
66
src/main.rs
66
src/main.rs
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue