Add a rule builder and the relevant update rules for sand and water
This commit is contained in:
parent
ae18dbac3d
commit
d40c870caf
|
@ -1,9 +1,12 @@
|
||||||
|
use super::rules::RuleBuilder;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum Element {
|
pub enum Element {
|
||||||
None,
|
None,
|
||||||
Air,
|
Air,
|
||||||
Sand,
|
Sand,
|
||||||
Water,
|
Water,
|
||||||
|
ElementCount,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u32> for Element {
|
impl From<u32> for Element {
|
||||||
|
@ -16,3 +19,49 @@ impl From<u32> for Element {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_sand(block: &mut RuleBuilder, me: Element) {
|
||||||
|
let swap_elements = [Element::Air, Element::Water];
|
||||||
|
|
||||||
|
let bottom = block.get(0, -1);
|
||||||
|
let bottom_left = block.get(-1, -1);
|
||||||
|
let bottom_right = block.get(1, -1);
|
||||||
|
|
||||||
|
if swap_elements.contains(&bottom) {
|
||||||
|
block.set(0, 0, bottom);
|
||||||
|
block.set(0, -1, me);
|
||||||
|
} else if swap_elements.contains(&bottom_left) {
|
||||||
|
block.set(0, 0, bottom_left);
|
||||||
|
block.set(-1, -1, me);
|
||||||
|
} else if swap_elements.contains(&bottom_right) {
|
||||||
|
block.set(0, 0, bottom_right);
|
||||||
|
block.set(1, -1, me);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_water(block: &mut RuleBuilder, me: Element) {
|
||||||
|
let swap_elements = [Element::Air];
|
||||||
|
|
||||||
|
let bottom = block.get(0, -1);
|
||||||
|
let bottom_left = block.get(-1, -1);
|
||||||
|
let bottom_right = block.get(1, -1);
|
||||||
|
let left = block.get(-1, 0);
|
||||||
|
let right = block.get(1, 0);
|
||||||
|
|
||||||
|
if swap_elements.contains(&bottom) {
|
||||||
|
block.set(0, 0, bottom);
|
||||||
|
block.set(0, -1, me);
|
||||||
|
} else if swap_elements.contains(&bottom_left) {
|
||||||
|
block.set(0, 0, bottom_left);
|
||||||
|
block.set(-1, -1, me);
|
||||||
|
} else if swap_elements.contains(&bottom_right) {
|
||||||
|
block.set(0, 0, bottom_right);
|
||||||
|
block.set(1, -1, me);
|
||||||
|
} else if swap_elements.contains(&left) {
|
||||||
|
block.set(0, 0, left);
|
||||||
|
block.set(-1, 0, me);
|
||||||
|
} else if swap_elements.contains(&right) {
|
||||||
|
block.set(0, 0, right);
|
||||||
|
block.set(1, 0, me);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,35 +1,65 @@
|
||||||
use bevy::{ecs::system::Resource, utils::HashMap};
|
use bevy::{ecs::system::Resource, utils::HashMap};
|
||||||
|
|
||||||
use super::Element;
|
use super::{
|
||||||
|
element::{update_sand, update_water},
|
||||||
|
Element,
|
||||||
|
};
|
||||||
|
|
||||||
struct RuleConfig {
|
pub struct RuleBuilder {
|
||||||
element_groups: Vec<Vec<Element>>,
|
elements: [Element; 4],
|
||||||
rules: Vec<([usize; 4], [usize; 4])>,
|
processed: [bool; 4],
|
||||||
|
position: i8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RuleConfig {
|
impl RuleBuilder {
|
||||||
fn compile(&self) -> HashMap<u32, u32> {
|
pub fn build(input: (Element, Element, Element, Element)) -> u32 {
|
||||||
let mut rules = HashMap::new();
|
let mut block = RuleBuilder {
|
||||||
for i in 0..self.rules.len() {
|
elements: [input.0, input.1, input.2, input.3],
|
||||||
let rule = self.rules[i];
|
processed: [false; 4],
|
||||||
|
position: 0,
|
||||||
|
};
|
||||||
|
|
||||||
let i0 = &self.element_groups[rule.0[0]];
|
for i in 0..4 {
|
||||||
let i1 = &self.element_groups[rule.0[1]];
|
if block.processed[i] {
|
||||||
let i2 = &self.element_groups[rule.0[2]];
|
continue;
|
||||||
let i3 = &self.element_groups[rule.0[3]];
|
}
|
||||||
|
|
||||||
let o0 = &self.element_groups[rule.1[0]];
|
block.position = i as i8;
|
||||||
let o1 = &self.element_groups[rule.1[1]];
|
let element = block.elements[i];
|
||||||
let o2 = &self.element_groups[rule.1[2]];
|
match element {
|
||||||
let o3 = &self.element_groups[rule.1[3]];
|
Element::Sand => update_sand(&mut block, element),
|
||||||
|
Element::Water => update_water(&mut block, element),
|
||||||
// !HACK: Assume element groups are length 1 for now!
|
_ => {}
|
||||||
let input = to_rule_state((i0[0], i1[0], i2[0], i3[0]));
|
}
|
||||||
let output = to_rule_state((o0[0], o1[0], o2[0], o3[0]));
|
|
||||||
rules.insert(input, output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rules
|
to_rule_state((
|
||||||
|
block.elements[0],
|
||||||
|
block.elements[1],
|
||||||
|
block.elements[2],
|
||||||
|
block.elements[3],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, x: i8, y: i8) -> Element {
|
||||||
|
let x = x + (self.position % 2);
|
||||||
|
let y = (self.position / 2) - y;
|
||||||
|
if !(0..2).contains(&x) || !(0..2).contains(&y) {
|
||||||
|
return Element::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let idx = (x + y * 2) as usize;
|
||||||
|
self.elements[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, x: i8, y: i8, element: Element) {
|
||||||
|
let x = x + (self.position % 2);
|
||||||
|
let y = (self.position / 2) - y;
|
||||||
|
if (0..2).contains(&x) && (0..2).contains(&y) {
|
||||||
|
let idx = (x + y * 2) as usize;
|
||||||
|
self.elements[idx] = element;
|
||||||
|
self.processed[idx] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,84 +70,31 @@ pub struct FallingSandRules {
|
||||||
|
|
||||||
impl Default for FallingSandRules {
|
impl Default for FallingSandRules {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
// Pre-computed rules
|
// Build a list of elements
|
||||||
// I should really start building a tool for this instead of manually calculating it huh
|
// We do it this way so it automatically handles adding new elements
|
||||||
let rule_config = RuleConfig {
|
let mut elements = vec![];
|
||||||
element_groups: vec![
|
for i in 0..(Element::ElementCount as u32) {
|
||||||
vec![Element::Air],
|
elements.push(Element::from(i));
|
||||||
vec![Element::Sand],
|
}
|
||||||
vec![Element::Water],
|
|
||||||
],
|
// Attempt to compute a rule for every possible element block permutation
|
||||||
rules: vec![
|
// Only bother keeping the rule if the state actually changes
|
||||||
// Air/Sand
|
// TODO: See if there's a better way to build the permutations than nesting loops
|
||||||
([0, 1, 0, 0], [0, 0, 0, 1]),
|
let mut rules = HashMap::new();
|
||||||
([0, 1, 0, 1], [0, 0, 1, 1]),
|
for a in 0..elements.len() {
|
||||||
([0, 1, 1, 0], [0, 0, 1, 1]),
|
for b in 0..elements.len() {
|
||||||
([1, 0, 0, 0], [0, 0, 1, 0]),
|
for c in 0..elements.len() {
|
||||||
([1, 0, 0, 1], [0, 0, 1, 1]),
|
for d in 0..elements.len() {
|
||||||
([1, 0, 1, 0], [0, 0, 1, 1]),
|
let input = (elements[a], elements[b], elements[c], elements[d]);
|
||||||
([1, 1, 0, 0], [0, 0, 1, 1]),
|
let in_rule = to_rule_state(input);
|
||||||
([1, 1, 0, 1], [0, 1, 1, 1]),
|
let out_rule = RuleBuilder::build(input);
|
||||||
([1, 1, 1, 0], [1, 0, 1, 1]),
|
if in_rule != out_rule {
|
||||||
// Air/Water
|
rules.insert(in_rule, out_rule);
|
||||||
([0, 2, 0, 0], [0, 0, 0, 2]),
|
}
|
||||||
([0, 2, 0, 2], [0, 0, 2, 2]),
|
}
|
||||||
([0, 2, 2, 0], [0, 0, 2, 2]),
|
}
|
||||||
([2, 0, 0, 0], [0, 0, 2, 0]),
|
}
|
||||||
([2, 0, 0, 2], [0, 0, 2, 2]),
|
}
|
||||||
([2, 0, 2, 0], [0, 0, 2, 2]),
|
|
||||||
([2, 2, 0, 0], [0, 0, 2, 2]),
|
|
||||||
([2, 2, 0, 2], [0, 2, 2, 2]),
|
|
||||||
([2, 2, 2, 0], [2, 0, 2, 2]),
|
|
||||||
([0, 2, 2, 2], [2, 0, 2, 2]),
|
|
||||||
([2, 0, 2, 2], [0, 2, 2, 2]),
|
|
||||||
// Air/Sand/Water
|
|
||||||
([0, 1, 0, 2], [0, 0, 2, 1]),
|
|
||||||
([0, 1, 1, 2], [0, 2, 1, 1]),
|
|
||||||
([0, 1, 2, 0], [0, 0, 2, 1]),
|
|
||||||
([0, 1, 2, 1], [2, 0, 1, 1]),
|
|
||||||
([0, 1, 2, 2], [0, 2, 2, 1]),
|
|
||||||
([0, 2, 0, 1], [0, 0, 2, 1]),
|
|
||||||
([0, 2, 1, 0], [0, 0, 1, 2]),
|
|
||||||
([0, 2, 1, 1], [2, 0, 1, 1]),
|
|
||||||
([0, 2, 1, 2], [2, 0, 1, 2]),
|
|
||||||
([0, 2, 2, 1], [2, 0, 2, 1]),
|
|
||||||
([1, 0, 0, 2], [0, 0, 1, 2]),
|
|
||||||
([1, 0, 1, 2], [0, 2, 1, 1]),
|
|
||||||
([1, 0, 2, 0], [0, 0, 1, 2]),
|
|
||||||
([1, 0, 2, 1], [2, 0, 1, 1]),
|
|
||||||
([1, 0, 2, 2], [2, 0, 1, 2]),
|
|
||||||
([1, 1, 0, 2], [0, 2, 1, 1]),
|
|
||||||
([1, 1, 1, 2], [1, 2, 1, 1]),
|
|
||||||
([1, 1, 2, 0], [2, 0, 1, 1]),
|
|
||||||
([1, 1, 2, 1], [2, 1, 1, 1]),
|
|
||||||
([1, 1, 2, 2], [2, 2, 1, 1]),
|
|
||||||
([1, 2, 0, 0], [0, 0, 1, 2]),
|
|
||||||
([1, 2, 0, 1], [0, 2, 1, 1]),
|
|
||||||
([1, 2, 0, 2], [0, 2, 1, 2]),
|
|
||||||
([1, 2, 1, 0], [0, 2, 1, 1]),
|
|
||||||
([1, 2, 1, 2], [2, 2, 1, 1]),
|
|
||||||
([1, 2, 2, 0], [0, 2, 1, 2]),
|
|
||||||
([1, 2, 2, 1], [2, 2, 1, 1]),
|
|
||||||
([1, 2, 2, 2], [2, 2, 1, 2]),
|
|
||||||
([2, 0, 0, 1], [0, 0, 2, 1]),
|
|
||||||
([2, 0, 1, 0], [0, 0, 1, 2]),
|
|
||||||
([2, 0, 1, 1], [0, 2, 1, 1]),
|
|
||||||
([2, 0, 1, 2], [0, 2, 1, 2]),
|
|
||||||
([2, 0, 2, 1], [0, 2, 2, 1]),
|
|
||||||
([2, 1, 0, 0], [0, 0, 2, 1]),
|
|
||||||
([2, 1, 0, 1], [2, 0, 1, 1]),
|
|
||||||
([2, 1, 0, 2], [2, 0, 2, 1]),
|
|
||||||
([2, 1, 1, 0], [2, 0, 1, 1]),
|
|
||||||
([2, 1, 1, 2], [2, 2, 1, 1]),
|
|
||||||
([2, 1, 2, 0], [2, 0, 2, 1]),
|
|
||||||
([2, 1, 2, 1], [2, 2, 1, 1]),
|
|
||||||
([2, 1, 2, 2], [2, 2, 2, 1]),
|
|
||||||
([2, 2, 0, 1], [0, 2, 2, 1]),
|
|
||||||
([2, 2, 1, 0], [2, 0, 1, 2]),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
let rules = rule_config.compile();
|
|
||||||
|
|
||||||
Self { rules }
|
Self { rules }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue