|
|
|
@ -1,15 +1,14 @@
|
|
|
|
// TODO:
|
|
|
|
// TODO:
|
|
|
|
// * Volume slider
|
|
|
|
// * Volume slider
|
|
|
|
// * Invert mute/unmute icons
|
|
|
|
|
|
|
|
// * Only move camera when cells move out of view
|
|
|
|
// * Only move camera when cells move out of view
|
|
|
|
|
|
|
|
// * "Follow" enhancement: Zoom in/out
|
|
|
|
|
|
|
|
// * optimize simulation_step
|
|
|
|
// * Stop creating cells when I didn't mean to (clicking UI, click + dragging)
|
|
|
|
// * Stop creating cells when I didn't mean to (clicking UI, click + dragging)
|
|
|
|
// * Keep some controls open when main ui panel is closed
|
|
|
|
// * Keep some controls open when main ui panel is closed
|
|
|
|
// * Exclude power button from web build
|
|
|
|
|
|
|
|
// * Show shadow of piece (same tile, just with alpha transparency)
|
|
|
|
// * Show shadow of piece (same tile, just with alpha transparency)
|
|
|
|
// * Dark mode
|
|
|
|
// * Dark mode
|
|
|
|
// * Make click and drag feel better
|
|
|
|
// * Make click and drag feel better
|
|
|
|
// * Add arrow keys to move around
|
|
|
|
// * Add arrow keys to move around
|
|
|
|
// * "Follow" enhancement: Zoom in/out
|
|
|
|
|
|
|
|
#![allow(clippy::complexity)]
|
|
|
|
#![allow(clippy::complexity)]
|
|
|
|
// Only because of FromTemplat macros unfortunately...
|
|
|
|
// Only because of FromTemplat macros unfortunately...
|
|
|
|
#![allow(dead_code)]
|
|
|
|
#![allow(dead_code)]
|
|
|
|
@ -821,61 +820,45 @@ enum SimulationPlayback {
|
|
|
|
fn simulation_step(
|
|
|
|
fn simulation_step(
|
|
|
|
q: Query<(Entity, &Coordinates), With<Cell>>,
|
|
|
|
q: Query<(Entity, &Coordinates), With<Cell>>,
|
|
|
|
mut writer: MessageWriter<Lifecycle>,
|
|
|
|
mut writer: MessageWriter<Lifecycle>,
|
|
|
|
|
|
|
|
mut alive: Local<HashMap<Coordinates, Entity>>,
|
|
|
|
|
|
|
|
mut candidates: Local<HashSet<Coordinates>>,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
fn is_cell_alive(
|
|
|
|
let living_neighbors = |a: &HashMap<Coordinates, Entity>, c: &Coordinates| -> usize {
|
|
|
|
coordinates: &Coordinates,
|
|
|
|
|
|
|
|
query: &Query<(Entity, &Coordinates), With<Cell>>,
|
|
|
|
|
|
|
|
) -> bool {
|
|
|
|
|
|
|
|
query.iter().any(|(_e, c)| *c == *coordinates)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn living_neighbors(
|
|
|
|
|
|
|
|
c: &Coordinates,
|
|
|
|
|
|
|
|
query: &Query<(Entity, &Coordinates), With<Cell>>,
|
|
|
|
|
|
|
|
) -> usize {
|
|
|
|
|
|
|
|
c.neighbors()
|
|
|
|
c.neighbors()
|
|
|
|
.filter(|neighbor| {
|
|
|
|
.filter(|neighbor| {
|
|
|
|
query
|
|
|
|
a.contains_key(neighbor)
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.any(|(_e, coordinates)| *neighbor == *coordinates)
|
|
|
|
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.count()
|
|
|
|
.count()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
fn cell_entity(
|
|
|
|
// Reset alive cell cache
|
|
|
|
coordinates: &Coordinates,
|
|
|
|
*alive = q.iter().map(|(e, c)| (c.clone(), e)).collect();
|
|
|
|
query: Query<(Entity, &Coordinates), With<Cell>>,
|
|
|
|
|
|
|
|
) -> Option<Entity> {
|
|
|
|
|
|
|
|
query
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.find_map(|(e, c)| (*c == *coordinates).then_some(e))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Collect list of candidate positions...
|
|
|
|
// Clear candidate coordinates
|
|
|
|
let mut coordinates: HashSet<Coordinates> = HashSet::new();
|
|
|
|
candidates.clear();
|
|
|
|
|
|
|
|
|
|
|
|
// Put all dead cells neighboring living cells into the coordinates hashset
|
|
|
|
// Put all dead cells neighboring living cells into the coordinates hashset
|
|
|
|
// Each living cell
|
|
|
|
// Each living cell
|
|
|
|
q.iter().for_each(|(_e, c)| {
|
|
|
|
q.iter().for_each(|(_e, c)| {
|
|
|
|
coordinates.insert(c.clone());
|
|
|
|
candidates.insert(c.clone());
|
|
|
|
// Iterator over neighbor positions
|
|
|
|
// Iterator over neighbor positions
|
|
|
|
// Filter to just dead neighbors
|
|
|
|
// Filter to just dead neighbors
|
|
|
|
c.neighbors()
|
|
|
|
c.neighbors()
|
|
|
|
.filter(|c| !is_cell_alive(c, &q))
|
|
|
|
.filter(|c| !alive.contains_key(c))
|
|
|
|
.for_each(|c| {
|
|
|
|
.for_each(|c| {
|
|
|
|
coordinates.insert(c);
|
|
|
|
candidates.insert(c);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Get Neighbor Count of living cells
|
|
|
|
// Get Neighbor Count of living cells
|
|
|
|
coordinates.iter().for_each(|c| {
|
|
|
|
candidates.iter().for_each(|c| {
|
|
|
|
let num_living_neighbors = living_neighbors(c, &q);
|
|
|
|
let num_living_neighbors = living_neighbors(&*alive, c);
|
|
|
|
let is_living = is_cell_alive(c, &q);
|
|
|
|
let is_living = alive.contains_key(c);
|
|
|
|
|
|
|
|
|
|
|
|
match num_living_neighbors {
|
|
|
|
match num_living_neighbors {
|
|
|
|
..2 | 4.. => {
|
|
|
|
..2 | 4.. => {
|
|
|
|
if is_living {
|
|
|
|
if is_living {
|
|
|
|
writer.write(Lifecycle::Dead(cell_entity(c, q).unwrap()));
|
|
|
|
writer.write(Lifecycle::Dead(alive.get(c).cloned().unwrap()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
2 => debug!("continue"),
|
|
|
|
2 => debug!("continue"),
|
|
|
|
|