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 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue