Compare commits
2 commits
f556c80f29
...
050eee0e3c
Author | SHA1 | Date | |
---|---|---|---|
050eee0e3c | |||
4793bc1b8f |
5 changed files with 114 additions and 112 deletions
|
@ -1 +1 @@
|
||||||
{"rules":[{"name":"new rule","base":{"width":2,"height":1,"contents":[[{"One":1},"None"],[{"One":0},{"One":2}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":true,"failrate":0},{"name":"sideways","base":{"width":2,"height":1,"contents":[[{"Group":0},{"One":0}],[{"One":0},{"Copy":[0,0]}]]},"enabled":true,"flip_x":true,"flip_y":false,"rotate":false,"failrate":164},{"name":"up","base":{"width":1,"height":2,"contents":[[{"One":0},{"Copy":[0,1]}],[{"Group":0},"None"]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":false,"failrate":0},{"name":"new rule","base":{"width":1,"height":1,"contents":[[{"One":2},{"One":3}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":false,"failrate":0},{"name":"new rule","base":{"width":1,"height":1,"contents":[[{"One":3},{"One":4}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":false,"failrate":0},{"name":"new rule","base":{"width":1,"height":1,"contents":[[{"One":4},{"One":5}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":false,"failrate":0},{"name":"new rule","base":{"width":1,"height":1,"contents":[[{"One":5},{"One":0}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":false,"failrate":0},{"name":"new rule","base":{"width":1,"height":1,"contents":[[{"One":5},{"One":0}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":false,"failrate":0}],"types":[{"name":"air","color":[0,0,0]},{"name":"source","color":[255,255,255]},{"name":"cell #2","color":[255,216,44]},{"name":"cell #3","color":[254,131,0]},{"name":"cell #4","color":[255,73,3]},{"name":"cell #5","color":[255,8,0]}],"groups":[{"name":"fire","void":false,"cells":[5,4,3,2]}]}
|
{"rules":[{"name":"new rule","base":{"width":2,"height":1,"contents":[[{"One":1},"None"],[{"One":0},{"One":2}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":true,"failrate":0},{"name":"sideways","base":{"width":2,"height":1,"contents":[[{"Group":0},{"One":0}],[{"One":0},{"Copy":[0,0]}]]},"enabled":true,"flip_x":true,"flip_y":false,"rotate":false,"failrate":164},{"name":"up","base":{"width":1,"height":2,"contents":[[{"One":0},{"Copy":[0,1]}],[{"Group":0},"None"]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":false,"failrate":148},{"name":"new rule","base":{"width":1,"height":1,"contents":[[{"One":2},{"One":3}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":false,"failrate":0},{"name":"new rule","base":{"width":1,"height":1,"contents":[[{"One":3},{"One":4}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":false,"failrate":0},{"name":"new rule","base":{"width":1,"height":1,"contents":[[{"One":4},{"One":5}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":false,"failrate":0},{"name":"new rule","base":{"width":1,"height":1,"contents":[[{"One":5},{"One":0}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":false,"failrate":0},{"name":"new rule","base":{"width":1,"height":1,"contents":[[{"One":5},{"One":0}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":false,"failrate":0}],"types":[{"name":"air","color":[0,0,0]},{"name":"source","color":[255,255,255]},{"name":"cell #2","color":[255,216,44]},{"name":"cell #3","color":[254,131,0]},{"name":"cell #4","color":[255,73,3]},{"name":"cell #5","color":[255,8,0]}],"groups":[{"name":"fire","void":false,"cells":[5,4,3,2]}]}
|
1
example_rulesets/maze.json
Normal file
1
example_rulesets/maze.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"rules":[{"name":"make maze","base":{"width":1,"height":3,"contents":[[{"One":0},{"One":1}],[{"One":0},{"One":2}],[{"One":1},{"One":1}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":true,"failrate":0},{"name":"remove corners","base":{"width":5,"height":5,"contents":[["Any","None"],["Any","None"],[{"Group":0},"None"],["Any","None"],["Any","None"],["Any","None"],["Any","None"],["Any","None"],["Any","None"],["Any","None"],[{"Group":0},"None"],["Any","None"],[{"One":1},{"One":2}],["Any","None"],[{"Group":0},"None"],["Any","None"],["Any","None"],["Any","None"],["Any","None"],["Any","None"],["Any","None"],["Any","None"],[{"Group":0},"None"],["Any","None"],["Any","None"]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":false,"failrate":0},{"name":"flood fill","base":{"width":1,"height":2,"contents":[[{"One":2},{"Copy":[0,1]}],[{"One":3},"None"]]},"enabled":false,"flip_x":false,"flip_y":false,"rotate":true,"failrate":0},{"name":"snake_move","base":{"width":1,"height":3,"contents":[[{"One":2},{"One":3}],[{"One":2},{"One":4}],[{"One":3},{"One":4}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":true,"failrate":0},{"name":"snake_end","base":{"width":3,"height":4,"contents":[["Any","None"],[{"One":4},{"One":5}],["Any","None"],["Any","None"],[{"One":4},{"One":2}],["Any","None"],[{"Group":2},"None"],[{"One":3},{"One":2}],[{"Group":2},"None"],["Any","None"],[{"Group":2},"None"],["Any","None"]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":true,"failrate":0},{"name":"snake_backtrack","base":{"width":1,"height":3,"contents":[[{"One":4},{"One":5}],[{"One":4},{"One":2}],[{"One":5},{"One":2}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":true,"failrate":0},{"name":"snake_reset","base":{"width":3,"height":3,"contents":[["Any","None"],[{"Group":1},"None"],["Any","None"],[{"Group":1},"None"],[{"One":5},{"One":3}],[{"Group":1},"None"],["Any","None"],[{"Group":1},"None"],["Any","None"]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":false,"failrate":0}],"types":[{"name":"air","color":[0,0,0]},{"name":"maze node","color":[255,147,219]},{"name":"passage","color":[238,202,118]},{"name":"head","color":[0,180,88]},{"name":"tail","color":[183,254,149]},{"name":"rev_head","color":[247,101,240]},{"name":"cell #6","color":[193,0,0]}],"groups":[{"name":"traversed","void":true,"cells":[2,1]},{"name":"not_snake","void":true,"cells":[2,0]},{"name":"air","void":true,"cells":[0]}]}
|
1
example_rulesets/sparks.json
Normal file
1
example_rulesets/sparks.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"rules":[{"name":"move","base":{"width":1,"height":2,"contents":[[{"Group":0},{"One":0}],[{"One":0},{"Copy":[0,0]}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":true,"failrate":0},{"name":"spark","base":{"width":1,"height":2,"contents":[[{"One":1},"None"],[{"One":0},{"One":2}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":true,"failrate":136},{"name":"fade","base":{"width":1,"height":1,"contents":[[{"One":2},{"One":0}]]},"enabled":true,"flip_x":false,"flip_y":false,"rotate":false,"failrate":174}],"types":[{"name":"air","color":[0,0,0]},{"name":"sparker","color":[255,117,212]},{"name":"spark","color":[244,179,229]}],"groups":[{"name":"empty","void":false,"cells":[2,1]}]}
|
201
petri/src/lib.rs
201
petri/src/lib.rs
|
@ -74,6 +74,12 @@ pub struct Rule {
|
||||||
struct SubRule {
|
struct SubRule {
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
|
/// offset from top-left corner used to find and sample matches fairly, normally only used by rotated/mirrored variants
|
||||||
|
#[serde(default, skip)]
|
||||||
|
origin_x: usize,
|
||||||
|
/// offset from top-left corner used to find and sample matches fairly, normally only used by rotated/mirrored variants
|
||||||
|
#[serde(default, skip)]
|
||||||
|
origin_y: usize,
|
||||||
contents: Vec<(RuleCellFrom, RuleCellTo)>,
|
contents: Vec<(RuleCellFrom, RuleCellTo)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +112,8 @@ impl SubRule {
|
||||||
Self {
|
Self {
|
||||||
width: 1,
|
width: 1,
|
||||||
height: 1,
|
height: 1,
|
||||||
|
origin_x: 0,
|
||||||
|
origin_y: 0,
|
||||||
contents: vec![Default::default()],
|
contents: vec![Default::default()],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,6 +244,10 @@ impl Rule {
|
||||||
self.variants.len()
|
self.variants.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dbg_variants(&self) {
|
||||||
|
dbg!(&self.variants);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn generate_variants(&mut self) {
|
pub fn generate_variants(&mut self) {
|
||||||
self.variants.clear();
|
self.variants.clear();
|
||||||
self.variants.push(self.base.clone());
|
self.variants.push(self.base.clone());
|
||||||
|
@ -254,6 +266,7 @@ impl Rule {
|
||||||
if self.flip_x {
|
if self.flip_x {
|
||||||
transform_variants(&mut self.variants, |base| {
|
transform_variants(&mut self.variants, |base| {
|
||||||
let mut new = base.clone();
|
let mut new = base.clone();
|
||||||
|
new.origin_x = new.width - new.origin_x - 1;
|
||||||
for y in 0..new.height {
|
for y in 0..new.height {
|
||||||
for x in 0..new.width {
|
for x in 0..new.width {
|
||||||
let mut cell = base.get(new.width - x - 1, y);
|
let mut cell = base.get(new.width - x - 1, y);
|
||||||
|
@ -269,6 +282,7 @@ impl Rule {
|
||||||
if self.flip_y {
|
if self.flip_y {
|
||||||
transform_variants(&mut self.variants, |base| {
|
transform_variants(&mut self.variants, |base| {
|
||||||
let mut new = base.clone();
|
let mut new = base.clone();
|
||||||
|
new.origin_y = new.height - new.origin_y - 1;
|
||||||
for y in 0..new.height {
|
for y in 0..new.height {
|
||||||
for x in 0..new.width {
|
for x in 0..new.width {
|
||||||
let mut cell = base.get(x, new.height - y - 1);
|
let mut cell = base.get(x, new.height - y - 1);
|
||||||
|
@ -285,6 +299,8 @@ impl Rule {
|
||||||
// 180° rotations (same as flipping x and y)
|
// 180° rotations (same as flipping x and y)
|
||||||
transform_variants(&mut self.variants, |base| {
|
transform_variants(&mut self.variants, |base| {
|
||||||
let mut new = base.clone();
|
let mut new = base.clone();
|
||||||
|
new.origin_x = new.width - new.origin_x - 1;
|
||||||
|
new.origin_y = new.height - new.origin_y - 1;
|
||||||
for y in 0..new.height {
|
for y in 0..new.height {
|
||||||
for x in 0..new.width {
|
for x in 0..new.width {
|
||||||
let mut cell = base.get(new.width - x - 1, new.height - y - 1);
|
let mut cell = base.get(new.width - x - 1, new.height - y - 1);
|
||||||
|
@ -303,6 +319,8 @@ impl Rule {
|
||||||
let mut new = base.clone();
|
let mut new = base.clone();
|
||||||
new.height = base.width;
|
new.height = base.width;
|
||||||
new.width = base.height;
|
new.width = base.height;
|
||||||
|
new.origin_x = base.height - base.origin_y - 1;
|
||||||
|
new.origin_y = base.origin_x;
|
||||||
for y in 0..new.height {
|
for y in 0..new.height {
|
||||||
for x in 0..new.width {
|
for x in 0..new.width {
|
||||||
let mut cell = base.get(y, new.width - x - 1);
|
let mut cell = base.get(y, new.width - x - 1);
|
||||||
|
@ -363,6 +381,8 @@ impl Dish {
|
||||||
base: SubRule {
|
base: SubRule {
|
||||||
width: 1,
|
width: 1,
|
||||||
height: 2,
|
height: 2,
|
||||||
|
origin_x: 0,
|
||||||
|
origin_y: 0,
|
||||||
contents: vec![
|
contents: vec![
|
||||||
(RuleCellFrom::One(Cell(1)), RuleCellTo::One(Cell(0))),
|
(RuleCellFrom::One(Cell(1)), RuleCellTo::One(Cell(0))),
|
||||||
(RuleCellFrom::One(Cell(0)), RuleCellTo::One(Cell(1))),
|
(RuleCellFrom::One(Cell(0)), RuleCellTo::One(Cell(1))),
|
||||||
|
@ -376,6 +396,8 @@ impl Dish {
|
||||||
base: SubRule {
|
base: SubRule {
|
||||||
width: 2,
|
width: 2,
|
||||||
height: 2,
|
height: 2,
|
||||||
|
origin_x: 0,
|
||||||
|
origin_y: 0,
|
||||||
contents: vec![
|
contents: vec![
|
||||||
(RuleCellFrom::One(Cell(1)), RuleCellTo::One(Cell(0))),
|
(RuleCellFrom::One(Cell(1)), RuleCellTo::One(Cell(0))),
|
||||||
(RuleCellFrom::Any, RuleCellTo::None),
|
(RuleCellFrom::Any, RuleCellTo::None),
|
||||||
|
@ -415,6 +437,14 @@ impl Dish {
|
||||||
new
|
new
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cache_count(&self) -> usize {
|
||||||
|
self.cache.iter().map(|c| c.matches.len()).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dbg_cache(&self) {
|
||||||
|
dbg!(&self.cache);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fill(&mut self, cell: Cell) {
|
pub fn fill(&mut self, cell: Cell) {
|
||||||
self.world.fill(cell);
|
self.world.fill(cell);
|
||||||
self.rebuild_cache();
|
self.rebuild_cache();
|
||||||
|
@ -460,9 +490,14 @@ impl Dish {
|
||||||
let border_x = rule.width as isize - 1;
|
let border_x = rule.width as isize - 1;
|
||||||
let border_y = rule.height as isize - 1;
|
let border_y = rule.height as isize - 1;
|
||||||
|
|
||||||
for px in -border_x..(CHUNK_SIZE as isize) {
|
for px in -border_x..(CHUNK_SIZE as isize + border_x) {
|
||||||
for py in -border_y..(CHUNK_SIZE as isize) {
|
for py in -border_y..(CHUNK_SIZE as isize + border_y) {
|
||||||
if self.world.subrule_matches(px, py, rule, &self.groups) {
|
let corner_x = px.wrapping_sub_unsigned(rule.origin_x);
|
||||||
|
let corner_y = py.wrapping_sub_unsigned(rule.origin_y);
|
||||||
|
if self
|
||||||
|
.world
|
||||||
|
.subrule_matches(corner_x, corner_y, rule, &self.groups)
|
||||||
|
{
|
||||||
matches.push((px, py));
|
matches.push((px, py));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -484,7 +519,7 @@ impl Dish {
|
||||||
self.update_match_cache();
|
self.update_match_cache();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_cache(&mut self, x: isize, y: isize, width: usize, height: usize) {
|
pub fn update_cache(&mut self, cx: isize, cy: isize, width: usize, height: usize) {
|
||||||
fn overlap(
|
fn overlap(
|
||||||
(x1, y1, w1, h1): (isize, isize, usize, usize),
|
(x1, y1, w1, h1): (isize, isize, usize, usize),
|
||||||
(x2, y2, w2, h2): (isize, isize, usize, usize),
|
(x2, y2, w2, h2): (isize, isize, usize, usize),
|
||||||
|
@ -494,7 +529,7 @@ impl Dish {
|
||||||
&& y2 < y1.saturating_add_unsigned(h1)
|
&& y2 < y1.saturating_add_unsigned(h1)
|
||||||
&& y1 < y2.saturating_add_unsigned(h2)
|
&& y1 < y2.saturating_add_unsigned(h2)
|
||||||
}
|
}
|
||||||
let edited_rect = (x, y, width, height);
|
let edited_rect = (cx, cy, width, height);
|
||||||
|
|
||||||
for cache in &mut self.cache {
|
for cache in &mut self.cache {
|
||||||
let rule = &self.rules[cache.rule].variants[cache.variant];
|
let rule = &self.rules[cache.rule].variants[cache.variant];
|
||||||
|
@ -505,7 +540,9 @@ impl Dish {
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < cache.matches.len() {
|
while i < cache.matches.len() {
|
||||||
let match_pos = cache.matches[i];
|
let match_pos = cache.matches[i];
|
||||||
let match_rect = (match_pos.0, match_pos.1, rule_width, rule_height);
|
let m_corner_x = match_pos.0.wrapping_sub_unsigned(rule.origin_x);
|
||||||
|
let m_corner_y = match_pos.1.wrapping_sub_unsigned(rule.origin_y);
|
||||||
|
let match_rect = (m_corner_x, m_corner_y, rule_width, rule_height);
|
||||||
if overlap(edited_rect, match_rect) {
|
if overlap(edited_rect, match_rect) {
|
||||||
cache.matches.swap_remove(i);
|
cache.matches.swap_remove(i);
|
||||||
} else {
|
} else {
|
||||||
|
@ -516,9 +553,20 @@ impl Dish {
|
||||||
let border_x = rule_width - 1;
|
let border_x = rule_width - 1;
|
||||||
let border_y = rule_height - 1;
|
let border_y = rule_height - 1;
|
||||||
|
|
||||||
for px in (x.wrapping_sub_unsigned(border_x))..(x.wrapping_add_unsigned(width)) {
|
let x_min = cx.wrapping_sub_unsigned(border_x);
|
||||||
for py in (y.wrapping_sub_unsigned(border_y))..(y.wrapping_add_unsigned(height)) {
|
let y_min = cy.wrapping_sub_unsigned(border_y);
|
||||||
if self.world.subrule_matches(px, py, rule, &self.groups) {
|
let x_max = cx
|
||||||
|
.wrapping_add_unsigned(width)
|
||||||
|
.wrapping_add_unsigned(border_x);
|
||||||
|
let y_max = cy
|
||||||
|
.wrapping_add_unsigned(height)
|
||||||
|
.wrapping_add_unsigned(border_y);
|
||||||
|
|
||||||
|
for px in x_min..x_max {
|
||||||
|
for py in y_min..y_max {
|
||||||
|
let cx = px.wrapping_sub_unsigned(rule.origin_x);
|
||||||
|
let cy = py.wrapping_sub_unsigned(rule.origin_y);
|
||||||
|
if self.world.subrule_matches(cx, cy, rule, &self.groups) {
|
||||||
cache.matches.push((px, py));
|
cache.matches.push((px, py));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -550,52 +598,40 @@ impl Dish {
|
||||||
let rule = &self.rules[rule_cache.rule].variants[rule_cache.variant];
|
let rule = &self.rules[rule_cache.rule].variants[rule_cache.variant];
|
||||||
let width = rule.width;
|
let width = rule.width;
|
||||||
let height = rule.height;
|
let height = rule.height;
|
||||||
|
let cx = x.wrapping_sub_unsigned(rule.origin_x);
|
||||||
|
let cy = y.wrapping_sub_unsigned(rule.origin_y);
|
||||||
|
|
||||||
self.apply_rule(x, y, rule_cache.rule, rule_cache.variant);
|
self.apply_rule(x, y, rule_cache.rule, rule_cache.variant);
|
||||||
self.update_cache(x, y, width, height);
|
self.update_cache(cx, cy, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Picks a random point and applies a random match at that position, if any exist.
|
/// Picks a random point and applies a random match at that position, if any exist.
|
||||||
/// The random point can be outside the world bounds, to catch cases where the root (top-left) of a match is outside the bounds.
|
/// The random point can be outside the world bounds, to catch cases where the origin of a match is outside the bounds.
|
||||||
/// TODO make sure max_rule_[width/height] is up to date after each rule.generate_variants
|
/// TODO make sure max_rule_[width/height] is up to date after each rule.generate_variants
|
||||||
pub fn fire_blindly_cached(&mut self) {
|
pub fn try_one_location(&mut self) {
|
||||||
let border_x = self.max_rule_width - 1;
|
let border_x = self.max_rule_width - 1;
|
||||||
let border_y = self.max_rule_height - 1;
|
let border_y = self.max_rule_height - 1;
|
||||||
let x = ((random::<usize>() % (CHUNK_SIZE + border_x)) as isize)
|
let origin_x = ((random::<usize>() % (CHUNK_SIZE + border_x * 2)) as isize)
|
||||||
.wrapping_sub_unsigned(border_x);
|
.wrapping_sub_unsigned(border_x);
|
||||||
let y = ((random::<usize>() % (CHUNK_SIZE + border_y)) as isize)
|
let origin_y = ((random::<usize>() % (CHUNK_SIZE + border_y * 2)) as isize)
|
||||||
.wrapping_sub_unsigned(border_y);
|
.wrapping_sub_unsigned(border_y);
|
||||||
|
|
||||||
let matches = self.get_matches_at_point(x, y);
|
let matches = self.get_matches_at_point(origin_x, origin_y);
|
||||||
if matches.is_empty() {
|
if matches.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let i = random::<usize>() % matches.len();
|
let i = random::<usize>() % matches.len();
|
||||||
let (rule_index, variant_index) = matches[i];
|
let (rule_index, variant_index) = matches[i];
|
||||||
self.apply_rule(x, y, rule_index, variant_index);
|
self.apply_rule(origin_x, origin_y, rule_index, variant_index);
|
||||||
let rule = &self.rules[rule_index].variants[variant_index];
|
let variant = &self.rules[rule_index].variants[variant_index];
|
||||||
let width = rule.width;
|
let width = variant.width;
|
||||||
let height = rule.height;
|
let height = variant.height;
|
||||||
self.update_cache(x, y, width, height);
|
self.update_cache(
|
||||||
}
|
origin_x.wrapping_sub_unsigned(variant.origin_x),
|
||||||
|
origin_y.wrapping_sub_unsigned(variant.origin_y),
|
||||||
/// Picks a random point and applies a random match that overlaps with it, if any exist.
|
width,
|
||||||
/// TODO benchmark and only keep one of try_one_position_overlapped and fire_blindly_cached
|
height,
|
||||||
pub fn try_one_position_overlapped(&mut self) {
|
);
|
||||||
let x = (random::<usize>() % CHUNK_SIZE) as isize;
|
|
||||||
let y = (random::<usize>() % CHUNK_SIZE) as isize;
|
|
||||||
|
|
||||||
let matches = self.get_matches_containing_point(x, y);
|
|
||||||
if matches.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let i = random::<usize>() % matches.len();
|
|
||||||
let (x, y, rule_index, variant_index) = matches[i];
|
|
||||||
self.apply_rule(x, y, rule_index, variant_index);
|
|
||||||
let rule = &self.rules[rule_index].variants[variant_index];
|
|
||||||
let width = rule.width;
|
|
||||||
let height = rule.height;
|
|
||||||
self.update_cache(x, y, width, height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_matches_at_point(&self, x: isize, y: isize) -> Vec<(usize, usize)> {
|
fn get_matches_at_point(&self, x: isize, y: isize) -> Vec<(usize, usize)> {
|
||||||
|
@ -609,62 +645,6 @@ impl Dish {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_matches_containing_point(
|
|
||||||
&self,
|
|
||||||
x: isize,
|
|
||||||
y: isize,
|
|
||||||
) -> Vec<(isize, isize, usize, usize)> {
|
|
||||||
fn contains((x, y, w, h): (isize, isize, usize, usize), (px, py): (isize, isize)) -> bool {
|
|
||||||
px >= x
|
|
||||||
&& py >= y && px < x.saturating_add_unsigned(w)
|
|
||||||
&& py < y.saturating_add_unsigned(h)
|
|
||||||
}
|
|
||||||
self.cache
|
|
||||||
.iter()
|
|
||||||
.flat_map(|rule| {
|
|
||||||
let variant = &self.rules[rule.rule].variants[rule.variant];
|
|
||||||
let (w, h) = (variant.width, variant.height);
|
|
||||||
rule.matches.iter().filter_map(move |&(mx, my)| {
|
|
||||||
if contains((mx, my, w, h), (x, y)) {
|
|
||||||
Some((mx, my, rule.rule, rule.variant))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fire_blindly(&mut self) {
|
|
||||||
if self.rules.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let enabled_rules = self
|
|
||||||
.rules
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter_map(|(i, r)| r.enabled.then_some(i))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
if enabled_rules.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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();
|
|
||||||
let border_x = variant.width - 1;
|
|
||||||
let border_y = variant.height - 1;
|
|
||||||
let x = ((random::<usize>() % (CHUNK_SIZE + border_x)) as isize)
|
|
||||||
.wrapping_sub_unsigned(border_x);
|
|
||||||
let y = ((random::<usize>() % (CHUNK_SIZE + border_y)) as isize)
|
|
||||||
.wrapping_sub_unsigned(border_y);
|
|
||||||
|
|
||||||
if self.world.subrule_matches(x, y, variant, &self.groups) {
|
|
||||||
self.apply_rule(x, y, rule_index, variant_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_rule(&mut self, x: isize, y: isize, rule_index: usize, variant_index: usize) {
|
fn apply_rule(&mut self, x: isize, y: isize, rule_index: usize, variant_index: usize) {
|
||||||
let rule = &self.rules[rule_index];
|
let rule = &self.rules[rule_index];
|
||||||
let variant = &rule.variants[variant_index].clone();
|
let variant = &rule.variants[variant_index].clone();
|
||||||
|
@ -680,16 +660,25 @@ impl Dish {
|
||||||
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 {
|
||||||
old_state.push(
|
let x = x
|
||||||
self.get_cell((x as usize).wrapping_add(dx), (y as usize).wrapping_add(dy)),
|
.wrapping_add_unsigned(dx)
|
||||||
);
|
.wrapping_sub_unsigned(variant.origin_x) as usize;
|
||||||
|
let y = y
|
||||||
|
.wrapping_add_unsigned(dy)
|
||||||
|
.wrapping_sub_unsigned(variant.origin_y) as usize;
|
||||||
|
old_state.push(self.get_cell(x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for dx in 0..width {
|
for dx in 0..width {
|
||||||
for dy in 0..height {
|
for dy in 0..height {
|
||||||
let px = x.wrapping_add_unsigned(dx) as usize;
|
let px = x
|
||||||
let py = y.wrapping_add_unsigned(dy) as usize;
|
.wrapping_add_unsigned(dx)
|
||||||
|
.wrapping_sub_unsigned(variant.origin_x) as usize;
|
||||||
|
let py = y
|
||||||
|
.wrapping_add_unsigned(dy)
|
||||||
|
.wrapping_sub_unsigned(variant.origin_y) as usize;
|
||||||
|
|
||||||
match variant.get(dx, dy).1 {
|
match variant.get(dx, dy).1 {
|
||||||
RuleCellTo::One(rule_cell) => {
|
RuleCellTo::One(rule_cell) => {
|
||||||
self.set_cell(px, py, rule_cell);
|
self.set_cell(px, py, rule_cell);
|
||||||
|
@ -749,11 +738,17 @@ impl World {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subrule_matches(&self, x: isize, y: isize, subrule: &SubRule, groups: &[CellGroup]) -> bool {
|
fn subrule_matches(
|
||||||
|
&self,
|
||||||
|
corner_x: isize,
|
||||||
|
corner_y: isize,
|
||||||
|
subrule: &SubRule,
|
||||||
|
groups: &[CellGroup],
|
||||||
|
) -> bool {
|
||||||
for dx in 0..subrule.width {
|
for dx in 0..subrule.width {
|
||||||
for dy in 0..subrule.height {
|
for dy in 0..subrule.height {
|
||||||
let x = x.wrapping_add_unsigned(dx) as usize;
|
let x = corner_x.wrapping_add_unsigned(dx) as usize;
|
||||||
let y = y.wrapping_add_unsigned(dy) as usize;
|
let y = corner_y.wrapping_add_unsigned(dy) as usize;
|
||||||
let cell = self.get_cell(x, y);
|
let cell = self.get_cell(x, y);
|
||||||
match subrule.get(dx, dy).0 {
|
match subrule.get(dx, dy).0 {
|
||||||
RuleCellFrom::One(rule_cell) => {
|
RuleCellFrom::One(rule_cell) => {
|
||||||
|
|
|
@ -79,12 +79,8 @@ impl eframe::App for UScope {
|
||||||
ctx.request_repaint();
|
ctx.request_repaint();
|
||||||
let sim_frame = Instant::now();
|
let sim_frame = Instant::now();
|
||||||
for _ in 0..self.speed {
|
for _ in 0..self.speed {
|
||||||
// benchmarks made with sand_stress_test at 50000 speed in a release build
|
self.dish.try_one_location();
|
||||||
// ~50ms
|
// self.dish.apply_one_match();
|
||||||
self.dish.try_one_position_overlapped();
|
|
||||||
// ~35ms
|
|
||||||
// TODO: has directional bias, figure out why and fix it
|
|
||||||
// self.dish.fire_blindly_cached();
|
|
||||||
}
|
}
|
||||||
let sim_time = sim_frame.elapsed();
|
let sim_time = sim_frame.elapsed();
|
||||||
// self.sim_times.push(sim_time.as_micros());
|
// self.sim_times.push(sim_time.as_micros());
|
||||||
|
@ -107,6 +103,9 @@ impl eframe::App for UScope {
|
||||||
if ui.button("regenerate rules and cache").clicked() {
|
if ui.button("regenerate rules and cache").clicked() {
|
||||||
self.dish.update_all_rules();
|
self.dish.update_all_rules();
|
||||||
}
|
}
|
||||||
|
if ui.button("debug cache").clicked() {
|
||||||
|
self.dish.dbg_cache();
|
||||||
|
}
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if ui.button("Save").clicked() {
|
if ui.button("Save").clicked() {
|
||||||
self.save_universe();
|
self.save_universe();
|
||||||
|
@ -223,8 +222,11 @@ impl eframe::App for UScope {
|
||||||
self.brush = clicked_cell;
|
self.brush = clicked_cell;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.dish.set_cell(x, y, self.brush);
|
let old = self.dish.get_cell(x, y);
|
||||||
self.dish.update_cache(x as isize, y as isize, 1, 1);
|
if Some(self.brush) != old {
|
||||||
|
self.dish.set_cell(x, y, self.brush);
|
||||||
|
self.dish.update_cache(x as isize, y as isize, 1, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -298,6 +300,9 @@ fn rule_editor(
|
||||||
ui.label("fail rate:");
|
ui.label("fail rate:");
|
||||||
ui.add(DragValue::new(&mut rule.failrate));
|
ui.add(DragValue::new(&mut rule.failrate));
|
||||||
ui.label(format!("variants: {}", rule.variant_count()));
|
ui.label(format!("variants: {}", rule.variant_count()));
|
||||||
|
if ui.button("debug").clicked() {
|
||||||
|
rule.dbg_variants();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
let cells_y = rule.height();
|
let cells_y = rule.height();
|
||||||
let cells_x = rule.width();
|
let cells_x = rule.width();
|
||||||
|
|
Loading…
Reference in a new issue