Compare commits

..

2 commits

Author SHA1 Message Date
4d61184edd add rule fail rate property 2024-05-04 16:56:06 +02:00
ef14cef49b add names to groups 2024-05-04 14:59:13 +02:00
2 changed files with 62 additions and 47 deletions

View file

@ -10,7 +10,14 @@ pub struct Cell(pub u16);
pub struct Dish { pub struct Dish {
pub chunk: Chunk, pub chunk: Chunk,
pub rules: Vec<Rule>, pub rules: Vec<Rule>,
pub cell_groups: Vec<Vec<Option<Cell>>>, pub cell_groups: Vec<CellGroup>,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct CellGroup {
pub name: String,
pub void: bool,
pub cells: Vec<Cell>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -26,12 +33,11 @@ pub struct Rule {
#[serde(skip)] #[serde(skip)]
variants: Vec<SubRule>, variants: Vec<SubRule>,
pub enabled: bool, pub enabled: bool,
// probability: u8
#[serde(alias = "flip_h")]
pub flip_x: bool, pub flip_x: bool,
#[serde(alias = "flip_v")]
pub flip_y: bool, pub flip_y: bool,
pub rotate: bool, pub rotate: bool,
#[serde(default)]
pub failrate: u8,
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@ -126,6 +132,7 @@ impl Rule {
flip_x: false, flip_x: false,
flip_y: false, flip_y: false,
rotate: false, rotate: false,
failrate: 0,
} }
} }
@ -331,7 +338,11 @@ impl Dish {
Self { Self {
chunk: Chunk::new().fill_random(), chunk: Chunk::new().fill_random(),
rules: default_rules, rules: default_rules,
cell_groups: vec![vec![None, Some(Cell(1))]], cell_groups: vec![CellGroup {
name: "empty".into(),
void: true,
cells: vec![Cell(0)],
}],
} }
} }
@ -374,6 +385,11 @@ impl Dish {
return; return;
} }
let fail: u8 = random();
if rule.failrate > fail {
return;
}
let width = variant.width; let width = variant.width;
let height = variant.height; let height = variant.height;
let mut old_state = Vec::new(); let mut old_state = Vec::new();
@ -395,11 +411,9 @@ impl Dish {
} }
RuleCellTo::GroupRandom(group_id) => { RuleCellTo::GroupRandom(group_id) => {
let group = &self.cell_groups[group_id]; let group = &self.cell_groups[group_id];
let i = random::<usize>() % group.len(); let i = random::<usize>() % group.cells.len();
let cell = group[i]; let cell = group.cells[i];
if let Some(cell) = cell { self.set_cell(px, py, cell);
self.set_cell(px, py, cell);
}
} }
RuleCellTo::Copy(x, y) => { RuleCellTo::Copy(x, y) => {
let cell = old_state[x + y * variant.width]; let cell = old_state[x + y * variant.width];
@ -427,7 +441,12 @@ impl Dish {
} }
} }
RuleCellFrom::Group(group_id) => { RuleCellFrom::Group(group_id) => {
if !self.cell_groups[group_id].contains(&cell) { let group = &self.cell_groups[group_id];
if let Some(cell) = cell {
if !group.cells.contains(&cell) {
return false;
}
} else if !group.void {
return false; return false;
} }
} }

View file

@ -10,12 +10,12 @@ use eframe::{
epaint::Hsva, epaint::Hsva,
NativeOptions, NativeOptions,
}; };
use egui::{collapsing_header::CollapsingState, PointerButton}; use egui::{collapsing_header::CollapsingState, DragValue, PointerButton};
use native_dialog::FileDialog; use native_dialog::FileDialog;
use rand::prelude::*; use rand::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use petri::{Cell, Chunk, Dish, Rule, RuleCellFrom, RuleCellTo, CHUNK_SIZE}; use petri::{Cell, CellGroup, Chunk, Dish, Rule, RuleCellFrom, RuleCellTo, CHUNK_SIZE};
use serde_json::{json, Value}; use serde_json::{json, Value};
fn main() { fn main() {
@ -147,29 +147,25 @@ impl eframe::App for UScope {
let (rect, _response) = let (rect, _response) =
ui.allocate_exact_size(Vec2::splat(CSIZE), Sense::click()); ui.allocate_exact_size(Vec2::splat(CSIZE), Sense::click());
draw_group(ui, rect, group, &self.cell_types); draw_group(ui, rect, group, &self.cell_types);
ui.menu_button("edit", |ui| { ui.horizontal(|ui| {
let mut void = group.contains(&None); ui.menu_button("edit", |ui| {
if ui.checkbox(&mut void, "void").changed() { ui.checkbox(&mut group.void, "void");
if void { for (i, celldata) in self.cell_types.iter().enumerate() {
group.push(None); let mut included = group.cells.contains(&Cell(i as u16));
} else { if ui.checkbox(&mut included, &celldata.name).changed() {
group.retain(|c| c.is_some()); if included {
} group.cells.push(Cell(i as u16));
} } else {
for (i, celldata) in self.cell_types.iter().enumerate() { group.cells.retain(|c| c != &Cell(i as u16));
let mut included = group.contains(&Some(Cell(i as u16))); }
if ui.checkbox(&mut included, &celldata.name).changed() {
if included {
group.push(Some(Cell(i as u16)));
} else {
group.retain(|c| c != &Some(Cell(i as u16)));
} }
} }
} });
ui.text_edit_singleline(&mut group.name);
}); });
} }
if ui.button("add group").clicked() { if ui.button("add group").clicked() {
self.dish.cell_groups.push(Vec::new()); self.dish.cell_groups.push(CellGroup::default());
} }
ui.heading("Rules"); ui.heading("Rules");
@ -177,7 +173,6 @@ impl eframe::App for UScope {
let mut to_remove = None; let mut to_remove = None;
let mut to_clone = None; let mut to_clone = None;
for (i, rule) in self.dish.rules.iter_mut().enumerate() { for (i, rule) in self.dish.rules.iter_mut().enumerate() {
// ui.separator();
rule_editor( rule_editor(
ui, ui,
rule, rule,
@ -252,7 +247,7 @@ fn rule_editor(
rule: &mut Rule, rule: &mut Rule,
index: usize, index: usize,
cells: &[CellData], cells: &[CellData],
groups: &[Vec<Option<Cell>>], groups: &[CellGroup],
to_remove: &mut Option<usize>, to_remove: &mut Option<usize>,
to_clone: &mut Option<usize>, to_clone: &mut Option<usize>,
) { ) {
@ -277,10 +272,13 @@ fn rule_editor(
rule.generate_variants(); rule.generate_variants();
} }
}); });
if ui.checkbox(&mut rule.rotate, "rotate").changed() { ui.horizontal(|ui| {
rule.generate_variants(); if ui.checkbox(&mut rule.rotate, "rotate").changed() {
} rule.generate_variants();
}
ui.label("fail rate");
ui.add(DragValue::new(&mut rule.failrate));
});
let cells_y = rule.height(); let cells_y = rule.height();
let cells_x = rule.width(); let cells_x = rule.width();
let patt_width = CSIZE * cells_x as f32; let patt_width = CSIZE * cells_x as f32;
@ -408,7 +406,7 @@ fn rule_cell_edit_from(
x: usize, x: usize,
y: usize, y: usize,
cells: &[CellData], cells: &[CellData],
groups: &[Vec<Option<Cell>>], groups: &[CellGroup],
) -> bool { ) -> bool {
let mut changed = false; let mut changed = false;
let rect = Rect::from_min_size( let rect = Rect::from_min_size(
@ -471,7 +469,7 @@ fn rule_cell_edit_to(
rule: &mut RuleCellTo, rule: &mut RuleCellTo,
(x, y): (usize, usize), (x, y): (usize, usize),
cells: &[CellData], cells: &[CellData],
groups: &[Vec<Option<Cell>>], groups: &[CellGroup],
(rule_width, rule_height): (usize, usize), (rule_width, rule_height): (usize, usize),
overlay_lines: &mut Vec<(Pos2, Pos2)>, overlay_lines: &mut Vec<(Pos2, Pos2)>,
) -> bool { ) -> bool {
@ -552,23 +550,21 @@ fn rule_cell_edit_to(
changed changed
} }
fn draw_group(ui: &mut Ui, rect: Rect, group: &[Option<Cell>], cells: &[CellData]) { fn draw_group(ui: &mut Ui, rect: Rect, group: &CellGroup, cells: &[CellData]) {
let mut group_size = group.len(); let group_size = group.cells.len();
let has_void = group.contains(&None);
if has_void {
group_size -= 1;
}
let radius_per_color = (CSIZE * 0.7) / (group_size as f32); let radius_per_color = (CSIZE * 0.7) / (group_size as f32);
for (i, cell) in group.iter().flatten().enumerate() { for (i, cell) in group.cells.iter().enumerate() {
let color = cells[cell.id()].color; let color = cells[cell.id()].color;
let radius = radius_per_color * ((group_size - i) as f32); let radius = radius_per_color * ((group_size - i) as f32);
ui.painter_at(rect) ui.painter_at(rect)
.circle_filled(rect.center(), radius, color); .circle_filled(rect.center(), radius, color);
} }
if has_void { if group.void {
ui.painter_at(rect) ui.painter_at(rect)
.line_segment([rect.min, rect.max], (1., Color32::WHITE)); .line_segment([rect.min, rect.max], (1., Color32::WHITE));
} }
ui.allocate_rect(rect, Sense::hover())
.on_hover_text(&group.name);
} }
impl CellData { impl CellData {