This is a continuation of the first post found here.
Writing the first part has been a very rewarding experience. I’m re-learning howvaluable teaching is for both the writer and reader.
Revisiting my old circuits to explain how they function has already revealedmany areas for improvement. I made some of those improvements while publishingpart 1 and I’ve got a few more to walk through in this post.
Unloader improvements
Adding a dedicated trash train was long overdue. Previously the seed traindoubled as my trash train. Over-filling the seed module manifest would leavelittle to no room to return items to base.
During base expansion I create and destroy many of these logistic stations.Waiting for stations to deconstruct to move the perimeter wall or clean up myrail network cost me hours of wait time & round trips.
I came up with a partial fix which I included in the last blueprint book:
This would replace the seed train stop with dedicated trash trains. However,this worked best when I was destroying the outpost, in others it required manualfixups. There is no substitute for building this feature directly into theoutpost:
I’m using a new trick here too: buffer chests to replace the requester and steelchests. These have a major advantage over the prior setup as they allow theplayer logistics, construction robots and regular logistics (assemblers,smelters) to pull from the trash chests while a train is en-route.
I frequently found that when refactoring outposts I would flush all my beacons,modules and assemblers through the trash circuit. When using using requester andsteel chests I would be unable to use those items again until a resupply trainarrives (the trash items would be loaded then unloaded immediately, still aPITA).
Using buffer chests in set-requests
mode is the best of both worlds. Itbehaves like a requester chest, it will not pull from other buffer chests & itmakes itself available to the logistics network.
The trash inserters do not need to overlap with the module slot inserteralignments so I found there was room for another 4 unloaders (from 4 to 8).
Cleaner index circuit
I found a few ways to shrink the size of the indexer circuit by using a negativemask rather than the *1.0M
, >1.0M
, -1.0M
trick in the previous postremoving 4 combinators.
Blueprint.txt
Install the Text Plates mod ifneeded.
There were some concessions made as well: the circuit will no longer pause whenthe train is fully loaded. This was most annoying when standing over it,building it. Most of the time I’m elsewhere.
Let’s explode the circuit and do a walk-through:
Blueprint.txt
There are two changes. The indexer circuit will now output everything except thecursor [*each] != [I]
. I then [*each] * -1.0M
to create a negative maskthat is joined with the WANT
constant combinator.
The MISSING
combinator takes three input signals:
- The want signal:
{rail: 100, chest: 50, inserter: 10}
- The negative mask from the indexer:
{rail: -1M, inserter: -1M}
whenchest
is selected. - The (negative) train contents:
{rail: -80, chest: -10}
Which sum to: {rail: -1M, chest: 40, inserter: -1M}
.
The MISSING
combinator filters negative values with [*each] > 0 ->[*each]
. Now we have an isolated item signal of exactly what is missing{chest: 40}
which is perfect to calculate the filter inserter hand size andfilter item (HAND
, ITEM
).
Cons
Index management is a pain. Setting new requests remotely requires a lot offiddling to correctly upgrade the manifests and indexes for each loader.
The dream is plonking down an upgraded, self-contained module at the loader andunloader with the circuit making all adjustments needed at both ends.
There’s (at least) two missing pieces:
- Generating an index from the manifest signal automatically. This would alsoensure the index stays in sync with the manifest items.
- Balancing items over multiple wagons (otherwise we’d be limited to 40 itemtypes for the entire train). Spoilers: I’ve not solved this yet.
Automatic indexer
Last week I thought this was impossible – and perhaps it should be. Here is acompact, fast, exact, auto-indexing logistic loader circuit that will also resetthe train loader cache, index & index builder on manifest changes.
blueprint.txt
Top-left is the request manifest (constant combinator).
Bottom-left is the exact cargo wagon contents.
Right is the index state, built to match the items in the request signal.
The steel chest is used as the persistent storage for the index.
The brainwave for this came after reading a Redditcommenton the first post. I can use a filter inserter to select and isolate a singleitem from a mixed item signal.
Naturally this will only work for item signals and you’ll need those itemswithin the current logistic network while building the index.
Previously I thought this would only be possible by using this Luascriptto generate a massive constant combinator array programmed with all in-gameitems. This would not be robust to non-vanilla items (without re-running thescript in each context), it has a huge footprint (at least 12 combinators) andthe iteration times over ~250 items to use 10 of them was very slow. Themanual index was a huge improvement at the time.
Index building
The procedure is:
- Iterate over the index range
(1..18)
with[clock] % 19
. - Count the number of item types at the current index position with
[*each] ->C: 1
. - If multiple items found (
[C]ount > 1
):- Pause the clock.
- Insert one of the items into the chest, moving it up into the next slot.
- Repeat.
Let simulate it with a bit of Rust:
fn main() { let mut db: std::collections::BTreeMap<char, i32> = ['A', 'B', 'C', 'D', 'E', 'F'] .iter() .map(|&item| (item, 1)) .collect(); let mut clock: i32 = 1234; loop { for (k, v) in &db { print!("{}{} ", k, v); } // convert clock signal into index position let index = clock % 19; // find items at this index position let items: Vec<char> = db .iter() .filter_map(|(k, v)| if *v == index { Some(*k) } else { None }) .collect(); let count = items.len(); print!("- index: {}", index); if count > 0 { print!(", items: {}", items.iter().collect::<String>()); } if count > 1 { let first = items[0]; // insert one of the multiple items to move it into the next slot (higher value). db.entry(first).and_modify(|e| *e += 1); println!(", insert: {}", first); } else { // resume clock clock += 1; println!(", resume"); } let mut values: Vec<&i32> = db.values().collect(); values.sort(); values.dedup(); if values.len() == db.len() && count == 0 { println!("done!"); break; } }}
A1 B1 C1 D1 E1 F1 - index: 18, resumeA1 B1 C1 D1 E1 F1 - index: 0, resumeA1 B1 C1 D1 E1 F1 - index: 1, items: ABCDEF, insert: AA2 B1 C1 D1 E1 F1 - index: 1, items: BCDEF, insert: BA2 B2 C1 D1 E1 F1 - index: 1, items: CDEF, insert: CA2 B2 C2 D1 E1 F1 - index: 1, items: DEF, insert: DA2 B2 C2 D2 E1 F1 - index: 1, items: EF, insert: EA2 B2 C2 D2 E2 F1 - index: 1, items: F, resumeA2 B2 C2 D2 E2 F1 - index: 2, items: ABCDE, insert: AA3 B2 C2 D2 E2 F1 - index: 2, items: BCDE, insert: BA3 B3 C2 D2 E2 F1 - index: 2, items: CDE, insert: CA3 B3 C3 D2 E2 F1 - index: 2, items: DE, insert: DA3 B3 C3 D3 E2 F1 - index: 2, items: E, resumeA3 B3 C3 D3 E2 F1 - index: 3, items: ABCD, insert: AA4 B3 C3 D3 E2 F1 - index: 3, items: BCD, insert: BA4 B4 C3 D3 E2 F1 - index: 3, items: CD, insert: CA4 B4 C4 D3 E2 F1 - index: 3, items: D, resumeA4 B4 C4 D3 E2 F1 - index: 4, items: ABC, insert: AA5 B4 C4 D3 E2 F1 - index: 4, items: BC, insert: BA5 B5 C4 D3 E2 F1 - index: 4, items: C, resumeA5 B5 C4 D3 E2 F1 - index: 5, items: AB, insert: AA6 B5 C4 D3 E2 F1 - index: 5, items: B, resumeA6 B5 C4 D3 E2 F1 - index: 6, items: A, resumeA6 B5 C4 D3 E2 F1 - index: 7, resumedone!
We’ve converted our request signal A1 B1 C1 D1 E1 F1
into a unique index A6B5 C4 D3 E2 F1
, woo!
Lets match this to the circuit:
blueprint.txt
The CLOCK SOURCE
, PAUSE?
, CLOCK
& TO INDEX
combinators are familiarelements from prior circuits.
INDEX
is the persistent memory where the index is built and INSERTER
is howwe shift items up the index slots one by one (hand size: 1).
IF BUILD
checks we are in the index building mode, the other mode FLUSH
istriggered when changing the constant combinator. When triggered, the FLUSH
mode will flush all caches/chests (train loading cache, index chest, requesterchest). Covered more later.
INDEX ITEMS
will select all items at the given index position with [*each] ==[I] -> [*each]: 1
.
ITEM COUNT
will emit C: 1
for each item found at the current index position.If C>1
we pause the clock (PAUSE TO FIX
) while we fix this index position.
IF MULTI ITEM
will send a signal to the filter inserter and requester chest ifC>1
.
Now, with what I’ve explained so far there is a big off-by-one error. The itemat index position 1 will never be inserted into the chest due to the baserequest signal rail: 1, chest: 1, inserter: 1, ..
, it needs a way toinvalidate the index=1 position. I did this by re-using the clock source dot:1
to occupy that slot and push every other item up a slot. The dot
is not anitem and can’t be inserted.
Flush circuit
blueprint.txt
This is the second circuit mode which will flush all state after a manifestchange and then allow an index rebuild to start from a known good state (zero).
The FLUSH
trigger is a small 3 combinator detection circuit CHANGEDETECTION
. Both arithmetic combinators do [*each] ^ 0 -> [*each]
to normalizethe request signals. The output of the first is fed into the second then bothfed into the SET ON CHANGE
decider with the [*each] != 2 -> S: 1
condition.
In the steady state, each value in the SET ON CHANGE
input signal will be 2(one from each arithmetic combinator). On changes, there will be a 1-tick memorydelay in the circuit in which one item will read a value of 1 triggering S: 1
and the SR-LATCH
to start the flush cycle.
The SR-LATCH
in the set state S > 0
will enable the 8 inserters that flushall chests into the active provider chests. The RESET WHEN FLUSHED
conditionis true when all active provider chests are empty.
There are two transistors 1
& 2
which will cut off signals to the requesterchests when the FLUSH
mode is active.
The indexer circuit in the middle remains the same as the prior circuits.
Station upgrade
And finally upgraded my logistic stations to share the same trash stop with thetrash trains:
Included in the blueprint book below.
Blueprint book!
blueprint-book.txt
That’s all for now!
Would love you hear your feedback, corrections, contributions & fixes!
Email: mason.larobina@gmail.com or raise an issue on the GitHubrepo.
For updates, star the repo or follow:
https://github.com/mason-larobina/factorio/commits/master.atom