start implementing rule match caching

This commit is contained in:
Crispy 2024-05-08 14:29:27 +02:00
parent d1d033565e
commit 0cc129eb3e

View file

@ -13,6 +13,17 @@ pub struct Dish {
pub rules: Vec<Rule>, pub rules: Vec<Rule>,
pub types: Vec<CellData>, pub types: Vec<CellData>,
pub groups: Vec<CellGroup>, pub groups: Vec<CellGroup>,
#[serde(skip)]
cache: Vec<RuleCache>,
#[serde(skip)]
match_cache: Vec<usize>,
}
#[derive(Debug)]
struct RuleCache {
rule: usize,
variant: usize,
matches: Vec<(isize, isize)>,
} }
#[derive(Debug, Default, Serialize, Deserialize)] #[derive(Debug, Default, Serialize, Deserialize)]
@ -359,6 +370,8 @@ impl Dish {
void: true, void: true,
cells: vec![Cell(0)], cells: vec![Cell(0)],
}], }],
cache: Vec::new(),
match_cache: Vec::new(),
} }
} }
@ -366,6 +379,76 @@ impl Dish {
for rule in &mut self.rules { for rule in &mut self.rules {
rule.generate_variants(); rule.generate_variants();
} }
self.rebuild_cache();
}
pub fn rebuild_cache(&mut self) {
self.cache.clear();
self.match_cache.clear();
}
pub fn update_cache(&mut self, x: isize, y: isize, width: usize, height: usize) {
fn overlap(
(x1, y1, w1, h1): (isize, isize, usize, usize),
(x2, y2, w2, h2): (isize, isize, usize, usize),
) -> bool {
let horizontal = x1..(x1.wrapping_add_unsigned(w1));
let vertical = y1..(y1.wrapping_add_unsigned(h1));
horizontal.contains(&x2)
|| horizontal.contains(&x2.wrapping_add_unsigned(w2))
|| vertical.contains(&y2)
|| vertical.contains(&y2.wrapping_add_unsigned(h2))
}
let edited_rect = (x, y, width, height);
for cache in &mut self.cache {
let rule = &self.rules[cache.rule].variants[cache.variant];
let rule_width = rule.width;
let rule_height = rule.height;
// discard all overlapping matches
let mut i = 0;
while i < cache.matches.len() {
let matc = cache.matches[i];
let match_rect = (matc.0, matc.1, rule_width, rule_height);
if overlap(edited_rect, match_rect) {
cache.matches.swap_remove(i);
} else {
i += 1;
}
}
// check entire changed area and add matches
let border_x = rule_width - 1;
let border_y = rule_height - 1;
for px in (x.wrapping_sub_unsigned(border_x))..(x.wrapping_add_unsigned(width)) {
for py in (y.wrapping_sub_unsigned(border_y))..(y.wrapping_add_unsigned(height)) {
// borrow checker bad >:(
// if self.subrule_matches(px, py, &rule) {
// }
}
}
// TODO
}
}
pub fn fire_once(&mut self) {
if self.match_cache.is_empty() {
return;
}
let i = random::<usize>() % self.match_cache.len();
let i = self.match_cache[i];
let rule_cache = &self.cache[i];
let match_pos_index = random::<usize>() % rule_cache.matches.len();
let (x, y) = rule_cache.matches[match_pos_index];
let rule = &self.rules[rule_cache.rule].variants[rule_cache.variant];
let width = rule.width;
let height = rule.height;
self.apply_rule(x, y, rule_cache.rule, rule_cache.variant);
self.update_cache(x, y, width, height);
} }
pub fn fire_blindly(&mut self) { pub fn fire_blindly(&mut self) {
@ -381,12 +464,8 @@ impl Dish {
if enabled_rules.is_empty() { if enabled_rules.is_empty() {
return; return;
} }
let rule = random::<usize>() % enabled_rules.len(); let enabled_rule_index = random::<usize>() % enabled_rules.len();
let rule = enabled_rules[rule]; let rule_index = enabled_rules[enabled_rule_index];
self.fire_rule(rule);
}
fn fire_rule(&mut self, rule_index: usize) {
let rule = &self.rules[rule_index]; let rule = &self.rules[rule_index];
let variant_index = random::<usize>() % rule.variants.len(); let variant_index = random::<usize>() % rule.variants.len();
let variant = &rule.variants[variant_index].clone(); let variant = &rule.variants[variant_index].clone();
@ -397,17 +476,22 @@ impl Dish {
let y = ((random::<usize>() % (CHUNK_SIZE + border_y)) as isize) let y = ((random::<usize>() % (CHUNK_SIZE + border_y)) as isize)
.wrapping_sub_unsigned(border_y); .wrapping_sub_unsigned(border_y);
if !self.subrule_matches(x, y, variant) { if self.subrule_matches(x, y, variant) {
return; self.apply_rule(x, y, rule_index, variant_index);
} }
}
let fail: u8 = random(); fn apply_rule(&mut self, x: isize, y: isize, rule_index: usize, variant_index: usize) {
if rule.failrate > fail { let rule = &self.rules[rule_index];
let variant = &rule.variants[variant_index].clone();
if rule.failrate > random() {
return; 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();
for dy in 0..height { for dy in 0..height {
for dx in 0..width { for dx in 0..width {
@ -436,6 +520,7 @@ impl Dish {
RuleCellTo::Copy(x, y) => { RuleCellTo::Copy(x, y) => {
let index = x + y * variant.width; let index = x + y * variant.width;
if index >= old_state.len() { if index >= old_state.len() {
// TODO sanitize the rules somewhere else and remove this bounds check
// the copy source is outside the rule bounds // the copy source is outside the rule bounds
continue; continue;
} }
@ -494,6 +579,7 @@ impl Dish {
if x >= CHUNK_SIZE || y >= CHUNK_SIZE { if x >= CHUNK_SIZE || y >= CHUNK_SIZE {
return; return;
} }
self.update_cache(x as isize, y as isize, 1, 1);
self.chunk.set_cell(x, y, cell) self.chunk.set_cell(x, y, cell)
} }
} }