redscript-mc 1.2.26 → 1.2.27
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/README.md +67 -9
- package/README.zh.md +61 -4
- package/dist/__tests__/stdlib-advanced.test.js +114 -0
- package/dist/__tests__/stdlib-bigint.test.d.ts +7 -0
- package/dist/__tests__/stdlib-bigint.test.js +428 -0
- package/dist/data/arena/function/__load.mcfunction +6 -0
- package/dist/data/arena/function/__tick.mcfunction +2 -0
- package/dist/data/arena/function/announce_leaders/else_1.mcfunction +3 -0
- package/dist/data/arena/function/announce_leaders/foreach_0/merge_2.mcfunction +1 -0
- package/dist/data/arena/function/announce_leaders/foreach_0/then_0.mcfunction +3 -0
- package/dist/data/arena/function/announce_leaders/foreach_0.mcfunction +7 -0
- package/dist/data/arena/function/announce_leaders/foreach_1/merge_2.mcfunction +1 -0
- package/dist/data/arena/function/announce_leaders/foreach_1/then_0.mcfunction +4 -0
- package/dist/data/arena/function/announce_leaders/foreach_1.mcfunction +6 -0
- package/dist/data/arena/function/announce_leaders/merge_2.mcfunction +1 -0
- package/dist/data/arena/function/announce_leaders/then_0.mcfunction +4 -0
- package/dist/data/arena/function/announce_leaders.mcfunction +6 -0
- package/dist/data/arena/function/arena_tick/merge_2.mcfunction +1 -0
- package/dist/data/arena/function/arena_tick/then_0.mcfunction +4 -0
- package/dist/data/arena/function/arena_tick.mcfunction +11 -0
- package/dist/data/counter/function/__load.mcfunction +5 -0
- package/dist/data/counter/function/__tick.mcfunction +2 -0
- package/dist/data/counter/function/counter_tick/merge_2.mcfunction +1 -0
- package/dist/data/counter/function/counter_tick/then_0.mcfunction +3 -0
- package/dist/data/counter/function/counter_tick.mcfunction +11 -0
- package/dist/data/gcd2/function/__load.mcfunction +3 -0
- package/dist/data/gcd2/function/abs/merge_2.mcfunction +3 -0
- package/dist/data/gcd2/function/abs/then_0.mcfunction +5 -0
- package/dist/data/gcd2/function/abs.mcfunction +7 -0
- package/dist/data/gcd2/function/gcd/loop_body_1.mcfunction +7 -0
- package/dist/data/gcd2/function/gcd/loop_check_0.mcfunction +5 -0
- package/dist/data/gcd2/function/gcd/loop_exit_2.mcfunction +3 -0
- package/dist/data/gcd2/function/gcd.mcfunction +14 -0
- package/dist/data/gcd3/function/__load.mcfunction +3 -0
- package/dist/data/gcd3/function/abs/merge_2.mcfunction +3 -0
- package/dist/data/gcd3/function/abs/then_0.mcfunction +5 -0
- package/dist/data/gcd3/function/abs.mcfunction +7 -0
- package/dist/data/gcd3/function/gcd/loop_body_1.mcfunction +7 -0
- package/dist/data/gcd3/function/gcd/loop_check_0.mcfunction +5 -0
- package/dist/data/gcd3/function/gcd/loop_exit_2.mcfunction +3 -0
- package/dist/data/gcd3/function/gcd.mcfunction +14 -0
- package/dist/data/gcd3/function/test.mcfunction +7 -0
- package/dist/data/gcd3nm/function/__load.mcfunction +3 -0
- package/dist/data/gcd3nm/function/abs/merge_2.mcfunction +3 -0
- package/dist/data/gcd3nm/function/abs/then_0.mcfunction +5 -0
- package/dist/data/gcd3nm/function/abs.mcfunction +7 -0
- package/dist/data/gcd3nm/function/gcd/loop_body_1.mcfunction +7 -0
- package/dist/data/gcd3nm/function/gcd/loop_check_0.mcfunction +5 -0
- package/dist/data/gcd3nm/function/gcd/loop_exit_2.mcfunction +3 -0
- package/dist/data/gcd3nm/function/gcd.mcfunction +14 -0
- package/dist/data/gcd3nm/function/test.mcfunction +7 -0
- package/dist/data/gcd_test/function/__load.mcfunction +3 -0
- package/dist/data/gcd_test/function/abs/merge_2.mcfunction +3 -0
- package/dist/data/gcd_test/function/abs/then_0.mcfunction +5 -0
- package/dist/data/gcd_test/function/abs.mcfunction +7 -0
- package/dist/data/gcd_test/function/gcd/loop_body_1.mcfunction +7 -0
- package/dist/data/gcd_test/function/gcd/loop_check_0.mcfunction +5 -0
- package/dist/data/gcd_test/function/gcd/loop_exit_2.mcfunction +3 -0
- package/dist/data/gcd_test/function/gcd.mcfunction +14 -0
- package/dist/data/isqrttest/function/__load.mcfunction +6 -0
- package/dist/data/isqrttest/function/isqrt/loop_body_4.mcfunction +12 -0
- package/dist/data/isqrttest/function/isqrt/loop_check_3.mcfunction +5 -0
- package/dist/data/isqrttest/function/isqrt/loop_exit_5.mcfunction +3 -0
- package/dist/data/isqrttest/function/isqrt/merge_2.mcfunction +4 -0
- package/dist/data/isqrttest/function/isqrt/merge_8.mcfunction +6 -0
- package/dist/data/isqrttest/function/isqrt/then_0.mcfunction +3 -0
- package/dist/data/isqrttest/function/isqrt/then_6.mcfunction +3 -0
- package/dist/data/isqrttest/function/isqrt.mcfunction +7 -0
- package/dist/data/isqrttest/function/test.mcfunction +6 -0
- package/dist/data/mathtest/function/__load.mcfunction +3 -0
- package/dist/data/mathtest/function/abs/merge_2.mcfunction +3 -0
- package/dist/data/mathtest/function/abs/then_0.mcfunction +5 -0
- package/dist/data/mathtest/function/abs.mcfunction +6 -0
- package/dist/data/mathtest/function/test.mcfunction +5 -0
- package/dist/data/minecraft/tags/function/load.json +5 -0
- package/dist/data/minecraft/tags/function/tick.json +5 -0
- package/dist/data/mypack/function/__load.mcfunction +13 -0
- package/dist/data/mypack/function/_atan_init.mcfunction +2 -0
- package/dist/data/mypack/function/abs/merge_2.mcfunction +3 -0
- package/dist/data/mypack/function/abs/then_0.mcfunction +5 -0
- package/dist/data/mypack/function/abs.mcfunction +6 -0
- package/dist/data/mypack/function/atan2_fixed/__sgi_1.mcfunction +2 -0
- package/dist/data/mypack/function/atan2_fixed/else_34.mcfunction +3 -0
- package/dist/data/mypack/function/atan2_fixed/loop_body_31.mcfunction +19 -0
- package/dist/data/mypack/function/atan2_fixed/loop_check_30.mcfunction +5 -0
- package/dist/data/mypack/function/atan2_fixed/loop_exit_32.mcfunction +6 -0
- package/dist/data/mypack/function/atan2_fixed/merge_11.mcfunction +6 -0
- package/dist/data/mypack/function/atan2_fixed/merge_14.mcfunction +3 -0
- package/dist/data/mypack/function/atan2_fixed/merge_17.mcfunction +6 -0
- package/dist/data/mypack/function/atan2_fixed/merge_2.mcfunction +5 -0
- package/dist/data/mypack/function/atan2_fixed/merge_20.mcfunction +5 -0
- package/dist/data/mypack/function/atan2_fixed/merge_23.mcfunction +5 -0
- package/dist/data/mypack/function/atan2_fixed/merge_26.mcfunction +6 -0
- package/dist/data/mypack/function/atan2_fixed/merge_29.mcfunction +4 -0
- package/dist/data/mypack/function/atan2_fixed/merge_38.mcfunction +5 -0
- package/dist/data/mypack/function/atan2_fixed/merge_41.mcfunction +5 -0
- package/dist/data/mypack/function/atan2_fixed/merge_44.mcfunction +5 -0
- package/dist/data/mypack/function/atan2_fixed/merge_47.mcfunction +5 -0
- package/dist/data/mypack/function/atan2_fixed/merge_5.mcfunction +5 -0
- package/dist/data/mypack/function/atan2_fixed/merge_8.mcfunction +3 -0
- package/dist/data/mypack/function/atan2_fixed/then_0.mcfunction +5 -0
- package/dist/data/mypack/function/atan2_fixed/then_12.mcfunction +3 -0
- package/dist/data/mypack/function/atan2_fixed/then_15.mcfunction +5 -0
- package/dist/data/mypack/function/atan2_fixed/then_18.mcfunction +5 -0
- package/dist/data/mypack/function/atan2_fixed/then_21.mcfunction +3 -0
- package/dist/data/mypack/function/atan2_fixed/then_24.mcfunction +3 -0
- package/dist/data/mypack/function/atan2_fixed/then_27.mcfunction +6 -0
- package/dist/data/mypack/function/atan2_fixed/then_3.mcfunction +3 -0
- package/dist/data/mypack/function/atan2_fixed/then_33.mcfunction +5 -0
- package/dist/data/mypack/function/atan2_fixed/then_36.mcfunction +5 -0
- package/dist/data/mypack/function/atan2_fixed/then_39.mcfunction +5 -0
- package/dist/data/mypack/function/atan2_fixed/then_42.mcfunction +3 -0
- package/dist/data/mypack/function/atan2_fixed/then_45.mcfunction +5 -0
- package/dist/data/mypack/function/atan2_fixed/then_6.mcfunction +3 -0
- package/dist/data/mypack/function/atan2_fixed/then_9.mcfunction +5 -0
- package/dist/data/mypack/function/atan2_fixed.mcfunction +7 -0
- package/dist/data/mypack/function/my_game.mcfunction +10 -0
- package/dist/data/quiz/function/__load.mcfunction +16 -0
- package/dist/data/quiz/function/__tick.mcfunction +6 -0
- package/dist/data/quiz/function/__trigger_quiz_a_dispatch.mcfunction +4 -0
- package/dist/data/quiz/function/__trigger_quiz_b_dispatch.mcfunction +4 -0
- package/dist/data/quiz/function/__trigger_quiz_c_dispatch.mcfunction +4 -0
- package/dist/data/quiz/function/__trigger_quiz_start_dispatch.mcfunction +4 -0
- package/dist/data/quiz/function/answer_a.mcfunction +4 -0
- package/dist/data/quiz/function/answer_b.mcfunction +4 -0
- package/dist/data/quiz/function/answer_c.mcfunction +4 -0
- package/dist/data/quiz/function/ask_question/else_1.mcfunction +5 -0
- package/dist/data/quiz/function/ask_question/else_4.mcfunction +5 -0
- package/dist/data/quiz/function/ask_question/else_7.mcfunction +4 -0
- package/dist/data/quiz/function/ask_question/merge_2.mcfunction +1 -0
- package/dist/data/quiz/function/ask_question/merge_5.mcfunction +2 -0
- package/dist/data/quiz/function/ask_question/merge_8.mcfunction +2 -0
- package/dist/data/quiz/function/ask_question/then_0.mcfunction +4 -0
- package/dist/data/quiz/function/ask_question/then_3.mcfunction +4 -0
- package/dist/data/quiz/function/ask_question/then_6.mcfunction +4 -0
- package/dist/data/quiz/function/ask_question.mcfunction +7 -0
- package/dist/data/quiz/function/finish_quiz.mcfunction +6 -0
- package/dist/data/quiz/function/handle_answer/else_1.mcfunction +5 -0
- package/dist/data/quiz/function/handle_answer/else_10.mcfunction +3 -0
- package/dist/data/quiz/function/handle_answer/else_16.mcfunction +3 -0
- package/dist/data/quiz/function/handle_answer/else_4.mcfunction +3 -0
- package/dist/data/quiz/function/handle_answer/else_7.mcfunction +5 -0
- package/dist/data/quiz/function/handle_answer/merge_11.mcfunction +2 -0
- package/dist/data/quiz/function/handle_answer/merge_14.mcfunction +2 -0
- package/dist/data/quiz/function/handle_answer/merge_17.mcfunction +2 -0
- package/dist/data/quiz/function/handle_answer/merge_2.mcfunction +8 -0
- package/dist/data/quiz/function/handle_answer/merge_5.mcfunction +2 -0
- package/dist/data/quiz/function/handle_answer/merge_8.mcfunction +2 -0
- package/dist/data/quiz/function/handle_answer/then_0.mcfunction +5 -0
- package/dist/data/quiz/function/handle_answer/then_12.mcfunction +5 -0
- package/dist/data/quiz/function/handle_answer/then_15.mcfunction +6 -0
- package/dist/data/quiz/function/handle_answer/then_3.mcfunction +6 -0
- package/dist/data/quiz/function/handle_answer/then_6.mcfunction +5 -0
- package/dist/data/quiz/function/handle_answer/then_9.mcfunction +6 -0
- package/dist/data/quiz/function/handle_answer.mcfunction +11 -0
- package/dist/data/quiz/function/start_quiz.mcfunction +5 -0
- package/dist/data/reqtest/function/__load.mcfunction +4 -0
- package/dist/data/reqtest/function/_table_init.mcfunction +2 -0
- package/dist/data/reqtest/function/no_trig.mcfunction +3 -0
- package/dist/data/reqtest/function/use_table.mcfunction +4 -0
- package/dist/data/reqtest2/function/__load.mcfunction +3 -0
- package/dist/data/reqtest2/function/no_trig.mcfunction +3 -0
- package/dist/data/runtime/function/__load.mcfunction +5 -0
- package/dist/data/runtime/function/__tick.mcfunction +2 -0
- package/dist/data/runtime/function/counter_tick/then_0.mcfunction +3 -0
- package/dist/data/runtime/function/counter_tick.mcfunction +13 -0
- package/dist/data/shop/function/__load.mcfunction +7 -0
- package/dist/data/shop/function/__tick.mcfunction +3 -0
- package/dist/data/shop/function/__trigger_shop_buy_dispatch.mcfunction +4 -0
- package/dist/data/shop/function/complete_purchase/else_1.mcfunction +5 -0
- package/dist/data/shop/function/complete_purchase/else_4.mcfunction +5 -0
- package/dist/data/shop/function/complete_purchase/else_7.mcfunction +3 -0
- package/dist/data/shop/function/complete_purchase/merge_2.mcfunction +2 -0
- package/dist/data/shop/function/complete_purchase/merge_5.mcfunction +2 -0
- package/dist/data/shop/function/complete_purchase/merge_8.mcfunction +2 -0
- package/dist/data/shop/function/complete_purchase/then_0.mcfunction +4 -0
- package/dist/data/shop/function/complete_purchase/then_3.mcfunction +4 -0
- package/dist/data/shop/function/complete_purchase/then_6.mcfunction +4 -0
- package/dist/data/shop/function/complete_purchase.mcfunction +7 -0
- package/dist/data/shop/function/handle_shop_trigger.mcfunction +3 -0
- package/dist/data/swap_test/function/__load.mcfunction +3 -0
- package/dist/data/swap_test/function/gcd_old/loop_body_1.mcfunction +7 -0
- package/dist/data/swap_test/function/gcd_old/loop_check_0.mcfunction +5 -0
- package/dist/data/swap_test/function/gcd_old/loop_exit_2.mcfunction +3 -0
- package/dist/data/swap_test/function/gcd_old.mcfunction +8 -0
- package/dist/data/turret/function/__load.mcfunction +5 -0
- package/dist/data/turret/function/__tick.mcfunction +4 -0
- package/dist/data/turret/function/__trigger_deploy_turret_dispatch.mcfunction +4 -0
- package/dist/data/turret/function/deploy_turret.mcfunction +8 -0
- package/dist/data/turret/function/turret_tick/at_1.mcfunction +2 -0
- package/dist/data/turret/function/turret_tick/foreach_0.mcfunction +2 -0
- package/dist/data/turret/function/turret_tick/foreach_2.mcfunction +2 -0
- package/dist/data/turret/function/turret_tick/tick_body.mcfunction +3 -0
- package/dist/data/turret/function/turret_tick/tick_skip.mcfunction +1 -0
- package/dist/data/turret/function/turret_tick.mcfunction +5 -0
- package/dist/gcd2.map.json +15 -0
- package/dist/gcd3.map.json +17 -0
- package/dist/gcd_test.map.json +15 -0
- package/dist/isqrttest.map.json +15 -0
- package/dist/lowering/index.js +71 -4
- package/dist/mathtest.map.json +6 -0
- package/dist/mypack.map.json +27 -0
- package/dist/pack.mcmeta +6 -0
- package/dist/reqtest.map.json +4 -0
- package/dist/reqtest2.map.json +4 -0
- package/dist/runtime/index.js +26 -5
- package/dist/runtime.map.json +7 -0
- package/dist/swap_test.map.json +14 -0
- package/editors/vscode/package-lock.json +2 -2
- package/editors/vscode/package.json +1 -1
- package/examples/showcase.mcrs +505 -0
- package/package.json +1 -1
- package/src/__tests__/stdlib-advanced.test.ts +120 -0
- package/src/__tests__/stdlib-bigint.test.ts +427 -0
- package/src/lowering/index.ts +75 -4
- package/src/runtime/index.ts +27 -5
- package/src/stdlib/advanced.mcrs +81 -0
- package/src/stdlib/bigint.mcrs +205 -0
- package/src/stdlib/math.mcrs +19 -6
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stdlib/bigint.mcrs — runtime behavioural tests
|
|
3
|
+
*
|
|
4
|
+
* Tests basic BigInt operations (base 10000, 8 limbs = 32 decimal digits).
|
|
5
|
+
* All arithmetic validated against known values.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'fs'
|
|
9
|
+
import * as path from 'path'
|
|
10
|
+
import { compile } from '../compile'
|
|
11
|
+
import { MCRuntime } from '../runtime'
|
|
12
|
+
|
|
13
|
+
const MATH_SRC = fs.readFileSync(path.join(__dirname, '../../src/stdlib/math.mcrs'), 'utf-8')
|
|
14
|
+
const BIGINT_SRC = fs.readFileSync(path.join(__dirname, '../../src/stdlib/bigint.mcrs'), 'utf-8')
|
|
15
|
+
|
|
16
|
+
function run(driver: string): MCRuntime {
|
|
17
|
+
const result = compile(driver, {
|
|
18
|
+
namespace: 'bitest',
|
|
19
|
+
librarySources: [MATH_SRC, BIGINT_SRC],
|
|
20
|
+
})
|
|
21
|
+
if (!result.success) throw new Error(result.error?.message ?? 'compile failed')
|
|
22
|
+
const rt = new MCRuntime('bitest')
|
|
23
|
+
for (const file of result.files ?? []) {
|
|
24
|
+
if (!file.path.endsWith('.mcfunction')) continue
|
|
25
|
+
const match = file.path.match(/data\/([^/]+)\/function\/(.+)\.mcfunction$/)
|
|
26
|
+
if (!match) continue
|
|
27
|
+
rt.loadFunction(`${match[1]}:${match[2]}`, file.content.split('\n'))
|
|
28
|
+
}
|
|
29
|
+
rt.load()
|
|
30
|
+
return rt
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function sc(rt: MCRuntime, key: string): number {
|
|
34
|
+
return rt.getScore('out', `bitest.${key}`) ?? 0
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ── storage_set_int roundtrip ─────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
describe('storage_set_int roundtrip', () => {
|
|
40
|
+
it('static index: write 99 to arr[2], read back', () => {
|
|
41
|
+
const rt = run(`fn test() {
|
|
42
|
+
storage_set_array("rs:t", "arr", "[10,20,30,40]");
|
|
43
|
+
storage_set_int("rs:t", "arr", 2, 99);
|
|
44
|
+
scoreboard_set("out", "r", storage_get_int("rs:t", "arr", 2));
|
|
45
|
+
}`)
|
|
46
|
+
rt.execFunction('test')
|
|
47
|
+
expect(sc(rt, 'r')).toBe(99)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('runtime index: write 777 to arr[idx], read back', () => {
|
|
51
|
+
const rt = run(`fn test() {
|
|
52
|
+
storage_set_array("rs:t", "arr", "[1,2,3,4,5]");
|
|
53
|
+
let idx: int = 3;
|
|
54
|
+
storage_set_int("rs:t", "arr", idx, 777);
|
|
55
|
+
scoreboard_set("out", "r", storage_get_int("rs:t", "arr", idx));
|
|
56
|
+
}`)
|
|
57
|
+
rt.execFunction('test')
|
|
58
|
+
expect(sc(rt, 'r')).toBe(777)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('loop write: fill arr[0..3] with 10,20,30,40', () => {
|
|
62
|
+
const rt = run(`fn test() {
|
|
63
|
+
storage_set_array("rs:t", "arr", "[0,0,0,0]");
|
|
64
|
+
let i: int = 0;
|
|
65
|
+
while (i < 4) {
|
|
66
|
+
storage_set_int("rs:t", "arr", i, (i + 1) * 10);
|
|
67
|
+
i = i + 1;
|
|
68
|
+
}
|
|
69
|
+
scoreboard_set("out", "a", storage_get_int("rs:t", "arr", 0));
|
|
70
|
+
scoreboard_set("out", "b", storage_get_int("rs:t", "arr", 1));
|
|
71
|
+
scoreboard_set("out", "c", storage_get_int("rs:t", "arr", 2));
|
|
72
|
+
scoreboard_set("out", "d", storage_get_int("rs:t", "arr", 3));
|
|
73
|
+
}`)
|
|
74
|
+
rt.execFunction('test')
|
|
75
|
+
expect(sc(rt, 'a')).toBe(10)
|
|
76
|
+
expect(sc(rt, 'b')).toBe(20)
|
|
77
|
+
expect(sc(rt, 'c')).toBe(30)
|
|
78
|
+
expect(sc(rt, 'd')).toBe(40)
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// ── bigint_init + from_int ────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
describe('bigint init and load', () => {
|
|
85
|
+
it('bigint_init zeros registers', () => {
|
|
86
|
+
const rt = run(`fn test() {
|
|
87
|
+
bigint_init();
|
|
88
|
+
scoreboard_set("out", "r", bigint_get_a(0));
|
|
89
|
+
}`)
|
|
90
|
+
rt.execFunction('test')
|
|
91
|
+
expect(sc(rt, 'r')).toBe(0)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('bigint_from_int_a(12345678): limb0=5678, limb1=1234', () => {
|
|
95
|
+
const rt = run(`fn test() {
|
|
96
|
+
bigint_init();
|
|
97
|
+
bigint_from_int_a(12345678);
|
|
98
|
+
scoreboard_set("out", "l0", bigint_get_a(0));
|
|
99
|
+
scoreboard_set("out", "l1", bigint_get_a(1));
|
|
100
|
+
scoreboard_set("out", "l2", bigint_get_a(2));
|
|
101
|
+
}`)
|
|
102
|
+
rt.execFunction('test')
|
|
103
|
+
expect(sc(rt, 'l0')).toBe(5678)
|
|
104
|
+
expect(sc(rt, 'l1')).toBe(1234)
|
|
105
|
+
expect(sc(rt, 'l2')).toBe(0)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('bigint_from_int_a(2000000000): limb0=0, limb1=0, limb2=20', () => {
|
|
109
|
+
// 2000000000 = 20 × 10000^2 (not 2000: 20 × 10^8 = 2×10^9 ✓)
|
|
110
|
+
const rt = run(`fn test() {
|
|
111
|
+
bigint_init();
|
|
112
|
+
bigint_from_int_a(2000000000);
|
|
113
|
+
scoreboard_set("out", "l0", bigint_get_a(0));
|
|
114
|
+
scoreboard_set("out", "l1", bigint_get_a(1));
|
|
115
|
+
scoreboard_set("out", "l2", bigint_get_a(2));
|
|
116
|
+
}`)
|
|
117
|
+
rt.execFunction('test')
|
|
118
|
+
expect(sc(rt, 'l0')).toBe(0)
|
|
119
|
+
expect(sc(rt, 'l1')).toBe(0)
|
|
120
|
+
expect(sc(rt, 'l2')).toBe(20)
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
// ── bigint_add ────────────────────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
describe('bigint_add', () => {
|
|
127
|
+
it('1 + 1 = 2 (simple)', () => {
|
|
128
|
+
const rt = run(`fn test() {
|
|
129
|
+
bigint_init();
|
|
130
|
+
bigint_from_int_a(1);
|
|
131
|
+
bigint_from_int_b(1);
|
|
132
|
+
bigint_add();
|
|
133
|
+
scoreboard_set("out", "r", bigint_get_c(0));
|
|
134
|
+
}`)
|
|
135
|
+
rt.execFunction('test')
|
|
136
|
+
expect(sc(rt, 'r')).toBe(2)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('9999 + 1 = 10000: carry across limb boundary', () => {
|
|
140
|
+
// a=[9999,0,...] + b=[1,0,...] = c=[0,1,0,...] (carry to limb1)
|
|
141
|
+
const rt = run(`fn test() {
|
|
142
|
+
bigint_init();
|
|
143
|
+
bigint_from_int_a(9999);
|
|
144
|
+
bigint_from_int_b(1);
|
|
145
|
+
bigint_add();
|
|
146
|
+
scoreboard_set("out", "l0", bigint_get_c(0));
|
|
147
|
+
scoreboard_set("out", "l1", bigint_get_c(1));
|
|
148
|
+
}`)
|
|
149
|
+
rt.execFunction('test')
|
|
150
|
+
expect(sc(rt, 'l0')).toBe(0)
|
|
151
|
+
expect(sc(rt, 'l1')).toBe(1)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it('99990000 + 10000 = 100000000: multi-limb carry', () => {
|
|
155
|
+
// a=[0,9999,...] + b=[0,1,...] = c=[0,0,1,...] (carry to limb2)
|
|
156
|
+
const rt = run(`fn test() {
|
|
157
|
+
bigint_init();
|
|
158
|
+
bigint_from_int_a(99990000);
|
|
159
|
+
bigint_from_int_b(10000);
|
|
160
|
+
bigint_add();
|
|
161
|
+
scoreboard_set("out", "l0", bigint_get_c(0));
|
|
162
|
+
scoreboard_set("out", "l1", bigint_get_c(1));
|
|
163
|
+
scoreboard_set("out", "l2", bigint_get_c(2));
|
|
164
|
+
}`)
|
|
165
|
+
rt.execFunction('test')
|
|
166
|
+
expect(sc(rt, 'l0')).toBe(0)
|
|
167
|
+
expect(sc(rt, 'l1')).toBe(0)
|
|
168
|
+
expect(sc(rt, 'l2')).toBe(1)
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('large add: 999999999 + 999999999', () => {
|
|
172
|
+
// 999999999 = [9999, 9999, 9, 0, ...]
|
|
173
|
+
// + same = [9998, 9999, 18, 0, ...] after carry
|
|
174
|
+
// = 1999999998: l0=9998, l1=9999, l2=19 (carry: 9+9=18, no carry from l2)
|
|
175
|
+
// Wait: l0=9999+9999=19998, carry=1, l0=9998
|
|
176
|
+
// l1=9999+9999+1=19999, carry=1, l1=9999
|
|
177
|
+
// l2=9+9+1=19, carry=0, l2=19
|
|
178
|
+
const rt = run(`fn test() {
|
|
179
|
+
bigint_init();
|
|
180
|
+
bigint_from_int_a(999999999);
|
|
181
|
+
bigint_from_int_b(999999999);
|
|
182
|
+
bigint_add();
|
|
183
|
+
scoreboard_set("out", "l0", bigint_get_c(0));
|
|
184
|
+
scoreboard_set("out", "l1", bigint_get_c(1));
|
|
185
|
+
scoreboard_set("out", "l2", bigint_get_c(2));
|
|
186
|
+
}`)
|
|
187
|
+
rt.execFunction('test')
|
|
188
|
+
expect(sc(rt, 'l0')).toBe(9998) // 1999999998 % 10000 = 9998
|
|
189
|
+
expect(sc(rt, 'l1')).toBe(9999) // floor(1999999998 / 10000) % 10000 = 9999
|
|
190
|
+
expect(sc(rt, 'l2')).toBe(19) // floor(1999999998 / 100000000) = 19
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
// ── bigint_sub ────────────────────────────────────────────────────────────────
|
|
195
|
+
|
|
196
|
+
describe('bigint_sub', () => {
|
|
197
|
+
it('10 - 3 = 7', () => {
|
|
198
|
+
const rt = run(`fn test() {
|
|
199
|
+
bigint_init();
|
|
200
|
+
bigint_from_int_a(10);
|
|
201
|
+
bigint_from_int_b(3);
|
|
202
|
+
bigint_sub();
|
|
203
|
+
scoreboard_set("out", "r", bigint_get_c(0));
|
|
204
|
+
}`)
|
|
205
|
+
rt.execFunction('test')
|
|
206
|
+
expect(sc(rt, 'r')).toBe(7)
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
it('10000 - 1 = 9999: borrow across limb boundary', () => {
|
|
210
|
+
// a=[0,1,...] - b=[1,0,...] = c=[9999,0,...] (borrow from limb1)
|
|
211
|
+
const rt = run(`fn test() {
|
|
212
|
+
bigint_init();
|
|
213
|
+
bigint_from_int_a(10000);
|
|
214
|
+
bigint_from_int_b(1);
|
|
215
|
+
bigint_sub();
|
|
216
|
+
scoreboard_set("out", "l0", bigint_get_c(0));
|
|
217
|
+
scoreboard_set("out", "l1", bigint_get_c(1));
|
|
218
|
+
}`)
|
|
219
|
+
rt.execFunction('test')
|
|
220
|
+
expect(sc(rt, 'l0')).toBe(9999)
|
|
221
|
+
expect(sc(rt, 'l1')).toBe(0)
|
|
222
|
+
})
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
// ── bigint_compare ────────────────────────────────────────────────────────────
|
|
226
|
+
|
|
227
|
+
describe('bigint_compare', () => {
|
|
228
|
+
it('1 == 1 → 0', () => {
|
|
229
|
+
const rt = run(`fn test() {
|
|
230
|
+
bigint_init();
|
|
231
|
+
bigint_from_int_a(1);
|
|
232
|
+
bigint_from_int_b(1);
|
|
233
|
+
scoreboard_set("out", "r", bigint_compare());
|
|
234
|
+
}`)
|
|
235
|
+
rt.execFunction('test')
|
|
236
|
+
expect(sc(rt, 'r')).toBe(0)
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
it('2 > 1 → 1', () => {
|
|
240
|
+
const rt = run(`fn test() {
|
|
241
|
+
bigint_init();
|
|
242
|
+
bigint_from_int_a(2);
|
|
243
|
+
bigint_from_int_b(1);
|
|
244
|
+
scoreboard_set("out", "r", bigint_compare());
|
|
245
|
+
}`)
|
|
246
|
+
rt.execFunction('test')
|
|
247
|
+
expect(sc(rt, 'r')).toBe(1)
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
it('1 < 2 → -1', () => {
|
|
251
|
+
const rt = run(`fn test() {
|
|
252
|
+
bigint_init();
|
|
253
|
+
bigint_from_int_a(1);
|
|
254
|
+
bigint_from_int_b(2);
|
|
255
|
+
scoreboard_set("out", "r", bigint_compare());
|
|
256
|
+
}`)
|
|
257
|
+
rt.execFunction('test')
|
|
258
|
+
expect(sc(rt, 'r')).toBe(-1)
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
// ── bigint_mul_small ──────────────────────────────────────────────────────────
|
|
263
|
+
|
|
264
|
+
describe('bigint_mul_small', () => {
|
|
265
|
+
it('12345 * 2 = 24690', () => {
|
|
266
|
+
const rt = run(`fn test() {
|
|
267
|
+
bigint_init();
|
|
268
|
+
bigint_from_int_a(12345);
|
|
269
|
+
bigint_mul_small(2);
|
|
270
|
+
scoreboard_set("out", "l0", bigint_get_c(0));
|
|
271
|
+
scoreboard_set("out", "l1", bigint_get_c(1));
|
|
272
|
+
}`)
|
|
273
|
+
rt.execFunction('test')
|
|
274
|
+
expect(sc(rt, 'l0')).toBe(4690)
|
|
275
|
+
expect(sc(rt, 'l1')).toBe(2)
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
it('9999 * 9999 = 99980001: carry', () => {
|
|
279
|
+
// c[0] = 99980001 % 10000 = 1
|
|
280
|
+
// c[1] = floor(99980001 / 10000) = 9998
|
|
281
|
+
const rt = run(`fn test() {
|
|
282
|
+
bigint_init();
|
|
283
|
+
bigint_from_int_a(9999);
|
|
284
|
+
bigint_mul_small(9999);
|
|
285
|
+
scoreboard_set("out", "l0", bigint_get_c(0));
|
|
286
|
+
scoreboard_set("out", "l1", bigint_get_c(1));
|
|
287
|
+
}`)
|
|
288
|
+
rt.execFunction('test')
|
|
289
|
+
expect(sc(rt, 'l0')).toBe(1) // 99980001 % 10000 = 1 (actually 0001)
|
|
290
|
+
expect(sc(rt, 'l1')).toBe(9998) // 9998
|
|
291
|
+
})
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
// ── bigint_mul ────────────────────────────────────────────────────────────────
|
|
295
|
+
|
|
296
|
+
describe('bigint_mul', () => {
|
|
297
|
+
it('3 * 4 = 12', () => {
|
|
298
|
+
const rt = run(`fn test() {
|
|
299
|
+
bigint_init();
|
|
300
|
+
bigint_from_int_a(3);
|
|
301
|
+
bigint_from_int_b(4);
|
|
302
|
+
bigint_mul();
|
|
303
|
+
scoreboard_set("out", "r", bigint_get_c(0));
|
|
304
|
+
}`)
|
|
305
|
+
rt.execFunction('test')
|
|
306
|
+
expect(sc(rt, 'r')).toBe(12)
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
it('9999 * 9999 = 99980001', () => {
|
|
310
|
+
const rt = run(`fn test() {
|
|
311
|
+
bigint_init();
|
|
312
|
+
bigint_from_int_a(9999);
|
|
313
|
+
bigint_from_int_b(9999);
|
|
314
|
+
bigint_mul();
|
|
315
|
+
scoreboard_set("out", "l0", bigint_get_c(0));
|
|
316
|
+
scoreboard_set("out", "l1", bigint_get_c(1));
|
|
317
|
+
}`)
|
|
318
|
+
rt.execFunction('test')
|
|
319
|
+
expect(sc(rt, 'l0')).toBe(1)
|
|
320
|
+
expect(sc(rt, 'l1')).toBe(9998)
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
it('100000 * 100000 = 10^10: spans 3 limbs', () => {
|
|
324
|
+
// 10^10 = [0, 0, 0, 1, 0, ...] in base 10000 (1 * 10000^3 = 10^12? no)
|
|
325
|
+
// 10^10 / 10000^0 % 10000 = 0
|
|
326
|
+
// 10^10 / 10000^1 % 10000 = 0
|
|
327
|
+
// 10^10 / 10000^2 % 10000 = 10000 → wait: 10^10 / 10^8 = 100, 100 % 10000 = 100
|
|
328
|
+
// Actually: 10^10 = 100 * 10^8 = 100 * (10^4)^2
|
|
329
|
+
// l0 = 10^10 % 10^4 = 0
|
|
330
|
+
// l1 = floor(10^10 / 10^4) % 10^4 = floor(10^6) % 10000 = 0
|
|
331
|
+
// l2 = floor(10^10 / 10^8) % 10^4 = 100 % 10000 = 100
|
|
332
|
+
const rt = run(`fn test() {
|
|
333
|
+
bigint_init();
|
|
334
|
+
bigint_from_int_a(100000);
|
|
335
|
+
bigint_from_int_b(100000);
|
|
336
|
+
bigint_mul();
|
|
337
|
+
scoreboard_set("out", "l0", bigint_get_c(0));
|
|
338
|
+
scoreboard_set("out", "l1", bigint_get_c(1));
|
|
339
|
+
scoreboard_set("out", "l2", bigint_get_c(2));
|
|
340
|
+
}`)
|
|
341
|
+
rt.execFunction('test')
|
|
342
|
+
expect(sc(rt, 'l0')).toBe(0)
|
|
343
|
+
expect(sc(rt, 'l1')).toBe(0)
|
|
344
|
+
expect(sc(rt, 'l2')).toBe(100)
|
|
345
|
+
})
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
// ── bigint_fib ────────────────────────────────────────────────────────────────
|
|
349
|
+
|
|
350
|
+
describe('bigint_fib', () => {
|
|
351
|
+
it('F(0) = 0', () => {
|
|
352
|
+
const rt = run(`fn test() {
|
|
353
|
+
bigint_fib(0);
|
|
354
|
+
scoreboard_set("out", "r", bigint_get_a(0));
|
|
355
|
+
}`)
|
|
356
|
+
rt.execFunction('test')
|
|
357
|
+
expect(sc(rt, 'r')).toBe(0)
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
it('F(1) = 1', () => {
|
|
361
|
+
const rt = run(`fn test() {
|
|
362
|
+
bigint_fib(1);
|
|
363
|
+
scoreboard_set("out", "r", bigint_get_a(0));
|
|
364
|
+
}`)
|
|
365
|
+
rt.execFunction('test')
|
|
366
|
+
expect(sc(rt, 'r')).toBe(1)
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
it('F(10) = 55', () => {
|
|
370
|
+
const rt = run(`fn test() {
|
|
371
|
+
bigint_fib(10);
|
|
372
|
+
scoreboard_set("out", "r", bigint_get_a(0));
|
|
373
|
+
}`)
|
|
374
|
+
rt.execFunction('test')
|
|
375
|
+
expect(sc(rt, 'r')).toBe(55)
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
it('F(20) = 6765', () => {
|
|
379
|
+
const rt = run(`fn test() {
|
|
380
|
+
bigint_fib(20);
|
|
381
|
+
scoreboard_set("out", "r", bigint_get_a(0));
|
|
382
|
+
}`)
|
|
383
|
+
rt.execFunction('test')
|
|
384
|
+
expect(sc(rt, 'r')).toBe(6765)
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
it('F(50) = 12586269025: limb0=9025, limb1=8626, limb2=125', () => {
|
|
388
|
+
// F(50) = 12,586,269,025
|
|
389
|
+
// 12586269025 % 10000 = 9025
|
|
390
|
+
// floor(12586269025 / 10000) % 10000 = 1258626 % 10000 = 8626
|
|
391
|
+
// floor(12586269025 / 10^8) = 125
|
|
392
|
+
const rt = run(`fn test() {
|
|
393
|
+
bigint_fib(50);
|
|
394
|
+
scoreboard_set("out", "l0", bigint_get_a(0));
|
|
395
|
+
scoreboard_set("out", "l1", bigint_get_a(1));
|
|
396
|
+
scoreboard_set("out", "l2", bigint_get_a(2));
|
|
397
|
+
}`)
|
|
398
|
+
rt.execFunction('test')
|
|
399
|
+
expect(sc(rt, 'l0')).toBe(9025)
|
|
400
|
+
expect(sc(rt, 'l1')).toBe(8626)
|
|
401
|
+
expect(sc(rt, 'l2')).toBe(125)
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
it('F(100) low limbs check', () => {
|
|
405
|
+
// F(100) = 354224848179261915075
|
|
406
|
+
// % 10000 = 5075
|
|
407
|
+
// floor / 10000 % 10000 = floor(35422484817926191.5075) % 10000 = ...
|
|
408
|
+
// Let's compute:
|
|
409
|
+
// 354224848179261915075 % 10000 = 5075
|
|
410
|
+
// floor(354224848179261915075 / 10000) = 35422484817926191507 (JS BigInt)
|
|
411
|
+
// 35422484817926191507 % 10000 = 1507
|
|
412
|
+
// floor(35422484817926191507 / 10000) = 3542248481792619 (roughly)
|
|
413
|
+
// % 10000 = 2619
|
|
414
|
+
const rt = run(`fn test() {
|
|
415
|
+
bigint_fib(100);
|
|
416
|
+
scoreboard_set("out", "l0", bigint_get_a(0));
|
|
417
|
+
scoreboard_set("out", "l1", bigint_get_a(1));
|
|
418
|
+
scoreboard_set("out", "l2", bigint_get_a(2));
|
|
419
|
+
}`)
|
|
420
|
+
rt.execFunction('test')
|
|
421
|
+
const f100 = BigInt('354224848179261915075')
|
|
422
|
+
const b = BigInt(10000)
|
|
423
|
+
expect(sc(rt, 'l0')).toBe(Number(f100 % b))
|
|
424
|
+
expect(sc(rt, 'l1')).toBe(Number((f100 / b) % b))
|
|
425
|
+
expect(sc(rt, 'l2')).toBe(Number((f100 / b / b) % b))
|
|
426
|
+
})
|
|
427
|
+
})
|
package/src/lowering/index.ts
CHANGED
|
@@ -108,6 +108,7 @@ const BUILTINS: Record<string, (args: string[]) => string | null> = {
|
|
|
108
108
|
clearInterval: () => null, // Special handling
|
|
109
109
|
storage_get_int: () => null, // Special handling (dynamic NBT array read via macro)
|
|
110
110
|
storage_set_array: () => null, // Special handling (write literal NBT array to storage)
|
|
111
|
+
storage_set_int: () => null, // Special handling (dynamic NBT array write via macro)
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
export interface Warning {
|
|
@@ -364,12 +365,24 @@ export class Lowering {
|
|
|
364
365
|
|
|
365
366
|
private preScanExpr(expr: Expr, paramNames: Set<string>, macroParams: Set<string>): void {
|
|
366
367
|
if (expr.kind === 'call' && BUILTINS[expr.fn] !== undefined) {
|
|
367
|
-
//
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
368
|
+
// Only trigger macro param detection for builtins that actually emit
|
|
369
|
+
// MC commands with $(param) inline in the current function body.
|
|
370
|
+
// Special-handled builtins (storage_get_int / storage_set_int / etc.) are
|
|
371
|
+
// declared as `() => null` — they create their own sub-functions for macro
|
|
372
|
+
// indirection and do NOT require the surrounding function to be a macro.
|
|
373
|
+
const handler = BUILTINS[expr.fn]!
|
|
374
|
+
const isSpecialHandled: boolean = (() => {
|
|
375
|
+
try { return (handler as () => string | null)() === null } catch { return false }
|
|
376
|
+
})()
|
|
377
|
+
if (!isSpecialHandled) {
|
|
378
|
+
for (const arg of expr.args) {
|
|
379
|
+
if (arg.kind === 'ident' && paramNames.has(arg.name)) {
|
|
380
|
+
macroParams.add(arg.name)
|
|
381
|
+
}
|
|
371
382
|
}
|
|
372
383
|
}
|
|
384
|
+
// Always recurse into args for nested calls/expressions
|
|
385
|
+
for (const arg of expr.args) this.preScanExpr(arg, paramNames, macroParams)
|
|
373
386
|
return
|
|
374
387
|
}
|
|
375
388
|
// Recurse into sub-expressions for other call types
|
|
@@ -2629,6 +2642,64 @@ export class Lowering {
|
|
|
2629
2642
|
return { kind: 'const', value: 0 }
|
|
2630
2643
|
}
|
|
2631
2644
|
|
|
2645
|
+
// storage_set_int(storage_ns, array_key, index, value) -> void
|
|
2646
|
+
// Writes one integer element into an NBT int-array stored in data storage.
|
|
2647
|
+
// storage_ns : e.g. "rs:bigint"
|
|
2648
|
+
// array_key : e.g. "a"
|
|
2649
|
+
// index : element index (const or runtime)
|
|
2650
|
+
// value : integer value to write (const or runtime)
|
|
2651
|
+
//
|
|
2652
|
+
// Const index + const value:
|
|
2653
|
+
// execute store result storage <ns> <key>[N] int 1 run scoreboard players set $const_V rs V
|
|
2654
|
+
// Runtime index or value: macro sub-function via rs:heap
|
|
2655
|
+
if (name === 'storage_set_int') {
|
|
2656
|
+
const storageNs = this.exprToString(args[0])
|
|
2657
|
+
const arrayKey = this.exprToString(args[1])
|
|
2658
|
+
const indexOperand = this.lowerExpr(args[2])
|
|
2659
|
+
const valueOperand = this.lowerExpr(args[3])
|
|
2660
|
+
|
|
2661
|
+
if (indexOperand.kind === 'const') {
|
|
2662
|
+
// Static index — use execute store result to write to the fixed slot
|
|
2663
|
+
const valVar = valueOperand.kind === 'var'
|
|
2664
|
+
? valueOperand.name
|
|
2665
|
+
: this.operandToVar(valueOperand)
|
|
2666
|
+
this.builder.emitRaw(
|
|
2667
|
+
`execute store result storage ${storageNs} ${arrayKey}[${indexOperand.value}] int 1 run scoreboard players get ${valVar} rs`
|
|
2668
|
+
)
|
|
2669
|
+
} else {
|
|
2670
|
+
// Runtime index: we need a macro sub-function.
|
|
2671
|
+
// Store index + value into rs:heap, call macro that does:
|
|
2672
|
+
// $data modify storage <ns> <key>[$(idx_key)] set value $(val_key)
|
|
2673
|
+
const macroIdxKey = `__ssi_i_${this.foreachCounter++}`
|
|
2674
|
+
const macroValKey = `__ssi_v_${this.foreachCounter++}` // kept to pin valVar in optimizer
|
|
2675
|
+
const subFnName = `${this.currentFn}/__ssi_${this.foreachCounter++}`
|
|
2676
|
+
const indexVar = indexOperand.kind === 'var'
|
|
2677
|
+
? indexOperand.name
|
|
2678
|
+
: this.operandToVar(indexOperand)
|
|
2679
|
+
const valVar = valueOperand.kind === 'var'
|
|
2680
|
+
? valueOperand.name
|
|
2681
|
+
: this.operandToVar(valueOperand)
|
|
2682
|
+
this.builder.emitRaw(
|
|
2683
|
+
`execute store result storage rs:heap ${macroIdxKey} int 1 run scoreboard players get ${indexVar} rs`
|
|
2684
|
+
)
|
|
2685
|
+
// Pin valVar in the optimizer's read-set so the assignment is not dead-code-eliminated.
|
|
2686
|
+
// The value is stored to rs:heap but NOT used by the macro (the macro reads the scoreboard
|
|
2687
|
+
// slot directly to avoid the MC 'data modify set value $(n)' macro substitution bug).
|
|
2688
|
+
this.builder.emitRaw(
|
|
2689
|
+
`execute store result storage rs:heap ${macroValKey} int 1 run scoreboard players get ${valVar} rs`
|
|
2690
|
+
)
|
|
2691
|
+
this.builder.emitRaw(`function ${this.namespace}:${subFnName} with storage rs:heap`)
|
|
2692
|
+
// Use execute store result (not 'data modify set value $(val)') to avoid MC macro
|
|
2693
|
+
// substitution bugs with numeric values. The scoreboard slot ${valVar} is hardcoded
|
|
2694
|
+
// into the macro sub-function at compile time — only the array index is macro-substituted.
|
|
2695
|
+
this.emitRawSubFunction(
|
|
2696
|
+
subFnName,
|
|
2697
|
+
`\x01execute store result storage ${storageNs} ${arrayKey}[$(${macroIdxKey})] int 1 run scoreboard players get ${valVar} rs`
|
|
2698
|
+
)
|
|
2699
|
+
}
|
|
2700
|
+
return { kind: 'const', value: 0 }
|
|
2701
|
+
}
|
|
2702
|
+
|
|
2632
2703
|
// data_merge(target, nbt) — merge NBT into entity/block/storage
|
|
2633
2704
|
// data_merge(@s, { Invisible: 1b, Silent: 1b })
|
|
2634
2705
|
if (name === 'data_merge') {
|
package/src/runtime/index.ts
CHANGED
|
@@ -697,17 +697,27 @@ export class MCRuntime {
|
|
|
697
697
|
continue
|
|
698
698
|
}
|
|
699
699
|
|
|
700
|
-
// Handle 'store result storage <ns:path> <field> <type> <scale>'
|
|
700
|
+
// Handle 'store result storage <ns:path> <field>[<idx>] <type> <scale>' (array element)
|
|
701
701
|
if (rest.startsWith('store result storage ')) {
|
|
702
|
-
|
|
703
|
-
//
|
|
704
|
-
|
|
702
|
+
const sliced = rest.slice(21)
|
|
703
|
+
// Try array-index form first: <ns:path> <field>[<idx>] <type> <scale> <run-cmd>
|
|
704
|
+
// Use [^\[\s]+ for field (no brackets or spaces) so that \[ matches correctly.
|
|
705
|
+
const arrParts = sliced.match(/^(\S+)\s+([^\[\s]+)\[(\d+)\]\s+(\S+)\s+([\d.]+)\s+(.*)$/)
|
|
706
|
+
if (arrParts) {
|
|
707
|
+
const [, storagePath, field, indexStr, , , remaining] = arrParts
|
|
708
|
+
storeTarget = { storagePath, field: `${field}[${indexStr}]`, type: 'result' }
|
|
709
|
+
rest = remaining.trim()
|
|
710
|
+
continue
|
|
711
|
+
}
|
|
712
|
+
// Plain form: <ns:path> <field> <type> <scale> <run-cmd>
|
|
713
|
+
const storageParts = sliced.match(/^(\S+)\s+(\S+)\s+(\S+)\s+([\d.]+)\s+(.*)$/)
|
|
705
714
|
if (storageParts) {
|
|
706
715
|
const [, storagePath, field, , , remaining] = storageParts
|
|
707
716
|
storeTarget = { storagePath, field, type: 'result' }
|
|
708
717
|
rest = remaining.trim()
|
|
709
718
|
continue
|
|
710
719
|
}
|
|
720
|
+
rest = sliced
|
|
711
721
|
}
|
|
712
722
|
|
|
713
723
|
// Handle 'store result score <player> <obj>'
|
|
@@ -834,8 +844,20 @@ export class MCRuntime {
|
|
|
834
844
|
return true
|
|
835
845
|
}
|
|
836
846
|
|
|
847
|
+
// data modify storage <ns:path> <field>[<index>] set value <val> (array element write)
|
|
848
|
+
const setArrMatch = cmd.match(/^data modify storage (\S+) ([^\[\s]+)\[(\d+)\] set value (.+)$/)
|
|
849
|
+
if (setArrMatch) {
|
|
850
|
+
const [, storagePath, field, indexStr, valueStr] = setArrMatch
|
|
851
|
+
const arr = this.getStorageField(storagePath, field)
|
|
852
|
+
const idx = parseInt(indexStr, 10)
|
|
853
|
+
if (Array.isArray(arr) && idx >= 0 && idx < arr.length) {
|
|
854
|
+
arr[idx] = this.parseDataValue(valueStr)
|
|
855
|
+
}
|
|
856
|
+
return true
|
|
857
|
+
}
|
|
858
|
+
|
|
837
859
|
// data get storage <ns:path> <field>[<index>] [scale] (array element access)
|
|
838
|
-
const getArrMatch = cmd.match(/^data get storage (\S+) (\
|
|
860
|
+
const getArrMatch = cmd.match(/^data get storage (\S+) ([^\[\s]+)\[(\d+)\](?:\s+[\d.]+)?$/)
|
|
839
861
|
if (getArrMatch) {
|
|
840
862
|
const [, storagePath, field, indexStr] = getArrMatch
|
|
841
863
|
const arr = this.getStorageField(storagePath, field)
|
package/src/stdlib/advanced.mcrs
CHANGED
|
@@ -247,3 +247,84 @@ fn julia_iter(z0r: int, z0i: int, cr: int, ci: int, max_iter: int) -> int {
|
|
|
247
247
|
}
|
|
248
248
|
return max_iter;
|
|
249
249
|
}
|
|
250
|
+
|
|
251
|
+
// ─── Category 5: Geometry experiments ────────────────────────────────────────
|
|
252
|
+
|
|
253
|
+
// Angle between two 2D vectors in degrees (×1 = degrees, unsigned 0..180).
|
|
254
|
+
// Strategy: normalize both vectors to unit (×1000 components), then
|
|
255
|
+
// angle = atan2(|cross|, dot) where cross,dot are from the unit vectors.
|
|
256
|
+
// Unit vector components are ≤ 1000, so cross/dot ≤ 10^6 (no overflow).
|
|
257
|
+
// After dividing by 1000 to re-scale, pass to atan2_fixed.
|
|
258
|
+
//
|
|
259
|
+
// angle_between(1000, 0, 0, 1000) == 90
|
|
260
|
+
// angle_between(1000, 0, 1000, 0) == 0
|
|
261
|
+
// angle_between(1000, 0, -1000, 0) == 180
|
|
262
|
+
fn angle_between(x1: int, y1: int, x2: int, y2: int) -> int {
|
|
263
|
+
let nx1: int = normalize2d_x(x1, y1);
|
|
264
|
+
let ny1: int = normalize2d_y(x1, y1);
|
|
265
|
+
let nx2: int = normalize2d_x(x2, y2);
|
|
266
|
+
let ny2: int = normalize2d_y(x2, y2);
|
|
267
|
+
if (nx1 == 0 && ny1 == 0) { return 0; }
|
|
268
|
+
if (nx2 == 0 && ny2 == 0) { return 0; }
|
|
269
|
+
// dot and cross of unit vectors (components ×1000 → result ×1000000)
|
|
270
|
+
let d: int = dot2d(nx1, ny1, nx2, ny2) / 1000; // cos × 1000
|
|
271
|
+
let c: int = abs(cross2d(nx1, ny1, nx2, ny2)) / 1000; // |sin| × 1000
|
|
272
|
+
return atan2_fixed(c, d);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Clamp a 2D point to lie within a circle of radius r centred at origin.
|
|
276
|
+
// r is in the same units as x, y (raw block coords, NOT fixed-point).
|
|
277
|
+
// Returns the clamped x component.
|
|
278
|
+
// Note: keep x, y < ~2000 to avoid overflow in normalize2d_x (x * 10^6).
|
|
279
|
+
//
|
|
280
|
+
// clamp_circle_x(3, 4, 10) == 3 (point at dist 5, inside r=10)
|
|
281
|
+
// clamp_circle_x(600, 0, 500) == 500
|
|
282
|
+
fn clamp_circle_x(x: int, y: int, r: int) -> int {
|
|
283
|
+
// length2d_fixed returns dist × 1000; compare with r × 1000
|
|
284
|
+
let dist: int = length2d_fixed(x, y);
|
|
285
|
+
if (dist <= r * 1000) { return x; }
|
|
286
|
+
return normalize2d_x(x, y) * r / 1000;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Y component of circle clamp.
|
|
290
|
+
// clamp_circle_y(0, 600, 500) == 500
|
|
291
|
+
fn clamp_circle_y(x: int, y: int, r: int) -> int {
|
|
292
|
+
let dist: int = length2d_fixed(x, y);
|
|
293
|
+
if (dist <= r * 1000) { return y; }
|
|
294
|
+
return normalize2d_y(x, y) * r / 1000;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Newton's method integer square root (alternative to isqrt).
|
|
298
|
+
// Converges quadratically; validates our while loop + division.
|
|
299
|
+
// newton_sqrt(25) == 5, newton_sqrt(100) == 10, newton_sqrt(2) == 1
|
|
300
|
+
fn newton_sqrt(n: int) -> int {
|
|
301
|
+
if (n <= 0) { return 0; }
|
|
302
|
+
if (n == 1) { return 1; }
|
|
303
|
+
let x: int = n / 2;
|
|
304
|
+
let prev: int = 0;
|
|
305
|
+
while (x != prev) {
|
|
306
|
+
prev = x;
|
|
307
|
+
x = (x + n / x) / 2;
|
|
308
|
+
}
|
|
309
|
+
return x;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Digital root: repeatedly sum digits until single digit.
|
|
313
|
+
// digital_root(493) == 7 (4+9+3=16 → 1+6=7)
|
|
314
|
+
// digital_root(9) == 9
|
|
315
|
+
// digital_root(0) == 0
|
|
316
|
+
fn digital_root(n: int) -> int {
|
|
317
|
+
if (n == 0) { return 0; }
|
|
318
|
+
let r: int = n % 9;
|
|
319
|
+
if (r == 0) { return 9; }
|
|
320
|
+
return r;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Ulam spiral ring number: which concentric square ring is n on?
|
|
324
|
+
// Ring 0: n=1; Ring 1: n=2..9 (3×3 square); Ring 2: n=10..25 (5×5); etc.
|
|
325
|
+
// Formula: floor((isqrt(n-1) + 1) / 2)
|
|
326
|
+
// spiral_ring(1) == 0, spiral_ring(9) == 1, spiral_ring(25) == 2
|
|
327
|
+
fn spiral_ring(n: int) -> int {
|
|
328
|
+
if (n <= 1) { return 0; }
|
|
329
|
+
return (isqrt(n - 1) + 1) / 2;
|
|
330
|
+
}
|