Talking about the circuit design process

Post all other topics which do not belong to any other category.
Gus_Smedstad
Fast Inserter
Fast Inserter
Posts: 163
Joined: Fri May 16, 2014 2:30 pm
Contact:

Talking about the circuit design process

Post by Gus_Smedstad »

While I can generally design most Factorio elements such as assemblers, beltways, and rail networks off the cuff in the game, I found that my head rapidly exploded when trying to do that with combinators circuits unless they were trivial. Too much abstraction, and not enough on-screen confirmation of what I was doing when trying to cobble things together without planning.

With that in mind, I want to talk about the process of circuit design. I'm going to do some examples, but this is more about how I got from concept to final working model than the particular problems.

First example: prioritizing fuels. The destination can be either steam engines or trains, but the idea is to set up something to regulate what gets burned first: wood, coal, or solid fuel. Wood's pretty much scrap, I always want to burn that first. Solid fuel is renewable, but sometimes oil refining is in high demand for plastics and sulfur. Coal is not renewable, and again needed for plastic and sulfur. If I've got a big surplus of either, I want to burn that first, and default to solid fuel if I have shortages.

Unfortunately, unless I've missed something, you can't use inventory sums for the entire logistics network with combinators, the way you can with smart inserters. So the first stumbling block is that I have to use the amounts in specific containers (a storage tank, a single chest) as proxies for my overall totals.

If I were to solve this in a normal programming language, it might look something like this:

Code: Select all

if wood > 0 then
  take wood
else
  if petroleum gas > 2000 then
    take solid fuel
  else 
    if coal > 2000 then
      take coal
    else
      take solid fuel
Combinators don't do statement blocks, though, so I need to re-write my approach as individual, standlone statements.

Code: Select all

if wood > 0 then
  take wood
if wood = 0 and petroleum gas > 2000 then
  take solid fuel
if wood = 0 and petroleum gas <= 2000 and coal > 2000 then
  take coal
if wood = 0 and petroleum gas <= 2000 and coal <= 2000 then
  take solid fuel
I've got two "take solid fuel" tests in there, and I can re-write that as one statement that covers both cases.

Code: Select all

if wood = 0 and (petroleum gas > 2000 or coal <= 2000) then
  take solid fuel
Now I want to do the work of a compiler, and turn this into the "machine language" of combinators. Inspired by a thread here, I've codified a simple language for describing combinators in text. My primary focus in writing this is that it should be highly readable, understandable by someone who knows Factorio and combinators, but who has never seen my language before.

First, I create a bunch of deciders to codify all the comparisons I wrote in my procedural code.

Code: Select all

decider have_wood( wood > 0 ) : A = 1
decider have_gas( petroleum gas > 2000 ) : B = 1
decider have_coal( coal > 4000 ) : C = 1
// inversions of the above tests - so I only need to change numbers in one place if I change my mind
decider low_wood( have_wood->A = 0 ) : blue = 1
decider low_gas( have_gas->B = 0 ) : green = 1
decider low_coal( have_coal->C = 0 ): red = 1
Now to implement my if-then statements using these tests

Code: Select all

// Wood's easy.
inserter wood_inserter( have_wood->A = 1, allow wood )

Code: Select all

// if wood = 0 and petroleum gas <= 2000 and coal > 2000 then
//  take coal
// test wood = 0 and gas <= 2000
multiplier multiplier_1( coal = low_wood->blue * low_gas->green )
// test (wood = 0 and gas < 2000) and coal > 2000
multiplier multiplier_2( coal = multiplier_1->coal * have_coal->C )
inserter coal_inserter( multiplier_2->coal > 0, allow coal )

Code: Select all

// if wood = 0 and (petroleum gas > 2000 or coal <= 2000) then
//  take solid fuel
// test gas > 2000 or coal <= 2000
adder adder_1( solid_fuel = have_gas->B + low_coal->red )
// wood = 0 AND (gas > 2000 or coal <= 2000)
multiplier multiplier_3( solid_fuel = low_wood->blue * adder_1->solid_fuel )
inserter solid_inserter( multiplier_3->solid_fuel > 0, allow solid fuel )
Now I've got something I can turn into actual objects in the game. Maybe it could be optimized, but my first focus is on something I know will work and is easy to understand.
Image
I've added lights below the combinators to make the current state of each clear. I don't have wood, I have both surplus solid fuel and coal, so the circuit chooses solid fuel.

Zhab
Fast Inserter
Fast Inserter
Posts: 101
Joined: Sat Jul 18, 2015 10:17 pm
Contact:

Re: Talking about the circuit design process

Post by Zhab »

Omg, that sound needlessly complicated. I don't even understand your part with arithmetic combinators.

If wood > 0 then A = 1
If gas > 2000 then B = 1
If coal > 2000 then C = 1
If B > A Then D = 1
If C > D Then E = 1

Wood inserters always work
Solid fuel inserters only work if D = 1
Coal inserters only work if E = 1

User avatar
MeduSalem
Smart Inserter
Smart Inserter
Posts: 1490
Joined: Sun Jun 08, 2014 8:13 pm
Contact:

Re: Talking about the circuit design process

Post by MeduSalem »

I like your thought process... because I'm normally facing similar problems due to how the circuit stuff is way too abstract for my every-day usage. I will probably use your approach the next time I want to do something with the circuit stuff and see if I cope better. :D



I know that this thread is more about the circuit design process so if you don't care don't open the spoiler:
Fuel Priority Alternative
Zhab wrote:Omg, that sound needlessly complicated. I don't even understand your part with arithmetic combinators.
The arithmetic combinators are basically doing the AND/OR... because the multiplication will work as AND gate (1*1=1, otherwise 0) and the the addition as OR gate (0+0=0, otherwise >=1).

If it can be done easier though is up for debate. xD
Last edited by MeduSalem on Sun Aug 02, 2015 2:51 am, edited 1 time in total.

Zhab
Fast Inserter
Fast Inserter
Posts: 101
Joined: Sat Jul 18, 2015 10:17 pm
Contact:

Re: Talking about the circuit design process

Post by Zhab »

MeduSalem wrote:I like your thought process... because I'm normally facing similar problems due to how the circuit stuff is way too abstract for my every-day usage. I will probably use your approach the next time I want to do something with the circuit stuff and see if I cope better. :D
I don't know if I would recommand his method. It produced an over complicated design using twice as much combinators.

That being said, I don't even have a method that I could possibly share (at least not one that I'm consciously following). So maybe I'm not in a position to critic the methods of others. At least he have something he can describe and that others can follow. Which is more than I can say for myself.
MeduSalem wrote:The arithmetic combinators are basically doing the AND/OR... because the multiplication will work as AND gate (1*1=1, otherwise 0) and the the addition as OR gate (0+0=0, otherwise >=1).

If it can be done easier though is up for debate. xD
Now that I can easily understand. But the following was not clear to me.

multiplier multiplier_3( solid_fuel = low_wood->blue * adder_1->solid_fuel )

User avatar
MeduSalem
Smart Inserter
Smart Inserter
Posts: 1490
Joined: Sun Jun 08, 2014 8:13 pm
Contact:

Re: Talking about the circuit design process

Post by MeduSalem »

Zhab wrote:I don't know if I would recommand his method. It produced an over complicated design using twice as much combinators.

That being said, I don't even have a method that I could possibly share (at least not one that I'm consciously following). So maybe I'm not in a position to critic the methods of others. At least he have something he can describe and that others can follow. Which is more than I can say for myself.
Well I guess you did a better job at figuring out the best simplification of the logic combinations by eliminating the redundant ones. So no wonder that you need much less combinators to implement the same.

But I guess that doesn't make his design approach bad, because he got a working solution, just not a very optimized one.

Zhab
Fast Inserter
Fast Inserter
Posts: 101
Joined: Sat Jul 18, 2015 10:17 pm
Contact:

Re: Talking about the circuit design process

Post by Zhab »

MeduSalem wrote:Well I guess you did a better job at figuring out the best simplification of the logic combinations by eliminating the redundant ones. So no wonder that you need much less combinators to implement the same.

But I guess that doesn't make his design approach bad, because he got a working solution, just not a very optimized one.
Hmmm now that you mention it, that might very well be the root of the problem. For example, his initial logic assume that the system needs to be told to use wood when the only time the system is supposed to not use wood is when there is none to be used to begin with. No need for a smart inserter here. If there is something in that chest just use it.

So maybe a better initial logic would produce a more optimized result. That is a very good point MeduSalem.

Zhab
Fast Inserter
Fast Inserter
Posts: 101
Joined: Sat Jul 18, 2015 10:17 pm
Contact:

Re: Talking about the circuit design process

Post by Zhab »

I've been thinking really hard to figure out what is the method my brain naturally use to come up with answers and I think I may have found it. Tell me if the following method make sense to you or if my crazy brain works in crazy ways

Phase 1

What are the simple initial questions that you need an answer to ? (no and/or allowed at this stage)

Code: Select all

When do I use wood ?
When do I use gas ?
When do I use Coal ?
Next step is answering the questions. Skip questions that you answer with "I need to check check chest/tank/whatever" or other raw game state checks.

Code: Select all

Whenever there is any wood available.
When there is no wood available and there is more than 2000 units of gas.
When there is no wood, there is 2000 units of gas or less and more than 2000 pieces of coal.
Do you need to ask new questions for these answers to be useful ?

Code: Select all

Do I have wood ?
Do I have more than 2000 units of gas. ?
Do I have more than 2000 pieces of coal ?
Next step is answering the questions. Skip questions that you answer with "I need to check check chest/tank/whatever" or other raw game state checks.

*All questions skipped*

Do you need to ask new questions for these answer to be useful ?

*What answers ?*

You alternate between asking questions and answering. But once one of these produce nothing you are ready for next phase.

Phase 2

First step is to consolidate everything in one list in reverse order of steps. But do not include the questions from the first step. Then assign a letter to everything.

Code: Select all

A = Do I have wood ?
B = Do I have more than 2000 units of gas. ?
C = Do I have more than 2000 pieces of coal ?

D = Whenever there is any available.
E = When A = 0 and B = 1.
F = When A = 0 and B = 0 and C = 1.
Spot any repetitions and replace with letter based conditions.

Code: Select all

A = Do I have wood ?
B = Do I have more than 2000 units of gas. ?
C = Do I have more than 2000 pieces of coal ?

D = When A = 1
E = When A= 0 and B = 1.
F = When A = 0 and B = 0 and C = 1.
Spot answers that are reused in others (including inverse) and replace repetitions with corresponding letter.

*No changes here*

Isolate repetitive condition parts

Code: Select all

A = Do I have wood ?
B = Do I have more than 2000 units of gas. ?
C = Do I have more than 2000 pieces of coal ?

D = When A = 1
When A = 0
      E = When B = 1
      F = When B = 0 and C = 1.
Spot any repetitions and replace with letter based condition.

*No changes here*

Spot answers that are reused in others (including inverse) and replace repetitions with corresponding letter.

Code: Select all

A = Do I have wood ?
B = Do I have more than 2000 units of gas. ?
C = Do I have more than 2000 pieces of coal ?

D = When A = 1
When A = 0
    E = When B = 1
    F = When E = 0 and C = 1.
Isolate repetitive condition parts

*No changes here*

Spot any repetitions and replace with letter based condition.

*No changes here*

Spot answers that are reused in others (including inverse) and replace repetitions with corresponding letter.

*No changes here*


When you hit "No changes here* 3 times in a row, you are ready for phase 3.

Phase 3

Reinsert inner most isolated conditions, but skip answers that use the letter of other answers that are part of the same "isolation block".

Code: Select all

A = Do I have wood ?
B = Do I have more than 2000 units of gas. ?
C = Do I have more than 2000 pieces of coal ?

D = When A = 1
E = When A = 0 and B = 1
F = When E = 0 and C = 1.
Reinsert inner most isolated conditions, but skip answers that use the letter of other answers that are part of the same "isolation block".

*No change here*

Phase 3 is over at the first "No change here*.

Phase 4

Eliminate any answers that rely on a single question and absolutely nothing else.

Code: Select all

A = Do I have wood ?
B = Do I have more than 2000 units of gas. ?
C = Do I have more than 2000 pieces of coal ?

E = When A = 0 and B = 1
F = When E = 0 and C = 1.
Reformulate questions into If *condition* Then *variable* = *value* format

Code: Select all

If wood > 0 then A = 1
If gas > 2000 then B = 1
If coal > 2000 then C = 1

E = When A = 0 and B = 1
F = When E = 0 and C = 1.
Reformulate answers into combinator logic gate format

Code: Select all

If wood > 0 then A = 1
If gas > 2000 then B = 1
If coal > 2000 then C = 1

If A < B then E = 1
If E < C then F = 1
Optional step. Associate each line to an in-game object

Code: Select all

Combinator 1 --> If wood > 0 then A = 1
Combinator 2 --> If gas > 2000 then B = 1
Combinator 3 --> If coal > 2000 then C = 1
Combinator 4 --> If A < B then E = 1

Wood inserters --------> *no need for management*
Soild fuel inserters --> If E > 0
Coal inserters --------> If E < C
Final cleanup (Also optional). ( in this case using wood signal instead of signal A is better. Then I reallocate letter usage)

Code: Select all

Combinator 1 --> If gas > 2000 then A = 1
Combinator 2 --> If coal > 2000 then B = 1
Combinator 3 --> If wood < A then C = 1

Wood inserters --------> *no need for management*
Soild fuel inserters --> If C > 0
Coal inserters --------> If C < B

End of method

At this point the combinator wizardry should be drastically clarified and you should have a very good idea of what you are supposed to build in-game.
Last edited by Zhab on Sun Aug 02, 2015 9:00 am, edited 2 times in total.

Lee_newsum
Filter Inserter
Filter Inserter
Posts: 436
Joined: Wed Jan 15, 2014 9:41 am
Contact:

Re: Talking about the circuit design process

Post by Lee_newsum »

lol all the work for some think that can be do with out circuit at all.

1) put a line of transport belts one end going to what you are ging to ful.
2) at the top belt/Requester chest in wood( at top it will all ways got on the line)
3) next in line Smart inserter with coal > 2k (so work if wore than 2k coal in system)
4) next in line Fast inserter with Solid fuel

Zhab
Fast Inserter
Fast Inserter
Posts: 101
Joined: Sat Jul 18, 2015 10:17 pm
Contact:

Re: Talking about the circuit design process

Post by Zhab »

Lee_newsum wrote:lol all the work for some think that can be do with out circuit at all.

1) put a line of transport belts one end going to what you are ging to ful.
2) at the top belt/Requester chest in wood( at top it will all ways got on the line)
3) next in line Smart inserter with coal > 2k (so work if wore than 2k coal in system)
4) next in line Fast inserter with Solid fuel
MeduSalem beat you to it in 3rd post.

Also, we know you can do with without combinator. The point of this thread isn't specifically to solve this problem. It is to find/share a method to help people solve problems with combinators.

Whether or not combinators were needed to solve the issue to begin with is irrelevant.

User avatar
ssilk
Global Moderator
Global Moderator
Posts: 12888
Joined: Tue Apr 16, 2013 10:35 pm
Contact:

Re: Talking about the circuit design process

Post by ssilk »

Some of that said stuff is also known as decision table.

https://en.wikipedia.org/wiki/Decision_table

(I think the German article is better: https://de.wikipedia.org/wiki/Entscheidungstabelle )

I mean thinking in that way, is much easier, as in the wall of text above. ;)
The decision table helps to minimize the afford, and you don't need programing skills.

Step #1: Create sensors.
This term comes from monitoring. A sensor is something, that you want to measure. A number or simple "on/off", "running/not running" is going out.

Step #2: Create border-signals.
Basically nothing else, than testing, if some very special state on that sensor is currently true. This step is basically converting one value into something, that is decideable (boolean true/false). In many cases the value can be just plugged through. But maybe it's also a good idea to have the same value also available as boolean NOT. That depends from the next step.

Step #3: Use the border-signals to decide things.
You can test each possible combination of course. But it makes sense to to minimize the needed number of combinators by using the decision table.


TL;DR: in truth you need to think vice versa. :)

PS: I want to have this as device: take 10 border-signals (more doesn't make sense, it get's really complex), have a decision table inside, output up to 8 output signals. A simple controller is born. Or a decision-table-blueprint-maker: Have an assistant, that generates you a blueprint, place blueprint and connect the inputs/outputs.
Cool suggestion: Eatable MOUSE-pointers.
Have you used the Advanced Search today?
Need help, question? FAQ - Wiki - Forum help
I still like small signatures...

ratchetfreak
Filter Inserter
Filter Inserter
Posts: 952
Joined: Sat May 23, 2015 12:10 pm
Contact:

Re: Talking about the circuit design process

Post by ratchetfreak »

eliminating redundant conditions can be done with a Karnaugh map

though with more than a handful of conditions it does get mind-bogglingly complex

Code: Select all

                     coal    no coal
                     _____________
wood        gas     | wood   wood
wood        no gas  | wood   wood
no wood     no gas  | coal   ALARM
no wood     gas     | gas    gas

Gus_Smedstad
Fast Inserter
Fast Inserter
Posts: 163
Joined: Fri May 16, 2014 2:30 pm
Contact:

Re: Talking about the circuit design process

Post by Gus_Smedstad »

Zhab wrote:I've been thinking really hard to figure out what is the method my brain naturally use to come up with answers and I think I may have found it.
I think talking about how you optimize is also useful. That's also talking about how to think about the logic that leads to circuits. As long as we're talking about the process of how to optimize, rather than focusing too much on a specific optimization.

What's not clear to me is why you went from

Code: Select all

F = When B = 0 and C = 1.
to

Code: Select all

F = When E = 0 and C = 1.
At that step, it doesn't look like a simplification.

Unless I'm missing something, your final logic doesn't produce the correct result. If I have no wood, less than 2000 gas, and less than 2000 coal, nothing gets moved. A = 0, B = 0, and C = 0, and all conditions are false. This is also true of your solution in the initial reply.

The undesired "do nothing" result comes up because of the way your formulated the questions in step 1. Its should be

Code: Select all

When there is any wood available, use wood.
When there is no wood and either 2000 units of gas are available or less than 2000 units of coal, use gas.
When there is no wood, there are 2000 units of gas or less and more than 2000 pieces of coal.
On further consideration, there is an optimization that should come at this step, because the conditions are better stated as

Code: Select all

when there is any wood available, use wood.
when there is no wood and 2000 units of coal available, and less than 2000 units of gas, use coal.
If not using wood or coal, use gas.

Gus_Smedstad
Fast Inserter
Fast Inserter
Posts: 163
Joined: Fri May 16, 2014 2:30 pm
Contact:

Re: Talking about the circuit design process

Post by Gus_Smedstad »

ratchetfreak wrote:

Code: Select all

                     coal    no coal
                     _____________
wood        gas     | wood   wood
wood        no gas  | wood   wood
no wood     no gas  | coal   ALARM
no wood     gas     | gas    gas
"ALARM" is not actually the result we want when all 3 conditions are false. In that case, we want to use gas. This is true because "no gas" is not actually a condition of Factorio - gas is always available, since oil never runs out, it's just that gas may be in very limited supply.

Zhab
Fast Inserter
Fast Inserter
Posts: 101
Joined: Sat Jul 18, 2015 10:17 pm
Contact:

Re: Talking about the circuit design process

Post by Zhab »

Gus_Smedstad wrote:What's not clear to me is why you went from

Code: Select all

F = When B = 0 and C = 1.
to

Code: Select all

F = When E = 0 and C = 1.
At that step, it doesn't look like a simplification.
I will be happy to explain.

Code: Select all

E = When B = 1
F = When B = 0 and C = 1.

E = When B = 1
F = When E = 0 and C = 1.
Now if E is true when B is true. Then checking for if B is false is the same as checking for if E is false.

In other words, The B in the 2nd line might as well be E.

In this particular stage of the simplification, B is shown to be equivalent to E. But in the grand scale of things the meaning of B is quite different from the meaning of E. So making this substitution is very significant. The method outline redundancies as things that can be substituted by others. Why are they redundant ? The method does not say, but there is a reason.

If you do some thinking about what redundancy this particular step remove, you would discover that it is your checking of if gas <= 2000. By it's very nature the check is superfluous (and so is checking wood). Because simply knowing that you are not supposed to use solid fuel already inform us that gas is <= 2000. So to know if gas is <= 2000 you can just ask whether or not you should be using solid fuel.
Gus_Smedstad wrote:Unless I'm missing something, your final logic doesn't produce the correct result. If I have no wood, less than 2000 gas, and less than 2000 coal, nothing gets moved. A = 0, B = 0, and C = 0, and all conditions are false. This is also true of your solution in the initial reply.
Well I was working from your innitial logic from the opening thread.

Code: Select all

if wood > 0 then
  take wood
else
  if petroleum gas > 2000 then
    take solid fuel
  else 
    if coal > 2000 then
      take coal
    else
      take solid fuel
But I missed the last else there at the end. Somehow overlooked it.

I assumed that this is what you wanted.

1) Use any wood available first and foremost
2) Then use solid fuel, but make sure to preserve 2000 gas.
3) Then use coal, but make sure to keep at least 2000 coal in reserve.

So I was not aware that you don't care about your gas reserve anymore if coal is below 2000. I was missing an element.
Gus_Smedstad wrote:The undesired "do nothing" result comes up because of the way your formulated the questions in step 1. Its should be

Code: Select all

When there is any wood available, use wood.
When there is no wood and either 2000 units of gas are available or less than 2000 units of coal, use gas.
When there is no wood, there are 2000 units of gas or less and more than 2000 pieces of coal.
On further consideration, there is an optimization that should come at this step, because the conditions are better stated as

Code: Select all

when there is any wood available, use wood.
when there is no wood and 2000 units of coal available, and less than 2000 units of gas, use coal.
If not using wood or coal, use gas.
Well you seem confused about my method. Those are not questions but answers. But regardless. I now understand what you meant to do.

First thing first. Be careful with your notation. saying that X units of gas are available (X = 2000) is not the same as there is more than X units of gas available (x > 2000) and not the same as there are at least 2000 units of gas available (x >= 2000). You switch between therms when you mean a specific one.

Second, you don't use gas you use solid fuel. But this doesn't impact the design. But it is a good habit with these designs to be specific and precise.

But all that said, seems like just the start of my method is already helping you out quite a bit. Quite the simplification you came with on the spot there. I'm going to work with your simplified version (2nd block).

Code: Select all

Combinator 1 ---> If coal > 2000 then A = 1
Combinator 2 ---> If gas < 2000 then A = 1
Combinator 3 ---> if A > 1 then B = 1
Combinator 4 ---> wood + B = C

Wood inserters ---> No management needed
Coal inserters ------------> If wood < B
Solid Fuel inserters ---> if C = 0
This should work as you intended. One extra combinator for one extra parameter. Sounds right.

Gus_Smedstad
Fast Inserter
Fast Inserter
Posts: 163
Joined: Fri May 16, 2014 2:30 pm
Contact:

Re: Talking about the circuit design process

Post by Gus_Smedstad »

The next general problem I want to address is how to describe timing. My "fuel priority" example has the virtue that there's no timing involved, and we can state everything in terms of tests of static conditions. But what about cases where we want to time something, or where the state transitions? This I'm struggling with, because the procedural language solution doesn't map exactly to how Factorio combinator circuits work.

Take the example of a steam-power controller. Pre-0.12 solutions detected a stored power shortage, and then turned the inserters feeding the steam power plant on or off. The problem with this was that the inserters loaded a good bit of fuel into the boilers, and it took a while for that to run out when the solar panels came back online.

Now in 0.12, we can control a pump, which will turn on power immediately, and shut it off immediately when accumulator power comes back. But in my early tests, "immediately" was too fast - the network oscillates too much between steam on / steam off. So my goal is now to have a controller than detects when power comes back, and delays shutting off the steam engines for a time I specify.

There's a timer in the Creations board, but it doesn't do what I want. The circuit design is:

Code: Select all

decider memory( external signal->red + memory->red > timer_expired->green ) : red = 1
decider timer( memory->red + timer->red < 1000 ) : red = memory->red + timer->red < 1000
decider timer_expired( timer->red = 900 ) : green = 1

pump steam_pump( memory > 0 )
In circuits with loops, signals can take on a life of their own, persisting indefinitely or changing with time.

In some circuits, particularly ones with deliberate timing loops, it's not good enough to mention a signal by name - the source of the signal matters. memory->red behaves differently from timer->red. memory->red is always either 0 or 1, but timer->red steadily counts up from 0 to 1000, and then resets to 0.

Describing this in logic terms looks something like this:

Code: Select all

loop
{
  if power loss signal
    memory = 1
  else if reset = 1
    memory = 0
  if (timer + memory < 1000)
   timer = timer + memory
  else 
    timer = 0
  reset = (timer = 900)
  if memory > 0 then run steam engine
}
Things I wanted to change about this: the timer starts when power stops, not when it comes back, and the timer behaves a bit oddly. The logic representation doesn't depict all the timing issues, but it's somewhat apparent that the tests to reset the memory and stop the timer have delays compared to the timer. In fact it's 2 ticks delay, so the timer always stops at 902. When it starts again, it starts at 902, since "timer" is a persistent signal, and the loop the second time power goes out is 98 ticks longer than the first.

First change: I want to start the timer based on when stored power returns, not when it fails. But I obviously can't just operate on "not (power low)," because "sufficient power" is the normal, non-emergency state of affairs, and the timer shouldn't be running.

The solution is to store a slightly delayed copy of the "power shortage" signal, and test for the difference.

Code: Select all

  if old_shortage > shortage
   memory = 1
  else if reset = 1
    memory = 0
  old_shortage = shortage
In actual circuit terms, this looks like

Code: Select all

// store a time-delayed copy of the external signal
adder   old_shortage( red = external signal->red + 0 ) 
// test if shortage went from TRUE to FALSE
decider regained_power( old_shortage->red > external signal->red ) : red = 1 
decider memory( regained_power->red + memory->red > timer_expired->green ) : red = 1
Whether you think the check for regaining power is important to this problem or not, it's an example of how to check for a transition from one state to another.

Now I want to re-write the timer logic so it resets to zero every time it starts. I also only want only one time-based test, so there aren't two separate values.

Code: Select all

loop
{
  if old_shortage > shortage
  {
    timer = 0
    memory = 1
  }
  else if reset = 1
    memory = 0
  timer = timer + memory
  reset = (timer > 800)
  if shortage or memory
    run steam engines
}
The problem is that "timer" is persistent, and it doesn't just go away easily. I tried, for example, inserting a stage where timer gets multiplied by a value of 0 or 1 before incrementing, but the timer -> multiplier -> timer loop resulted in an oscillating signal, between 0 and the last timer value. Maybe there's a solution that works that way, but I didn't find it.

What eventually worked for me was to subtract timer from itself, like this:

Code: Select all

  if (old_shortage > shortage)
  {
    memory = 1
    timer = timer - timer
  }
The final circuit looked like this:

Code: Select all

adder      old_shortage( red = external signal-> red + 0 ) 
// test if shortage went from TRUE to FALSE
decider    power_regained( old_shortage->red > external_signal->red ) : red = 1
decider    memory( power_regained->red + memory->red > timer_expired->green ) : red = 1

multiplier regained_as_minus_1( yellow = power_regained->red * -1 ) 
multiplier timer_negated( red = timer->red * regained_as_minus_1->yellow ) 
decider    timer( memory->red + timer->red + timer_negated->red > 0 ) red = memory->red + timer->red + timer_negated->red

decider    timer_expired( timer->red > 800 ) : green

// unrelated to the timer circuit - just used to check "power shortage OR memory"  
adder      run_steam( steam engine = external_signal->red + memory->red )
pump       steam_pump( run_steam->steam_engine > 0 )

SpeedDaemon
Fast Inserter
Fast Inserter
Posts: 124
Joined: Fri May 22, 2015 3:31 pm
Contact:

Re: Talking about the circuit design process

Post by SpeedDaemon »

Ratchetfreak beat me to the Karnaugh map suggestion. :) But, I think it bears some additional explanation.

The first thing to do is think about how you're going to control things in the game. In Gus's original example, that would probably be three inserters for wood, coal, or solid fuel. For any given combination of conditions, each one of those inserters can only have one of two states (running/not running).

Once you know what you're going to use in-game, you can simplify the problem by taking each one individually, and coming up with a boolean expression describing its behavior based on the inputs.

So, let's define our inputs (from Gus's example in the first post), which will either be true or false (1 or 0):
Let:
W = (wood > 0)
C = (coal > 2000)
P = (petroleum gas > 2000)

And the outputs:
W' is true when the wood inserter should be running
C' for the coal inserter
P' for the solid fuel one

From that and Gus's pseudo code, we can build truth tables for each of the inserters (I've combined them to save space - each output column is a separate table):

Code: Select all

W C P | W' C' P'
----------------
0 0 0 | 0  0  1
0 0 1 | 0  0  1
0 1 0 | 0  1  0
0 1 1 | 0  0  1
1 0 0 | 1  0  0
1 0 1 | 1  0  0
1 1 0 | 1  0  0
1 1 1 | 1  0  0
The above tells us exactly what each inserter should be doing for all possible combinations of inputs (this method gets really hairy when you go over four inputs).
Now we can translate that into simplified boolean expressions for each of the outputs. You can use a calculator (like this one), to directly input the truth table and get the simplified expression back, if you don't want to have to learn boolean algebra. :)

What we end up with is:
W': F(WCP) = W (easy!)
C': F(WCP) = !WC!P (not W AND C AND not P)
P': F(WCP) = !WP + !W!C ((not W AND P) OR (not W AND not C))

The nice part about doing this is that those equations can be directly translated into AND, OR, and NOT gates, for which there are well-known idioms in-game. It also shows you where you can cut corners - if more than one equation has identical terms, you can steal the output of that part of the circuit and use it again.

Zhab
Fast Inserter
Fast Inserter
Posts: 101
Joined: Sat Jul 18, 2015 10:17 pm
Contact:

Re: Talking about the circuit design process

Post by Zhab »

SpeedDaemon wrote:What we end up with is:
W': F(WCP) = W (easy!)
C': F(WCP) = !WC!P (not W AND C AND not P)
P': F(WCP) = !WP + !W!C ((not W AND P) OR (not W AND not C))
Well I guess you can translate this in something like 6 combinators. I guess it is an improvement to his original 10 combinators method, but I'll stick to my 4 combinators way.

User avatar
ssilk
Global Moderator
Global Moderator
Posts: 12888
Joined: Tue Apr 16, 2013 10:35 pm
Contact:

Re: Talking about the circuit design process

Post by ssilk »

Code: Select all

    W C P | W' C' P'
    ----------------
    0 0 0 | 0  0  1
    0 0 1 | 0  0  1
    0 1 0 | 0  1  0
    0 1 1 | 0  0  1
    1 0 0 | 1  0  0
    1 0 1 | 1  0  0
    1 1 0 | 1  0  0
    1 1 1 | 1  0  0
This map can be simplified like so:

Code: Select all

    W C P | W' C' P'
    ----------------
    0 0 * | 0  0  1
    0 1 0 | 0  1  0
    0 1 1 | 0  0  1
    1 * * | 1  0  0
A naive thought: Four states => four combinators.

Correct or not?
Cool suggestion: Eatable MOUSE-pointers.
Have you used the Advanced Search today?
Need help, question? FAQ - Wiki - Forum help
I still like small signatures...

Gus_Smedstad
Fast Inserter
Fast Inserter
Posts: 163
Joined: Fri May 16, 2014 2:30 pm
Contact:

Re: Talking about the circuit design process

Post by Gus_Smedstad »

ssilk wrote: A naive thought: Four states => four combinators.

Correct or not?
Well, not automatically, no. Four states = four combinators only works if you can test for each state with a single combinator, which isn't even assured if there are just 2 signals, and we've got 3. Zhab's 4 combinator solution has 6 logical tests, he's just offloaded two of the tests from combinators to inserters.

Using your table,

wood requires no logic
solid fuel = (not( wood) and not( coal )) or (not(wood) and coal and gas)
use coal = not( wood ) and coal and not(gas)

... which are the same statements as SpeedDaemon's.

Gus_Smedstad
Fast Inserter
Fast Inserter
Posts: 163
Joined: Fri May 16, 2014 2:30 pm
Contact:

Re: Talking about the circuit design process

Post by Gus_Smedstad »

SpeedDaemon wrote:What we end up with is:
W': F(WCP) = W (easy!)
C': F(WCP) = !WC!P (not W AND C AND not P)
P': F(WCP) = !WP + !W!C ((not W AND P) OR (not W AND not C))
Let's examine those.

We can re-write P' by distribution

P' = not W and (P or not C)

There's a common sub-term here if we invert, since

P or not C = not(not P and C)

Let's call "not P and C" a new term, X. Obviously, I thought of this because of what I said upthread about solid fuel being the default after wood.

So C' = not W and X
P' = not W and not X

We only need 2 tests against values: not P and C. So:

decider low_gas( gas <= 2000 ) : blue
decider surplus_coal( coal > 2000 ) : red

and the common sub-term

multiplier more_coal_than_gas( low_gas->blue * surplus_coal->red ) : yellow

... slightly misleading, I know, but I wanted something more descriptive than "X."

We can offload the test for coal on to the inserter

inserter coal_inserter( more_coal_than_gas->yellow > wood )

the inversion of the sub-term

decider inversion( more_coal_than_gas->yellow = 0 ) : green

and offload the final test onto a inserter

inserter solid_fuel_inserter( inversion->green > wood )

... which is similar to Zhab's solution, though not identical. It's 4 combinators and 2 logic tests in the inserters, but the reasoning is slightly different. I almost feel like we should be able to do without the "inversion" decider, but I can't see how you'd test for (not wood and not X) directly.

Post Reply

Return to “General discussion”