diff --git a/Cargo.lock b/Cargo.lock index eba0b13..59f7be6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2127,6 +2127,25 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -4098,6 +4117,26 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "read-fonts" version = "0.36.0" @@ -4607,6 +4646,7 @@ dependencies = [ "bevy", "rand 0.10.1", "rand_chacha 0.10.0", + "rayon", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e589110..e541598 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,5 @@ edition = "2024" [dependencies] bevy = { version = "0.18.1", features = ["keyboard"] } rand = "0.10.1" -rand_chacha = "0.10.0" \ No newline at end of file +rand_chacha = "0.10.0" +rayon = "1.12.0" \ No newline at end of file diff --git a/src/basic.rs b/src/basic.rs index d033047..5c75209 100644 --- a/src/basic.rs +++ b/src/basic.rs @@ -30,7 +30,7 @@ impl Table { &self.data[index] } - fn get_dim(&mut self, x: usize, y: usize) -> &T { + fn get_dim(&self, x: usize, y: usize) -> &T { self.get(x + y * self.side) } @@ -60,7 +60,7 @@ impl Table { self.side = new_side; } - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { self.data.iter() } @@ -69,6 +69,23 @@ impl Table { table.data[i] = f(self[i].clone()) } } + + pub fn around_line(&self, index: usize) -> Vec<&T> { + self.around(index % self.side, index / self.side) + } + + + pub fn around(&self, x: usize, y: usize) -> Vec<&T> { + let coordinates = vec![ + (x as isize, y as isize - 1), + (x as isize, y as isize + 1), + (x as isize + 1, y as isize), + (x as isize - 1, y as isize), + ]; + coordinates.iter().filter(|(x, y)| { + *x >= 0 && *x < self.side as isize && *y >= 0 && *y < self.side as isize + }).map(|(x, y)| self.get_dim(*x as usize, *y as usize)).collect::>() + } } impl Into> for Table { @@ -99,6 +116,23 @@ impl IntoImage for Table { data } } +impl IntoImage for Table { + fn get_image_data(&self) -> Vec { + let mut data: Vec = vec![0; self.side * self.side * 4]; + self.data + .iter() + .zip((0..self.data.len()).collect::>()) + .for_each(|(color, idx)| { + let idx = idx * 4; + let color = color.to_linear(); + data[idx] = (color.red * 255.0) as u8; + data[idx + 1] = (color.green * 255.0) as u8; + data[idx + 2] = (color.blue * 255.0) as u8; + data[idx + 3] = 255; + }); + data + } +} impl Index for Table { type Output = T; diff --git a/src/main.rs b/src/main.rs index 9648f69..47b83eb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,28 +1,38 @@ mod basic; mod seed_automation; mod plate_automation; +mod subplate_automation; use crate::basic::{IntoImage, Table}; use bevy::asset::io::embedded::GetAssetServer; use bevy::input::keyboard::keyboard_input_system; use bevy::prelude::*; +use bevy::window::{ExitCondition, WindowResolution}; use rand::{Rng, RngExt}; use rand_chacha::ChaCha8Rng; use rand_chacha::rand_core::SeedableRng; use seed_automation::SeedAutomation; -const RECTANGLE_SIDE: f32 = 500.0; +const RECTANGLE_SIDE: f32 = 750.0; fn main() { App::new() - .add_plugins(DefaultPlugins) + .add_plugins(DefaultPlugins.set(WindowPlugin { + primary_window: Some(Window { + title: "Automations".into(), + resolution: WindowResolution::new(RECTANGLE_SIDE as u32, RECTANGLE_SIDE as u32), + ..default() + }), + ..default() + })) .add_systems(Startup, setup) .insert_resource(Time::::from_hz(1024.0)) - // .add_systems(Update, automation_keyboard_input_system) - .add_systems(Update, seed_automation::update_automation_seed) - .add_systems(Update, seed_automation::update_automation_view_seed) - .add_systems(Update, plate_automation::update_automation_plate) - .add_systems(Update, plate_automation::update_automation_view_plate) + .add_systems(Update, seed_automation::update_automation) + .add_systems(Update, seed_automation::update_automation_view) + .add_systems(Update, plate_automation::update_automation) + .add_systems(Update, plate_automation::update_automation_view) + .add_systems(Update, subplate_automation::update_automation) + .add_systems(Update, subplate_automation::update_automation_view) .run(); } @@ -44,27 +54,51 @@ fn setup(mut commands: Commands, mut images: ResMut>) { commands.insert_resource(SeededRng(seeded_rng)); } -// fn automation_keyboard_input_system(keys: Res>, mut commands: Commands) { -// if keys.just_pressed(KeyCode::Space) { -// commands.spawn((AutomationNext,)); -// } -// -// if keys.just_pressed(KeyCode::Tab) { -// commands.spawn((AutomationMigrate,)); -// } -// -// if keys.just_pressed(KeyCode::AltLeft) { -// commands.spawn((AutomationScale,)); -// } -// } -// -// #[derive(Component)] -// struct AutomationNext; -// -// #[derive(Component)] -// struct AutomationScale; -// -// #[derive(Component)] -// struct AutomationMigrate; #[derive(Resource)] struct SeededRng(ChaCha8Rng); + +#[cfg(test)] +mod tests { + use crate::basic::Table; + + #[test] + fn around_1(){ + let mut table = Table::new(false, 16); + let around = table.around(0, 0); + assert_eq!(around.len(), 2); + } + + #[test] + fn around_2(){ + let mut table = Table::new(false, 16); + let around = table.around(15, 15); + assert_eq!(around.len(), 2); + } + + #[test] + fn around_3(){ + let mut table = Table::new(false, 16); + let around = table.around(5, 5); + assert_eq!(around.len(), 4); + } + #[test] + fn around_1_line(){ + let mut table = Table::new(false, 16); + let around = table.around_line(0); + assert_eq!(around.len(), 2); + } + + #[test] + fn around_2_line(){ + let mut table = Table::new(false, 16); + let around = table.around_line(16 * 16 - 1); + assert_eq!(around.len(), 2); + } + + #[test] + fn around_3_line(){ + let mut table = Table::new(false, 16); + let around = table.around_line(24); + assert_eq!(around.len(), 4); + } +} \ No newline at end of file diff --git a/src/plate_automation.rs b/src/plate_automation.rs index be3724f..9493154 100644 --- a/src/plate_automation.rs +++ b/src/plate_automation.rs @@ -3,12 +3,14 @@ use crate::basic::{IntoImage, Table}; use bevy::asset::{Assets, RenderAssetUsages}; use bevy::image::{BevyDefault, Image}; use bevy::input::ButtonInput; -use bevy::prelude::{Component, KeyCode, Query, Res, ResMut, Sprite}; +use bevy::prelude::{Color, Commands, Component, Entity, KeyCode, Query, Res, ResMut, Sprite}; use bevy::render::render_resource::{Extent3d, TextureDimension, TextureFormat}; use rand::RngExt; use rand_chacha::ChaCha8Rng; +use crate::seed_automation::SeedAutomation; +use crate::subplate_automation::SubPlateAutomation; -pub fn update_automation_view_plate( +pub fn update_automation_view( query: Query<(&Sprite, &PlateAutomation)>, mut images: ResMut>, ) { @@ -34,17 +36,18 @@ pub fn update_automation_view_plate( images.insert(sprite.image.id(), image).unwrap(); } -pub fn update_automation_plate( - mut query: Query<&mut PlateAutomation>, +pub fn update_automation( + mut query: Query<(&mut PlateAutomation, Entity)>, mut seeded_rng: ResMut, keys: Res>, + mut commands: Commands, ) { if query.is_empty() { return; } - - let mut automation = query.iter_mut().next().unwrap(); - if keys.just_pressed(KeyCode::Space) { + + let (mut automation, entity) = query.iter_mut().next().unwrap(); + if keys.just_pressed(KeyCode::Space) || keys.all_pressed([KeyCode::Space, KeyCode::ShiftLeft]) { let random = &mut seeded_rng.0; automation.next(random) @@ -53,6 +56,15 @@ pub fn update_automation_plate( if keys.just_pressed(KeyCode::AltLeft) { automation.world.grow() } + + if keys.just_pressed(KeyCode::Tab) { + println!("Switching to SubPlateAutomation"); + let mut new_table = Table::::new(Color::BLACK, automation.world.side); + automation.world.convert_copy(&mut new_table, |value| { if value { Color::WHITE } else { Color::BLACK } }); + commands.entity(entity).remove::().insert(SubPlateAutomation{ + world: new_table + }); + } } #[derive(Component)] @@ -62,6 +74,22 @@ pub struct PlateAutomation { impl PlateAutomation { fn next(&mut self, rng: &mut ChaCha8Rng) { - println!("Next for plate automation"); + for index in 0..self.world.side * self.world.side { + if *self.world.get(index) { + continue; + } + + let around = self.world.around_line(index).iter().filter(|v| ***v).count(); + + if around == 0 { + continue; + } + let chance = (around as f32 * 0.2).powi(2); + if rng.random::() > chance { + continue; + } + + self.world.set(index, true); + } } } diff --git a/src/seed_automation.rs b/src/seed_automation.rs index d13577d..62dfa3f 100644 --- a/src/seed_automation.rs +++ b/src/seed_automation.rs @@ -9,7 +9,7 @@ use rand::RngExt; use rand_chacha::ChaCha8Rng; use crate::plate_automation::PlateAutomation; -pub fn update_automation_view_seed( +pub fn update_automation_view( query: Query<(&Sprite, &SeedAutomation)>, mut images: ResMut>, ) { @@ -36,7 +36,7 @@ pub fn update_automation_view_seed( images.insert(sprite.image.id(), image).unwrap(); } -pub fn update_automation_seed( +pub fn update_automation( mut query: Query<(&mut SeedAutomation, Entity)>, mut seeded_rng: ResMut, keys: Res>, diff --git a/src/subplate_automation.rs b/src/subplate_automation.rs new file mode 100644 index 0000000..8d09aa0 --- /dev/null +++ b/src/subplate_automation.rs @@ -0,0 +1,106 @@ +use crate::SeededRng; +use crate::basic::{IntoImage, Table}; +use bevy::asset::{Assets, RenderAssetUsages}; +use bevy::image::{BevyDefault, Image}; +use bevy::input::ButtonInput; +use bevy::prelude::{Color, Commands, Component, Entity, KeyCode, Query, Res, ResMut, Sprite}; +use bevy::render::render_resource::{Extent3d, TextureDimension, TextureFormat}; +use rand::{ RngExt}; +use rand_chacha::ChaCha8Rng; + +pub fn update_automation_view( + query: Query<(&Sprite, &SubPlateAutomation)>, + mut images: ResMut>, +) { + if query.is_empty() { + return; + } + let (sprite, automation) = query.iter().next().unwrap(); + let size = automation.world.side as u32; + let data = automation.world.get_image_data(); + let image = Image::new( + Extent3d { + width: size, + height: size, + depth_or_array_layers: 1, + }, + TextureDimension::D2, + data, + TextureFormat::bevy_default(), + RenderAssetUsages::default(), + ); + + images.remove(sprite.image.id()); + images.insert(sprite.image.id(), image).unwrap(); +} + +pub fn update_automation( + mut query: Query<(&mut SubPlateAutomation, Entity)>, + mut seeded_rng: ResMut, + keys: Res>, + commands: Commands, + +) { + if query.is_empty() { + return; + } + + let (mut automation, entity) = query.iter_mut().next().unwrap(); + if keys.just_pressed(KeyCode::Space) || keys.all_pressed([KeyCode::Space, KeyCode::ShiftLeft]) { + let random = &mut seeded_rng.0; + + automation.next(random) + } + + if keys.just_pressed(KeyCode::AltLeft) { + let random = &mut seeded_rng.0; + + automation.seed(random); + } + + if keys.just_pressed(KeyCode::Tab) { + } +} + +#[derive(Component)] +pub struct SubPlateAutomation { + pub(crate) world: Table, +} + +impl SubPlateAutomation { + fn next(&mut self, rng: &mut ChaCha8Rng) { + for index in 0..self.world.side * self.world.side { + let current_color = *self.world.get(index); + if current_color == Color::BLACK || current_color != Color::WHITE { + continue; + } + + if rng.random::() < 0.7 { + continue; + } + + let around = self.world.around_line(index).iter().filter(|v| ***v != Color::WHITE && ***v != Color::BLACK).map(|color| (*color).clone()).collect::>(); + + if around.len() == 0 { + continue; + } + let color = around[rng.random_range(0..around.len())]; + + + self.world.set(index, color); + } + } + + fn seed(&mut self, rng: &mut ChaCha8Rng){ + let len = (self.world.side * self.world.side) as f32; + loop { + let index = (rng.random::() * len) as usize; + if *self.world.get(index) == Color::BLACK { + continue; + } + + self.world.set(index, Color::hsv(rng.random::() * 360.0, 1.0, 1.0)); + break; + } + } +}