Now: You get complexity or scale, but not both.
State | Effects |
---|---|
Persistent across ticks | Computed once per tick |
Queried for potential effects | Merged into state |
Have an initial value | Have a combiner function |
Read+Update only | Merge only |
State/effect similar to barrier-based programming
class Unit {
state:
number player = 0; number type = 0;
number x = 0; number y = 0;
number health = 0; Unit target = null;
effects:
number vx : avg; number vy : avg;
number damage : sum; number healing : max;
Unit acquired : priority;
update:
x = x + vx; y = y + vy;
health = health - damage;
target = (acquired != null ? acquired : target);
}
let (number dist = (x-target.x)*(x-target.x)+
(y-target.y)*(y-target.y)) in {
if (dist < ATTACK_RANGE) {
// If in range, attack.
target.damage <- DMG_AMOUNT;
} else {
// Else move closer (use unit velocity)
vx <- (target.x-x)/dist;
vx <- (target.y-y)/dist;
} }
target.damage <- DMG_AMOUNT;
Damage is an effect. How is damage merged?
The total damage can be expressed as: SUM(DMG_AMOUNT)
. ... over what data?
There are two types of units: Soldiers and Skeletons
Design an AI for a soldier who attacks the nearest skeleton if there are at more soldiers than skeletons within 50 ft.
Given 8 numbers...
Precompute $index_j = \sum_{i \leq j} data_i$ for each j (... is $O(N)$).
For a summation query on (3,7]... (... is normally $O(|Q|)$)
...use $index_7 - index_{3}$ (... is now $O(1)$).
At most $O(\log N)$ lookups.