big revamp tilesets intro
This commit is contained in:
@@ -1,17 +1,19 @@
|
||||
#import "/lib.typ": diorama, example, note, todo
|
||||
|
||||
#show raw.where(lang: "mc-diorama"): it => diorama(autoplay: true, loop: true, theta: 160, phi: 20, it.text)
|
||||
|
||||
= Tilesets <tilesets>
|
||||
|
||||
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.
|
||||
incremented. The fundamental cause for all types of interference is that there is a chance for
|
||||
unexpected entities to spawn in-between the measurement items, which increments the counter and
|
||||
corrupts the data. The solution, then, is to make that window *as short as possible* so the chance
|
||||
for interference is minimized.
|
||||
|
||||
We use the tile tick priority queue to define a global order of sub-tick execution.
|
||||
The fundamental principle is to use the game scheduled tick priority queue to do this. A *tile
|
||||
sequence* is a chain of redstone components that all update in the scheduled tick phase. A *tile
|
||||
set* is a system of constructing tile sequences that enforces a particular global ordering.
|
||||
|
||||
#show raw.where(lang: "mc-diorama"): it => diorama(autoplay: true, loop: true, theta: 160, phi: 20, it.text)
|
||||
```mc-diorama
|
||||
p 0 -1 0 smooth_stone_slab type=top
|
||||
p 1 -1 0 smooth_stone_slab type=top
|
||||
@@ -45,112 +47,241 @@ t 20
|
||||
p 0 0 0
|
||||
```
|
||||
|
||||
#todo[fix tileset naming. the tileset is the *structure*. the particular sequence is just one instance of that tileset]
|
||||
== The Scheduled Tick Priority Queue
|
||||
|
||||
A *tile sequence* is a chain of redstone components that all update in the tile tick phase.
|
||||
The fundamental principle here is to use tile-tick priority (TTP) to set a global order.
|
||||
#link("https://youtu.be/sLftwVwqPQE")[See Charlie's great video on TTP for details].
|
||||
There are two aspects that affect a scheduled tick's placement in the queue: its _priority_ and its
|
||||
_delay_. Scheduled ticks with higher (more negative) priority update first in a given game tick.
|
||||
Scheduled ticks with equal priority update in the order they were scheduled.
|
||||
|
||||
A *tile set* is a system of constructing tile sequences that enforces a particular global ordering.
|
||||
See @block-priority for a list of priorities. The most important point is that repeaters have higher
|
||||
priority than other components.
|
||||
|
||||
== Binary Tilesets
|
||||
|
||||
The simplest (and generally most useful) tilesets use only two priorities. This allows us to
|
||||
identify each tile sequence with a binary value and easily read the update order and carve up the
|
||||
the full tile set for different systems. Typically we use comparators (or observers) and 2gt
|
||||
repeaters.
|
||||
|
||||
We identify the higher priority (repeater) with `0`, since it sorts/updates earlier, and identify
|
||||
the lower priority (observer/comparator) with `1`. Then we can read off the elements of the tile
|
||||
sequence to form a binary number.
|
||||
|
||||
Components that activate later in the sequence have a stronger effect on the order, so to match our
|
||||
left-to-right reading convention, we diagram tile sequences so the signal flows right-to-left.
|
||||
|
||||
== The Tile Tick Priority Queue
|
||||
Consider this 4gt tileset.
|
||||
|
||||
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.
|
||||
#show raw.where(lang: "mc-diorama"): it => diorama(
|
||||
autoplay: true,
|
||||
loop: true,
|
||||
theta: 90,
|
||||
phi: 90,
|
||||
speed: 0.33,
|
||||
radius: 8,
|
||||
orbit: false,
|
||||
it.text,
|
||||
)
|
||||
|
||||
#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.
|
||||
]
|
||||
```mc-diorama
|
||||
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 5 -1 0 smooth_stone_slab type=top
|
||||
p 6 -1 0 smooth_stone_slab type=top
|
||||
p 7 -1 0 smooth_stone_slab type=top
|
||||
|
||||
Components with different priority always update in priority order, but components with equal
|
||||
priority update in scheduled order.
|
||||
p 0 0 0 redstone_wire east=side west=none south=side power=0
|
||||
p 1 0 0 redstone_wire east=side west=side south=side power=0
|
||||
p 2 0 0 redstone_wire east=side west=side south=side power=0
|
||||
p 3 0 0 redstone_wire east=side west=side south=side power=0
|
||||
p 4 0 0 redstone_wire east=side west=side south=side power=0
|
||||
p 5 0 0 redstone_wire east=side west=side south=side power=0
|
||||
p 6 0 0 redstone_wire east=side west=side south=side power=0
|
||||
p 7 0 0 redstone_wire east=none west=side south=side power=0
|
||||
|
||||
#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.
|
||||
p 0 0 1 repeater facing=north delay=1 powered=false locked=false
|
||||
p 1 0 1 comparator facing=north mode=compare powered=false
|
||||
p 2 0 1 repeater facing=north delay=1 powered=false locked=false
|
||||
p 3 0 1 comparator facing=north mode=compare powered=false
|
||||
p 4 0 1 repeater facing=north delay=1 powered=false locked=false
|
||||
p 5 0 1 comparator facing=north mode=compare powered=false
|
||||
p 6 0 1 repeater facing=north delay=1 powered=false locked=false
|
||||
p 7 0 1 comparator facing=north mode=compare powered=false
|
||||
|
||||
// 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.
|
||||
p 0 0 2 repeater facing=north delay=1 powered=false locked=false
|
||||
p 1 0 2 repeater facing=north delay=1 powered=false locked=false
|
||||
p 2 0 2 comparator facing=north mode=compare powered=false
|
||||
p 3 0 2 comparator facing=north mode=compare powered=false
|
||||
p 4 0 2 repeater facing=north delay=1 powered=false locked=false
|
||||
p 5 0 2 repeater facing=north delay=1 powered=false locked=false
|
||||
p 6 0 2 comparator facing=north mode=compare powered=false
|
||||
p 7 0 2 comparator facing=north mode=compare powered=false
|
||||
|
||||
]
|
||||
p 0 0 3 repeater facing=north delay=1 powered=false locked=false
|
||||
p 1 0 3 repeater facing=north delay=1 powered=false locked=false
|
||||
p 2 0 3 repeater facing=north delay=1 powered=false locked=false
|
||||
p 3 0 3 repeater facing=north delay=1 powered=false locked=false
|
||||
p 4 0 3 comparator facing=north mode=compare powered=false
|
||||
p 5 0 3 comparator facing=north mode=compare powered=false
|
||||
p 6 0 3 comparator facing=north mode=compare powered=false
|
||||
p 7 0 3 comparator facing=north mode=compare powered=false
|
||||
|
||||
#example[
|
||||
#todo[this explanation is so janky]
|
||||
t 0
|
||||
p 0 0 0 power=15
|
||||
p 1 0 0 power=15
|
||||
p 2 0 0 power=15
|
||||
p 3 0 0 power=15
|
||||
p 4 0 0 power=15
|
||||
p 5 0 0 power=15
|
||||
p 6 0 0 power=15
|
||||
p 7 0 0 power=15
|
||||
|
||||
Say these three tilesets are started in the same gametick, `t=0`.
|
||||
t 1
|
||||
p 0 0 1 powered=true
|
||||
p 2 0 1 powered=true
|
||||
p 4 0 1 powered=true
|
||||
p 6 0 1 powered=true
|
||||
|
||||
```
|
||||
<------
|
||||
1 rep cmp ab
|
||||
2 cmp rep cd
|
||||
3 2rep e
|
||||
<------
|
||||
```
|
||||
t 2
|
||||
p 1 0 1 powered=true
|
||||
p 3 0 1 powered=true
|
||||
p 5 0 1 powered=true
|
||||
p 7 0 1 powered=true
|
||||
|
||||
Once `b`, `d`, and `e` are scheduled, the priority queue looks like this:
|
||||
t 3
|
||||
p 0 0 0 power=0
|
||||
p 1 0 0 power=0
|
||||
p 2 0 0 power=0
|
||||
p 3 0 0 power=0
|
||||
p 4 0 0 power=0
|
||||
p 5 0 0 power=0
|
||||
p 6 0 0 power=0
|
||||
p 7 0 0 power=0
|
||||
|
||||
```
|
||||
t=2 [d] [b]
|
||||
t=4 [e]
|
||||
```
|
||||
t 4
|
||||
p 0 0 2 powered=true
|
||||
p 4 0 2 powered=true
|
||||
t 5
|
||||
p 1 0 2 powered=true
|
||||
p 5 0 2 powered=true
|
||||
t 6
|
||||
p 2 0 2 powered=true
|
||||
p 6 0 2 powered=true
|
||||
t 7
|
||||
p 3 0 2 powered=true
|
||||
p 7 0 2 powered=true
|
||||
|
||||
Now at `t=2`, we process the queue in order.
|
||||
t 8
|
||||
p 0 0 1 powered=false
|
||||
p 2 0 1 powered=false
|
||||
p 4 0 1 powered=false
|
||||
p 6 0 1 powered=false
|
||||
|
||||
- `d` activates and schedules `c`.
|
||||
- `b` activates and schedules `a`.
|
||||
t 9
|
||||
p 0 0 3 powered=true
|
||||
t 10
|
||||
p 1 0 3 powered=true
|
||||
t 11
|
||||
p 2 0 3 powered=true
|
||||
t 12
|
||||
p 3 0 3 powered=true
|
||||
|
||||
```
|
||||
t=4 [e a] [c]
|
||||
```
|
||||
t 13
|
||||
p 1 0 1 powered=false
|
||||
p 3 0 1 powered=false
|
||||
p 5 0 1 powered=false
|
||||
p 7 0 1 powered=false
|
||||
|
||||
Now at `t=4`, the end of the tileset, the lanes will always update in order `3 1 2`
|
||||
t 14
|
||||
p 4 0 3 powered=true
|
||||
t 15
|
||||
p 5 0 3 powered=true
|
||||
t 16
|
||||
p 6 0 3 powered=true
|
||||
t 17
|
||||
p 7 0 3 powered=true
|
||||
|
||||
]
|
||||
t 18
|
||||
p 0 0 2 powered=false
|
||||
p 4 0 2 powered=false
|
||||
t 19
|
||||
p 1 0 2 powered=false
|
||||
p 5 0 2 powered=false
|
||||
t 20
|
||||
p 2 0 2 powered=false
|
||||
p 6 0 2 powered=false
|
||||
t 21
|
||||
p 3 0 2 powered=false
|
||||
p 7 0 2 powered=false
|
||||
|
||||
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.
|
||||
t 22
|
||||
p 0 0 3 powered=false
|
||||
t 23
|
||||
p 1 0 3 powered=false
|
||||
t 24
|
||||
p 2 0 3 powered=false
|
||||
t 25
|
||||
p 3 0 3 powered=false
|
||||
t 26
|
||||
p 4 0 3 powered=false
|
||||
t 27
|
||||
p 5 0 3 powered=false
|
||||
t 28
|
||||
p 6 0 3 powered=false
|
||||
t 29
|
||||
p 7 0 3 powered=false
|
||||
|
||||
== Permutation Tilesets
|
||||
|
||||
#todo[describe the mixed delay permutation tilesets and the alphabetization procedure]
|
||||
|
||||
== Binary Tilesets
|
||||
|
||||
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)
|
||||
t 30
|
||||
p 0 0 0
|
||||
```
|
||||
|
||||
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.
|
||||
We can express the same tileset numerically like so:
|
||||
|
||||
```
|
||||
<------
|
||||
cmp cmp (BB)
|
||||
cmp rep (BA)
|
||||
rep cmp (AB)
|
||||
rep rep (AA)
|
||||
000 = 0
|
||||
001 = 1
|
||||
010 = 2
|
||||
011 = 3
|
||||
100 = 4
|
||||
101 = 5
|
||||
110 = 6
|
||||
111 = 7
|
||||
```
|
||||
|
||||
Using the same pattern, we can generate a tile sequence for an arbitrary value of arbitrary
|
||||
bit-length. For example, the 8-bit tileset for `150` is:
|
||||
|
||||
10010110
|
||||
|
||||
#show raw.where(lang: "mc-diorama"): it => diorama(
|
||||
theta: 90,
|
||||
phi: 90,
|
||||
speed: 0.33,
|
||||
radius: 5,
|
||||
orbit: false,
|
||||
it.text,
|
||||
)
|
||||
|
||||
```mc-diorama
|
||||
p 0 -1 0 smooth_stone_slab type=top
|
||||
p 0 -1 1 smooth_stone_slab type=top
|
||||
p 0 -1 2 smooth_stone_slab type=top
|
||||
p 0 -1 3 smooth_stone_slab type=top
|
||||
p 0 -1 4 smooth_stone_slab type=top
|
||||
p 0 -1 5 smooth_stone_slab type=top
|
||||
p 0 -1 6 smooth_stone_slab type=top
|
||||
p 0 -1 7 smooth_stone_slab type=top
|
||||
p 0 0 0 repeater facing=north powered=false locked=false delay=1
|
||||
p 0 0 1 comparator facing=north powered=false mode=compare
|
||||
p 0 0 2 comparator facing=north powered=false mode=compare
|
||||
p 0 0 3 repeater facing=north powered=false locked=false delay=1
|
||||
p 0 0 4 comparator facing=north powered=false mode=compare
|
||||
p 0 0 5 repeater facing=north powered=false locked=false delay=1
|
||||
p 0 0 6 repeater facing=north powered=false locked=false delay=1
|
||||
p 0 0 7 comparator facing=north powered=false mode=compare
|
||||
```
|
||||
|
||||
To break this down: the bottom two (AA, AB) end in repeaters, so they must come first. Among those,
|
||||
@@ -197,6 +328,81 @@ next lane by simply incrementing by one.
|
||||
cmp rep rep cmp cmp rep rep
|
||||
```
|
||||
|
||||
|
||||
// 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]
|
||||
|
||||
|
||||
== Lexicographic Tilesets
|
||||
|
||||
== Jamming
|
||||
|
||||
== Observer Bug <observer-bug>
|
||||
|
||||
== Tables
|
||||
|
||||
=== Block Tick Priorities <block-priority>
|
||||
|
||||
#table(
|
||||
columns: 4,
|
||||
table.cell(rowspan: 3)[Repeater],
|
||||
[Facing diode (that is not facing it)],
|
||||
[`EXTREMELY_HIGH`],
|
||||
[-3],
|
||||
[Powering off],
|
||||
[`VERY_HIGH`],
|
||||
[-2],
|
||||
[Powering on],
|
||||
[`HIGH`],
|
||||
[-1],
|
||||
table.cell(rowspan: 2)[Comparator],
|
||||
[Facing diode (that is not facing it)],
|
||||
[`HIGH`],
|
||||
[-1],
|
||||
[Otherwise],
|
||||
[`NORMAL`],
|
||||
[0],
|
||||
table.cell(colspan: 2)[All other components],
|
||||
[`NORMAL`],
|
||||
[0],
|
||||
)
|
||||
|
||||
=== Block Tick Delays <block-delay>
|
||||
|
||||
#table(
|
||||
columns: 2,
|
||||
[Repeater], [2gt, 4gt, 6gt, 8gt],
|
||||
[Comparator], table.cell(rowspan: 4)[2gt],
|
||||
[Redstone Torch],
|
||||
[Falling Blocks],
|
||||
[Observer (see @observer-bug)],
|
||||
[Dispenser], table.cell(rowspan: 3)[4gt],
|
||||
[Dropper],
|
||||
[Redstone Lamp],
|
||||
[Iron Pressure Plate], table.cell(rowspan: 4)[10gt],
|
||||
[Gold Pressure Plate],
|
||||
[Tripwire Hook],
|
||||
[Tripwire],
|
||||
[Stone Button], table.cell(rowspan: 3)[20gt],
|
||||
[Stone Pressure Plate],
|
||||
[Detector Rail],
|
||||
[Wooden Button], table.cell(rowspan: 2)[30gt],
|
||||
[Wooden Pressure Plate],
|
||||
)
|
||||
|
||||
=== Fluid Tick Delays <fluid-delay>
|
||||
|
||||
#table(
|
||||
columns: 2,
|
||||
[Water], [5gt],
|
||||
[Nether Lava], [10gt],
|
||||
[Lava], [30gt],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user