Arms, Belts and Blueprint Simulation

<
>
December 12, 2020

Since the last post I added both the Arm and Belt Structure.
Previously there was only the Module that had no orientation.
For the Belt and Arm the orientation/direction matters since both are supposed to move items from A to B, hence I added a type for it added:

pub enum Dir {
    N,
    E,
    S,
    W,
}

Since I only plan on allowing 90 degree rotations, supporting only the four directions should be good enough.
Since now there’s a need to support multiple Structures and orientations, the editor became quite complex.
I tried to find a reasonable character mapping:

impl Structure {
    pub fn parse(c: char) -> Option<Self> {
        match c {
            '^' => Some(Self::Belt(Belt::n())),
            '>' => Some(Self::Belt(Belt::e())),
            'v' => Some(Self::Belt(Belt::s())),
            '<' => Some(Self::Belt(Belt::w())),
            'N' => Some(Self::Belt(Belt {
                dir: Dir::N,
                content: Some(Material::Wood),
            })),
            ')' => Some(Self::Belt(Belt {
                dir: Dir::E,
                content: Some(Material::Wood),
            })),
            'U' => Some(Self::Belt(Belt {
                dir: Dir::S,
                content: Some(Material::Wood),
            })),
            '(' => Some(Self::Belt(Belt {
                dir: Dir::W,
                content: Some(Material::Wood),
            })),
            'a' => Some(Self::Arm(Arm::n())),
            'e' => Some(Self::Arm(Arm::e())),
            'A' => Some(Self::Arm(Arm::s())),
            'E' => Some(Self::Arm(Arm::w())),
            'M' => Some(Self::Module(Module::default())),
            _ => None,
        }
    }
}

With an example Blueprint now looking like this:

    let mut bp = Blueprint::parse(
"MMMMMMMMMM\
MMMMMMMMMM\
MMMM MM<<<\
MMMMMMMMM^\
))))>vMMM^\
MMMMMvMMM^\
MMMMMUMMMa\
MMMMM>>)>>\
MMMMMMMMMM\
MMMMMMMMMM",
    )
    .unwrap();

I also added some Materials such as Wood so the Arms and Belts have something to move around.

There’s now also very basic simulation logic, where the content of Belts is being moved around.
Arms simply connect two structures, allowing material flow between them.

impl Blueprint {
...
    pub fn step(&mut self) -> bool {
        let mut changed = false;
        let mut blocked = HashSet::new();
        for row in 0..self.structures.len() {
            for col in 0..self.structures[row].len() {
                let pos = Pos {
                    row: row as u8,
                    col: col as u8,
                };
                if blocked.contains(&pos) {
                    continue;
                }
                if let Some(current) = &self.structures[row][col] {
                    match current {
                        Structure::Belt(belt) => {
                            if let Some(pos_target) = Self::neighbour(&pos, belt.dir) {
                                if let Some(target) = &self.structures[pos_target.row as usize]
                                    [pos_target.col as usize]
                                {
                                    let (new_current, new_target, new_changed) =
                                        Self::interact(current.clone(), target.clone());
                                    if new_changed {
                                        blocked.insert(pos_target.clone());
                                    }
                                    changed = changed || new_changed;
                                    self.structures[row][col] = Some(new_current);
                                    self.structures[pos_target.row as usize]
                                        [pos_target.col as usize] = Some(new_target);
                                }
                            }
                        }
                        Structure::Arm(arm) => {
                            ...
                        }
                        Structure::Module(_) => (),
                    }
                }
            }
        }
        changed
    }

    fn interact(mut source: Structure, mut target: Structure) -> (Structure, Structure, bool) {
        let mut changed = false;
        match (&mut source, &mut target) {
            (Structure::Belt(bs), Structure::Belt(bt)) => {
                match (&mut bs.content, &mut bt.content) {
                    (Some(_), None) => {
                        changed = true;
                        std::mem::swap(&mut bs.content, &mut bt.content)
                    }
                    _ => (),
                }
            }
            _ => (),
        }
        (source, target, changed)
    }
}

Combined with the rendering of a Blueprint to text, it’s now possible to visualize the behavior of the currently bugged simulation:

    loop {
        bp.render();
        println!("\n\n------------------------------------------\n\n");
        let changed = bp.step();
        if !changed {
            bp.render();
            break;
        }
    }
MMMMMMMMMM
MMMMMMMMMM
MMMM MM<<<
MMMMMMMMM^
))))>vMMM^
MMMMMvMMM^
MMMMMUMMMa
MMMMM>>)>>
MMMMMMMMMM
MMMMMMMMMM


------------------------------------------


MMMMMMMMMM
MMMMMMMMMM
MMMM MM<<<
MMMMMMMMM^
)))>)vMMM^
MMMMMvMMM^
MMMMMvMMMa
MMMMM)>>)>
MMMMMMMMMM
MMMMMMMMMM


------------------------------------------


MMMMMMMMMM
MMMMMMMMMM
MMMM MM<<<
MMMMMMMMM^
))>)>UMMM^
MMMMMvMMM^
MMMMMvMMMa
MMMMM>)>>)
MMMMMMMMMM
MMMMMMMMMM


------------------------------------------


MMMMMMMMMM
MMMMMMMMMM
MMMM MM<<<
MMMMMMMMM^
)>)>)vMMM^
MMMMMUMMMN
MMMMMvMMMa
MMMMM>>)>>
MMMMMMMMMM
MMMMMMMMMM


------------------------------------------


MMMMMMMMMM
MMMMMMMMMM
MMMM MM<<<
MMMMMMMMM^
>)>)>UMMMN
MMMMMvMMM^
MMMMMUMMMa
MMMMM>>>)>
MMMMMMMMMM
MMMMMMMMMM
...