Here is a guard for a game loop:
IF NOT (dead OR finished) THEN
CALL keepPlaying
ENDYou can work out what it means, but it takes a moment. Two negatives fighting each other: not one thing or the other thing.
Now here is another line:
IF alive AND unfinished THEN
CALL keepPlaying
ENDThat one you read at a glance. The claim of this unit is that they are the same test, and that you can prove it under explicit assumptions rather than take it on trust.
Proving it
For this comparison, alive must equal NOT dead, and unfinished must equal
NOT finished, using the same current Boolean values. These are names for the
complementary answers, not independently stored flags that we hope agree.
So the claim is:
NOT (dead OR finished) is NOT dead AND NOT finished
Build both columns and compare:
| dead | finished | NOT (dead OR finished) | NOT dead AND NOT finished |
|---|---|---|---|
| 0 | 0 | 1 | 1 |
| 0 | 1 | 0 | 0 |
| 1 | 0 | 0 | 0 |
| 1 | 1 | 0 | 0 |
Every row matches. Not most rows. Every row, and the table lists all of them, so there is no case left where they could differ.
That proves equal Boolean results for the same input values. Under the stated complement assumptions, choose whichever wording reads better.
A table does not measure execution cost or establish a target language’s operand evaluation rules. If a test calls a job, changes state or can fail, check that language’s behaviour as well.
What if dead is TRUE but a separately stored alive flag is still TRUE?
With finished FALSE and unfinished TRUE, NOT (dead OR finished) is FALSE,
but alive AND unfinished is TRUE. The stale flag breaks our complement
assumption. De Morgan’s law has not failed; the renamed inputs no longer mean
the same current facts.
The rule behind it
This is not a one-off. It works the other way round too:
| A | B | NOT (A AND B) | NOT A OR NOT B |
|---|---|---|---|
| 0 | 0 | 1 | 1 |
| 0 | 1 | 1 | 1 |
| 1 | 0 | 1 | 1 |
| 1 | 1 | 0 | 0 |
Both together are called De Morgan’s laws, after the mathematician who wrote them down:
NOT (A AND B)is the same asNOT A OR NOT BNOT (A OR B)is the same asNOT A AND NOT B
The pattern is short enough to keep in your head. Push the NOT inwards, and AND swaps
with OR.
What it is for
Two things, and the second is the one that pays.
Untangling. A NOT in front of a bracket is hard to read. Push it in, and often the
double negatives cancel and leave you a line that says what it means.
Choosing your side. Sometimes the positive version is clearer, sometimes the negative one. De Morgan’s lets you pick without changing behaviour. A guard is easier to trust when it is phrased the way you think about the problem.
When the tables disagree
The interesting case is when you build both columns and they do not match.
That is not a failure. It means the two lines are genuinely different tests, and you have just found out which cases tell them apart. Look at the rows that differ, because one of them is a bug you were about to ship.
This is the whole method: when you are unsure whether two versions behave the same, do not squint at them. Build both columns and read across.
When it’s wrong, see why
- You dropped a
NOTwhile pushing it in. Every term inside the bracket gets one.NOT (A AND B)isNOT A OR NOT B, notNOT A OR B. - You forgot to swap the joining word. Pushing a
NOTin turnsANDintoORand back. Leaving it alone gives a different test that happens to look right. - The columns disagree in one row. Trust the table. Find that case in the game and decide which behaviour you want.
What you’ve learnt
- Matching columns prove equal Boolean results for the same inputs, not every runtime behaviour.
- De Morgan’s laws: push a
NOTinside a bracket andANDswaps withOR. - The point is readable guards: say a condition the way you think about it.
- Columns that disagree have shown you a case worth looking at.
Where this leaves you
You can write down every case a test has, compare two tests honestly, and rewrite one into the other while preserving its Boolean result under the stated assumptions. Keep the actual input meanings and target-language rules in view.
The tables have a second life, though. Every one of them can be built out of wire. Once you have met binary in Counting in Twos, Gates and the Adder picks these same three tables up and makes them add.
For a language-specific connection, Keep guessing checks the lower bound, upper bound and whole-number rule separately. Its complete BASIC listing and earlier entry help remain available; you do not need BASIC to finish these tables. Compare which cases its range checks reject before moving on.
The optional Game State Machine pattern shows why current facts and policy both matter: its transition table gives death priority over pause. It uses its own illustrative pseudocode, not our Foundations notation, and is not an execution record or required next lesson.
Sources
Sinclair Research, ZX Spectrum BASIC Programming, chapter 13, pp. 85–86, Conditions, describes AND, OR, NOT and De Morgan’s laws, then explains Sinclair BASIC’s numeric rules. Those target rules are distinct from the Boolean table notation used here.