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.
- package/CHANGELOG.md +20 -0
- package/README.md +18 -2
- package/dist/src/__tests__/tuner/engine.test.d.ts +4 -0
- package/dist/src/__tests__/tuner/engine.test.js +232 -0
- package/dist/src/tuner/adapters/ln-polynomial.d.ts +23 -0
- package/dist/src/tuner/adapters/ln-polynomial.js +142 -0
- package/dist/src/tuner/adapters/sqrt-newton.d.ts +28 -0
- package/dist/src/tuner/adapters/sqrt-newton.js +125 -0
- package/dist/src/tuner/cli.d.ts +5 -0
- package/dist/src/tuner/cli.js +168 -0
- package/dist/src/tuner/engine.d.ts +17 -0
- package/dist/src/tuner/engine.js +215 -0
- package/dist/src/tuner/metrics.d.ts +15 -0
- package/dist/src/tuner/metrics.js +51 -0
- package/dist/src/tuner/simulator.d.ts +35 -0
- package/dist/src/tuner/simulator.js +78 -0
- package/dist/src/tuner/types.d.ts +32 -0
- package/dist/src/tuner/types.js +6 -0
- package/docs/STDLIB_ROADMAP.md +142 -0
- package/editors/vscode/package-lock.json +3 -3
- package/editors/vscode/package.json +1 -1
- package/package.json +1 -1
- package/src/__tests__/tuner/engine.test.ts +260 -0
- package/src/stdlib/bigint.mcrs +155 -192
- package/src/stdlib/bits.mcrs +158 -0
- package/src/stdlib/color.mcrs +160 -0
- package/src/stdlib/geometry.mcrs +124 -0
- package/src/stdlib/list.mcrs +125 -0
- package/src/stdlib/math.mcrs +90 -0
- package/src/stdlib/math_hp.mcrs +65 -0
- package/src/stdlib/random.mcrs +67 -0
- package/src/stdlib/signal.mcrs +112 -0
- package/src/stdlib/vec.mcrs +27 -0
- package/src/tuner/adapters/ln-polynomial.ts +147 -0
- package/src/tuner/adapters/sqrt-newton.ts +135 -0
- package/src/tuner/cli.ts +158 -0
- package/src/tuner/engine.ts +272 -0
- package/src/tuner/metrics.ts +66 -0
- package/src/tuner/simulator.ts +69 -0
- package/src/tuner/types.ts +44 -0
- package/docs/ARCHITECTURE.zh.md +0 -1088
- package/docs/COMPILATION_STATS.md +0 -142
- 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
|
+
}
|
package/src/stdlib/math.mcrs
CHANGED
|
@@ -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
|
+
}
|