split tilesets, bed

This commit is contained in:
David Allemang
2026-07-29 10:11:59 -04:00
parent af8119f120
commit 0aa52320a3
5 changed files with 208 additions and 285 deletions

View File

@@ -2,215 +2,7 @@
#show raw.where(lang: "mc-diorama"): it => diorama(autoplay: true, loop: true, theta: 160, phi: 20, it.text)
= 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
```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 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
```
#todo[fix tileset naming. the tileset is the *structure*. the particular sequence is just one instance of that tileset]
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].
A *tile set* is a system of constructing tile sequences that enforces a particular global ordering.
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
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
= Block Event Delay
Block Event Delay is not a complete substitute for tilesets, but it can significantly reduce the
required length of tilesets and compact repetetive systems. It is based on a behavior of piston push
@@ -247,7 +39,6 @@ p 4 -1 -1 piston facing=up extended=false
p 4 0 -2 observer facing=south powered=false
p 4 0 0 light_gray_terracotta
p 4 1 0 redstone_wire east=side west=side power=0
t 0
p -1 0 0 powered=true
p 0 1 0 power=15
@@ -255,7 +46,6 @@ p 1 1 0 power=14
p 2 1 0 power=13
p 3 1 0 power=12
p 4 1 0 power=11
t 1
p 0 0 -1 extended=true
p 0 -0.5 -1 piston_head facing=down short=true type=normal
@@ -267,7 +57,6 @@ p 3 -1 -1 extended=true
p 3 -0.5 -1 piston_head facing=up short=true type=normal
p 4 -1 -1 extended=true
p 4 -0.5 -1 piston_head facing=up short=true type=normal
t 2
p -1 0 0 powered=false
p 0 1 0 power=0
@@ -289,7 +78,6 @@ p 1 0 -2 powered=true
p 2 0 -2 powered=true
p 3 0 -2 powered=true
p 4 0 -2 powered=true
t 3
p 0 -1 -1 air
p 1 0 -1 air
@@ -301,7 +89,6 @@ p 1 -0.5 -1 piston_head facing=up short=true type=normal
p 2 -0.5 -1 piston_head facing=up short=true type=normal
p 3 -0.5 -1 piston_head facing=up short=true type=normal
p 4 -0.5 -1 piston_head facing=up short=true type=normal
t 4
p 0 -0.5 -1 air
p 1 -0.5 -1 air
@@ -317,7 +104,6 @@ p 1 0 -2 powered=false
p 2 0 -2 powered=false
p 3 0 -2 powered=false
p 4 0 -2 powered=false
t 20
p 0 0 0
```
@@ -425,22 +211,18 @@ p 1 1 8 redstone_wire east=side west=side power=0
p 2 1 8 redstone_wire east=side west=side power=0
p 3 1 8 redstone_wire east=side west=side power=0
p 4 1 8 redstone_wire east=side west=side power=0
t 1
p -3 0 0 powered=true
p -3 0 4 powered=true
p 7 0 8 powered=true
t 3
p -2 0 0 powered=true
p -2 0 4 powered=true
p 6 0 8 powered=true
t 6
p -3 0 0 powered=false
p -3 0 4 powered=false
p 7 0 8 powered=false
t 8
p 5 0 8 powered=true
p 0 1 8 power=15
@@ -448,7 +230,6 @@ p 1 1 8 power=14
p 2 1 8 power=13
p 3 1 8 power=12
p 4 1 8 power=11
t 9
p -1 0 4 powered=true
p 0 1 4 power=15
@@ -456,7 +237,6 @@ p 1 1 4 power=14
p 2 1 4 power=13
p 3 1 4 power=12
p 4 1 4 power=11
t 10
p -1 0 0 powered=true
p 0 1 0 power=11
@@ -464,72 +244,55 @@ p 1 1 0 power=12
p 2 1 0 power=13
p 3 1 0 power=14
p 4 1 0 power=15
t 11
p 4 0 7 extended=true
p 4 -1 7 piston_head type=normal facing=down short=false
t 12
p 4 0 3 extended=true
p 4 -1 3 piston_head type=normal facing=down short=false
t 13
p 4 0 -1 extended=true
p 4 -1 -1 piston_head type=normal facing=down short=false
t 14
p 3 -1 7 extended=true
p 3 0 7 piston_head type=normal facing=up short=false
t 15
p 3 -1 3 extended=true
p 3 0 3 piston_head type=normal facing=up short=false
t 16
p 3 -1 -1 extended=true
p 3 0 -1 piston_head type=normal facing=up short=false
t 17
p 2 -1 7 extended=true
p 2 0 7 piston_head type=normal facing=up short=false
t 18
p 2 -1 3 extended=true
p 2 0 3 piston_head type=normal facing=up short=false
t 19
p 2 -1 -1 extended=true
p 2 0 -1 piston_head type=normal facing=up short=false
t 20
p 1 -1 7 extended=true
p 1 0 7 piston_head type=normal facing=up short=false
t 21
p 1 -1 3 extended=true
p 1 0 3 piston_head type=normal facing=up short=false
t 22
p 1 -1 -1 extended=true
p 1 0 -1 piston_head type=normal facing=up short=false
t 23
p 0 -1 7 extended=true
p 0 0 7 piston_head type=normal facing=up short=false
t 24
p 0 -1 3 extended=true
p 0 0 3 piston_head type=normal facing=up short=false
t 25
p 0 -1 -1 extended=true
p 0 0 -1 piston_head type=normal facing=up short=false
t 26
p -2 0 0 powered=false
p -2 0 4 powered=false
p 6 0 8 powered=false
t 28
p 5 0 8 powered=false
p 0 1 8 power=0
@@ -537,7 +300,6 @@ p 1 1 8 power=0
p 2 1 8 power=0
p 3 1 8 power=0
p 4 1 8 power=0
t 29
p -1 0 4 powered=false
p 0 1 4 power=0
@@ -545,7 +307,6 @@ p 1 1 4 power=0
p 2 1 4 power=0
p 3 1 4 power=0
p 4 1 4 power=0
t 30
p -1 0 0 powered=false
p 0 1 0 power=0
@@ -553,139 +314,99 @@ p 1 1 0 power=0
p 2 1 0 power=0
p 3 1 0 power=0
p 4 1 0 power=0
t 31
p 3 0 6 powered=true
t 32
p 3 0 2 powered=true
t 33
p 3 0 -2 powered=true
t 34
p 2 0 6 powered=true
t 35
p 2 0 2 powered=true
t 36
p 2 0 -2 powered=true
t 37
p 1 0 6 powered=true
t 38
p 1 0 2 powered=true
t 39
p 1 0 -2 powered=true
t 40
p 0 0 6 powered=true
t 41
p 0 0 2 powered=true
t 42
p 0 0 -2 powered=true
t 43
p 4 0 7 extended=false
p 4 -1 7 air
t 44
p 4 0 3 extended=false
p 4 -1 3 air
t 45
p 4 0 -1 extended=false
p 4 -1 -1 air
t 46
p 3 -1 7 extended=false
p 3 0 7 air
t 47
p 3 -1 3 extended=false
p 3 0 3 air
t 48
p 3 -1 -1 extended=false
p 3 0 -1 air
t 49
p 2 -1 7 extended=false
p 2 0 7 air
t 50
p 2 -1 3 extended=false
p 2 0 3 air
t 51
p 2 -1 -1 extended=false
p 2 0 -1 air
t 52
p 1 -1 7 extended=false
p 1 0 7 air
t 53
p 1 -1 3 extended=false
p 1 0 3 air
t 54
p 1 -1 -1 extended=false
p 1 0 -1 air
t 55
p 0 -1 7 extended=false
p 0 0 7 air
t 56
p 0 -1 3 extended=false
p 0 0 3 air
t 57
p 0 -1 -1 extended=false
p 0 0 -1 air
t 58
p 3 0 6 powered=false
t 59
p 3 0 2 powered=false
t 60
p 3 0 -2 powered=false
t 61
p 2 0 6 powered=false
t 62
p 2 0 2 powered=false
t 63
p 2 0 -2 powered=false
t 64
p 1 0 6 powered=false
t 65
p 1 0 2 powered=false
t 66
p 1 0 -2 powered=false
t 67
p 0 0 6 powered=false
t 68
p 0 0 2 powered=false
t 69
p 0 0 -2 powered=false
t 72
p 0 0 0
```

View File

@@ -14,7 +14,7 @@ surprising. An exhaustive list is available at
In general, the solution is to spawn our item entities at nearly the same time, so there is no
chance for other entities to spawn in-between. For example, by spawning all items with droppers in
the same game tick, all the items are created in the same tick phase, so player inputs and world
events cannot affect it. See @timing for details.
events cannot affect it. See @tilesets for details.
== Singleplayer (before 26.2) <singleplayer>

202
content/tilesets.typ Normal file
View File

@@ -0,0 +1,202 @@
#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.
We use the tile tick priority queue to define a global order of sub-tick execution.
```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 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
```
#todo[fix tileset naming. the tileset is the *structure*. the particular sequence is just one instance of that tileset]
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].
A *tile set* is a system of constructing tile sequences that enforces a particular global ordering.
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
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 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)
```
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 Tilesets
== Jamming

View File

@@ -14,8 +14,7 @@
#let note = callout.with(kind: "note", label: "Note")
#let todo(label, ..args) = {
// callout(args.pos().join(), kind: "todo", label: "[TODO] " + label, outlined: true)
none
callout(args.pos().join(), kind: "todo", label: "[TODO] " + label, outlined: true)
}
#let example(body) = todo("example", body)

View File

@@ -21,8 +21,9 @@
doc("index.html", include "content/index.typ")
doc("core.html", include "content/core.typ")
// doc("data-protocols.html", include "content/data-protocols.typ")
doc("interference-causes.html", include "content/interference-causes.typ")
doc("interference-fixes.html", include "content/interference-fixes.typ")
doc("interference.html", include "content/interference.typ")
doc("tilesets.html", include "content/tilesets.typ")
doc("block-event-delay.html", include "content/block-event-delay.typ")
// doc("design-notes.html", include "content/design.typ")
// doc("channel-selectors.html", include "content/channels.typ")
// doc("bulk-transmission.html", include "content/bulk.typ")