Structure generators

<
>
June 24, 2022

For some time now I wanted to have an easy way as developer to generate large factories for testing purposes.
For this I introduced Generators that make it easy for a developer (me) to create factories.

Generators

I introduced the generic Generator trait

pub trait Generator {
    fn size(&self) -> Size<i64>;
    fn generate(&self, ..., anchor: PosAnchor<i64>, target: &mut StructContainer) -> AddResult;
}

To be easily composable, it should report its size.
generate() can then be used to ‘write’ its Structures to the target container. There’s also an anchor position to set the generators origin in the target.
I then introduced multiple Generator primitives.
Single which places a single Structure (part of ContainerElement):
The dev_foo() calls are more efficient due to lack of checks, but are only available in developer builds.

pub struct Single {
    element: ContainerElement,
    size: Size<i64>,
}

impl Single {
    ...
}

impl Generator for Single {
    fn size(&self) -> Size<i64> {
        self.size
    }

    fn generate(&self, ms: &MS, anchor: PosAnchor<i64>, target: &mut StructContainer) -> AddResult {
        let mut element = self.element.clone();
        element.pos.0 += anchor.0;
        target.dev_mark_pos_tree_dirty();
        target.dev_add_unchecked(ms, element)?;

        Ok(())
    }
}

Many to do the same for multiple Structures:

pub struct Many {
    elements: Vec<ContainerElement>,
    size: Size<i64>,
}
...

A Repeater to place the content of its inner Generator many times:

pub enum GenDir {
    Right,
    Down,
}
...
pub struct Repeater<G>
where
    G: Generator,
{
    times: u64,
    padding: u32,
    dir: GenDir,
    inner: G,
    size: Size<i64>,
}
...

An Aligned to place the content of multiple child Generators next to each other:

pub struct Aligned {
    dir: GenDir,
    size: Size<i64>,
    generators: Vec<Box<dyn Generator>>,
}

I also added some helper functions such as:

pub fn row_of(ms: &MS, sm: StructureModule, rot: Rot, times: u64, padding: u32) -> impl Generator {
    Repeater::new(times, padding, GenDir::Right, Single::new(ms, rot, sm))
}

pub fn column_of(
    ms: &MS,
    sm: StructureModule,
    rot: Rot,
    times: u64,
    padding: u32,
) -> impl Generator {
    Repeater::new(times, padding, GenDir::Down, Single::new(ms, rot, sm))
}

With above it becomes rather easy to generate large factories from code.
Below generates multiple rows of Belts being fed by a Source with Coal, which eventually end up in a Hole to be deleted.
There’s also Starter Structures to ensure there’s enough energy available and that everything is within Influence range.
(Complicated padding/count math for Starter to place as few as possible while still having full Influence coverage)

...
let n_belts = 30_000;
let n_rows = 2;

Repeater::new(
    n_rows,
    5,
    GenDir::Down,
    Aligned::new(
        GenDir::Down,
        vec![
            Box::new(Aligned::new(
                GenDir::Right,
                vec![
                    Box::new(Single::new(
                        &self.ms,
                        Rot::D0,
                        Source::new(Material::Coal.into()).into(),
                    )),
                    Box::new(Single::new(
                        &self.ms,
                        Rot::D0,
                        Arm::new(ArmMode::Normal).into(),
                    )),
                    Box::new(row_of(
                        &self.ms,
                        Belt::new(BeltMode::Normal).into(),
                        Rot::D0,
                        n_belts,
                        0,
                    )),
                    Box::new(Single::new(
                        &self.ms,
                        Rot::D0,
                        Arm::new(ArmMode::Normal).into(),
                    )),
                    Box::new(Single::new(&self.ms, Rot::D0, Hole::default().into())),
                ],
            )),
            Box::new(row_of(
                &self.ms,
                Starter::new(StarterMode::Normal).into(),
                Rot::D0,
                1 + n_belts / (2 * R_STARTER as u64 - 5),
                2 * (R_STARTER as u32 - 5),
            )),
        ],
    ),
)
.generate(
    &self.ms,
    PosAnchor(Pos { x: 0, y: 0 }),
    self.planets[0].1.dev_structs_mut(),
)
.unwrap_dev();
...

The below image shows a small part of the generated Structures.

At a later point in time I’d like to add more helper functions to create specific sub-factories like for example coal_iron_furnace(), gear_factory().