Seed automation works

This commit is contained in:
2026-06-18 12:04:53 +03:00
parent 2cd2328b9e
commit d2d9809f15
6 changed files with 4216 additions and 1463 deletions
Generated
+4018 -1287
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -4,6 +4,6 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
bevy = { version = "0.18.1", features = ["keyboard"] }
ggez = "0.9.3" rand = "0.10.1"
rand = "0.9.1" rand_chacha = "0.10.0"
+14 -9
View File
@@ -1,5 +1,5 @@
use bevy::prelude::Color;
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};
use ggez::graphics::Color;
pub struct Table<T: Clone> { pub struct Table<T: Clone> {
default: T, default: T,
@@ -12,7 +12,7 @@ impl<T : Clone> Clone for Table<T>{
Table { Table {
default: self.default.clone(), default: self.default.clone(),
data: self.data.clone(), data: self.data.clone(),
side: self.side side: self.side,
} }
} }
} }
@@ -77,19 +77,24 @@ impl<T: Clone> Into<Vec<T>> for Table<T> {
} }
} }
impl Into<Vec<u8>> for Table<Color> { pub trait IntoImage {
fn into(self) -> Vec<u8> { fn get_image_data(&self) -> Vec<u8>;
}
impl IntoImage for Table<bool> {
fn get_image_data(&self) -> Vec<u8> {
let mut data: Vec<u8> = vec![0; self.side * self.side * 4]; let mut data: Vec<u8> = vec![0; self.side * self.side * 4];
self.data self.data
.iter() .iter()
.zip((0..self.data.len()).collect::<Vec<_>>()) .zip((0..self.data.len()).collect::<Vec<_>>())
.for_each(|(color, idx)| { .for_each(|(color, idx)| {
let (r, g, b, a) = color.to_rgba();
let idx = idx * 4; let idx = idx * 4;
data[idx] = r; let value = if *color { 255 } else { 0 };
data[idx + 1] = g;
data[idx + 2] = b; data[idx] = value;
data[idx + 3] = a; data[idx + 1] = value;
data[idx + 2] = value;
data[idx + 3] = 255;
}); });
data data
} }
+37 -154
View File
@@ -1,165 +1,48 @@
mod basic; mod basic;
mod seed_automation;
mod plate_automation;
use crate::basic::Table; use crate::basic::{IntoImage, Table};
use ggez::conf::{WindowMode, WindowSetup}; use bevy::asset::io::embedded::GetAssetServer;
use ggez::event::{self, EventHandler}; use bevy::input::keyboard::keyboard_input_system;
use ggez::glam::Vec2; use bevy::prelude::*;
use ggez::graphics::{self, Color, DrawParam, Image, ImageFormat}; use rand::{Rng, RngExt};
use ggez::{Context, ContextBuilder, GameResult}; use rand_chacha::ChaCha8Rng;
use rand::Rng; use rand_chacha::rand_core::SeedableRng;
use rand::rngs::ThreadRng; use seed_automation::SeedAutomation;
const WIN_SIZE_F: f32 = 1000.0; const RECTANGLE_SIDE: f32 = 500.0;
fn main() { fn main() {
// Make a Context. App::new()
let (mut ctx, event_loop) = ContextBuilder::new("tectonic_generator", "YaslePoy") .add_plugins(DefaultPlugins)
.window_setup(WindowSetup::default().title("Tectonic generator")) .add_systems(Startup, setup)
.window_mode( .insert_resource(Time::<Fixed>::from_hz(1024.0))
WindowMode::default() .add_systems(Update, keyboard_input_system)
.resizable(false) .add_systems(Update, seed_automation::update_automation_seed)
.dimensions(WIN_SIZE_F, WIN_SIZE_F), .add_systems(Update, seed_automation::update_automation_view_seed)
) .add_systems(Update, plate_automation::update_automation_plate)
.build() .add_systems(Update, plate_automation::update_automation_view_plate)
.expect("aieee, could not create ggez context!"); .run();
// Create an instance of your event handler.
// Usually, you should provide it with the Context object to
// use when setting your game up.
let my_game = MyGame::new(&mut ctx);
// Run!
event::run(ctx, event_loop, my_game);
} }
struct MyGame { fn setup(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
display: Image, commands.spawn(Camera2d);
pub raw_view: Table<Color>,
require_draw: bool,
state: Box<dyn LandscapeState>,
}
impl MyGame { let handle = images.add(Image::default());
pub fn new(_ctx: &mut Context) -> MyGame {
// Load/create resources such as images here. let mut shape = Sprite::from_image(handle);
let initial_size = 32_u32; shape.custom_size = Some(Vec2::new(RECTANGLE_SIDE, RECTANGLE_SIDE));
let raw = Table::new(Color::BLACK, initial_size as usize);
let game = MyGame { let automation = SeedAutomation {
display: Image::from_pixels( world: Table::new(false, 16),
_ctx,
&vec![0_u8; (initial_size * initial_size * 4) as usize],
ImageFormat::Rgba8UnormSrgb,
initial_size,
initial_size,
),
raw_view: raw,
require_draw: false,
state: Box::new(SeedLandscape::new(initial_size)),
}; };
game
} commands.spawn((shape, automation));
let seeded_rng = ChaCha8Rng::seed_from_u64(rand::random());
commands.insert_resource(SeededRng(seeded_rng));
} }
impl EventHandler for MyGame { #[derive(Resource)]
fn update(&mut self, _ctx: &mut Context) -> GameResult { struct SeededRng(ChaCha8Rng);
if _ctx
.keyboard
.is_key_just_pressed(ggez::input::keyboard::KeyCode::T)
{
self.state.propagate(LandscapeTick::Tick);
self.require_draw = true;
}
if _ctx
.keyboard
.is_key_just_pressed(ggez::input::keyboard::KeyCode::N)
{
self.state.propagate(LandscapeTick::Next);
self.require_draw = true;
}
if _ctx
.keyboard
.is_key_just_pressed(ggez::input::keyboard::KeyCode::S)
{
self.raw_view.grow();
self.state.propagate(LandscapeTick::Scale);
self.require_draw = true;
}
Ok(())
}
fn draw(&mut self, ctx: &mut Context) -> GameResult {
let mut canvas = graphics::Canvas::from_frame(ctx, Color::BLACK);
if self.require_draw {
self.state.render(&mut self.raw_view);
let pixels: Vec<u8> = self.raw_view.clone().try_into().unwrap();
self.display = Image::from_pixels(
ctx,
&pixels,
ImageFormat::Rgba8UnormSrgb,
self.raw_view.side as u32,
self.raw_view.side as u32,
);
self.require_draw = false;
}
let scale = WIN_SIZE_F / self.raw_view.side as f32;
canvas.draw(
&self.display,
DrawParam::default().scale(Vec2::new(scale, scale)),
);
canvas.finish(ctx)
}
}
enum LandscapeTick {
Scale,
Tick,
Next,
}
trait LandscapeState {
fn propagate(&mut self, action: LandscapeTick);
fn render(&mut self, display: &mut Table<Color>);
}
struct SeedLandscape {
field: Table<bool>,
random: ThreadRng,
color: Color,
}
impl SeedLandscape {
pub fn new(size: u32) -> SeedLandscape {
Self {
field: Table::new(false, size as usize),
random: rand::rng(),
color: Color::WHITE,
}
}
}
impl LandscapeState for SeedLandscape {
fn propagate(&mut self, action: LandscapeTick) {
match action {
LandscapeTick::Scale => self.field.grow(),
LandscapeTick::Tick => loop {
let index = self.random.random_range(0..self.field.data.len());
if !self.field[index] {
self.field[index] = true;
break;
}
},
LandscapeTick::Next => {
}
}
}
fn render(&mut self, display: &mut Table<Color>) {
self.field.convert_copy(display, |x| {
if x { self.color } else { Color::BLACK }
});
}
}
+68
View File
@@ -0,0 +1,68 @@
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::{Component, KeyCode, Query, ResMut, Sprite};
use bevy::render::render_resource::{Extent3d, TextureDimension, TextureFormat};
use rand::RngExt;
use rand_chacha::ChaCha8Rng;
pub fn update_automation_view_plate(
query: Query<(&Sprite, &PlateAutomation)>,
mut images: ResMut<Assets<Image>>,
) {
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_plate(
mut query: Query<&mut PlateAutomation>,
mut seeded_rng: ResMut<SeededRng>,
keys: ResMut<ButtonInput<KeyCode>>,
) {
let mut automation = query.iter_mut().next().unwrap();
if keys.just_pressed(KeyCode::Space) {
let random = &mut seeded_rng.0;
automation.next(random)
}
if keys.just_pressed(KeyCode::AltLeft) {
automation.world.grow()
}
}
#[derive(Component)]
pub struct PlateAutomation {
pub(crate) world: Table<bool>,
}
impl PlateAutomation {
fn next(&mut self, rng: &mut ChaCha8Rng) {
let len = (self.world.side * self.world.side) as f32;
loop {
let index = (rng.random::<f32>() * len) as usize;
if *self.world.get(index) {
continue;
}
self.world.set(index, true);
break;
}
}
}
+66
View File
@@ -0,0 +1,66 @@
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::{Commands, Component, Entity, KeyCode, Query, ResMut, Sprite};
use bevy::render::render_resource::{Extent3d, TextureDimension, TextureFormat};
use rand::RngExt;
use rand_chacha::ChaCha8Rng;
pub fn update_automation_view_seed(
query: Query<(&Sprite, &SeedAutomation)>,
mut images: ResMut<Assets<Image>>,
) {
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_seed(
mut query: Query<(&mut SeedAutomation, Entity)>,
mut seeded_rng: ResMut<SeededRng>,
keys: ResMut<ButtonInput<KeyCode>>,
mut commands: Commands) {
let (mut automation, entity) = query.iter_mut().next().unwrap();
if keys.just_pressed(KeyCode::Space){
let random = &mut seeded_rng.0;
automation.next(random)
}
if keys.just_pressed(KeyCode::Tab){
commands.entity(entity).;
}
}
#[derive(Component)]
pub struct SeedAutomation {
pub(crate) world: Table<bool>,
}
impl SeedAutomation {
fn next(&mut self, rng: &mut ChaCha8Rng) {
let len = (self.world.side * self.world.side) as f32;
loop {
let index = (rng.random::<f32>() * len) as usize;
if *self.world.get(index) {
continue;
}
self.world.set(index, true);
break;
}
}
}