The Availabilitizer -- my first meaningful combinator build

This board is to show, discuss and archive useful combinator- and logic-creations.
Smart triggering, counters and sensors, useful circuitry, switching as an art :), computers.
Please provide if possible always a blueprint of your creation.
Post Reply
golfmiketango
Filter Inserter
Filter Inserter
Posts: 549
Joined: Fri Jan 29, 2016 2:48 am
Contact:

The Availabilitizer -- my first meaningful combinator build

Post by golfmiketango »

Background

In my Bob's campaign, I have for various reasons found myself fighting a more or less continuous all-out war around the entire perimeter of a fairly large base with mid-game technologies. I'd be a goner but for my army of over 100k robots who conduct the war on my behalf.

This strategy requires large amounts of materiel, mostly ammo, turrets, belts, and requestor chests to be rapidly deployed at arbitrary locations (basically wherever I want to expand my base). I use one large robotic network, the vast majority of which are MK2 Bob's models. I am reluctant to try to break up my logistic network, as this would be incompatible with how I've structured my military and transportation designs and require that I more-or-less tear everything down and start over.

But, my increasingly "mega-" logisitic network has resulted in ever-more-insufferable delivery times from logistics.

My first attempt to solve the problem was the "repliquester", which is what I call any tileable blueprint containing requesters for precisely the material it consists of, plus an equal number of passive provider chests (or, storage chests, if the stuff is to go back into logistics). I also typically include wired lights as indicators, to let me know when delivery is fulfilled. Once the lights are all on, I grab the provider chests from the requester chest, and stamp them down over the requester chests. Only then do I order construction of the next blueprint. The reason I do it this way, rather than automating this process somehow with inserters, is probably familiar to most of you -- simultaneously attempting to request and provide the same item causes my robots to move all the stuff from the provider to the requester. If I were to then automatically move the stuff from the requester into the provider, I create an infinite loop that accomplishes nothing but to suck huge numbers of robots into a pointless vortex moving the same items back and forth.

This "repliquester" approach works shockingly well. The moment I stamp down my provider chest and place my next set of construction orders, the sky is instantly a blur of turrets and belts; by the time the biters make the decision to chase my robots, they are already in a hailstorm of lasers and bullets, and that's the end of them.

But, although they may work fantastically once filled, as my base has expanded, those repliquester chests have started to take longer and longer to fill up. I've started to go back and forth between fronts rather than wait, make coffee or watch TV. It's lame. I had to come up with something better if I was to salvage my one-big-network approach. I sort-of have. Here it is:

Behold The Availabilitizer

The core meatspace output interface of the Availabilitizer is depicted below:
The Availabilitizer Output Interface
Let's label things so it doesn't get too confusing:
Labels
Recall that I want the stuff I'm building with to come from logistics, and I want it nearby. To solve this, I use an operational principle roughly equivalent to that of the "repliquester" -- request, fulfill, and only then provide, taking care never to simultaneously provide and request.

To do so, an amount of materiel, say, 1000 ammo, is requested into Box "R", and immediately moved by Inserter "R" into the non-logistic Box "S," in the middle. Inserter "P" does not operate yet. Once Box "S" contains the amount I want, both inserters are stopped until Box "R" once again fills. Then Inserter "R" moves everything out of Box "S" into box "P" where it is provided to logistics. Assuming everything goes smoothly, we should never see a single robot move anything from box "P" to box "R". Finally, the inserters stop, again, until Boxes "S" and "P" are both empty, and then we go back to filling boxes "S", and "R", ad infinitum.

To achieve this, I had to learn how to use combinators, which took some doing. I doubt I have created an optimal design, this being my first shot at a semi-sophisticated combinator design, but here's what I was able to cook up -- perhaps you can help me to optimize it:
Finite State Machine and Inserter Control
The above image contains the core state management mechanism: a single bit of storage implemented as an S/R latch, a la GopherAtl's Tutorial. In the initial state, we employ the decider on the bottom right to test whether Box "S" is filled to the desired capacity. Then, if required, we kind of have an out-of-band intermediate state of waiting until "R" fills. Once both boxes are filled, we then trigger the S/R latch and Inserter "P" operates unconditionally, until we find Boxes "P" and "S" to both be empty, at which point, we go back to the initial state.

So, to be clear, the input to the left-hand side of the S/R latch is what would cause the transition from requesting mode to providing mode, and is by far the more complicated conditional. The exact criteria I am using there is: "Box S contains at least all the requested items AND box R contains at least all the requested items". The other side of the latch will cause the transition from providing mode to requesting mode. It's precise criterion is supposed to be "Box P is empty and Box S is empty". The actual criterion is "The sum of items in boxes P and S is zero" which actually might be slightly dangerous if there is any kind of weird dynamic that allows a provider box to report a negative number of items as its contents. I am assuming that, no, at the circuit level, it outputs the literal instantaneous contents of the box itself, ignoring logistics entirely. Note that since the providing criterion requires box "S" to be full and the requesting criterion requires it to be empty, there is no need to worry about whether these conditionals will be simultaneously true -- at least, not at the moment a state transition is requested.

Now here comes the inevitable complication:

If, when inserter "P" moves the last bit of materiel from Box "S" to Box "P", it is possible for box "P" to be empty, as we may have been working with less than the robot stack size to begin with, or robots may have already grabbed everything else from box "P". But as mentioned above, that is the criterion that causes the transition to requesting state. If we allowed the state-transition to occur, here, before the inserter finished inserting, we would start simultaneously operating inserter "R", creating space in the requester chest. Once the materiel landed in Box "P", we would then find ourselves simultaneously requesting and providing a single inserter-stack-size of items... whoops!

Also, for reasons I didn't bother trying to fully understand, an even worse outcome happens moving too quickly from the requesting state to the providing state: blinking-lights-everywhere-forever! I already knew from GopherAtl's tutorial that, when feeding these state-transitions into the S/R latch, it is important to disallow transient (1-tick) flip-flops, or else the S/R latch basically goes apeshit. Well, for this application, it's not terribly important that we optimize absolutely every tick. These state transitions occur rarely and we just want a rock-solid mutex. To solve these kinds of problems I decided to aggressively nerf the conditional inputs, especially when they transition from "on" to "off", to ensure that no spurious "off"-to-"on" transitions feed into the S/R latch. Here is the mechanism I used for that:
Transient-Negative-State-Change-Conditional Scrubber
For demonstrative purposes, I've jacked up the values here up to 100k, but in production, I use a much smaller number (both my production and demonstrative blueprints are attached below). These dual conditional scrubbers just set a timer when their corresponding conditional inputs go negative, and force the signal to stay True a few ticks longer, by dividing a large number by two until the result rounds to zero. The signal remains true for about log2(100k) + a few; maybe 25 ticks or so. Without something like this, the circuit simply freaks out. In the production version I hard-coded the constant as 4096.

So the flow of information is that the deciders you see at the top, sticking out like "arms," emit a "1" when the S/R toggle conditions are met. The lights show which phase we are in and correspond to the active inserter (it may nevertheless be inactive, if we are in one of the "waiting" states). I've already explained the criteria for the right-hand decider -- it is a simple arrangement that receives the aggregate contents of boxes "S" and "R" and outputs "1" on channel "0" (which I use almost everywhere) there are no contents at all.

The implementation of the decider on the left is considerably more involved. Here is the main logic area which compiles the information it needs to make its decision... sorry, it's a bit messy, despite having been spread out for demonstrative purposes:
Main Logic Implementation
The Request Constant must simply match what you are requesting from logistics -- unfortunately, you can't just copy-paste, so you get to input your request twice :(

The Constant Manipulation area has been revised, since I made these images, to use just two combinators. For each quantity "x" of the requested product, it returns [ (x-1) * (-1)], so for our 1000 ammo it yields -999 ammo. I already described the "=" combinator on the right which limits the operation of inserter "R" to only those times when both (a) the S/R latch is in the initial "Requesting" state and (b) Box "S" does not contain the requested amount of materiel (which is, in turn, decided by the decider in the middle, described below).

The two deciders on the top left (numbered "1" and "2," almost imperceptibly) correspond to the sum of the output from the Constant Manipulator and content of the boxes "P" and "S", respectively. They emit a "1" if this value is greater than zero for all items (which would mean the respective boxes they contained as much or more than the requested quantity).

Note that this creates an awkward corner-case. If, for example, we've requested 1000 ammo and only 999 ammo has been delivered, they do not see any ammo inputs. If we had additionally requested 100 fish, and there were 102 fish in the box in question, then these deciders would see an input of just "3 fish" and emit a "1".

The rest of the apparatus is dedicated to solving this problem by getting a bitmask of all the requested products and summing the bits, and then comparing these sums to the equivalent bitmask-sums coming from the respective chests. This does create a slight bug that could probably be fixed programmatically -- if some kind of "garbage" got into box "S" or "R", it will not be filtered from these calculations and the circuit will decide that the box is not full, eventually causing the availabilitizer to become inoperative. In Bob's mod, for example, this is quite possible plausible in millitary contexts, as there is cotton candy constantly spewing all over the place, and cotton-candy under a ghost-box winds up in the box upon its construction. The obvious programmatic solution would be to filter the bitmasks coming from the chests by ANDing them, bitwise with the bitmask of requested items before summing -- but I haven't implemented that yet.

All four of conditionals (deciders "1" and "2" and the corresponding non-corner-case-deciders just described) are what goes off the top of the screen on the left; they are summed into a noop combinator (due to the wires not reaching) and then ANDed by a final decider ("[0] == 4"), which feeds into the transient-negative scrubber controlling the transition back from "Providing" to "Requesting". The combinator in the middle, which decides whether box "S" is full, works by NANDing the two of these conditionals looking at box "S" (by deciding if "[0] < 2).

If you find all that to be a bit tldr; the best way to grok it may be to play around with the
blueprint
Here is what the compacted "production" version looks like. Note that several combinators are eliminated from this version of the design at the expense of comprehensibility, but it is (I think) functionally equivalent:
Availabilitizer-06-CompactVersion.png
Availabilitizer-06-CompactVersion.png (646.42 KiB) Viewed 2406 times
So... how to operate this thing again?

Use this
Blueprint
of the above-pictured compact version.

Put your requested quantity of crap into the constant combinator and the requester chest -- unfortunately, you have to enter your request twice. Make sure they match or something bad will probably happen.

Be advised that, although it tries to do something not totally crazy if you put in requests for multiple items into a single availabilitizer, it is by far preferred to have one availabilitizer per-type-of-item, which, if you have a lot of items, might start to become a moderately space-intensive proposition. Probably not a problem, though, if your base is mega- enough to need this in the first place. The reason for this limitation is that, there is no good way to limit the action of the inserters to only those items which need to be moved, which is especially problematic when trying to fill box "S", which will probably lead to massive over-provisioning of one thing or another depending on the relative scarcity and proportions requested.

One other thing is very important to keep in mind: your availabilitizer puts stuff in a passive provider chest, which is fine for construction bots. But logistic bots will ignore your passive provider chests until all of your storage is empty of the item in question! To address this, you probably will want to use the Advanced Logistics System mod to figure out what the situation is with respect to stored items. Better yet, rather than attempt to empty out all your storage, you can just chain an inserter onto the passive provider box next to your passive provider which moves everything into a space-limited storage box. This might end up getting clogged with some other item, however, which is not easily fixed in a reliable manner, without yet more combinator work. Also, using this approach, you'll need to add a green wire from the storage chest to the passive provider chest so that the availabilitizer treats it as part of the "provided" bundle of stuff -- otherwise, you'll get infinite-logistic-loop problems.

Success?

Sort of. The Availabilitizer works as intended, but, by design, it spends some of it's time providing and the rest requesting. If you are tearing through the requested resource at a rate that quickly exceeds the amount provided by your availabilitizer, then half the time it helps you and the other half it actually competes with you for the resource. So it's useful for stuff you are using in intermittent batches. For continuous production, a train is still way better. Taken to absurd levels, your robots can gobble literally gigawatts of power just delivering stuff from A to B. Also, if you are going to leave it around forever, it will hog the amount of the resource requested in the requester chest most of the time. But, again, that is by design and the best work-around I can think of for the limitations of the logistics network as currently designed. I'd love to hear other ideas if you guys have them.

Also, there is a problem in that you can really only run one of these at a time for a given resource. If you have two of them running simultaneously, I suspect they will synchronize in inverse lockstep, defeating the whole purpose of the design by creating the very infinite loop you were trying to avoid (this time, perhaps sprawling across your entire base). This could be solved by synchronizing them but that requires a lot of work and probably just isn't worth it. I just deconstruct them when not using them, seeding a storage chest nearby to absorb the materials in case I want to revive it later.

In the right circumstances, this does exactly what it's supposed to do. Used sparingly and under the right circumstances, it can definitely make the difference between a frustrating and enjoyable experience of a mega-logistic-network.

Future Ideas

One way this would be much nicer would be if we could replace the constant combinator somehow. Unfortunately there is no way I know of to pull the requested amount out of a requester chest into the circuit network -- nor is there any way to reliably analyse its behavior and determine the amount programmatically due to robot stack size effects. So perhaps a mod is in order to allow that, so we don't have to input everything twice -- at the very least, if the developers could facilitate copy-paste from a requester chest to a constant combinator that would be almost as good.

Another thing I will be thinking about is how to solve the problem mentioned a couple paragraphs up, about requests for multiple types of items, without specializing the circuit by item-type. Perhaps there is some hack involving additional chests and inserters in a loop or two that can be used to considerably mitigate that problem.

Finally there is the matter of how to make multiple availabilitizers work simultaneously. If I allow for it to depend on the logistics combinator mod, we can use logistic switches to wirelessly coordinate the behaviors of the availabilitizers. Or we could require the user to wire them all together. Either way, it would be possible for all the availabilitizers availabilitizing a particular item-type to all temporarily push everything provided into a temporary non-logistic smart-chest while any of the availabilitizers is in requester mode. Although this might be pretty disruptive, in practice, so maybe I'll need a more sophisticated approach. It's actually a fairly difficult problem. Anyhow, this thing will never make a train look bad, until that limitation is somehow addressed.

Post Reply

Return to “Combinator Creations”