redscript-mc 1.2.25 → 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/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 +154 -18
- package/dist/codegen/var-allocator.d.ts +17 -0
- package/dist/codegen/var-allocator.js +26 -0
- 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 +143 -19
- package/src/codegen/var-allocator.ts +29 -0
- 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
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
|
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// vec.mcrs — 2D and 3D vector math for RedScript datapacks.
|
|
2
|
+
//
|
|
3
|
+
// Fixed-point convention: scale = 1000 (same as math.mcrs)
|
|
4
|
+
// A unit vector has components in [-1000, 1000].
|
|
5
|
+
// A position / displacement in block coordinates uses raw integers.
|
|
6
|
+
//
|
|
7
|
+
// Dependency: import "math.mcrs" before this file (or compile with librarySources).
|
|
8
|
+
// Requires: abs, sqrt_fixed, sin_fixed, cos_fixed, lerp, mulfix, length2d_fixed
|
|
9
|
+
//
|
|
10
|
+
// Phase 1: 2D geometry (dot, cross, distance, length, normalize, lerp)
|
|
11
|
+
// Phase 2: 2D direction (rotate, atan2)
|
|
12
|
+
// Phase 3: 3D geometry (dot, cross, length, distance, manhattan, chebyshev)
|
|
13
|
+
|
|
14
|
+
module library;
|
|
15
|
+
|
|
16
|
+
// ─── Phase 1: 2D geometry ────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
// Dot product of two 2D integer vectors.
|
|
19
|
+
// dot2d(3, 4, 3, 4) == 25
|
|
20
|
+
fn dot2d(ax: int, ay: int, bx: int, by: int) -> int {
|
|
21
|
+
return ax * bx + ay * by;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Z-component of the 3D cross product (ax*by − ay*bx).
|
|
25
|
+
// Positive: b is counter-clockwise from a.
|
|
26
|
+
// cross2d(1, 0, 0, 1) == 1
|
|
27
|
+
fn cross2d(ax: int, ay: int, bx: int, by: int) -> int {
|
|
28
|
+
return ax * by - ay * bx;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Euclidean length of a 2D integer vector, result in fixed-point (×1000).
|
|
32
|
+
// length2d_fixed(3, 4) == 5000 (√25 × 1000)
|
|
33
|
+
// Note: |x|, |y| ≤ ~1000 to avoid overflow; scale larger vectors down first.
|
|
34
|
+
fn length2d_fixed(x: int, y: int) -> int {
|
|
35
|
+
return sqrt_fixed((x * x + y * y) * 1000);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Euclidean distance between two integer-coordinate points, result ×1000.
|
|
39
|
+
// distance2d_fixed(0, 0, 3, 4) == 5000
|
|
40
|
+
fn distance2d_fixed(x1: int, y1: int, x2: int, y2: int) -> int {
|
|
41
|
+
let dx: int = x1 - x2;
|
|
42
|
+
let dy: int = y1 - y2;
|
|
43
|
+
return length2d_fixed(dx, dy);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Manhattan distance (taxicab): |dx| + |dy|.
|
|
47
|
+
// manhattan(0, 0, 3, 4) == 7
|
|
48
|
+
fn manhattan(x1: int, y1: int, x2: int, y2: int) -> int {
|
|
49
|
+
let dx: int = x1 - x2;
|
|
50
|
+
let dy: int = y1 - y2;
|
|
51
|
+
if (dx < 0) { dx = 0 - dx; }
|
|
52
|
+
if (dy < 0) { dy = 0 - dy; }
|
|
53
|
+
return dx + dy;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Chebyshev distance (chess-king): max(|dx|, |dy|).
|
|
57
|
+
// chebyshev(0, 0, 3, 4) == 4
|
|
58
|
+
fn chebyshev(x1: int, y1: int, x2: int, y2: int) -> int {
|
|
59
|
+
let dx: int = x1 - x2;
|
|
60
|
+
let dy: int = y1 - y2;
|
|
61
|
+
if (dx < 0) { dx = 0 - dx; }
|
|
62
|
+
if (dy < 0) { dy = 0 - dy; }
|
|
63
|
+
if (dx > dy) { return dx; }
|
|
64
|
+
return dy;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// X-component of the normalized unit vector (× 1000).
|
|
68
|
+
// normalize2d_x(3, 4) == 600 (3/5 × 1000)
|
|
69
|
+
// Returns 0 for the zero vector.
|
|
70
|
+
fn normalize2d_x(x: int, y: int) -> int {
|
|
71
|
+
let len: int = length2d_fixed(x, y);
|
|
72
|
+
if (len == 0) { return 0; }
|
|
73
|
+
return x * 1000000 / len;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Y-component of the normalized unit vector (× 1000).
|
|
77
|
+
// normalize2d_y(3, 4) == 800 (4/5 × 1000)
|
|
78
|
+
fn normalize2d_y(x: int, y: int) -> int {
|
|
79
|
+
let len: int = length2d_fixed(x, y);
|
|
80
|
+
if (len == 0) { return 0; }
|
|
81
|
+
return y * 1000000 / len;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Linearly interpolate between vectors a=(ax,ay) and b=(bx,by), return x.
|
|
85
|
+
// t in [0, 1000] (fixed-point).
|
|
86
|
+
// lerp2d_x(0, 0, 100, 200, 500) == 50
|
|
87
|
+
fn lerp2d_x(ax: int, ay: int, bx: int, by: int, t: int) -> int {
|
|
88
|
+
return ax + (bx - ax) * t / 1000;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Linearly interpolate between vectors a and b, return y.
|
|
92
|
+
// lerp2d_y(0, 0, 100, 200, 500) == 100
|
|
93
|
+
fn lerp2d_y(ax: int, ay: int, bx: int, by: int, t: int) -> int {
|
|
94
|
+
return ay + (by - ay) * t / 1000;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ─── Phase 2: 2D direction ────────────────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
// tan lookup table for atan2_fixed binary search
|
|
100
|
+
fn _atan_init() {
|
|
101
|
+
storage_set_array("math:tables", "tan",
|
|
102
|
+
"[0, 17, 35, 52, 70, 87, 105, 123, 141, 158, 176, 194, 213, 231, 249, 268, 287, 306, 325, 344, 364, 384, 404, 424, 445, 466, 488, 510, 532, 554, 577, 601, 625, 649, 675, 700, 727, 754, 781, 810, 839, 869, 900, 933, 966, 1000]"
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Angle of the vector (y, x) in integer degrees [0, 359].
|
|
107
|
+
// Uses binary search on the tan table → O(log 46) per call.
|
|
108
|
+
//
|
|
109
|
+
// atan2_fixed(0, 1) == 0 (east)
|
|
110
|
+
// atan2_fixed(1, 0) == 90
|
|
111
|
+
// atan2_fixed(0, -1) == 180 (west)
|
|
112
|
+
// atan2_fixed(-1, 0) == 270
|
|
113
|
+
// atan2_fixed(1, 1) == 45
|
|
114
|
+
//
|
|
115
|
+
// Inputs clamped to ≤ 46340 to avoid overflow. For larger vectors:
|
|
116
|
+
// atan2_fixed(dy / 1000, dx / 1000)
|
|
117
|
+
@require_on_load(_atan_init)
|
|
118
|
+
fn atan2_fixed(y: int, x: int) -> int {
|
|
119
|
+
if (x == 0) {
|
|
120
|
+
if (y > 0) { return 90; }
|
|
121
|
+
if (y < 0) { return 270; }
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
if (y == 0) {
|
|
125
|
+
if (x > 0) { return 0; }
|
|
126
|
+
return 180;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
let ax: int = x;
|
|
130
|
+
if (ax < 0) { ax = 0 - ax; }
|
|
131
|
+
let ay: int = y;
|
|
132
|
+
if (ay < 0) { ay = 0 - ay; }
|
|
133
|
+
|
|
134
|
+
if (ax > 46340) { ax = 46340; }
|
|
135
|
+
if (ay > 46340) { ay = 46340; }
|
|
136
|
+
|
|
137
|
+
let swapped: int = 0;
|
|
138
|
+
if (ay > ax) {
|
|
139
|
+
let tmp: int = ax;
|
|
140
|
+
ax = ay;
|
|
141
|
+
ay = tmp;
|
|
142
|
+
swapped = 1;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let lo: int = 0;
|
|
146
|
+
let hi: int = 45;
|
|
147
|
+
while (lo < hi) {
|
|
148
|
+
let mid: int = (lo + hi + 1) / 2;
|
|
149
|
+
let t: int = storage_get_int("math:tables", "tan", mid);
|
|
150
|
+
if (t * ax > ay * 1000) {
|
|
151
|
+
hi = mid - 1;
|
|
152
|
+
} else {
|
|
153
|
+
lo = mid;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
let theta: int = lo;
|
|
157
|
+
if (swapped == 1) { theta = 90 - theta; }
|
|
158
|
+
|
|
159
|
+
if (x > 0) {
|
|
160
|
+
if (y >= 0) { return theta; }
|
|
161
|
+
return 360 - theta;
|
|
162
|
+
}
|
|
163
|
+
if (y >= 0) { return 180 - theta; }
|
|
164
|
+
return 180 + theta;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Rotate a 2D vector (x, y) by deg degrees, return the new X component.
|
|
168
|
+
// Uses sin/cos table from math.mcrs — @require_on_load(_math_init) is
|
|
169
|
+
// automatically pulled in because this function calls sin_fixed/cos_fixed.
|
|
170
|
+
// rotate2d_x(1000, 0, 90) == 0 (unit vector east, rotated 90° = north)
|
|
171
|
+
// rotate2d_x(1000, 0, 45) == 707 (cos 45° × 1000)
|
|
172
|
+
fn rotate2d_x(x: int, y: int, deg: int) -> int {
|
|
173
|
+
// x' = x*cos(deg) - y*sin(deg) (all in fixed-point, /1000 via mulfix)
|
|
174
|
+
return mulfix(x, cos_fixed(deg)) - mulfix(y, sin_fixed(deg));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Rotate a 2D vector (x, y) by deg degrees, return the new Y component.
|
|
178
|
+
// rotate2d_y(1000, 0, 90) == 1000 (unit vector east, rotated 90° = north)
|
|
179
|
+
fn rotate2d_y(x: int, y: int, deg: int) -> int {
|
|
180
|
+
// y' = x*sin(deg) + y*cos(deg)
|
|
181
|
+
return mulfix(x, sin_fixed(deg)) + mulfix(y, cos_fixed(deg));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ─── Phase 3: 3D geometry ────────────────────────────────────────────────────
|
|
185
|
+
|
|
186
|
+
// Dot product of two 3D integer vectors.
|
|
187
|
+
fn dot3d(ax: int, ay: int, az: int, bx: int, by: int, bz: int) -> int {
|
|
188
|
+
return ax * bx + ay * by + az * bz;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Cross product X component: ay*bz − az*by
|
|
192
|
+
fn cross3d_x(ax: int, ay: int, az: int, bx: int, by: int, bz: int) -> int {
|
|
193
|
+
return ay * bz - az * by;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Cross product Y component: az*bx − ax*bz
|
|
197
|
+
fn cross3d_y(ax: int, ay: int, az: int, bx: int, by: int, bz: int) -> int {
|
|
198
|
+
return az * bx - ax * bz;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Cross product Z component: ax*by − ay*bx
|
|
202
|
+
fn cross3d_z(ax: int, ay: int, az: int, bx: int, by: int, bz: int) -> int {
|
|
203
|
+
return ax * by - ay * bx;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Euclidean length of a 3D integer vector, result in fixed-point (×1000).
|
|
207
|
+
// length3d_fixed(1, 1, 1) == 1732 (√3 × 1000)
|
|
208
|
+
// Note: |x|, |y|, |z| ≤ ~800 to keep x²+y²+z² ≤ ~2,000,000 (safe for *1000).
|
|
209
|
+
fn length3d_fixed(x: int, y: int, z: int) -> int {
|
|
210
|
+
return sqrt_fixed((x * x + y * y + z * z) * 1000);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Euclidean distance between two 3D integer points, result ×1000.
|
|
214
|
+
fn distance3d_fixed(x1: int, y1: int, z1: int, x2: int, y2: int, z2: int) -> int {
|
|
215
|
+
let dx: int = x1 - x2;
|
|
216
|
+
let dy: int = y1 - y2;
|
|
217
|
+
let dz: int = z1 - z2;
|
|
218
|
+
return length3d_fixed(dx, dy, dz);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Manhattan distance in 3D.
|
|
222
|
+
// manhattan3d(0,0,0, 1,2,3) == 6
|
|
223
|
+
fn manhattan3d(x1: int, y1: int, z1: int, x2: int, y2: int, z2: int) -> int {
|
|
224
|
+
let dx: int = x1 - x2;
|
|
225
|
+
let dy: int = y1 - y2;
|
|
226
|
+
let dz: int = z1 - z2;
|
|
227
|
+
if (dx < 0) { dx = 0 - dx; }
|
|
228
|
+
if (dy < 0) { dy = 0 - dy; }
|
|
229
|
+
if (dz < 0) { dz = 0 - dz; }
|
|
230
|
+
return dx + dy + dz;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Chebyshev distance in 3D.
|
|
234
|
+
// chebyshev3d(0,0,0, 3,1,2) == 3
|
|
235
|
+
fn chebyshev3d(x1: int, y1: int, z1: int, x2: int, y2: int, z2: int) -> int {
|
|
236
|
+
let dx: int = x1 - x2;
|
|
237
|
+
let dy: int = y1 - y2;
|
|
238
|
+
let dz: int = z1 - z2;
|
|
239
|
+
if (dx < 0) { dx = 0 - dx; }
|
|
240
|
+
if (dy < 0) { dy = 0 - dy; }
|
|
241
|
+
if (dz < 0) { dz = 0 - dz; }
|
|
242
|
+
let m: int = dx;
|
|
243
|
+
if (dy > m) { m = dy; }
|
|
244
|
+
if (dz > m) { m = dz; }
|
|
245
|
+
return m;
|
|
246
|
+
}
|