import sources from scattered branches
This commit is contained in:
217
content/interference-fixes.typ
Normal file
217
content/interference-fixes.typ
Normal file
@@ -0,0 +1,217 @@
|
||||
#import "/lib.typ": diorama, example, todo, world
|
||||
|
||||
= Preventing Interference <timing>
|
||||
|
||||
The protocols as described are very sensitive to the exact timing that the EID counter is
|
||||
incremented. The fundamental cause for all types of interference is that we spawn a reference and
|
||||
measurement item with too much delay, and some unexpected entity (or entities) spawn in-between. To
|
||||
mitigate interference, then, we need that window to be *as short as possible* while still allowing
|
||||
us to spawn transmission entities in that window.
|
||||
|
||||
== Tilesets
|
||||
|
||||
The fundamental principle here is to use tile-tick priority (TTP) to set a global order. See
|
||||
Charlie's great video on TTP for details. #todo[link video]
|
||||
|
||||
#todo[fix tileset naming. the tileset is the *structure*. the particular sequence is just one instance of that tileset]
|
||||
|
||||
A *tileset* is a sequence of redstone components which all update in the tile tick phase. Components
|
||||
later in the sequence have greater significance. To make the significance match our left-to-right
|
||||
reading convention, we diagram them so the signal flows right-to-left. For example:
|
||||
|
||||
|
||||
#diorama(
|
||||
theta: 160,
|
||||
phi: 20,
|
||||
autoplay: true,
|
||||
loop: true,
|
||||
world(
|
||||
"
|
||||
p 0 -1 0 smooth_stone_slab type=top
|
||||
p 1 -1 0 smooth_stone_slab type=top
|
||||
p 2 -1 0 smooth_stone_slab type=top
|
||||
p 3 -1 0 smooth_stone_slab type=top
|
||||
p 4 -1 0 smooth_stone_slab type=top
|
||||
|
||||
p 0 0 0 repeater facing=west powered=false locked=false delay=1
|
||||
p 1 0 0 observer facing=west powered=false
|
||||
p 2 0 0 repeater facing=west powered=false locked=false delay=2
|
||||
p 3 0 0 comparator facing=west powered=false mode=compare
|
||||
p 4 0 0 repeater facing=west powered=false locked=false delay=1
|
||||
|
||||
t 2
|
||||
p 0 0 0 powered=true
|
||||
t 4
|
||||
p 0 0 0 powered=false
|
||||
p 1 0 0 powered=true
|
||||
t 6
|
||||
p 1 0 0 powered=false
|
||||
t 8
|
||||
p 2 0 0 powered=true
|
||||
t 10
|
||||
p 3 0 0 powered=true
|
||||
t 12
|
||||
p 2 0 0 powered=false
|
||||
p 4 0 0 powered=true
|
||||
t 14
|
||||
p 3 0 0 powered=false
|
||||
t 16
|
||||
p 4 0 0 powered=false
|
||||
|
||||
t 20
|
||||
p 0 0 0
|
||||
",
|
||||
),
|
||||
)
|
||||
|
||||
This section will cover how to read and construct tilesets that enforce a *global* ordering for use
|
||||
in wireless redstone.
|
||||
|
||||
=== The Tile Tick Priority Queue
|
||||
|
||||
The game schedules tile updates through a priority queue; different components have different
|
||||
priority, so each stage of the tileset iteratively refines the global order. Across the full
|
||||
tileset, we can enforce an arbitrary global ordering.
|
||||
|
||||
#todo[the priority table][
|
||||
call out here that basic comparators and all other components, so from here on out we're just
|
||||
going to use comparators for simplicity.
|
||||
]
|
||||
|
||||
Components with different priority always update in priority order, but components with equal
|
||||
priority update in scheduled order.
|
||||
|
||||
#todo[rephrase or remove][
|
||||
The core principal is that repeaters have higher priority than other components. For example, if a
|
||||
repeater and comparator are scheduled to activate in the same tick, the repeater always (with one
|
||||
exception, if the comparator faces into a diode and the repeater does not) activates before the
|
||||
comparator does. By choosing a regular building pattern, we can avoid this exception entirely.
|
||||
|
||||
// Among repeaters, they activate in the order in which they were scheduled. And among
|
||||
// comparators, they activate in the order in which they are scheduled. But every repeater
|
||||
// activates before any comparator (aside from that one exception). This gives us a *stable sort*
|
||||
// which we can use to globally define update order with arbitrary precision.
|
||||
|
||||
]
|
||||
|
||||
#example[
|
||||
#todo[this explanation is so janky]
|
||||
|
||||
Say these three tilesets are started in the same gametick, `t=0`.
|
||||
|
||||
```
|
||||
<------
|
||||
1 rep cmp ab
|
||||
2 cmp rep cd
|
||||
3 2rep e
|
||||
<------
|
||||
```
|
||||
|
||||
Once `b`, `d`, and `e` are scheduled, the priority queue looks like this:
|
||||
|
||||
```
|
||||
t=2 [d] [b]
|
||||
t=4 [e]
|
||||
```
|
||||
|
||||
Now at `t=2`, we process the queue in order.
|
||||
|
||||
- `d` activates and schedules `c`.
|
||||
- `b` activates and schedules `a`.
|
||||
|
||||
```
|
||||
t=4 [e a] [c]
|
||||
```
|
||||
|
||||
Now at `t=4`, the end of the tileset, the lanes will always update in order `3 1 2`
|
||||
|
||||
]
|
||||
|
||||
So the full picture involves arbitrary components and priorities. As long as all the tilesets have
|
||||
the same total delay and end at the same time, we can determine a global ordering. However the
|
||||
general picture is hard to reason about, we basically have to simulate the priority queue to make
|
||||
predictions. If we restrict the design of the tilesets a bit, there are two simplifications we could
|
||||
take to make things easier to reason about.
|
||||
|
||||
=== Permutation Tilesets
|
||||
|
||||
#todo[describe the mixed delay permutation tilesets and the alphabetization procedure]
|
||||
|
||||
=== Binary
|
||||
|
||||
The simplest tilesets are made entirely of comparators and 2gt repeaters (alternatively: observers
|
||||
and 2gt repeaters). Repeaters activate before comparators, so if we think of it like sorting words
|
||||
alphabetically, we can identify repeaters with "A" and comparators with "B".
|
||||
|
||||
So, suppose we have a "binary tileset" that is 2gt long. There are two options, A and B. If we
|
||||
alphabetize these, we see the A (repeater) always executes before the B (comparator). With such a
|
||||
short tileset, that seems trivial; things get more interesting as we add more elements.
|
||||
|
||||
```
|
||||
<--
|
||||
cmp (B)
|
||||
rep (A)
|
||||
```
|
||||
|
||||
Now let's use 2 components for a 4gt tileset. There are now four options, AA, AB, BA, BB. We can
|
||||
alphabetize these and see the order.
|
||||
|
||||
```
|
||||
<------
|
||||
cmp cmp (BB)
|
||||
cmp rep (BA)
|
||||
rep cmp (AB)
|
||||
rep rep (AA)
|
||||
```
|
||||
|
||||
To break this down: the bottom two (AA, AB) end in repeaters, so they must come first. Among those,
|
||||
(AA) comes first. Among the top two (BA, BB), (BA) comes first. It's standard alphabetizing. Just as
|
||||
the order of the alphabet creates an ordering over all words, the ordering of the comparator and
|
||||
repeater creates an ordering of all tilesets.
|
||||
|
||||
The 6gt tileset. There are now eight options.
|
||||
|
||||
```
|
||||
<---------
|
||||
...
|
||||
```
|
||||
|
||||
As the tilesets get larger, it's less useful to lay out the entire tileset and more useful to find
|
||||
the next and previous lanes.
|
||||
|
||||
For example, take this 7-diode tileset.
|
||||
|
||||
```
|
||||
cmp rep rep cmp rep cmp cmp
|
||||
<--------------------------
|
||||
B A A B A B B
|
||||
```
|
||||
|
||||
We can easily find the next lane by thinking of this not as a *word* but as a *number*. We have two
|
||||
options, and one is greater than the other.
|
||||
|
||||
```
|
||||
cmp rep rep cmp rep cmp cmp
|
||||
<--------------------------
|
||||
B A A B A B B
|
||||
1 0 0 1 0 1 1
|
||||
```
|
||||
|
||||
We can think of this tileset as a 7-bit binary number, in this case the value 75. We can find the
|
||||
next lane by simply incrementing by one.
|
||||
|
||||
```
|
||||
1 0 0 1 0 1 1 (75)
|
||||
+ 1
|
||||
1 0 0 1 1 0 0 (76)
|
||||
<--------------------------
|
||||
cmp rep rep cmp cmp rep rep
|
||||
```
|
||||
|
||||
=== Lexicographic
|
||||
|
||||
=== Jamming
|
||||
|
||||
== Block Event Delay
|
||||
|
||||
== Single-Priority Loops
|
||||
Reference in New Issue
Block a user