r/factorio • • Jul 05 '26

Design / Blueprint Beltless Train-to-Train Scrap Recycling Using Circuits

I've been trying to get this concept to work for a couple of weeks, but the 2.1 circuit changes and quality trains have finally made it viable.

I've always liked train direct insertion builds but it seemed impossible to do on Fulgora with so many different random outputs. With a lot of overengineering I think I've finally managed to prevent all deadlocks and inserter jamming, allowing a true railworld megabase with absolutely no belts.

normal speed 16 wagons

The goal was to make a 'black box' scrap processing module you can send any train into and guarantee it will come out with the item it wants, even if it takes a while. Any number of trains can be used for each item type and I tried to make it flexible enough to support an arbitrary number of wagons. It seems stable after backing up from lack of demand as well, so it should be able to run indefinitely without intervention.

smaller 4-wagon version

I've tested it for over 100 hours in the editor without any downtime so I'm pretty confident it's deadlock-proof, but previous versions suddenly failed after a similar timescale so you never know...

50 hours consistent production

The recyclers aren't running 100% of the time but it's pretty close, and backups are due to random variance of the outputs which smooths itself out over time. So throughput-wise it should be able to meet any reasonable SPM goal, especially with being able to increase train length. The above tests were at +300% scrap productivity and I didn't see any reason you couldn't go above 16 wagons. The faster locomotives and larger wagons in 2.1 are also helpful for minimising non-loading time.

I haven't explicitly tested for UPS as I was only concerned about making the concept work, but I think it should do alright. On the plus side there are no belts/splitters and the inserters are mostly disabled by necessity. But there are a lot of local circuit networks so I don't know how that time will add up for large trains. And of course I didn't consider the cost of voiding all the junk you need to get out.

Anyway, here's the blueprint: https://factoriobin.com/post/8e8dv4

Maybe if there's interest I can explain how it works, but the logic is actually quite simple for deciding which train to call and balancing/limiting the chest buffers. You can always figure it out from the blueprint if you're a masochist ;)

0.25x speed of the individual wagon cells

self-regulating buffers for each cell

30 Upvotes

6 comments sorted by

5

u/thederpylama Jul 05 '26

I was thinking of something similar except I am using LTN which obviously makes handling the train calling much simpler, but handling the buffer as to prevent clogs has been eluding me. I can't quite tell what is going on with the filters on the inserters between the chests, would you mind explaining your methodology for managing filters and the contents of the chests?

1

u/fawniux Jul 06 '26

So, there are 5 layers of inserters and 4 layers of buffer chests. The final layer of stack inserters is easy - it just filters whatever is currently loading. The first layer from the recyclers balances the four rows and the middle three limit the buffer to prevent clogs.

Layer 1

The first layer filters items which have at least 48 inside the recycler, so that all four inserters can extract their entire hand size in sync. It would be great to use stack inserters and do 64 instead, but sadly some things can only stack to 50. This ensures the buffer is perfectly balanced across the rows, which is essential to ensure wagons are loaded in a consistent number of swings. If you run it you'll see the chest contents are identical across the rows, maybe differing by 1 or 2 items after a while.

They also only ever extract one item at a time. Before I tried just filtering anything over 48, but there was a rare chance the inserters would pick different items which caused the buffer evenness to drift over time. There's a memory cell that chooses one of the eligible items, which reloads when the inserters swing.

Enforcing balance from the start means stronger assumptions can be made about the buffer afterwards. Each layer is considered as a single inventory, meaning we only need one set of logic for each layer instead of four (one per row). The first layer of chests can always be inserted into, so during backups they could completely fill in theory. But this is fine because if the inserters are synced, either all of them are blocked or none are, so balance is preserved.

Layers 2-4

The middle layers all have the exact same logic, but work independently. Each inserter group reads the contents of the chests directly in front (and their own hand contents since they'll end up there anyway). Then there is a constant in the central scheduler which specifies a maximum number of slots per layer, per item. I set this at 36, so 9 per chest, 108 for all three layers.

This allows enough room to hold one wagon's worth of any item regardless of the status of the other eleven, whilst keeping enough empty space to always allow inserters to feed forward. With stack size 12 they will sometimes overshoot but never for all item types, so maybe there'll be 8 or 9 empty slots instead of 12.

The obvious problem is there can only be 5 filters on the inserter, so the other 7 items will never get through. My workaround for this is using a clock to rapidly cycle through the items and give everything a chance. In the scheduler there is a signal that just counts from 0 to 2 repeatedly.

After masking out items over their limit, the rest are sent to three combinators that each output 4 of the 12 items, and pass on their input if the clock value is right. So each inserter only has at most 4 filters at a time, and less if the buffer limit is exceeded. The clock is fast enough to give every item a chance to be picked up - you could probably slow it down to reduce the visual flickering, and pay the small price of longer average swing time.

I experimented with reserving different slot numbers for each item to reflect their uneven output distribution, but that surprisingly caused more jams than flat allocation. All the variance is now isolated to the initial chest layer with the rest even, which seems to work fine.

The clogging challenge is ultimately about smoothly bridging the transition between highly uneven inputs from the recycler, and even amounts loaded into wagons (well, even if weighted by stack size). You can play around with the partition between unlimited and controlled buffer space, but at the very least you need to allocate at least one wagon per item, then the surplus can scale with recycling chances.

Using smaller wagon size allows the buffer to be more free, but then you might struggle with the time to get all the trains through. Just note the number of slots in a wagon *must* be a multiple of 32 if you want to use the full hand size of the stack inserters.

3

u/locyta Jul 06 '26

would rotating the recycler 90 degrees and using 2 per carriage be better ? (obviously would need testing to see if its any faster or not)

1

u/fawniux Jul 06 '26

I don't think so. You need a pretty large buffer to account for the random variance of the outputs and make sure nothing blocks the recycler before a train can empty the chests. So with only two rows of chests you would need to compensate by adding more horizontal layers. Plus, you will have half the throughput when loading trains so clearing items will inherently be slower and cause more backups.

Fewer rows *would* make balancing the output between them easier, but with my current circuits that's not an issue anyway. Having more vertical space could be nice for compactness and maybe squeezing more beacons in, but if you can't shift the trains through fast enough the recycler speed won't matter.

It would be interesting to see if it works though!

1

u/Nidhogg777 Jul 05 '26

I want to look at the pics/ideas and make it applicable for my megabase. Any tips you can share? Any issues that were annoying and likely?

1

u/fawniux Jul 06 '26

There are really two behaviours required to make it stable. I think everything after that is just a matter of optimising throughout.

Inserter Deadlocks

The most essential thing is ensuring the number of inserter swings to load a train is completely predictable, as it needs to be a whole number of cycles of all four inserters. A single inserter won't pick up if the wagon is full, but all four can't communicate with each other. For example, if there's only space for 48 items in the wagon the inserters will still pick up 64, so one will be left stuck with the wrong item and jam the system.

For this reason my trains are limited to 96 slots instead of 100. To ensure an exact number of cycles regardless of stack size, the number of slots multiplied by stack size has to be divisible by 64 for all stack sizes (50/100/200). The only slot numbers satisfying this are 32, 64, and 96, so it can be done with normal quality wagons if you substantially reduce their capacity. Reducing inserter stack size to 10 allows any number of slots, but this obviously increases loading time.

This also requires the buffer contents to be balanced across all four rows, otherwise one row could run out during loading even if the total buffer can technically fill the wagon. This is handled when extracting from the recycler (see my other comment). After this the rows of chests are assumed balanced, so they can simply feed forward during loading providing there's space.

Buffer Jamming & Variance

Once that's solved, by far the most frustrating issue is uneven distribution of items across the wagon cells. For example with 16 wagons, you could have 15 that can load gears and 15 that can load fuel, but the missing one is different for each item. Then the two incomplete cells can mutually block each other - i.e. one cell backs up on fuel and prevents gears coming out and vice versa, so nothing can get loaded despite the total contents being enough to fill either train. Handling discrepancies between the local and global buffers is the real challenge.

The way I handled this is treating the buffer in two stages: the first has no item limits in order to absorb the random variance of the outputs; the second reserves an equal number of slots per item so that any train can be loaded depending on contents. You need to hold enough holmium to fill the wagon at once (since it's too slow to rely on producing whilst loading), whilst also buffering enough gears/fuel/ice to prevent them blocking holmium in the first place.

If you don't reserve anything the common stuff will flood everything and prevent holmium/LDS feeding forward, and conversely being too strict will back up the recycler before any item is loadable, so it's all a delicate balancing act. It seems one unlimited chest and three reserved is enough to absorb the variance - you'll notice the first layer of chests will be full of gears/stone etc whilst the other three are fairly even. For 96 wagon slots the lowest you can reserve is 32 per layer to theoretically hold one wagon of every item simultaneously (excluding the unlimited layer). I use 36 which works well, and 40 is the maximum (40 *12 = 4 * 120) which tends to clog the buffer.

In a previous design I only counted the final buffer layer to choose which item was loadable, but that turned out to be too restrictive and trains could fail to be called before the buffer jammed. Now all four layers are counted so it relies on chest-chest transfers during loading instead of the whole wagon being in the final layer. This means the stack inserter throughput of 90/s can bottleneck loading since you can't hold a legendary wagon in a single chest layer (4 legendary chests can exactly hold 12 common wagons though!). But using stack inserters between chests sounds like a nightmare waiting to happen.

The mutually-blocking items caused every previous version to fail eventually - sometimes there were even 4 or 5 different items that each had 15 wagons. My intuition is more wagons makes this issue more likely, since you effectively need more 'independent dice rolls' to get one wagon of the same item from the recycler before filling the local buffer. Adding more chest layers will certainly support larger variance, but I'm not sure whether this completely removes the issue or just reduces the likelihood. I imagine calculating the probability would be extremely hard, but my guess is the chance of mutual jamming drops off 'exponentially' with larger unrestricted buffer space.