redscript-mc 2.2.1 → 2.3.0

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +18 -2
  3. package/dist/src/__tests__/tuner/engine.test.d.ts +4 -0
  4. package/dist/src/__tests__/tuner/engine.test.js +232 -0
  5. package/dist/src/tuner/adapters/ln-polynomial.d.ts +23 -0
  6. package/dist/src/tuner/adapters/ln-polynomial.js +142 -0
  7. package/dist/src/tuner/adapters/sqrt-newton.d.ts +28 -0
  8. package/dist/src/tuner/adapters/sqrt-newton.js +125 -0
  9. package/dist/src/tuner/cli.d.ts +5 -0
  10. package/dist/src/tuner/cli.js +168 -0
  11. package/dist/src/tuner/engine.d.ts +17 -0
  12. package/dist/src/tuner/engine.js +215 -0
  13. package/dist/src/tuner/metrics.d.ts +15 -0
  14. package/dist/src/tuner/metrics.js +51 -0
  15. package/dist/src/tuner/simulator.d.ts +35 -0
  16. package/dist/src/tuner/simulator.js +78 -0
  17. package/dist/src/tuner/types.d.ts +32 -0
  18. package/dist/src/tuner/types.js +6 -0
  19. package/docs/STDLIB_ROADMAP.md +142 -0
  20. package/editors/vscode/package-lock.json +3 -3
  21. package/editors/vscode/package.json +1 -1
  22. package/package.json +1 -1
  23. package/src/__tests__/tuner/engine.test.ts +260 -0
  24. package/src/stdlib/bigint.mcrs +155 -192
  25. package/src/stdlib/bits.mcrs +158 -0
  26. package/src/stdlib/color.mcrs +160 -0
  27. package/src/stdlib/geometry.mcrs +124 -0
  28. package/src/stdlib/list.mcrs +125 -0
  29. package/src/stdlib/math.mcrs +90 -0
  30. package/src/stdlib/math_hp.mcrs +65 -0
  31. package/src/stdlib/random.mcrs +67 -0
  32. package/src/stdlib/signal.mcrs +112 -0
  33. package/src/stdlib/vec.mcrs +27 -0
  34. package/src/tuner/adapters/ln-polynomial.ts +147 -0
  35. package/src/tuner/adapters/sqrt-newton.ts +135 -0
  36. package/src/tuner/cli.ts +158 -0
  37. package/src/tuner/engine.ts +272 -0
  38. package/src/tuner/metrics.ts +66 -0
  39. package/src/tuner/simulator.ts +69 -0
  40. package/src/tuner/types.ts +44 -0
  41. package/docs/ARCHITECTURE.zh.md +0 -1088
  42. package/docs/COMPILATION_STATS.md +0 -142
  43. package/docs/IMPLEMENTATION_GUIDE.md +0 -512
@@ -0,0 +1,160 @@
1
+ // color.mcrs — Color utilities for RedScript datapacks.
2
+ //
3
+ // All RGB values: 0-255
4
+ // All HSL values: H 0-360, S 0-100, L 0-100
5
+ // Packed int format: 0xRRGGBB (R in bits 16-23, G in bits 8-15, B in bits 0-7)
6
+ // packed = R*65536 + G*256 + B
7
+ //
8
+ // Note: No native bitwise ops in MC scoreboard — packing/unpacking uses multiply/divide.
9
+
10
+ module library;
11
+
12
+ // ─── Pack / Unpack ────────────────────────────────────────────────────────────
13
+
14
+ // rgb_pack(r, g, b): pack RGB into a single int (0xRRGGBB)
15
+ fn rgb_pack(r: int, g: int, b: int): int {
16
+ return r * 65536 + g * 256 + b;
17
+ }
18
+
19
+ // rgb_r(packed): extract red component
20
+ fn rgb_r(packed: int): int {
21
+ return packed / 65536 % 256;
22
+ }
23
+
24
+ // rgb_g(packed): extract green component
25
+ fn rgb_g(packed: int): int {
26
+ return packed / 256 % 256;
27
+ }
28
+
29
+ // rgb_b(packed): extract blue component
30
+ fn rgb_b(packed: int): int {
31
+ return packed % 256;
32
+ }
33
+
34
+ // ─── Blending ─────────────────────────────────────────────────────────────────
35
+
36
+ // rgb_lerp(a, b, t): linear blend between two packed colors, t ∈ [0, 1000]
37
+ // t=0 → a, t=1000 → b
38
+ fn rgb_lerp(a: int, b: int, t: int): int {
39
+ let ra: int = a / 65536 % 256;
40
+ let ga: int = a / 256 % 256;
41
+ let ba_: int = a % 256;
42
+ let rb: int = b / 65536 % 256;
43
+ let gb: int = b / 256 % 256;
44
+ let bb_: int = b % 256;
45
+ let r: int = ra + (rb - ra) * t / 1000;
46
+ let g: int = ga + (gb - ga) * t / 1000;
47
+ let bv: int = ba_ + (bb_ - ba_) * t / 1000;
48
+ return r * 65536 + g * 256 + bv;
49
+ }
50
+
51
+ // ─── HSL ↔ RGB ────────────────────────────────────────────────────────────────
52
+
53
+ // hsl_to_rgb_r/g/b: convert HSL to individual RGB components
54
+ // H: 0-360, S: 0-100, L: 0-100, returns 0-255
55
+ fn _hsl_chroma_x(h: int, c: int): int {
56
+ // X = C * (1 - |H/60 mod 2 - 1|)
57
+ let sector: int = h / 60;
58
+ let rem: int = h % 120;
59
+ if (rem > 60) { rem = 120 - rem; }
60
+ return c * rem / 60;
61
+ }
62
+
63
+ fn hsl_to_r(h: int, s: int, l: int): int {
64
+ let c: int = s * (1000 - (2 * l * 10 - 1000)) / 1000;
65
+ if (c < 0) { c = 0 - c; }
66
+ c = c / 10;
67
+ let x: int = _hsl_chroma_x(h, c);
68
+ let m: int = l * 255 / 100 - c / 2;
69
+ let sector: int = h / 60;
70
+ if (sector == 0) { return (c + m); }
71
+ if (sector == 1) { return (x + m); }
72
+ if (sector == 2) { return m; }
73
+ if (sector == 3) { return m; }
74
+ if (sector == 4) { return (x + m); }
75
+ return (c + m);
76
+ }
77
+
78
+ fn hsl_to_g(h: int, s: int, l: int): int {
79
+ let c: int = s * (1000 - (2 * l * 10 - 1000)) / 1000;
80
+ if (c < 0) { c = 0 - c; }
81
+ c = c / 10;
82
+ let x: int = _hsl_chroma_x(h, c);
83
+ let m: int = l * 255 / 100 - c / 2;
84
+ let sector: int = h / 60;
85
+ if (sector == 0) { return (x + m); }
86
+ if (sector == 1) { return (c + m); }
87
+ if (sector == 2) { return (c + m); }
88
+ if (sector == 3) { return (x + m); }
89
+ if (sector == 4) { return m; }
90
+ return m;
91
+ }
92
+
93
+ fn hsl_to_b(h: int, s: int, l: int): int {
94
+ let c: int = s * (1000 - (2 * l * 10 - 1000)) / 1000;
95
+ if (c < 0) { c = 0 - c; }
96
+ c = c / 10;
97
+ let x: int = _hsl_chroma_x(h, c);
98
+ let m: int = l * 255 / 100 - c / 2;
99
+ let sector: int = h / 60;
100
+ if (sector == 0) { return m; }
101
+ if (sector == 1) { return m; }
102
+ if (sector == 2) { return (x + m); }
103
+ if (sector == 3) { return (c + m); }
104
+ if (sector == 4) { return (c + m); }
105
+ return (x + m);
106
+ }
107
+
108
+ // hsl_to_packed(h, s, l): HSL → packed RGB int
109
+ fn hsl_to_packed(h: int, s: int, l: int): int {
110
+ return rgb_pack(hsl_to_r(h, s, l), hsl_to_g(h, s, l), hsl_to_b(h, s, l));
111
+ }
112
+
113
+ // ─── RGB → HSL ────────────────────────────────────────────────────────────────
114
+
115
+ // rgb_to_l(r, g, b): lightness 0-100
116
+ fn rgb_to_l(r: int, g: int, b: int): int {
117
+ let mx: int = r;
118
+ if (g > mx) { mx = g; }
119
+ if (b > mx) { mx = b; }
120
+ let mn: int = r;
121
+ if (g < mn) { mn = g; }
122
+ if (b < mn) { mn = b; }
123
+ return (mx + mn) * 100 / 510;
124
+ }
125
+
126
+ // rgb_to_s(r, g, b): saturation 0-100
127
+ fn rgb_to_s(r: int, g: int, b: int): int {
128
+ let mx: int = r;
129
+ if (g > mx) { mx = g; }
130
+ if (b > mx) { mx = b; }
131
+ let mn: int = r;
132
+ if (g < mn) { mn = g; }
133
+ if (b < mn) { mn = b; }
134
+ let c: int = mx - mn;
135
+ if (c == 0) { return 0; }
136
+ let l: int = (mx + mn) * 100 / 510;
137
+ if (l <= 50) { return c * 100 / (mx + mn); }
138
+ return c * 100 / (510 - mx - mn);
139
+ }
140
+
141
+ // rgb_to_h(r, g, b): hue 0-360
142
+ fn rgb_to_h(r: int, g: int, b: int): int {
143
+ let mx: int = r;
144
+ if (g > mx) { mx = g; }
145
+ if (b > mx) { mx = b; }
146
+ let mn: int = r;
147
+ if (g < mn) { mn = g; }
148
+ if (b < mn) { mn = b; }
149
+ let c: int = mx - mn;
150
+ if (c == 0) { return 0; }
151
+ if (mx == r) {
152
+ let h: int = 60 * (g - b) / c;
153
+ if (h < 0) { h = h + 360; }
154
+ return h;
155
+ }
156
+ if (mx == g) {
157
+ return 60 * (b - r) / c + 120;
158
+ }
159
+ return 60 * (r - g) / c + 240;
160
+ }
@@ -0,0 +1,124 @@
1
+ // geometry.mcrs — 3D geometry helpers for Minecraft datapacks.
2
+ //
3
+ // Fixed-point convention: coordinates × 100 (so 1.0 block = 100 units)
4
+ // Angles in degrees × 10000
5
+ //
6
+ // Requires: import "stdlib/math" (for sqrt_fx, sin_fixed, cos_fixed, abs)
7
+
8
+ module library;
9
+
10
+ // ─── Midpoint ─────────────────────────────────────────────────────────────────
11
+
12
+ // midpoint_x/y/z(a, b): integer midpoint of two coordinates
13
+ fn midpoint(a: int, b: int): int {
14
+ return (a + b) / 2;
15
+ }
16
+
17
+ // ─── Bounding box ─────────────────────────────────────────────────────────────
18
+
19
+ // aabb_contains(px, py, pz, minx, miny, minz, maxx, maxy, maxz):
20
+ // Returns 1 if point (px,py,pz) is inside axis-aligned bounding box, 0 otherwise.
21
+ fn aabb_contains(px: int, py: int, pz: int,
22
+ minx: int, miny: int, minz: int,
23
+ maxx: int, maxy: int, maxz: int): int {
24
+ if (px < minx) { return 0; }
25
+ if (px > maxx) { return 0; }
26
+ if (py < miny) { return 0; }
27
+ if (py > maxy) { return 0; }
28
+ if (pz < minz) { return 0; }
29
+ if (pz > maxz) { return 0; }
30
+ return 1;
31
+ }
32
+
33
+ // sphere_contains(px, py, pz, cx, cy, cz, r):
34
+ // Returns 1 if point is inside sphere (all coords in same unit, r is radius).
35
+ // Avoids sqrt by comparing squared distances.
36
+ fn sphere_contains(px: int, py: int, pz: int,
37
+ cx: int, cy: int, cz: int,
38
+ r: int): int {
39
+ let dx: int = px - cx;
40
+ let dy: int = py - cy;
41
+ let dz: int = pz - cz;
42
+ let dist_sq: int = dx * dx + dy * dy + dz * dz;
43
+ if (dist_sq <= r * r) { return 1; }
44
+ return 0;
45
+ }
46
+
47
+ // cylinder_contains(px, pz, cx, cz, r):
48
+ // Returns 1 if point is inside vertical cylinder (ignores Y). 2D circle check.
49
+ fn cylinder_contains(px: int, pz: int, cx: int, cz: int, r: int): int {
50
+ let dx: int = px - cx;
51
+ let dz: int = pz - cz;
52
+ if (dx * dx + dz * dz <= r * r) { return 1; }
53
+ return 0;
54
+ }
55
+
56
+ // ─── Parabola (projectile) ───────────────────────────────────────────────────
57
+ //
58
+ // MC projectile physics: velocity in blocks/tick, gravity ≈ 0.05 blocks/tick²
59
+ // For a simplified fixed-point model:
60
+ // pos(t) = init_pos + init_vel * t - gravity_half * t²
61
+ //
62
+ // All values × 100 (1 block = 100 units), time in ticks.
63
+
64
+ // parabola_y(y0, vy0, t): Y position at tick t.
65
+ // y0: initial Y × 100, vy0: initial Y velocity × 100 (blocks/tick × 100)
66
+ // gravity: 5 units/tick² (= 0.05 blocks/tick² × 100)
67
+ fn parabola_y(y0: int, vy0: int, t: int): int {
68
+ return y0 + vy0 * t - 5 * t * t / 2;
69
+ }
70
+
71
+ // parabola_x(x0, vx0, t): horizontal X position at tick t (no drag, constant).
72
+ fn parabola_x(x0: int, vx0: int, t: int): int {
73
+ return x0 + vx0 * t;
74
+ }
75
+
76
+ // parabola_land_t(vy0): approximate tick when projectile returns to Y=0.
77
+ // t_land ≈ 2 * vy0 / gravity = 2 * vy0 / 5 (in fixed-point units)
78
+ fn parabola_land_t(vy0: int): int {
79
+ return 2 * vy0 / 5;
80
+ }
81
+
82
+ // ─── Grid / tile math ────────────────────────────────────────────────────────
83
+
84
+ // tile_of(coord): which tile a coordinate belongs to (floor division).
85
+ // tile_of(250, 100) = 2 (block 2 when tile size = 100)
86
+ fn tile_of(coord: int, tile_size: int): int {
87
+ if (coord >= 0) {
88
+ return coord / tile_size;
89
+ }
90
+ return (coord - tile_size + 1) / tile_size;
91
+ }
92
+
93
+ // tile_center(tile, tile_size): center coordinate of a tile
94
+ fn tile_center(tile: int, tile_size: int): int {
95
+ return tile * tile_size + tile_size / 2;
96
+ }
97
+
98
+ // ─── Angle helpers ────────────────────────────────────────────────────────────
99
+
100
+ // angle_normalize(deg): normalize angle to [0, 3600000) (degrees × 10000)
101
+ fn angle_normalize(deg: int): int {
102
+ let a: int = deg;
103
+ while (a < 0) { a = a + 3600000; }
104
+ while (a >= 3600000) { a = a - 3600000; }
105
+ return a;
106
+ }
107
+
108
+ // angle_diff(a, b): signed shortest angular difference (degrees × 10000)
109
+ // Result in (-1800000, 1800000]
110
+ fn angle_diff(a: int, b: int): int {
111
+ let d: int = angle_normalize(b - a);
112
+ if (d > 1800000) { d = d - 3600000; }
113
+ return d;
114
+ }
115
+
116
+ // ─── Sun angle helper ────────────────────────────────────────────────────────
117
+
118
+ // mc_day_angle(daytime): MC sun angle × 10000 from /time query daytime
119
+ // Daytime 0 = dawn (sun at east = 90°), 6000 = noon (sun at south = 180°)
120
+ fn mc_day_angle(daytime: int): int {
121
+ // MC daytime: 0-24000 ticks per day
122
+ // Sun angle = (daytime * 360 / 24000 + 90) mod 360
123
+ return ((daytime * 360 / 24000 + 90) % 360) * 10000;
124
+ }
@@ -0,0 +1,125 @@
1
+ // list.mcrs — Dynamic NBT-backed integer list operations.
2
+ //
3
+ // Lists are stored in NBT storage as int arrays.
4
+ // The storage namespace is "rs:lists" and path is the variable name.
5
+ //
6
+ // IMPORTANT: These functions use raw() to emit NBT commands.
7
+ // They operate on a NAMED list stored at a specific NBT path.
8
+ // The caller must pass the list path as a namespace:path string.
9
+ //
10
+ // Example usage:
11
+ // // Create a list (declare as int[])
12
+ // let mylist: int[] = [1, 2, 3];
13
+ //
14
+ // // Use list_* functions with raw() for dynamic operations.
15
+ // // Note: push/pop/len use raw() NBT commands directly.
16
+ //
17
+ // For fixed-size arrays, use int[] literals directly.
18
+ // For dynamic lists, use the functions below.
19
+ //
20
+ // Storage convention: lists live at storage rs:lists <varname>
21
+
22
+ module library;
23
+
24
+ // list_len_of(ns, path): count elements in an NBT int list.
25
+ // Returns the count stored in a scoreboard temp.
26
+ // Use: raw("execute store result score $len __ns run data get storage ns:name path")
27
+ // This is a convenience wrapper — in practice use raw() directly.
28
+
29
+ // list_sum(a, b, c, d, e): sum of up to 5 int values (utility)
30
+ fn list_sum5(a: int, b: int, c: int, d: int, e: int): int {
31
+ return a + b + c + d + e;
32
+ }
33
+
34
+ fn list_sum4(a: int, b: int, c: int, d: int): int {
35
+ return a + b + c + d;
36
+ }
37
+
38
+ fn list_sum3(a: int, b: int, c: int): int {
39
+ return a + b + c;
40
+ }
41
+
42
+ // list_min3 / list_max3: min/max over 3 values (useful for array-derived work)
43
+ fn list_min3(a: int, b: int, c: int): int {
44
+ let m: int = a;
45
+ if (b < m) { m = b; }
46
+ if (c < m) { m = c; }
47
+ return m;
48
+ }
49
+
50
+ fn list_max3(a: int, b: int, c: int): int {
51
+ let m: int = a;
52
+ if (b > m) { m = b; }
53
+ if (c > m) { m = c; }
54
+ return m;
55
+ }
56
+
57
+ fn list_min5(a: int, b: int, c: int, d: int, e: int): int {
58
+ let m: int = a;
59
+ if (b < m) { m = b; }
60
+ if (c < m) { m = c; }
61
+ if (d < m) { m = d; }
62
+ if (e < m) { m = e; }
63
+ return m;
64
+ }
65
+
66
+ fn list_max5(a: int, b: int, c: int, d: int, e: int): int {
67
+ let m: int = a;
68
+ if (b > m) { m = b; }
69
+ if (c > m) { m = c; }
70
+ if (d > m) { m = d; }
71
+ if (e > m) { m = e; }
72
+ return m;
73
+ }
74
+
75
+ // ─── Sorting (in-register, fixed size) ───────────────────────────────────────
76
+
77
+ // sort3_asc: sort 3 values ascending, returns packed as 3 separate ints via
78
+ // side-effect scoreboard. Caller reads $sort3_a, $sort3_b, $sort3_c.
79
+ // (This is a placeholder — proper NBT list sort requires runtime support.)
80
+
81
+ // swap helper: sort2 returns the smaller value
82
+ fn sort2_min(a: int, b: int): int {
83
+ if (a <= b) { return a; }
84
+ return b;
85
+ }
86
+
87
+ fn sort2_max(a: int, b: int): int {
88
+ if (a >= b) { return a; }
89
+ return b;
90
+ }
91
+
92
+ // bubble_sort3: returns sorted value at position pos (0=min, 1=mid, 2=max)
93
+ fn sort3(a: int, b: int, c: int, pos: int): int {
94
+ // Sorting network for 3 elements
95
+ let x: int = a;
96
+ let y: int = b;
97
+ let z: int = c;
98
+ // Step 1: compare-swap (x, y)
99
+ if (x > y) { let tmp: int = x; x = y; y = tmp; }
100
+ // Step 2: compare-swap (y, z)
101
+ if (y > z) { let tmp: int = y; y = z; z = tmp; }
102
+ // Step 3: compare-swap (x, y)
103
+ if (x > y) { let tmp: int = x; x = y; y = tmp; }
104
+ if (pos == 0) { return x; }
105
+ if (pos == 1) { return y; }
106
+ return z;
107
+ }
108
+
109
+ // ─── Statistics ──────────────────────────────────────────────────────────────
110
+
111
+ // avg3(a, b, c): integer average (truncated)
112
+ fn avg3(a: int, b: int, c: int): int {
113
+ return (a + b + c) / 3;
114
+ }
115
+
116
+ fn avg5(a: int, b: int, c: int, d: int, e: int): int {
117
+ return (a + b + c + d + e) / 5;
118
+ }
119
+
120
+ // clamp_list3(a, b, c, lo, hi): clamp all 3 values
121
+ fn clamp3(a: int, lo: int, hi: int): int {
122
+ if (a < lo) { return lo; }
123
+ if (a > hi) { return hi; }
124
+ return a;
125
+ }
@@ -300,3 +300,93 @@ fn smootherstep(lo: int, hi: int, x: int) -> int {
300
300
  let inner: int = 10000 - 15 * t10 + 6 * t2 / 100;
301
301
  return t3 * inner / 100000;
302
302
  }
303
+
304
+ // AUTO-GENERATED by redscript tune — DO NOT EDIT
305
+ // Adapter: ln-polynomial | Date: 2026-03-17
306
+ // max_error: 0.000557 | mae: 0.000186 | estimated_cmds: 27
307
+ // Run `redscript tune --adapter ln-polynomial` to regenerate
308
+ //
309
+ // atanh series coefficients (×10000 scale):
310
+ // A1 = 20010 (theoretical 2×10000 = 20000)
311
+ // A3 = 6856 (theoretical 2/3×10000 = 6667)
312
+ // A5 = 2539 (theoretical 2/5×10000 = 4000)
313
+
314
+ fn ln(x: int): int {
315
+ // Input x: fixed-point ×10000; returns ln(x/10000)×10000
316
+ // Valid range: x ∈ [100, 1000000] (0.01 ~ 100.0)
317
+ // max_error: 0.000557 (in ×10000 units, i.e. 0.00000006 in real units)
318
+
319
+ let scale: int = 10000;
320
+ let ln2: int = 6931;
321
+ let A1: int = 20010;
322
+ let A3: int = 6856;
323
+ let A5: int = 2539;
324
+
325
+ // Step 1: range reduction — bring x into [scale, 2*scale)
326
+ let xr: int = x;
327
+ let k: int = 0;
328
+ while (xr < scale) {
329
+ xr = xr * 2;
330
+ k = k - 1;
331
+ }
332
+ while (xr >= scale * 2) {
333
+ xr = xr / 2;
334
+ k = k + 1;
335
+ }
336
+
337
+ // Step 2: s = (xr - scale) * scale / (xr + scale)
338
+ let s: int = (xr - scale) * scale / (xr + scale);
339
+
340
+ // Step 3: atanh series ln(xr/scale) ≈ A1*s + A3*s³ + A5*s⁵ (all /scale)
341
+ let s2: int = s * s / scale;
342
+ let s3: int = s * s2;
343
+ let s5: int = s3 * s2;
344
+ let lnxr: int = A1 * s / scale + A3 * s3 / scale + A5 * s5 / scale;
345
+
346
+ // Step 4: ln(x) = k*ln(2) + ln(xr/scale)
347
+ return k * ln2 + lnxr;
348
+ }
349
+
350
+ // sqrt_fx(x): fixed-point sqrt, scale ×10000
351
+ // sqrt_fx(10000) == 10000 (√1.0 = 1.0)
352
+ // sqrt_fx(20000) ≈ 14142 (√2.0 ≈ 1.4142)
353
+ // sqrt_fx(40000) == 20000 (√4.0 = 2.0)
354
+ fn sqrt_fx(x: int) -> int {
355
+ return isqrt(x * 100) * 100;
356
+ }
357
+
358
+ // exp_fx(x): e^(x/10000) × 10000, fixed-point ×10000
359
+ // exp_fx(0) == 10000 (e^0 = 1.0)
360
+ // exp_fx(10000) ≈ 27183 (e^1 ≈ 2.7183)
361
+ // exp_fx(6931) ≈ 20000 (e^ln2 = 2.0)
362
+ // exp_fx(-10000) ≈ 3679 (e^-1 ≈ 0.3679)
363
+ // Valid range: x ∈ [-100000, 100000] (real: -10.0 to 10.0)
364
+ fn exp_fx(x: int): int {
365
+ // Range reduction: x = k*ln2 + r, r in [0, 6931]
366
+ let ln2: int = 6931;
367
+ let k: int = x / ln2;
368
+ let r: int = x - k * ln2;
369
+ if (r < 0) { r = r + ln2; k = k - 1; }
370
+
371
+ // 6-term Horner-form Taylor: e^r ≈ 1 + r + r²/2! + r³/3! + ...
372
+ // Coefficients scaled ×10000, nested for numerical stability
373
+ let t: int = 14;
374
+ t = 83 + t * r / 10000;
375
+ t = 417 + t * r / 10000;
376
+ t = 1667 + t * r / 10000;
377
+ t = 5000 + t * r / 10000;
378
+ t = 10000 + t * r / 10000;
379
+ let er: int = 10000 + t * r / 10000;
380
+
381
+ // Scale by 2^k
382
+ if (k >= 0) {
383
+ let i: int = 0;
384
+ while (i < k) { er = er * 2; i = i + 1; }
385
+ }
386
+ if (k < 0) {
387
+ let i: int = 0;
388
+ let nk: int = 0 - k;
389
+ while (i < nk) { er = er / 2; i = i + 1; }
390
+ }
391
+ return er;
392
+ }
@@ -0,0 +1,65 @@
1
+ // math_hp.mcrs — High-precision trig using MC entity rotation trick.
2
+ //
3
+ // MC internally uses Java Math.sin/cos (double precision).
4
+ // By teleporting a Marker entity one block in its local forward direction,
5
+ // Pos[0] = cos(θ) and Pos[2] = sin(θ) with ~15 significant figures.
6
+ //
7
+ // SCALE: angles in degrees × 10000 (e.g. 900000 = 90°)
8
+ // outputs × 10000 (e.g. 10000 = 1.0, -7071 ≈ sin 45°)
9
+ //
10
+ // ⚠ SETUP REQUIRED: Call init_trig() in your @load function:
11
+ // @load fn setup() { init_trig(); }
12
+ //
13
+ // Usage:
14
+ // import "stdlib/math_hp"
15
+ // @load fn setup() { init_trig(); }
16
+ // fn my_fn() {
17
+ // let s: int = sin_hp(450000); // sin(45°) ≈ 7071
18
+ // let c: int = cos_hp(450000); // cos(45°) ≈ 7071
19
+ // }
20
+
21
+ module library;
22
+
23
+ // Internal tag for the helper Marker entity.
24
+ // Do NOT kill or modify entities with this tag.
25
+ let _TRIG_TAG: string = "rs_trig";
26
+
27
+ // init_trig(): create the Marker entity used by sin_hp/cos_hp.
28
+ // Safe to call multiple times (uses execute unless entity).
29
+ // Must be called from your @load function.
30
+ @require_on_load(init_trig)
31
+ fn init_trig() {
32
+ raw("execute unless entity @e[tag=rs_trig,limit=1] run summon minecraft:marker ~ 0 ~ {Tags:[\"rs_trig\"]}");
33
+ }
34
+
35
+ // sin_hp(angle): high-precision sine.
36
+ // angle: degrees × 10000 (e.g. 450000 = 45°, 900000 = 90°)
37
+ // returns: sin(angle/10000°) × 10000
38
+ // Example: sin_hp(900000) == 10000, sin_hp(0) == 0, sin_hp(450000) ≈ 7071
39
+ @require_on_load(init_trig)
40
+ fn sin_hp(angle: int): int {
41
+ // Step 1: store angle into scoreboard, then use execute store to set Rotation[0]
42
+ scoreboard_set("$sin_hp_in", "__rs_math_hp", angle);
43
+ raw("execute store result entity @e[tag=rs_trig,limit=1] Rotation[0] float 0.0001 run scoreboard players get $sin_hp_in __rs_math_hp");
44
+ // Step 2: teleport Marker 1 block forward along its facing direction
45
+ raw("execute as @e[tag=rs_trig,limit=1] rotated as @s positioned 0.0 0.0 0.0 run tp @s ^ ^ ^1 ~ ~");
46
+ // Step 3: read Pos[2] (= sin θ), scale ×10000
47
+ raw("execute store result score $sin_hp_out __rs_math_hp run data get entity @e[tag=rs_trig,limit=1] Pos[2] 10000.0");
48
+ // Step 4: reset Marker position
49
+ raw("tp @e[tag=rs_trig,limit=1] 0 0 0");
50
+ return scoreboard_get("$sin_hp_out", "__rs_math_hp");
51
+ }
52
+
53
+ // cos_hp(angle): high-precision cosine.
54
+ // angle: degrees × 10000 (e.g. 450000 = 45°)
55
+ // returns: cos(angle/10000°) × 10000
56
+ @require_on_load(init_trig)
57
+ fn cos_hp(angle: int): int {
58
+ scoreboard_set("$cos_hp_in", "__rs_math_hp", angle);
59
+ raw("execute store result entity @e[tag=rs_trig,limit=1] Rotation[0] float 0.0001 run scoreboard players get $cos_hp_in __rs_math_hp");
60
+ raw("execute as @e[tag=rs_trig,limit=1] rotated as @s positioned 0.0 0.0 0.0 run tp @s ^ ^ ^1 ~ ~");
61
+ // Pos[0] = cos θ
62
+ raw("execute store result score $cos_hp_out __rs_math_hp run data get entity @e[tag=rs_trig,limit=1] Pos[0] 10000.0");
63
+ raw("tp @e[tag=rs_trig,limit=1] 0 0 0");
64
+ return scoreboard_get("$cos_hp_out", "__rs_math_hp");
65
+ }
@@ -0,0 +1,67 @@
1
+ // random.mcrs — Pseudo-random number generators for RedScript datapacks.
2
+ //
3
+ // LCG (Linear Congruential Generator):
4
+ // Simple, fast, sufficient for most game use-cases.
5
+ // Parameters: a=1664525, c=1013904223 (Numerical Recipes)
6
+ // Modulus: 2^32 via int32 overflow (free in MC scoreboard arithmetic).
7
+ //
8
+ // Usage:
9
+ // let seed: int = 12345; // any non-zero starting value
10
+ // seed = next_lcg(seed); // advance state
11
+ // let roll: int = random_range(seed, 0, 100);
12
+
13
+ module library;
14
+
15
+ // next_lcg(seed): advance LCG state, return next pseudo-random int32.
16
+ // The returned value is also the new seed for the next call.
17
+ fn next_lcg(seed: int): int {
18
+ return seed * 1664525 + 1013904223;
19
+ }
20
+
21
+ // random_range(seed, lo, hi): return integer in [lo, hi).
22
+ // seed should already be the output of next_lcg.
23
+ fn random_range(seed: int, lo: int, hi: int): int {
24
+ let r: int = seed;
25
+ if (r < 0) { r = 0 - r; }
26
+ return lo + r % (hi - lo);
27
+ }
28
+
29
+ // random_bool(seed): return 0 or 1 with equal probability.
30
+ fn random_bool(seed: int): int {
31
+ let r: int = seed;
32
+ if (r < 0) { r = 0 - r; }
33
+ return r % 2;
34
+ }
35
+
36
+ // ─── PCG (Permuted Congruential Generator) ───────────────────────────────────
37
+ // Better statistical quality than LCG — passes more randomness tests.
38
+ // Requires stdlib/bits for bit_xor, bit_shr.
39
+ //
40
+ // PCG-XSH-RR (32-bit output from 64-bit state is approximated here with two
41
+ // int32 words; this is a simplified PCG32 variant that fits MC scoreboard).
42
+ //
43
+ // Reference: https://www.pcg-random.org/
44
+ //
45
+ // Usage: maintain two state variables (hi, lo); update both each call.
46
+
47
+ // pcg_next_lo(state_lo, state_hi): advance PCG state (low word).
48
+ // Returns new low word. Use with pcg_next_hi.
49
+ fn pcg_next_lo(state_lo: int): int {
50
+ return state_lo * 747796405 + 2891336453;
51
+ }
52
+
53
+ // pcg_next_hi(state_lo, state_hi): advance PCG state (high word).
54
+ fn pcg_next_hi(state_hi: int, state_lo: int): int {
55
+ return state_hi * 747796405 + state_lo;
56
+ }
57
+
58
+ // pcg_output(state_lo): extract output value from PCG low word.
59
+ // Applies XSH-RR output permutation (simplified to XOR-shift).
60
+ fn pcg_output(state_lo: int): int {
61
+ let xsh: int = state_lo;
62
+ // xsh = ((state >> 18) ^ state) >> 27 (simplified: use single xor-shift)
63
+ let shifted: int = xsh / 262144; // >> 18
64
+ let xored: int = xsh / 32 + shifted; // approximation of XSH
65
+ if (xored < 0) { xored = 0 - xored; }
66
+ return xored;
67
+ }