start implementing rule match caching
This commit is contained in:
parent
d1d033565e
commit
0cc129eb3e
1 changed files with 96 additions and 10 deletions
106
petri/src/lib.rs
106
petri/src/lib.rs
|
@ -13,6 +13,17 @@ pub struct Dish {
|
|||
pub rules: Vec<Rule>,
|
||||
pub types: Vec<CellData>,
|
||||
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)]
|
||||
|
@ -359,6 +370,8 @@ impl Dish {
|
|||
void: true,
|
||||
cells: vec![Cell(0)],
|
||||
}],
|
||||
cache: Vec::new(),
|
||||
match_cache: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -366,6 +379,76 @@ impl Dish {
|
|||
for rule in &mut self.rules {
|
||||
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) {
|
||||
|
@ -381,12 +464,8 @@ impl Dish {
|
|||
if enabled_rules.is_empty() {
|
||||
return;
|
||||
}
|
||||
let rule = random::<usize>() % enabled_rules.len();
|
||||
let rule = enabled_rules[rule];
|
||||
self.fire_rule(rule);
|
||||
}
|
||||
|
||||
fn fire_rule(&mut self, rule_index: usize) {
|
||||
let enabled_rule_index = random::<usize>() % enabled_rules.len();
|
||||
let rule_index = enabled_rules[enabled_rule_index];
|
||||
let rule = &self.rules[rule_index];
|
||||
let variant_index = random::<usize>() % rule.variants.len();
|
||||
let variant = &rule.variants[variant_index].clone();
|
||||
|
@ -397,17 +476,22 @@ impl Dish {
|
|||
let y = ((random::<usize>() % (CHUNK_SIZE + border_y)) as isize)
|
||||
.wrapping_sub_unsigned(border_y);
|
||||
|
||||
if !self.subrule_matches(x, y, variant) {
|
||||
return;
|
||||
if self.subrule_matches(x, y, variant) {
|
||||
self.apply_rule(x, y, rule_index, variant_index);
|
||||
}
|
||||
}
|
||||
|
||||
let fail: u8 = random();
|
||||
if rule.failrate > fail {
|
||||
fn apply_rule(&mut self, x: isize, y: isize, rule_index: usize, variant_index: usize) {
|
||||
let rule = &self.rules[rule_index];
|
||||
let variant = &rule.variants[variant_index].clone();
|
||||
|
||||
if rule.failrate > random() {
|
||||
return;
|
||||
}
|
||||
|
||||
let width = variant.width;
|
||||
let height = variant.height;
|
||||
|
||||
let mut old_state = Vec::new();
|
||||
for dy in 0..height {
|
||||
for dx in 0..width {
|
||||
|
@ -436,6 +520,7 @@ impl Dish {
|
|||
RuleCellTo::Copy(x, y) => {
|
||||
let index = x + y * variant.width;
|
||||
if index >= old_state.len() {
|
||||
// TODO sanitize the rules somewhere else and remove this bounds check
|
||||
// the copy source is outside the rule bounds
|
||||
continue;
|
||||
}
|
||||
|
@ -494,6 +579,7 @@ impl Dish {
|
|||
if x >= CHUNK_SIZE || y >= CHUNK_SIZE {
|
||||
return;
|
||||
}
|
||||
self.update_cache(x as isize, y as isize, 1, 1);
|
||||
self.chunk.set_cell(x, y, cell)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue