redscript-mc 1.2.24 → 1.2.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish-extension-on-ci.yml +1 -0
- package/dist/__tests__/cli.test.js +1 -1
- package/dist/__tests__/codegen.test.js +12 -6
- package/dist/__tests__/e2e.test.js +6 -6
- package/dist/__tests__/lowering.test.js +8 -8
- package/dist/__tests__/optimizer.test.js +31 -0
- package/dist/__tests__/stdlib-advanced.test.d.ts +4 -0
- package/dist/__tests__/stdlib-advanced.test.js +264 -0
- package/dist/__tests__/stdlib-math.test.d.ts +7 -0
- package/dist/__tests__/stdlib-math.test.js +352 -0
- package/dist/__tests__/stdlib-vec.test.d.ts +4 -0
- package/dist/__tests__/stdlib-vec.test.js +264 -0
- package/dist/ast/types.d.ts +17 -1
- package/dist/codegen/mcfunction/index.js +159 -18
- package/dist/codegen/var-allocator.d.ts +17 -0
- package/dist/codegen/var-allocator.js +33 -3
- package/dist/compile.d.ts +14 -0
- package/dist/compile.js +62 -5
- package/dist/index.js +20 -1
- package/dist/ir/types.d.ts +4 -0
- package/dist/lexer/index.d.ts +1 -1
- package/dist/lexer/index.js +1 -0
- package/dist/lowering/index.d.ts +5 -0
- package/dist/lowering/index.js +83 -10
- package/dist/optimizer/dce.js +21 -5
- package/dist/optimizer/passes.js +18 -6
- package/dist/optimizer/structure.js +7 -0
- package/dist/parser/index.d.ts +5 -0
- package/dist/parser/index.js +43 -2
- package/dist/runtime/index.d.ts +6 -0
- package/dist/runtime/index.js +109 -9
- package/editors/vscode/package-lock.json +3 -3
- package/editors/vscode/package.json +1 -1
- package/package.json +1 -1
- package/src/__tests__/cli.test.ts +1 -1
- package/src/__tests__/codegen.test.ts +12 -6
- package/src/__tests__/e2e.test.ts +6 -6
- package/src/__tests__/lowering.test.ts +8 -8
- package/src/__tests__/optimizer.test.ts +33 -0
- package/src/__tests__/stdlib-advanced.test.ts +259 -0
- package/src/__tests__/stdlib-math.test.ts +374 -0
- package/src/__tests__/stdlib-vec.test.ts +259 -0
- package/src/ast/types.ts +11 -1
- package/src/codegen/mcfunction/index.ts +148 -19
- package/src/codegen/var-allocator.ts +36 -3
- package/src/compile.ts +72 -5
- package/src/index.ts +21 -1
- package/src/ir/types.ts +2 -0
- package/src/lexer/index.ts +2 -1
- package/src/lowering/index.ts +96 -10
- package/src/optimizer/dce.ts +22 -5
- package/src/optimizer/passes.ts +18 -5
- package/src/optimizer/structure.ts +6 -1
- package/src/parser/index.ts +47 -2
- package/src/runtime/index.ts +108 -10
- package/src/stdlib/advanced.mcrs +249 -0
- package/src/stdlib/math.mcrs +259 -19
- package/src/stdlib/vec.mcrs +246 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
// advanced.mcrs — Higher-order integer math and "fun" algorithms.
|
|
2
|
+
//
|
|
3
|
+
// Requires: math.mcrs (for lerp, smoothstep, mulfix, abs, isqrt, sqrt_fixed)
|
|
4
|
+
//
|
|
5
|
+
// Category 1: Number theory — fib, is_prime, collatz_steps, digit_sum, reverse_int, mod_pow
|
|
6
|
+
// Category 2: Hashing/noise — hash_int, noise1d
|
|
7
|
+
// Category 3: Curves — bezier_quad, bezier_cubic
|
|
8
|
+
// Category 4: Fractals 🤯 — mandelbrot_iter (fixed-point complex arithmetic)
|
|
9
|
+
|
|
10
|
+
module library;
|
|
11
|
+
|
|
12
|
+
// ─── Category 1: Number theory ───────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
// Fibonacci number F(n) using simple iteration.
|
|
15
|
+
// Overflow: F(46) = 1836311903 ≈ INT_MAX; use n ≤ 46.
|
|
16
|
+
// fib(0) == 0, fib(1) == 1, fib(10) == 55
|
|
17
|
+
fn fib(n: int) -> int {
|
|
18
|
+
if (n <= 0) { return 0; }
|
|
19
|
+
if (n == 1) { return 1; }
|
|
20
|
+
let a: int = 0;
|
|
21
|
+
let b: int = 1;
|
|
22
|
+
let i: int = 2;
|
|
23
|
+
while (i <= n) {
|
|
24
|
+
let c: int = a + b;
|
|
25
|
+
a = b;
|
|
26
|
+
b = c;
|
|
27
|
+
i = i + 1;
|
|
28
|
+
}
|
|
29
|
+
return b;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Primality test by trial division up to √n.
|
|
33
|
+
// Returns 1 if n is prime, 0 otherwise.
|
|
34
|
+
// is_prime(2) == 1, is_prime(4) == 0, is_prime(97) == 1
|
|
35
|
+
fn is_prime(n: int) -> int {
|
|
36
|
+
if (n < 2) { return 0; }
|
|
37
|
+
if (n == 2) { return 1; }
|
|
38
|
+
if (n % 2 == 0) { return 0; }
|
|
39
|
+
let i: int = 3;
|
|
40
|
+
while (i * i <= n) {
|
|
41
|
+
if (n % i == 0) { return 0; }
|
|
42
|
+
i = i + 2;
|
|
43
|
+
}
|
|
44
|
+
return 1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Number of steps in the Collatz sequence starting at n until reaching 1.
|
|
48
|
+
// collatz_steps(1) == 0
|
|
49
|
+
// collatz_steps(6) == 8
|
|
50
|
+
// collatz_steps(27) == 111 (world record among small numbers)
|
|
51
|
+
fn collatz_steps(n: int) -> int {
|
|
52
|
+
if (n <= 1) { return 0; }
|
|
53
|
+
let x: int = n;
|
|
54
|
+
let steps: int = 0;
|
|
55
|
+
while (x != 1) {
|
|
56
|
+
if (x % 2 == 0) {
|
|
57
|
+
x = x / 2;
|
|
58
|
+
} else {
|
|
59
|
+
x = 3 * x + 1;
|
|
60
|
+
}
|
|
61
|
+
steps = steps + 1;
|
|
62
|
+
}
|
|
63
|
+
return steps;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Sum of decimal digits. Negative input uses absolute value.
|
|
67
|
+
// digit_sum(123) == 6, digit_sum(0) == 0
|
|
68
|
+
fn digit_sum(n: int) -> int {
|
|
69
|
+
let x: int = n;
|
|
70
|
+
if (x < 0) { x = 0 - x; }
|
|
71
|
+
if (x == 0) { return 0; }
|
|
72
|
+
let sum: int = 0;
|
|
73
|
+
while (x > 0) {
|
|
74
|
+
sum = sum + x % 10;
|
|
75
|
+
x = x / 10;
|
|
76
|
+
}
|
|
77
|
+
return sum;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Count decimal digits. 0 has 1 digit. Negative: counts absolute digits.
|
|
81
|
+
// count_digits(0) == 1, count_digits(100) == 3
|
|
82
|
+
fn count_digits(n: int) -> int {
|
|
83
|
+
let x: int = n;
|
|
84
|
+
if (x < 0) { x = 0 - x; }
|
|
85
|
+
if (x == 0) { return 1; }
|
|
86
|
+
let cnt: int = 0;
|
|
87
|
+
while (x > 0) {
|
|
88
|
+
cnt = cnt + 1;
|
|
89
|
+
x = x / 10;
|
|
90
|
+
}
|
|
91
|
+
return cnt;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Reverse the decimal digits of an integer. Sign is preserved.
|
|
95
|
+
// reverse_int(12345) == 54321, reverse_int(-42) == -24
|
|
96
|
+
fn reverse_int(n: int) -> int {
|
|
97
|
+
let x: int = n;
|
|
98
|
+
let neg: int = 0;
|
|
99
|
+
if (x < 0) { x = 0 - x; neg = 1; }
|
|
100
|
+
let result: int = 0;
|
|
101
|
+
while (x > 0) {
|
|
102
|
+
result = result * 10 + x % 10;
|
|
103
|
+
x = x / 10;
|
|
104
|
+
}
|
|
105
|
+
if (neg == 1) { return 0 - result; }
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Modular exponentiation: (base ^ exp) mod m using fast O(log exp) squaring.
|
|
110
|
+
// IMPORTANT: m must be ≤ 46340 to avoid b*b overflow (46340² < INT_MAX).
|
|
111
|
+
// mod_pow(2, 10, 1000) == 24 (1024 mod 1000)
|
|
112
|
+
fn mod_pow(base: int, exp: int, m: int) -> int {
|
|
113
|
+
if (m == 1) { return 0; }
|
|
114
|
+
let result: int = 1;
|
|
115
|
+
let b: int = base % m;
|
|
116
|
+
if (b < 0) { b = b + m; }
|
|
117
|
+
let e: int = exp;
|
|
118
|
+
while (e > 0) {
|
|
119
|
+
if (e % 2 == 1) {
|
|
120
|
+
result = result * b % m;
|
|
121
|
+
}
|
|
122
|
+
b = b * b % m;
|
|
123
|
+
e = e / 2;
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ─── Category 2: Hashing & noise ─────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
// Integer hash function. Output is non-negative, range [0, ~2 × 10⁹).
|
|
131
|
+
// Deterministic, no randomness — same input always produces the same output.
|
|
132
|
+
// Suitable as a seeded pseudo-random value for procedural generation.
|
|
133
|
+
// hash_int(0) != hash_int(1), repeatable across runs.
|
|
134
|
+
fn hash_int(n: int) -> int {
|
|
135
|
+
let h: int = n;
|
|
136
|
+
if (h < 0) { h = 0 - h; }
|
|
137
|
+
h = h % 46340; // Clamp so h² fits in int32
|
|
138
|
+
h = h * 46337 + 97; // 46337 is prime; h*46337 ≤ 46340*46337 < INT_MAX
|
|
139
|
+
if (h < 0) { h = 0 - h; }
|
|
140
|
+
h = h % 46340;
|
|
141
|
+
h = h * 46337 + 211;
|
|
142
|
+
if (h < 0) { h = 0 - h; }
|
|
143
|
+
return h;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 1D value noise. Input x in fixed-point (scale = 1000).
|
|
147
|
+
// Output in [0, 999] — smoothly interpolated between hashed lattice points.
|
|
148
|
+
// noise1d(0) == noise1d(0), noise1d(500) is between noise1d(0) and noise1d(1000).
|
|
149
|
+
fn noise1d(x: int) -> int {
|
|
150
|
+
let ix: int = x / 1000;
|
|
151
|
+
let frac: int = x % 1000;
|
|
152
|
+
// Handle negative x so frac is always in [0, 999]
|
|
153
|
+
if (frac < 0) {
|
|
154
|
+
ix = ix - 1;
|
|
155
|
+
frac = frac + 1000;
|
|
156
|
+
}
|
|
157
|
+
let h0: int = hash_int(ix) % 1000;
|
|
158
|
+
let h1: int = hash_int(ix + 1) % 1000;
|
|
159
|
+
if (h0 < 0) { h0 = 0 - h0; }
|
|
160
|
+
if (h1 < 0) { h1 = 0 - h1; }
|
|
161
|
+
// Smoothstep for C1 continuity
|
|
162
|
+
let t: int = smoothstep(0, 1000, frac);
|
|
163
|
+
return lerp(h0, h1, t);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ─── Category 3: Curves ──────────────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
// Quadratic Bezier: B(t) = lerp(lerp(p0,p1,t), lerp(p1,p2,t), t)
|
|
169
|
+
// De Casteljau's algorithm — numerically stable, safe for large coordinates.
|
|
170
|
+
// t in [0, 1000] (fixed-point).
|
|
171
|
+
//
|
|
172
|
+
// bezier_quad(0, 500, 1000, 0) == 0 (t=0: start)
|
|
173
|
+
// bezier_quad(0, 500, 1000, 500) == 500 (t=500: midpoint of curve)
|
|
174
|
+
// bezier_quad(0, 500, 1000, 1000) == 1000 (t=1000: end)
|
|
175
|
+
// bezier_quad(0, 1000, 0, 500) == 500 (arch at midpoint)
|
|
176
|
+
fn bezier_quad(p0: int, p1: int, p2: int, t: int) -> int {
|
|
177
|
+
let m0: int = lerp(p0, p1, t);
|
|
178
|
+
let m1: int = lerp(p1, p2, t);
|
|
179
|
+
return lerp(m0, m1, t);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Cubic Bezier: 4-point curve using De Casteljau's algorithm.
|
|
183
|
+
// t in [0, 1000].
|
|
184
|
+
// bezier_cubic(0, 333, 667, 1000, 0) == 0
|
|
185
|
+
// bezier_cubic(0, 333, 667, 1000, 1000) == 1000
|
|
186
|
+
fn bezier_cubic(p0: int, p1: int, p2: int, p3: int, t: int) -> int {
|
|
187
|
+
let m0: int = lerp(p0, p1, t);
|
|
188
|
+
let m1: int = lerp(p1, p2, t);
|
|
189
|
+
let m2: int = lerp(p2, p3, t);
|
|
190
|
+
let n0: int = lerp(m0, m1, t);
|
|
191
|
+
let n1: int = lerp(m1, m2, t);
|
|
192
|
+
return lerp(n0, n1, t);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ─── Category 4: Fractals 🤯 ─────────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
// Mandelbrot set iteration count.
|
|
198
|
+
//
|
|
199
|
+
// cx, cy: fixed-point coordinates of the complex number c = cx/1000 + i*cy/1000
|
|
200
|
+
// cx = -2000..1000 (i.e. real part -2.0..1.0)
|
|
201
|
+
// cy = -1000..1000 (imaginary part -1.0..1.0)
|
|
202
|
+
//
|
|
203
|
+
// Returns the number of iterations before |z| > 2 (escape), or max_iter if
|
|
204
|
+
// the point is in the Mandelbrot set. Use the return value to colour blocks!
|
|
205
|
+
//
|
|
206
|
+
// Points in the set: mandelbrot_iter(-1000, 0, 100) == 100 (c = -1+0i)
|
|
207
|
+
// Points outside: mandelbrot_iter(1000, 0, 100) == 0 (c = 1+0i, escapes immediately)
|
|
208
|
+
// Boundary region: mandelbrot_iter(-500, 500, 50) → varies
|
|
209
|
+
//
|
|
210
|
+
// Algorithm: z₀ = 0, z_{n+1} = z_n² + c
|
|
211
|
+
// z_n = (zr + zi·i), z_n² = zr²−zi² + 2·zr·zi·i
|
|
212
|
+
// Escape when |z|² = (zr²+zi²)/10⁶ > 4 ↔ mulfix(zr,zr)+mulfix(zi,zi) > 4000
|
|
213
|
+
fn mandelbrot_iter(cx: int, cy: int, max_iter: int) -> int {
|
|
214
|
+
let zr: int = 0;
|
|
215
|
+
let zi: int = 0;
|
|
216
|
+
let i: int = 0;
|
|
217
|
+
while (i < max_iter) {
|
|
218
|
+
let zr2: int = mulfix(zr, zr) - mulfix(zi, zi) + cx;
|
|
219
|
+
let zi2: int = 2 * mulfix(zr, zi) + cy;
|
|
220
|
+
zr = zr2;
|
|
221
|
+
zi = zi2;
|
|
222
|
+
if (mulfix(zr, zr) + mulfix(zi, zi) > 4000) {
|
|
223
|
+
return i;
|
|
224
|
+
}
|
|
225
|
+
i = i + 1;
|
|
226
|
+
}
|
|
227
|
+
return max_iter;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Julia set iteration count (generalised Mandelbrot with fixed c and variable z₀).
|
|
231
|
+
// z0r, z0i: starting point (fixed-point, scale=1000)
|
|
232
|
+
// cr, ci: constant c (fixed-point, scale=1000)
|
|
233
|
+
// Same escape condition as mandelbrot_iter.
|
|
234
|
+
fn julia_iter(z0r: int, z0i: int, cr: int, ci: int, max_iter: int) -> int {
|
|
235
|
+
let zr: int = z0r;
|
|
236
|
+
let zi: int = z0i;
|
|
237
|
+
let i: int = 0;
|
|
238
|
+
while (i < max_iter) {
|
|
239
|
+
let zr2: int = mulfix(zr, zr) - mulfix(zi, zi) + cr;
|
|
240
|
+
let zi2: int = 2 * mulfix(zr, zi) + ci;
|
|
241
|
+
zr = zr2;
|
|
242
|
+
zi = zi2;
|
|
243
|
+
if (mulfix(zr, zr) + mulfix(zi, zi) > 4000) {
|
|
244
|
+
return i;
|
|
245
|
+
}
|
|
246
|
+
i = i + 1;
|
|
247
|
+
}
|
|
248
|
+
return max_iter;
|
|
249
|
+
}
|
package/src/stdlib/math.mcrs
CHANGED
|
@@ -1,49 +1,289 @@
|
|
|
1
|
-
// Integer math helpers for RedScript datapacks.
|
|
1
|
+
// math.mcrs — Integer & fixed-point math helpers for RedScript datapacks.
|
|
2
|
+
//
|
|
3
|
+
// Fixed-point convention: scale = 1000
|
|
4
|
+
// 1.0 → 1000
|
|
5
|
+
// 3.14 → 3140
|
|
6
|
+
// Use `sqrt_fixed` / `lerp` for sub-integer precision.
|
|
7
|
+
//
|
|
8
|
+
// Phase 1: basic integer helpers (abs, sign, min, max, clamp, lerp)
|
|
9
|
+
// Phase 2: iterative algorithms (isqrt, sqrt_fixed, pow_int, gcd)
|
|
10
|
+
// Phase 3: trig (sin_fixed, cos_fixed) — lookup table in NBT storage
|
|
11
|
+
|
|
12
|
+
// All functions in this file are library-mode: only compiled into the
|
|
13
|
+
// datapack if actually called from user code. Use via `librarySources`
|
|
14
|
+
// compile option or concatenate with user code (module library; handles it).
|
|
15
|
+
module library;
|
|
16
|
+
// Phase 4: number theory & utilities (lcm, map, ceil_div, log2_int)
|
|
17
|
+
|
|
18
|
+
// ─── Phase 1: Basic integer helpers ──────────────────────────────────────────
|
|
2
19
|
|
|
3
20
|
fn abs(x: int) -> int {
|
|
4
21
|
if (x < 0) {
|
|
5
|
-
return -x;
|
|
6
|
-
}
|
|
7
|
-
|
|
22
|
+
return 0 - x;
|
|
23
|
+
}
|
|
24
|
+
return x;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
fn sign(x: int) -> int {
|
|
28
|
+
if (x > 0) {
|
|
29
|
+
return 1;
|
|
30
|
+
}
|
|
31
|
+
if (x < 0) {
|
|
32
|
+
return -1;
|
|
8
33
|
}
|
|
34
|
+
return 0;
|
|
9
35
|
}
|
|
10
36
|
|
|
11
37
|
fn min(a: int, b: int) -> int {
|
|
12
38
|
if (a < b) {
|
|
13
39
|
return a;
|
|
14
|
-
} else {
|
|
15
|
-
return b;
|
|
16
40
|
}
|
|
41
|
+
return b;
|
|
17
42
|
}
|
|
18
43
|
|
|
19
44
|
fn max(a: int, b: int) -> int {
|
|
20
45
|
if (a > b) {
|
|
21
46
|
return a;
|
|
22
|
-
} else {
|
|
23
|
-
return b;
|
|
24
47
|
}
|
|
48
|
+
return b;
|
|
25
49
|
}
|
|
26
50
|
|
|
27
51
|
fn clamp(x: int, lo: int, hi: int) -> int {
|
|
28
52
|
if (x < lo) {
|
|
29
53
|
return lo;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
54
|
+
}
|
|
55
|
+
if (x > hi) {
|
|
56
|
+
return hi;
|
|
57
|
+
}
|
|
58
|
+
return x;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Linear interpolation (fixed-point t, t in [0, 1000])
|
|
62
|
+
// lerp(0, 1000, 500) == 500
|
|
63
|
+
// lerp(100, 200, 750) == 175
|
|
64
|
+
fn lerp(a: int, b: int, t: int) -> int {
|
|
65
|
+
return a + (b - a) * t / 1000;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ─── Phase 2: Iterative algorithms ───────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
// Integer square root: floor(sqrt(n))
|
|
71
|
+
// Uses Newton's method, converges in ≤16 iterations for all int32.
|
|
72
|
+
// isqrt(9) == 3, isqrt(10) == 3, isqrt(0) == 0
|
|
73
|
+
fn isqrt(n: int) -> int {
|
|
74
|
+
if (n <= 0) {
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
let x: int = n;
|
|
78
|
+
let i: int = 0;
|
|
79
|
+
while (i < 16) {
|
|
80
|
+
let next: int = (x + n / x) / 2;
|
|
81
|
+
if (next >= x) {
|
|
34
82
|
return x;
|
|
35
83
|
}
|
|
84
|
+
x = next;
|
|
85
|
+
i = i + 1;
|
|
36
86
|
}
|
|
87
|
+
return x;
|
|
37
88
|
}
|
|
38
89
|
|
|
39
|
-
|
|
40
|
-
|
|
90
|
+
// Fixed-point sqrt (scale = 1000)
|
|
91
|
+
// sqrt_fixed(2000) ≈ 1414 (√2 × 1000)
|
|
92
|
+
// sqrt_fixed(1000) == 1000 (√1 × 1000)
|
|
93
|
+
fn sqrt_fixed(x: int) -> int {
|
|
94
|
+
return isqrt(x * 1000);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Integer power: base^exp (exp ≥ 0)
|
|
98
|
+
// Fast exponentiation by squaring: O(log exp)
|
|
99
|
+
// pow_int(2, 10) == 1024, pow_int(3, 0) == 1
|
|
100
|
+
fn pow_int(base: int, exp: int) -> int {
|
|
101
|
+
if (exp <= 0) {
|
|
41
102
|
return 1;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
103
|
+
}
|
|
104
|
+
let result: int = 1;
|
|
105
|
+
let b: int = base;
|
|
106
|
+
let e: int = exp;
|
|
107
|
+
while (e > 0) {
|
|
108
|
+
if (e % 2 == 1) {
|
|
109
|
+
result = result * b;
|
|
47
110
|
}
|
|
111
|
+
b = b * b;
|
|
112
|
+
e = e / 2;
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Greatest common divisor (Euclidean algorithm)
|
|
118
|
+
// gcd(12, 8) == 4, gcd(0, 5) == 5
|
|
119
|
+
fn gcd(a: int, b: int) -> int {
|
|
120
|
+
// Inline abs to avoid cross-function scoreboard variable collision.
|
|
121
|
+
let x: int = a;
|
|
122
|
+
if (x < 0) { x = 0 - x; }
|
|
123
|
+
let y: int = b;
|
|
124
|
+
if (y < 0) { y = 0 - y; }
|
|
125
|
+
while (y > 0) {
|
|
126
|
+
let r: int = x % y;
|
|
127
|
+
x = y;
|
|
128
|
+
y = r;
|
|
129
|
+
}
|
|
130
|
+
return x;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ─── Phase 3: Trigonometry ────────────────────────────────────────────────────
|
|
134
|
+
//
|
|
135
|
+
// sin/cos lookup table stored in NBT storage (math:tables).
|
|
136
|
+
// 91 entries covering 0-90°; other quadrants derived by symmetry.
|
|
137
|
+
// All values are sin(i°) × 1000, rounded to nearest integer.
|
|
138
|
+
//
|
|
139
|
+
// Accuracy: ≤ 1 unit error (≤0.1% relative) for all integer degrees.
|
|
140
|
+
// Cost: ~4 MC commands per call + 1 NBT read per call (O(1)).
|
|
141
|
+
|
|
142
|
+
// Initialize the sin lookup table.
|
|
143
|
+
// This function has NO @load — it is called from __load only when sin_fixed
|
|
144
|
+
// or cos_fixed are compiled into the project (via @requires). If you only
|
|
145
|
+
// use Phase 1/2/4 functions, this never runs and adds zero overhead.
|
|
146
|
+
fn _math_init() {
|
|
147
|
+
storage_set_array("math:tables", "sin",
|
|
148
|
+
"[0, 17, 35, 52, 70, 87, 105, 122, 139, 156, 174, 191, 208, 225, 242, 259, 276, 292, 309, 326, 342, 358, 375, 391, 407, 423, 438, 454, 469, 485, 500, 515, 530, 545, 559, 574, 588, 602, 616, 629, 643, 656, 669, 682, 695, 707, 719, 731, 743, 755, 766, 777, 788, 799, 809, 819, 829, 839, 848, 857, 866, 875, 883, 891, 899, 906, 914, 921, 927, 934, 940, 946, 951, 956, 961, 966, 970, 974, 978, 982, 985, 988, 990, 993, 995, 996, 998, 999, 999, 1000, 1000]"
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// sin_fixed(deg) → sin(deg°) × 1000 (integer degrees, any value)
|
|
153
|
+
// sin_fixed(30) == 500
|
|
154
|
+
// sin_fixed(90) == 1000
|
|
155
|
+
// sin_fixed(180) == 0
|
|
156
|
+
// sin_fixed(270) == -1000
|
|
157
|
+
//
|
|
158
|
+
// @require_on_load(_math_init): the sin lookup table is written to NBT storage at
|
|
159
|
+
// world load automatically when this function is included in a project.
|
|
160
|
+
@require_on_load(_math_init)
|
|
161
|
+
fn sin_fixed(deg: int) -> int {
|
|
162
|
+
// Normalise to [0, 359]
|
|
163
|
+
let d: int = deg % 360;
|
|
164
|
+
if (d < 0) { d = d + 360; }
|
|
165
|
+
|
|
166
|
+
// Quadrant reduction to first quadrant index [0, 90]
|
|
167
|
+
if (d <= 90) {
|
|
168
|
+
return storage_get_int("math:tables", "sin", d);
|
|
169
|
+
}
|
|
170
|
+
if (d <= 180) {
|
|
171
|
+
return storage_get_int("math:tables", "sin", 180 - d);
|
|
172
|
+
}
|
|
173
|
+
if (d <= 270) {
|
|
174
|
+
let idx: int = d - 180;
|
|
175
|
+
return 0 - storage_get_int("math:tables", "sin", idx);
|
|
176
|
+
}
|
|
177
|
+
// d in (270, 360)
|
|
178
|
+
let idx: int = 360 - d;
|
|
179
|
+
return 0 - storage_get_int("math:tables", "sin", idx);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// cos_fixed(deg) → cos(deg°) × 1000 (integer degrees, any value)
|
|
183
|
+
// cos_fixed(0) == 1000
|
|
184
|
+
// cos_fixed(90) == 0
|
|
185
|
+
// cos_fixed(180) == -1000
|
|
186
|
+
@require_on_load(_math_init)
|
|
187
|
+
fn cos_fixed(deg: int) -> int {
|
|
188
|
+
return sin_fixed(deg + 90);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ─── Phase 4: Number theory & utilities ──────────────────────────────────────
|
|
192
|
+
|
|
193
|
+
// Least common multiple
|
|
194
|
+
// lcm(4, 6) == 12, lcm(0, 5) == 0
|
|
195
|
+
fn lcm(a: int, b: int) -> int {
|
|
196
|
+
let g: int = gcd(a, b);
|
|
197
|
+
if (g == 0) { return 0; }
|
|
198
|
+
return a / g * b;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Range map: scale x from [in_lo, in_hi] onto [out_lo, out_hi].
|
|
202
|
+
// Uses integer arithmetic — multiply before dividing to preserve precision.
|
|
203
|
+
// map(5, 0, 10, 0, 100) == 50
|
|
204
|
+
// map(1, 0, 10, 100, 200) == 110
|
|
205
|
+
fn map(x: int, in_lo: int, in_hi: int, out_lo: int, out_hi: int) -> int {
|
|
206
|
+
let in_range: int = in_hi - in_lo;
|
|
207
|
+
if (in_range == 0) { return out_lo; }
|
|
208
|
+
return out_lo + (x - in_lo) * (out_hi - out_lo) / in_range;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Ceiling division: ⌈a / b⌉ (b > 0 required)
|
|
212
|
+
// ceil_div(7, 3) == 3, ceil_div(6, 3) == 2
|
|
213
|
+
fn ceil_div(a: int, b: int) -> int {
|
|
214
|
+
return (a + b - 1) / b;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Integer log base 2: ⌊log₂(n)⌋
|
|
218
|
+
// log2_int(1) == 0, log2_int(8) == 3, log2_int(7) == 2
|
|
219
|
+
// Returns -1 for n ≤ 0 (error sentinel).
|
|
220
|
+
fn log2_int(n: int) -> int {
|
|
221
|
+
if (n <= 0) { return -1; }
|
|
222
|
+
let result: int = 0;
|
|
223
|
+
let v: int = n;
|
|
224
|
+
while (v > 1) {
|
|
225
|
+
v = v / 2;
|
|
226
|
+
result = result + 1;
|
|
48
227
|
}
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ─── Phase 5: Fixed-point arithmetic & easing ────────────────────────────────
|
|
232
|
+
// Vector functions (dot2d, cross2d, length2d_fixed, manhattan, chebyshev,
|
|
233
|
+
// atan2_fixed, rotate2d, normalize2d, etc.) have moved to vec.mcrs.
|
|
234
|
+
|
|
235
|
+
// Fixed-point multiply: (a × b) / 1000
|
|
236
|
+
// Use instead of a*b when both operands are fixed-point (scale=1000),
|
|
237
|
+
// e.g. mulfix(sin_fixed(30), cos_fixed(45)) instead of sin*cos which overflows.
|
|
238
|
+
// mulfix(500, 707) == 353 (≈ 0.5 × 0.707)
|
|
239
|
+
fn mulfix(a: int, b: int) -> int {
|
|
240
|
+
return a * b / 1000;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Fixed-point divide: (a × 1000) / b
|
|
244
|
+
// Inverse of mulfix; produces a fixed-point result from two integers.
|
|
245
|
+
// divfix(1, 3) == 333 (≈ 0.333)
|
|
246
|
+
fn divfix(a: int, b: int) -> int {
|
|
247
|
+
if (b == 0) { return 0; }
|
|
248
|
+
return a * 1000 / b;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Smooth Hermite interpolation (Ken Perlin's smoothstep).
|
|
252
|
+
// Returns a value in [0, 1000] (fixed-point scale = 1000).
|
|
253
|
+
// Eases in AND out — useful for animations and value transitions.
|
|
254
|
+
//
|
|
255
|
+
// smoothstep(0, 100, 0) == 0
|
|
256
|
+
// smoothstep(0, 100, 50) == 500
|
|
257
|
+
// smoothstep(0, 100, 100) == 1000
|
|
258
|
+
// smoothstep(0, 100, 25) == 156 (≈ 3×0.25² − 2×0.25³ = 0.15625)
|
|
259
|
+
fn smoothstep(lo: int, hi: int, x: int) -> int {
|
|
260
|
+
let range: int = hi - lo;
|
|
261
|
+
if (range == 0) { return 0; }
|
|
262
|
+
// t in [0, 1000]
|
|
263
|
+
let t: int = (x - lo) * 1000 / range;
|
|
264
|
+
if (t < 0) { t = 0; }
|
|
265
|
+
if (t > 1000) { t = 1000; }
|
|
266
|
+
// f(t) = t² × (3 − 2t) / 1000 (all in scale=1000)
|
|
267
|
+
// Rewrite to avoid int32 overflow: use t/10 as intermediate
|
|
268
|
+
let t10: int = t / 10; // [0, 100], avoids 1000² = 10⁶ overflow
|
|
269
|
+
return t10 * t10 * (3000 - 2 * t) / 10000;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Smoother step (Perlin's order-5): 6t⁵ − 15t⁴ + 10t³
|
|
273
|
+
// Higher-order derivative at edges → smoother acceleration curve.
|
|
274
|
+
// smootherstep(0, 100, 25) ≈ 103
|
|
275
|
+
fn smootherstep(lo: int, hi: int, x: int) -> int {
|
|
276
|
+
let range: int = hi - lo;
|
|
277
|
+
if (range == 0) { return 0; }
|
|
278
|
+
let t: int = (x - lo) * 1000 / range;
|
|
279
|
+
if (t < 0) { t = 0; }
|
|
280
|
+
if (t > 1000) { t = 1000; }
|
|
281
|
+
// f(t) = t³ × (10 − t × (15 − 6t) / 1000) / 1000000
|
|
282
|
+
// Compute in stages to stay within int32
|
|
283
|
+
let t10: int = t / 10; // [0, 100]
|
|
284
|
+
let t2: int = t10 * t10; // [0, 10000]
|
|
285
|
+
let t3: int = t2 * t10 / 100; // [0, 10000]
|
|
286
|
+
// 10t³ − 15t⁴ + 6t⁵ = t³(10 − 15t + 6t²) at scale=1000
|
|
287
|
+
let inner: int = 10000 - 15 * t10 + 6 * t2 / 100;
|
|
288
|
+
return t3 * inner / 100000;
|
|
49
289
|
}
|