This commit is contained in:
David Allemang
2026-06-22 10:57:16 -04:00
parent d0d1a87cf5
commit d7c4dee0cf
2 changed files with 124 additions and 81 deletions

View File

@@ -1,6 +1,4 @@
#import "/lib.typ": callout, example, note, solution, tip, todo
#let mod4 = $#h(0.5em)&(mod 4)$
#import "/lib.typ": callout, details, example, note, solution, tip, todo
= Core Mechanics <core>
@@ -18,48 +16,59 @@ the game*.
Typically, item entities fall to the floor and sit stationary soon after being created. Processing
movement for these stationary entities would be wasteful, so an optimization was added in 1.14: if
an item is sitting stationary on a block, then only process movement every 4th tick.
an item is sitting stationary on a block, then only process movement for that item every 4th tick.
However, it would also be wasteful if every 4th tick processed _every_ item, so instead the items
are divided into four groups. Each tick stationary items from only one of the four groups check for
movement.
However, it would also be wasteful if every 4th tick processed all items, so instead the items are
divided into four staggered groups. Each tick stationary items from only one of the four groups
check for movement.
#callout(kind: "tip", label: "Key Point:")[
Stationary items can only begin to fall when their $"age" + "id"$ is a multiple of $4$.
Stationary items only begin to fall when their $"age" + "id"$ is a multiple of $4$.
]
Here is the relevant code path for the optimization:
In other words, divide the value by 4 and look at the remainder. If the remainder is 0, the item
checks for movement. That condition is written as `... % 4 == 0` in Java and as $... equiv 0 &(mod
4)$ in math.
```java
public class ItemEntity extends Entity {
public void tick() {
...
if (this.onGround() && !(this.getDeltaMovement().horizontalDistanceSqr() > (double)1.0E-5F) && (this.tickCount + this.getId()) % 4 != 0) {
...
} else {
this.move(MoverType.SELF, this.getDeltaMovement());
...
}
...
}
}
#tip[
You can freely add or subtract multiples of 4 to any expression without changing the value $mod 4$.
public class Entity {
public void move(final MoverType moverType, Vec3 delta) {
...
this.setOnGroundWithMovement(this.verticalCollisionBelow, this.horizontalCollision, movement);
...
}
For example, $-1 equiv 3 &(mod 4)$ because $3 = -1 + 4$.
]
public void setOnGroundWithMovement(final boolean onGround, final boolean horizontalCollision, final Vec3 movement) {
this.onGround = onGround;
...
}
}
```
#note[ `onGround` is only ever updated via `move`, and this branch is the *only* place that
`ItemEntity.move` is called. ]
#details(label: [the relevant Java code])[
#note[ `onGround` is only ever updated via `move`, and this branch is the *only* place that
`ItemEntity.move` is called. `tickCount` is the item's age. ]
```java
public class ItemEntity extends Entity {
public void tick() {
...
if (this.onGround() && !(this.getDeltaMovement().horizontalDistanceSqr() > (double)1.0E-5F) && (this.tickCount + this.getId()) % 4 != 0) {
...
} else {
this.move(MoverType.SELF, this.getDeltaMovement());
...
}
...
}
}
public class Entity {
public void move(final MoverType moverType, Vec3 delta) {
...
this.setOnGroundWithMovement(this.verticalCollisionBelow, this.horizontalCollision, movement);
...
}
public void setOnGroundWithMovement(final boolean onGround, final boolean horizontalCollision, final Vec3 movement) {
this.onGround = onGround;
...
}
}
```
]
The effect is that if the item is on a block and has negligible horizontal momentum, it *cannot
fall* until the condition is satisfied, even if it's no longer on the ground. It can only fall early
@@ -86,36 +95,44 @@ of other items.
Based on when item $B$ fell, when should we expect the other items to fall?
#solution[
To simplify the arithmetic, orient everything around $t=0$. So we'll let $"age"_0$ to be the
To simplify the arithmetic, orient everything around $t=0$. So we'll let $"age"_0$ be the
items' ages at $t=0$ and add tick offsets from there. All items spawned in the same tick, so
they all have the same $"age"_0$.
Since $B$ falls at $t=1$, the relation for $"id"^B$ must be
$ "age"_0 + "id"^B + 1 = 0 mod4 $
Since $B$ falls at $t=1$, the relation for $"id"_B$ must be
$A$ spawned right before $B$, so $"id"^A + 1 = "id"^B$. Plug that in to the relation we got for
$"id"^B$.
$ "age"_0 + "id"^A + 1 + 1 = 0 mod4 $
$ "age"_0 + "id"_B + 1 equiv 0 & (mod 4) $
Item $A$ gets the ID 1 before $B$, so $"id"_A + 1 = "id"_B$.
Plug that in to the relation we got for $"id"_B$.
$ "age"_0 + "id"_A + 1 + 1 equiv 0 & (mod 4) $
*$A$ must fall at $t=2$.*
$C$ spawned right after $B$, so its id is $"id"^C - 1= "id"^B$. Plug that in.
$ "age"_0 + "id"^C - 1 + 1 = 0 mod4 $
Item $C$ gets the ID 1 after $B$, so $"id"_C - 1= "id"_B$. Plug that in.
$ "age"_0 + "id"_C - 1 + 1 equiv 0 & (mod 4) $
*$C$ must fall at $t=0$.*
$D$ spawned right after $C$, so its id is $"id"^D - 2 = "id"^B$. Plug that in.
#tip[You can freely add or subtract 4 to either side of a mod 4 equality.]
Item $D$ gets the ID 2 after $B$, so $"id"_D - 2 = "id"_B$. Plug that in.
$
"age"_0 + "id"^D - 2 + 1 &= 0 mod4 \
"age"_0 + "id"^D - 1 &= 0 mod4 \
"age"_0 + "id"^D - 1 + 4 &= 0 mod4 \
"age"_0 + "id"^D + 3 &= 0 mod4
"age"_0 + "id"_D - 2 + 1 & equiv 0 & (mod 4) \
"age"_0 + "id"_D + 3 & equiv 0 & (mod 4)
$
*$D$ must fall at $t=3$.*
#callout(kind: "tip", label: "Therefore:")[
The expected fall times are:
- $C$ at $t=0$.
- $B$ at $t=1$.
- $A$ at $t=2$.
- $D$ at $t=3$.
]
]
#todo[Demonstration]
@@ -137,22 +154,42 @@ of other items.
#solution[
We'll need to track the item ages separately this time, but still oriented around $t=0$.
Let $"age"^B_0$ be item $B$'s age at $t=0$.
Let $"age"_(0, B)$ be item $B$'s age at $t=0$.
We know item $B$ fell at $t=1$, so we have the relation
$ "age"^B_0 + "id"^B + 1 = 0 mod4 $
$ "age"_(0, B) + "id"_B + 1 equiv 0 & (mod 4) $
#todo[Add "gt" to the glossary]
Item $A$ gets the ID 1 before $B$, so $"id"_A + 1 = "id"_B$.
It spawned 2gt earlier, so $"age"_(0, A) - 2= "age"_(0, B)$. Plug those in.
$ "age"_(0, A) - 2 + "id"_A + 1 + 1 equiv 0 & (mod 4) $
Item $A$ spawned just before item $B$, so $"id"^A + 1 = "id"^B$. It spawned 2gt earlier, so $"age"^A_0 - 2= "age"^B_0$. Plug those in.
$ "age"^A_0 - 2 + "id"^A + 1 + 1 = 0 mod4 $
*$A$ must fall at $t=0$.*
Item $C$ spawned just after item $B$, so $"id"^C - 1 = "id"^B$. It spawned in the same tick, so $"age"^C_0 = "age"^B_0$. Plug those in.
$ "age"^C_0 + "id"^C - 1 + 1 = 0 mod4 $
Item $C$ gets the ID 1 after $B$, so $"id"_C - 1 = "id"_B$.
It spawned in the same tick, so $"age"_(0, C) = "age"_(0, B)$. Plug those in.
$ "age"_(0, C) + "id"_C - 1 + 1 equiv 0 & (mod 4) $
*$C$ must fall at $t=0$.*
Item $D$ spawned just after item $B$, so $"id"^D - 2 = "id"^B$. It spawned 1gt later, so $"age"^D_0 + 1 = "age"^B_0$. Plug those in.
$ "age"^D_0 + 1 + "id"^D - 2 + 1 = 0 mod4 $
Item $D$ gets the ID 2 after $B$, so $"id"_D - 2 = "id"_B$.
It spawned 1gt later, so $"age"_(0, D) + 1 = "age"_(0, B)$. Plug those in.
$ "age"_(0, D) + 1 + "id"_D - 2 + 1 equiv 0 & (mod 4) $
*$D$ must fall at $t=0$.*
#callout(kind: "tip", label: "Therefore:")[
The expected fall times are:
- $A$ at $t=0$.
- $C$ at $t=0$.
- $D$ at $t=0$.
- $B$ at $t=1$.
]
]
#todo[Demonstration]
@@ -165,7 +202,7 @@ of other items.
1. Spawn items $A$, $B$, and $C$ in order in the same tick. Let them settle on a block.
2. At tick $t=0$, remove the supporting blocks. Item $C$ begins to fall.
3. At tick $t=1$, item $A$ begins to fall.
3. At tick $t=2$, item $B$ begins to fall.
4. At tick $t=2$, item $B$ begins to fall.
What number of entities must have spawned between items $A$ and $B$? Between $B$ and $C$?
@@ -175,43 +212,48 @@ of other items.
The ids of $A$ and $B$ are related by
$ "id"^A + 1 + x = "id"^B $
$ "id"_A + 1 + x = "id"_B $
And we observe the drop delay related by
And the observed drop delays satisfy
$ "age"_0 + "id"^A + 1 = 0 mod4 $
$ "age"_0 + "id"^B + 2 = 0 mod4 $
$ "age"_0 + "id"_A + 1 equiv 0 & (mod 4) $
$ "age"_0 + "id"_B + 2 equiv 0 & (mod 4) $
Substitute $"id"^B$ and solve for $x$.
Substitute $"id"_B$ and solve for $x$.
$
"age"_0 + "id"^A + 1 & = "age"_0 + "id"^A + 1 + x + 2 mod4 \
1 & = 3 + x mod4 \
-2 & = x mod4 \
-2 + 4 & = x mod4 \
2 & = x mod4 \
"age"_0 + "id"_A + 1 + x + 2 & equiv "age"_0 + "id"_A + 1 & (mod 4) \
x + 2 & equiv 0 & (mod 4) \
x & equiv 2 & (mod 4) \
$
*So 2 entities must have spawned between $A$ and $B$.* (Or any number with a remainder of 2, like 6, 10, etc.)
*So the number of entities between $A$ and $B$ is $2 &(mod 4)$.* (For example, 2, 6, 10, ...)
The ids of $B$ and $C$ are related by
$ "id"^B + 1 + y = "id"^C $
$ "id"_B + 1 + y = "id"_C $
And we observe the drop delay related related by
And the observed drop delays satisfy
$ "age"_0 + "id"^B + 2 = 0 mod4 $
$ "age"_0 + "id"^C + 0 = 0 mod4 $
$ "age"_0 + "id"_B + 2 equiv 0 & (mod 4) $
$ "age"_0 + "id"_C + 0 equiv 0 & (mod 4) $
Substitute $"id"^C$ and solve for $y$.
Substitute $"id"_C$ and solve for $y$.
$
"age"_0 + "id"^B + 2 & = "age"_0 + "id"^B + 1 + y + 0 mod4 \
2 & = 1 + y mod4 \
1 & = y mod4 \
"age"_0 + "id"_B + 1 + y + 0 & equiv "age"_0 + "id"_B + 2 & (mod 4) \
y - 1 & equiv 0 & (mod 4) \
y & equiv 1 & (mod 4) \
$
*So 1 entity must have spawned between $B$ and $C$.* (Or any number with a remainder of 1, like 5, 9, etc.)
*So the number of entities between $B$ and $C$ is $1 &(mod 4)$.* (For example, 1, 5, 9, ...)
#callout(kind: "tip", label: "Therefore:")[
The number of spawned entities must be
- $2 &(mod 4)$ between $A$ and $B$.
- $1 &(mod 4)$ between $B$ and $C$.
]
]
#todo[Demonstration]

View File

@@ -16,9 +16,10 @@
warn: color.rgb("#a52a2a"),
tip: color.rgb("#228b22"),
todo: color.rgb("#ff0000"),
eg: auto,
).at(kind)
block(
fill: col.transparentize(85%),
fill: if (col != auto) {col.transparentize(85%)} else {auto},
stroke: (y: col),
width: 100%,
outset: (x: 1em, y: 0.5em),