redscript-mc 1.2.26 → 1.2.28

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 (243) hide show
  1. package/README.md +78 -9
  2. package/README.zh.md +72 -4
  3. package/dist/__tests__/cli.test.js +13 -13
  4. package/dist/__tests__/optimizer-advanced.test.js +4 -4
  5. package/dist/__tests__/stdlib-advanced.test.js +114 -0
  6. package/dist/__tests__/stdlib-bigint.test.d.ts +7 -0
  7. package/dist/__tests__/stdlib-bigint.test.js +428 -0
  8. package/dist/cli.js +13 -5
  9. package/dist/codegen/mcfunction/index.d.ts +4 -0
  10. package/dist/codegen/mcfunction/index.js +9 -4
  11. package/dist/compile.d.ts +4 -0
  12. package/dist/compile.js +9 -1
  13. package/dist/data/arena/function/__load.mcfunction +6 -0
  14. package/dist/data/arena/function/__tick.mcfunction +2 -0
  15. package/dist/data/arena/function/announce_leaders/else_1.mcfunction +3 -0
  16. package/dist/data/arena/function/announce_leaders/foreach_0/merge_2.mcfunction +1 -0
  17. package/dist/data/arena/function/announce_leaders/foreach_0/then_0.mcfunction +3 -0
  18. package/dist/data/arena/function/announce_leaders/foreach_0.mcfunction +7 -0
  19. package/dist/data/arena/function/announce_leaders/foreach_1/merge_2.mcfunction +1 -0
  20. package/dist/data/arena/function/announce_leaders/foreach_1/then_0.mcfunction +4 -0
  21. package/dist/data/arena/function/announce_leaders/foreach_1.mcfunction +6 -0
  22. package/dist/data/arena/function/announce_leaders/merge_2.mcfunction +1 -0
  23. package/dist/data/arena/function/announce_leaders/then_0.mcfunction +4 -0
  24. package/dist/data/arena/function/announce_leaders.mcfunction +6 -0
  25. package/dist/data/arena/function/arena_tick/merge_2.mcfunction +1 -0
  26. package/dist/data/arena/function/arena_tick/then_0.mcfunction +4 -0
  27. package/dist/data/arena/function/arena_tick.mcfunction +11 -0
  28. package/dist/data/counter/function/__load.mcfunction +5 -0
  29. package/dist/data/counter/function/__tick.mcfunction +2 -0
  30. package/dist/data/counter/function/counter_tick/merge_2.mcfunction +1 -0
  31. package/dist/data/counter/function/counter_tick/then_0.mcfunction +3 -0
  32. package/dist/data/counter/function/counter_tick.mcfunction +11 -0
  33. package/dist/data/gcd2/function/__load.mcfunction +3 -0
  34. package/dist/data/gcd2/function/abs/merge_2.mcfunction +3 -0
  35. package/dist/data/gcd2/function/abs/then_0.mcfunction +5 -0
  36. package/dist/data/gcd2/function/abs.mcfunction +7 -0
  37. package/dist/data/gcd2/function/gcd/loop_body_1.mcfunction +7 -0
  38. package/dist/data/gcd2/function/gcd/loop_check_0.mcfunction +5 -0
  39. package/dist/data/gcd2/function/gcd/loop_exit_2.mcfunction +3 -0
  40. package/dist/data/gcd2/function/gcd.mcfunction +14 -0
  41. package/dist/data/gcd3/function/__load.mcfunction +3 -0
  42. package/dist/data/gcd3/function/abs/merge_2.mcfunction +3 -0
  43. package/dist/data/gcd3/function/abs/then_0.mcfunction +5 -0
  44. package/dist/data/gcd3/function/abs.mcfunction +7 -0
  45. package/dist/data/gcd3/function/gcd/loop_body_1.mcfunction +7 -0
  46. package/dist/data/gcd3/function/gcd/loop_check_0.mcfunction +5 -0
  47. package/dist/data/gcd3/function/gcd/loop_exit_2.mcfunction +3 -0
  48. package/dist/data/gcd3/function/gcd.mcfunction +14 -0
  49. package/dist/data/gcd3/function/test.mcfunction +7 -0
  50. package/dist/data/gcd3nm/function/__load.mcfunction +3 -0
  51. package/dist/data/gcd3nm/function/abs/merge_2.mcfunction +3 -0
  52. package/dist/data/gcd3nm/function/abs/then_0.mcfunction +5 -0
  53. package/dist/data/gcd3nm/function/abs.mcfunction +7 -0
  54. package/dist/data/gcd3nm/function/gcd/loop_body_1.mcfunction +7 -0
  55. package/dist/data/gcd3nm/function/gcd/loop_check_0.mcfunction +5 -0
  56. package/dist/data/gcd3nm/function/gcd/loop_exit_2.mcfunction +3 -0
  57. package/dist/data/gcd3nm/function/gcd.mcfunction +14 -0
  58. package/dist/data/gcd3nm/function/test.mcfunction +7 -0
  59. package/dist/data/gcd_test/function/__load.mcfunction +3 -0
  60. package/dist/data/gcd_test/function/abs/merge_2.mcfunction +3 -0
  61. package/dist/data/gcd_test/function/abs/then_0.mcfunction +5 -0
  62. package/dist/data/gcd_test/function/abs.mcfunction +7 -0
  63. package/dist/data/gcd_test/function/gcd/loop_body_1.mcfunction +7 -0
  64. package/dist/data/gcd_test/function/gcd/loop_check_0.mcfunction +5 -0
  65. package/dist/data/gcd_test/function/gcd/loop_exit_2.mcfunction +3 -0
  66. package/dist/data/gcd_test/function/gcd.mcfunction +14 -0
  67. package/dist/data/isqrttest/function/__load.mcfunction +6 -0
  68. package/dist/data/isqrttest/function/isqrt/loop_body_4.mcfunction +12 -0
  69. package/dist/data/isqrttest/function/isqrt/loop_check_3.mcfunction +5 -0
  70. package/dist/data/isqrttest/function/isqrt/loop_exit_5.mcfunction +3 -0
  71. package/dist/data/isqrttest/function/isqrt/merge_2.mcfunction +4 -0
  72. package/dist/data/isqrttest/function/isqrt/merge_8.mcfunction +6 -0
  73. package/dist/data/isqrttest/function/isqrt/then_0.mcfunction +3 -0
  74. package/dist/data/isqrttest/function/isqrt/then_6.mcfunction +3 -0
  75. package/dist/data/isqrttest/function/isqrt.mcfunction +7 -0
  76. package/dist/data/isqrttest/function/test.mcfunction +6 -0
  77. package/dist/data/mathtest/function/__load.mcfunction +3 -0
  78. package/dist/data/mathtest/function/abs/merge_2.mcfunction +3 -0
  79. package/dist/data/mathtest/function/abs/then_0.mcfunction +5 -0
  80. package/dist/data/mathtest/function/abs.mcfunction +6 -0
  81. package/dist/data/mathtest/function/test.mcfunction +5 -0
  82. package/dist/data/minecraft/tags/function/load.json +5 -0
  83. package/dist/data/minecraft/tags/function/tick.json +5 -0
  84. package/dist/data/mypack/function/__load.mcfunction +13 -0
  85. package/dist/data/mypack/function/_atan_init.mcfunction +2 -0
  86. package/dist/data/mypack/function/abs/merge_2.mcfunction +3 -0
  87. package/dist/data/mypack/function/abs/then_0.mcfunction +5 -0
  88. package/dist/data/mypack/function/abs.mcfunction +6 -0
  89. package/dist/data/mypack/function/atan2_fixed/__sgi_1.mcfunction +2 -0
  90. package/dist/data/mypack/function/atan2_fixed/else_34.mcfunction +3 -0
  91. package/dist/data/mypack/function/atan2_fixed/loop_body_31.mcfunction +19 -0
  92. package/dist/data/mypack/function/atan2_fixed/loop_check_30.mcfunction +5 -0
  93. package/dist/data/mypack/function/atan2_fixed/loop_exit_32.mcfunction +6 -0
  94. package/dist/data/mypack/function/atan2_fixed/merge_11.mcfunction +6 -0
  95. package/dist/data/mypack/function/atan2_fixed/merge_14.mcfunction +3 -0
  96. package/dist/data/mypack/function/atan2_fixed/merge_17.mcfunction +6 -0
  97. package/dist/data/mypack/function/atan2_fixed/merge_2.mcfunction +5 -0
  98. package/dist/data/mypack/function/atan2_fixed/merge_20.mcfunction +5 -0
  99. package/dist/data/mypack/function/atan2_fixed/merge_23.mcfunction +5 -0
  100. package/dist/data/mypack/function/atan2_fixed/merge_26.mcfunction +6 -0
  101. package/dist/data/mypack/function/atan2_fixed/merge_29.mcfunction +4 -0
  102. package/dist/data/mypack/function/atan2_fixed/merge_38.mcfunction +5 -0
  103. package/dist/data/mypack/function/atan2_fixed/merge_41.mcfunction +5 -0
  104. package/dist/data/mypack/function/atan2_fixed/merge_44.mcfunction +5 -0
  105. package/dist/data/mypack/function/atan2_fixed/merge_47.mcfunction +5 -0
  106. package/dist/data/mypack/function/atan2_fixed/merge_5.mcfunction +5 -0
  107. package/dist/data/mypack/function/atan2_fixed/merge_8.mcfunction +3 -0
  108. package/dist/data/mypack/function/atan2_fixed/then_0.mcfunction +5 -0
  109. package/dist/data/mypack/function/atan2_fixed/then_12.mcfunction +3 -0
  110. package/dist/data/mypack/function/atan2_fixed/then_15.mcfunction +5 -0
  111. package/dist/data/mypack/function/atan2_fixed/then_18.mcfunction +5 -0
  112. package/dist/data/mypack/function/atan2_fixed/then_21.mcfunction +3 -0
  113. package/dist/data/mypack/function/atan2_fixed/then_24.mcfunction +3 -0
  114. package/dist/data/mypack/function/atan2_fixed/then_27.mcfunction +6 -0
  115. package/dist/data/mypack/function/atan2_fixed/then_3.mcfunction +3 -0
  116. package/dist/data/mypack/function/atan2_fixed/then_33.mcfunction +5 -0
  117. package/dist/data/mypack/function/atan2_fixed/then_36.mcfunction +5 -0
  118. package/dist/data/mypack/function/atan2_fixed/then_39.mcfunction +5 -0
  119. package/dist/data/mypack/function/atan2_fixed/then_42.mcfunction +3 -0
  120. package/dist/data/mypack/function/atan2_fixed/then_45.mcfunction +5 -0
  121. package/dist/data/mypack/function/atan2_fixed/then_6.mcfunction +3 -0
  122. package/dist/data/mypack/function/atan2_fixed/then_9.mcfunction +5 -0
  123. package/dist/data/mypack/function/atan2_fixed.mcfunction +7 -0
  124. package/dist/data/mypack/function/my_game.mcfunction +10 -0
  125. package/dist/data/quiz/function/__load.mcfunction +16 -0
  126. package/dist/data/quiz/function/__tick.mcfunction +6 -0
  127. package/dist/data/quiz/function/__trigger_quiz_a_dispatch.mcfunction +4 -0
  128. package/dist/data/quiz/function/__trigger_quiz_b_dispatch.mcfunction +4 -0
  129. package/dist/data/quiz/function/__trigger_quiz_c_dispatch.mcfunction +4 -0
  130. package/dist/data/quiz/function/__trigger_quiz_start_dispatch.mcfunction +4 -0
  131. package/dist/data/quiz/function/answer_a.mcfunction +4 -0
  132. package/dist/data/quiz/function/answer_b.mcfunction +4 -0
  133. package/dist/data/quiz/function/answer_c.mcfunction +4 -0
  134. package/dist/data/quiz/function/ask_question/else_1.mcfunction +5 -0
  135. package/dist/data/quiz/function/ask_question/else_4.mcfunction +5 -0
  136. package/dist/data/quiz/function/ask_question/else_7.mcfunction +4 -0
  137. package/dist/data/quiz/function/ask_question/merge_2.mcfunction +1 -0
  138. package/dist/data/quiz/function/ask_question/merge_5.mcfunction +2 -0
  139. package/dist/data/quiz/function/ask_question/merge_8.mcfunction +2 -0
  140. package/dist/data/quiz/function/ask_question/then_0.mcfunction +4 -0
  141. package/dist/data/quiz/function/ask_question/then_3.mcfunction +4 -0
  142. package/dist/data/quiz/function/ask_question/then_6.mcfunction +4 -0
  143. package/dist/data/quiz/function/ask_question.mcfunction +7 -0
  144. package/dist/data/quiz/function/finish_quiz.mcfunction +6 -0
  145. package/dist/data/quiz/function/handle_answer/else_1.mcfunction +5 -0
  146. package/dist/data/quiz/function/handle_answer/else_10.mcfunction +3 -0
  147. package/dist/data/quiz/function/handle_answer/else_16.mcfunction +3 -0
  148. package/dist/data/quiz/function/handle_answer/else_4.mcfunction +3 -0
  149. package/dist/data/quiz/function/handle_answer/else_7.mcfunction +5 -0
  150. package/dist/data/quiz/function/handle_answer/merge_11.mcfunction +2 -0
  151. package/dist/data/quiz/function/handle_answer/merge_14.mcfunction +2 -0
  152. package/dist/data/quiz/function/handle_answer/merge_17.mcfunction +2 -0
  153. package/dist/data/quiz/function/handle_answer/merge_2.mcfunction +8 -0
  154. package/dist/data/quiz/function/handle_answer/merge_5.mcfunction +2 -0
  155. package/dist/data/quiz/function/handle_answer/merge_8.mcfunction +2 -0
  156. package/dist/data/quiz/function/handle_answer/then_0.mcfunction +5 -0
  157. package/dist/data/quiz/function/handle_answer/then_12.mcfunction +5 -0
  158. package/dist/data/quiz/function/handle_answer/then_15.mcfunction +6 -0
  159. package/dist/data/quiz/function/handle_answer/then_3.mcfunction +6 -0
  160. package/dist/data/quiz/function/handle_answer/then_6.mcfunction +5 -0
  161. package/dist/data/quiz/function/handle_answer/then_9.mcfunction +6 -0
  162. package/dist/data/quiz/function/handle_answer.mcfunction +11 -0
  163. package/dist/data/quiz/function/start_quiz.mcfunction +5 -0
  164. package/dist/data/reqtest/function/__load.mcfunction +4 -0
  165. package/dist/data/reqtest/function/_table_init.mcfunction +2 -0
  166. package/dist/data/reqtest/function/no_trig.mcfunction +3 -0
  167. package/dist/data/reqtest/function/use_table.mcfunction +4 -0
  168. package/dist/data/reqtest2/function/__load.mcfunction +3 -0
  169. package/dist/data/reqtest2/function/no_trig.mcfunction +3 -0
  170. package/dist/data/runtime/function/__load.mcfunction +5 -0
  171. package/dist/data/runtime/function/__tick.mcfunction +2 -0
  172. package/dist/data/runtime/function/counter_tick/then_0.mcfunction +3 -0
  173. package/dist/data/runtime/function/counter_tick.mcfunction +13 -0
  174. package/dist/data/shop/function/__load.mcfunction +7 -0
  175. package/dist/data/shop/function/__tick.mcfunction +3 -0
  176. package/dist/data/shop/function/__trigger_shop_buy_dispatch.mcfunction +4 -0
  177. package/dist/data/shop/function/complete_purchase/else_1.mcfunction +5 -0
  178. package/dist/data/shop/function/complete_purchase/else_4.mcfunction +5 -0
  179. package/dist/data/shop/function/complete_purchase/else_7.mcfunction +3 -0
  180. package/dist/data/shop/function/complete_purchase/merge_2.mcfunction +2 -0
  181. package/dist/data/shop/function/complete_purchase/merge_5.mcfunction +2 -0
  182. package/dist/data/shop/function/complete_purchase/merge_8.mcfunction +2 -0
  183. package/dist/data/shop/function/complete_purchase/then_0.mcfunction +4 -0
  184. package/dist/data/shop/function/complete_purchase/then_3.mcfunction +4 -0
  185. package/dist/data/shop/function/complete_purchase/then_6.mcfunction +4 -0
  186. package/dist/data/shop/function/complete_purchase.mcfunction +7 -0
  187. package/dist/data/shop/function/handle_shop_trigger.mcfunction +3 -0
  188. package/dist/data/swap_test/function/__load.mcfunction +3 -0
  189. package/dist/data/swap_test/function/gcd_old/loop_body_1.mcfunction +7 -0
  190. package/dist/data/swap_test/function/gcd_old/loop_check_0.mcfunction +5 -0
  191. package/dist/data/swap_test/function/gcd_old/loop_exit_2.mcfunction +3 -0
  192. package/dist/data/swap_test/function/gcd_old.mcfunction +8 -0
  193. package/dist/data/turret/function/__load.mcfunction +5 -0
  194. package/dist/data/turret/function/__tick.mcfunction +4 -0
  195. package/dist/data/turret/function/__trigger_deploy_turret_dispatch.mcfunction +4 -0
  196. package/dist/data/turret/function/deploy_turret.mcfunction +8 -0
  197. package/dist/data/turret/function/turret_tick/at_1.mcfunction +2 -0
  198. package/dist/data/turret/function/turret_tick/foreach_0.mcfunction +2 -0
  199. package/dist/data/turret/function/turret_tick/foreach_2.mcfunction +2 -0
  200. package/dist/data/turret/function/turret_tick/tick_body.mcfunction +3 -0
  201. package/dist/data/turret/function/turret_tick/tick_skip.mcfunction +1 -0
  202. package/dist/data/turret/function/turret_tick.mcfunction +5 -0
  203. package/dist/gcd2.map.json +15 -0
  204. package/dist/gcd3.map.json +17 -0
  205. package/dist/gcd_test.map.json +15 -0
  206. package/dist/index.d.ts +4 -0
  207. package/dist/index.js +11 -6
  208. package/dist/isqrttest.map.json +15 -0
  209. package/dist/lowering/index.d.ts +3 -0
  210. package/dist/lowering/index.js +161 -64
  211. package/dist/mathtest.map.json +6 -0
  212. package/dist/mypack.map.json +27 -0
  213. package/dist/optimizer/commands.d.ts +1 -0
  214. package/dist/optimizer/commands.js +18 -11
  215. package/dist/optimizer/structure.d.ts +1 -0
  216. package/dist/optimizer/structure.js +6 -1
  217. package/dist/pack.mcmeta +6 -0
  218. package/dist/reqtest.map.json +4 -0
  219. package/dist/reqtest2.map.json +4 -0
  220. package/dist/runtime/index.js +26 -5
  221. package/dist/runtime.map.json +7 -0
  222. package/dist/swap_test.map.json +14 -0
  223. package/editors/vscode/package-lock.json +3 -3
  224. package/editors/vscode/package.json +1 -1
  225. package/examples/math-showcase.mcrs +146 -0
  226. package/examples/readme-demo.mcrs +92 -0
  227. package/examples/showcase.mcrs +505 -0
  228. package/package.json +1 -1
  229. package/src/__tests__/cli.test.ts +13 -13
  230. package/src/__tests__/optimizer-advanced.test.ts +4 -4
  231. package/src/__tests__/stdlib-advanced.test.ts +120 -0
  232. package/src/__tests__/stdlib-bigint.test.ts +427 -0
  233. package/src/cli.ts +14 -5
  234. package/src/codegen/mcfunction/index.ts +14 -5
  235. package/src/compile.ts +15 -2
  236. package/src/index.ts +17 -7
  237. package/src/lowering/index.ts +165 -63
  238. package/src/optimizer/commands.ts +18 -12
  239. package/src/optimizer/structure.ts +6 -2
  240. package/src/runtime/index.ts +27 -5
  241. package/src/stdlib/advanced.mcrs +81 -0
  242. package/src/stdlib/bigint.mcrs +205 -0
  243. package/src/stdlib/math.mcrs +19 -6
@@ -20,8 +20,14 @@ export interface CommandFunction {
20
20
  commands: IRCommand[]
21
21
  }
22
22
 
23
- const SCOREBOARD_READ_RE =
24
- /^execute store result score (\$[A-Za-z0-9_]+) rs run scoreboard players get (\S+) (\S+)$/
23
+ // Matches scoreboard reads for LICM/CSE — objective is captured in group 2
24
+ // so the optimizer can reconstruct the command with the same objective.
25
+ let _OBJ_PATTERN = 'rs'
26
+ export function setOptimizerObjective(obj: string): void { _OBJ_PATTERN = obj }
27
+
28
+ function scoreboardReadRe(): RegExp {
29
+ return new RegExp(`^execute store result score (\\$[A-Za-z0-9_]+) ${_OBJ_PATTERN} run scoreboard players get (\\S+) (\\S+)$`)
30
+ }
25
31
  const SCOREBOARD_WRITE_RE =
26
32
  /^(?:scoreboard players (?:set|add|remove|reset)\s+(\S+)\s+(\S+)|scoreboard players operation\s+(\S+)\s+(\S+)\s+[+\-*/%]?= )/
27
33
  const EXECUTE_STORE_SCORE_RE =
@@ -130,7 +136,7 @@ function applyLICMInternal(functions: CommandFunction[]): Partial<OptimizationSt
130
136
  const scoreboardWrites = new Set<string>()
131
137
 
132
138
  for (const inner of loopFn.commands) {
133
- const readMatch = inner.cmd.match(SCOREBOARD_READ_RE)
139
+ const readMatch = inner.cmd.match(scoreboardReadRe())
134
140
  if (readMatch) {
135
141
  const [, temp, player, objective] = readMatch
136
142
  const key = `${player} ${objective}`
@@ -147,7 +153,7 @@ function applyLICMInternal(functions: CommandFunction[]): Partial<OptimizationSt
147
153
  for (const info of readInfo.values()) {
148
154
  const matches = inner.cmd.match(TEMP_RE) ?? []
149
155
  const usageCount = matches.filter(name => name === info.temp).length
150
- const isDef = inner.cmd.startsWith(`execute store result score ${info.temp} rs run scoreboard players get `)
156
+ const isDef = inner.cmd.startsWith(`execute store result score ${info.temp} ${_OBJ_PATTERN} run scoreboard players get `)
151
157
  if (!isDef) {
152
158
  info.uses += usageCount
153
159
  }
@@ -172,7 +178,7 @@ function applyLICMInternal(functions: CommandFunction[]): Partial<OptimizationSt
172
178
  const rewrittenLoopCommands: IRCommand[] = []
173
179
 
174
180
  for (const inner of loopFn.commands) {
175
- const readMatch = inner.cmd.match(SCOREBOARD_READ_RE)
181
+ const readMatch = inner.cmd.match(scoreboardReadRe())
176
182
  if (readMatch && hoistedTemps.has(readMatch[1])) {
177
183
  continue
178
184
  }
@@ -182,7 +188,7 @@ function applyLICMInternal(functions: CommandFunction[]): Partial<OptimizationSt
182
188
  loopFn.commands = rewrittenLoopCommands
183
189
  nextCommands.push(
184
190
  ...hoistable.map(item => ({
185
- cmd: `execute store result score ${item.temp} rs run scoreboard players get ${item.player} ${item.objective}`,
191
+ cmd: `execute store result score ${item.temp} ${_OBJ_PATTERN} run scoreboard players get ${item.player} ${item.objective}`,
186
192
  })),
187
193
  command
188
194
  )
@@ -198,9 +204,9 @@ function applyLICMInternal(functions: CommandFunction[]): Partial<OptimizationSt
198
204
 
199
205
  function extractArithmeticExpression(commands: IRCommand[], index: number): { key: string; dst: string } | null {
200
206
  const assign =
201
- commands[index]?.cmd.match(/^scoreboard players operation (\$[A-Za-z0-9_]+) rs = (\$[A-Za-z0-9_]+|\$const_-?\d+) rs$/) ??
202
- commands[index]?.cmd.match(/^scoreboard players set (\$[A-Za-z0-9_]+) rs (-?\d+)$/)
203
- const op = commands[index + 1]?.cmd.match(/^scoreboard players operation (\$[A-Za-z0-9_]+) rs ([+\-*/%]=) (\$[A-Za-z0-9_]+|\$const_-?\d+) rs$/)
207
+ commands[index]?.cmd.match(new RegExp(`^scoreboard players operation (\\$[A-Za-z0-9_]+) ${_OBJ_PATTERN} = (\\$[A-Za-z0-9_]+|\\$const_-?\\d+) ${_OBJ_PATTERN}$`)) ??
208
+ commands[index]?.cmd.match(new RegExp(`^scoreboard players set (\\$[A-Za-z0-9_]+) ${_OBJ_PATTERN} (-?\\d+)$`))
209
+ const op = commands[index + 1]?.cmd.match(new RegExp(`^scoreboard players operation (\\$[A-Za-z0-9_]+) ${_OBJ_PATTERN} ([+\\-*/%]=) (\\$[A-Za-z0-9_]+|\\$const_-?\\d+) ${_OBJ_PATTERN}$`))
204
210
  if (!assign || !op || assign[1] !== op[1]) {
205
211
  return null
206
212
  }
@@ -234,14 +240,14 @@ function applyCSEInternal(functions: CommandFunction[]): Partial<OptimizationSta
234
240
 
235
241
  for (let i = 0; i < commands.length; i++) {
236
242
  const command = commands[i]
237
- const readMatch = command.cmd.match(SCOREBOARD_READ_RE)
243
+ const readMatch = command.cmd.match(scoreboardReadRe())
238
244
  if (readMatch) {
239
245
  const [, dst, player, objective] = readMatch
240
246
  const key = `${player} ${objective}`
241
247
  const cached = readCache.get(key)
242
248
  if (cached) {
243
249
  stats.cseRedundantReads = (stats.cseRedundantReads ?? 0) + 1
244
- rewritten.push({ ...command, cmd: `scoreboard players operation ${dst} rs = ${cached} rs` })
250
+ rewritten.push({ ...command, cmd: `scoreboard players operation ${dst} ${_OBJ_PATTERN} = ${cached} ${_OBJ_PATTERN}` })
245
251
  } else {
246
252
  readCache.set(key, dst)
247
253
  rewritten.push(command)
@@ -255,7 +261,7 @@ function applyCSEInternal(functions: CommandFunction[]): Partial<OptimizationSta
255
261
  if (expr) {
256
262
  const cached = exprCache.get(expr.key)
257
263
  if (cached) {
258
- rewritten.push({ ...commands[i], cmd: `scoreboard players operation ${expr.dst} rs = ${cached} rs` })
264
+ rewritten.push({ ...commands[i], cmd: `scoreboard players operation ${expr.dst} ${_OBJ_PATTERN} = ${cached} ${_OBJ_PATTERN}` })
259
265
  stats.cseArithmetic = (stats.cseArithmetic ?? 0) + 1
260
266
  i += 1
261
267
  } else {
@@ -1,7 +1,11 @@
1
1
  import type { IRBlock, IRCommand, IRFunction, IRInstr, Operand, Terminator } from '../ir/types'
2
- import { createEmptyOptimizationStats, mergeOptimizationStats, optimizeCommandFunctions, type OptimizationStats } from './commands'
2
+ import { createEmptyOptimizationStats, mergeOptimizationStats, optimizeCommandFunctions, setOptimizerObjective, type OptimizationStats } from './commands'
3
3
 
4
- const OBJ = 'rs'
4
+ let OBJ = 'rs'
5
+ export function setStructureObjective(obj: string): void {
6
+ OBJ = obj
7
+ setOptimizerObjective(obj)
8
+ }
5
9
  const INLINE_THRESHOLD = 8
6
10
 
7
11
  const BOP_OP: Record<string, string> = {
@@ -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
- rest = rest.slice(21)
703
- // format: <ns:path> <field> <type> <scale> <run-cmd>
704
- const storageParts = rest.match(/^(\S+)\s+(\S+)\s+(\S+)\s+([\d.]+)\s+(.*)$/)
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+) (\S+)\[(\d+)\](?:\s+[\d.]+)?$/)
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)
@@ -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
+ }
@@ -0,0 +1,205 @@
1
+ // bigint.mcrs — Arbitrary precision integer arithmetic for RedScript.
2
+ //
3
+ // Representation:
4
+ // 8 limbs, base B = 10000 (10^4) per limb.
5
+ // Limb[0] = least significant (ones place in base 10000).
6
+ // Maximum value: 10^32 - 1 (32 decimal digits).
7
+ // Stored in NBT int arrays inside data storage ("rs:bigint").
8
+ //
9
+ // Registers: "a", "b", "c" (three independent BigInt values).
10
+ //
11
+ // Usage example:
12
+ // bigint_init();
13
+ // bigint_from_int_a(12345678); // a = 12345678
14
+ // bigint_from_int_b(87654321); // b = 87654321
15
+ // bigint_add(); // c = a + b = 99999999
16
+ // let limb0: int = bigint_get_c(0); // → 9999 (c[0])
17
+ // let limb1: int = bigint_get_c(1); // → 9999 (c[1])
18
+ //
19
+ // Multiply overflow note:
20
+ // bigint_mul uses grade-school O(n^2) algorithm.
21
+ // Per-product max: 9999 * 9999 + 9999 + 9999 = 99,999,999 < INT32 ✓
22
+
23
+ module library;
24
+
25
+ // ── Initialization ────────────────────────────────────────────────────────────
26
+
27
+ // Initialize (or reset) all BigInt registers to zero.
28
+ // Must be called once before using any bigint operation.
29
+ fn bigint_init() {
30
+ storage_set_array("rs:bigint", "a", "[0,0,0,0,0,0,0,0]");
31
+ storage_set_array("rs:bigint", "b", "[0,0,0,0,0,0,0,0]");
32
+ storage_set_array("rs:bigint", "c", "[0,0,0,0,0,0,0,0]");
33
+ }
34
+
35
+ // ── Load from int32 ───────────────────────────────────────────────────────────
36
+
37
+ // Set register a from a 32-bit integer (splits into up to 3 limbs).
38
+ // Supports any n in [0, 999999999999] (12-digit safe limit).
39
+ fn bigint_from_int_a(n: int) {
40
+ storage_set_array("rs:bigint", "a", "[0,0,0,0,0,0,0,0]");
41
+ storage_set_int("rs:bigint", "a", 0, n % 10000);
42
+ storage_set_int("rs:bigint", "a", 1, n / 10000 % 10000);
43
+ storage_set_int("rs:bigint", "a", 2, n / 10000 / 10000);
44
+ }
45
+
46
+ // Set register b from a 32-bit integer.
47
+ fn bigint_from_int_b(n: int) {
48
+ storage_set_array("rs:bigint", "b", "[0,0,0,0,0,0,0,0]");
49
+ storage_set_int("rs:bigint", "b", 0, n % 10000);
50
+ storage_set_int("rs:bigint", "b", 1, n / 10000 % 10000);
51
+ storage_set_int("rs:bigint", "b", 2, n / 10000 / 10000);
52
+ }
53
+
54
+ // ── Read limb ─────────────────────────────────────────────────────────────────
55
+
56
+ fn bigint_get_a(i: int) -> int { return storage_get_int("rs:bigint", "a", i); }
57
+ fn bigint_get_b(i: int) -> int { return storage_get_int("rs:bigint", "b", i); }
58
+ fn bigint_get_c(i: int) -> int { return storage_get_int("rs:bigint", "c", i); }
59
+
60
+ // ── Copy between registers ────────────────────────────────────────────────────
61
+
62
+ fn bigint_copy_a_to_b() {
63
+ let i: int = 0;
64
+ while (i < 8) {
65
+ storage_set_int("rs:bigint", "b", i, storage_get_int("rs:bigint", "a", i));
66
+ i = i + 1;
67
+ }
68
+ }
69
+
70
+ fn bigint_copy_b_to_a() {
71
+ let i: int = 0;
72
+ while (i < 8) {
73
+ storage_set_int("rs:bigint", "a", i, storage_get_int("rs:bigint", "b", i));
74
+ i = i + 1;
75
+ }
76
+ }
77
+
78
+ fn bigint_copy_c_to_a() {
79
+ let i: int = 0;
80
+ while (i < 8) {
81
+ storage_set_int("rs:bigint", "a", i, storage_get_int("rs:bigint", "c", i));
82
+ i = i + 1;
83
+ }
84
+ }
85
+
86
+ fn bigint_copy_c_to_b() {
87
+ let i: int = 0;
88
+ while (i < 8) {
89
+ storage_set_int("rs:bigint", "b", i, storage_get_int("rs:bigint", "c", i));
90
+ i = i + 1;
91
+ }
92
+ }
93
+
94
+ // ── Addition: c = a + b ───────────────────────────────────────────────────────
95
+
96
+ // c = a + b (carry propagated across all 8 limbs)
97
+ fn bigint_add() {
98
+ let carry: int = 0;
99
+ let i: int = 0;
100
+ while (i < 8) {
101
+ let s: int = storage_get_int("rs:bigint", "a", i)
102
+ + storage_get_int("rs:bigint", "b", i)
103
+ + carry;
104
+ carry = s / 10000;
105
+ storage_set_int("rs:bigint", "c", i, s % 10000);
106
+ i = i + 1;
107
+ }
108
+ }
109
+
110
+ // ── Subtraction: c = a - b (assumes a ≥ b) ──────────────────────────────────
111
+
112
+ fn bigint_sub() {
113
+ let borrow: int = 0;
114
+ let i: int = 0;
115
+ while (i < 8) {
116
+ let d: int = storage_get_int("rs:bigint", "a", i)
117
+ - storage_get_int("rs:bigint", "b", i)
118
+ - borrow;
119
+ if (d < 0) {
120
+ d = d + 10000;
121
+ borrow = 1;
122
+ } else {
123
+ borrow = 0;
124
+ }
125
+ storage_set_int("rs:bigint", "c", i, d);
126
+ i = i + 1;
127
+ }
128
+ }
129
+
130
+ // ── Comparison: returns 1 (a>b), 0 (a==b), -1 (a<b) ─────────────────────────
131
+
132
+ fn bigint_compare() -> int {
133
+ let i: int = 7;
134
+ while (i >= 0) {
135
+ let ai: int = storage_get_int("rs:bigint", "a", i);
136
+ let bi: int = storage_get_int("rs:bigint", "b", i);
137
+ if (ai > bi) { return 1; }
138
+ if (ai < bi) { return -1; }
139
+ i = i - 1;
140
+ }
141
+ return 0;
142
+ }
143
+
144
+ // ── Multiply by small constant: c = a × k (k < 10000) ───────────────────────
145
+ // Max per-limb: 9999 × 9999 + 9999 = 99,989,999 < INT32 ✓
146
+
147
+ fn bigint_mul_small(k: int) {
148
+ let carry: int = 0;
149
+ let i: int = 0;
150
+ while (i < 8) {
151
+ let p: int = storage_get_int("rs:bigint", "a", i) * k + carry;
152
+ carry = p / 10000;
153
+ storage_set_int("rs:bigint", "c", i, p % 10000);
154
+ i = i + 1;
155
+ }
156
+ }
157
+
158
+ // ── Multiply: c = a × b (grade-school O(n²), n=8) ───────────────────────────
159
+ // Per-product max: 9999 × 9999 + 9999 (prev c) + 9999 (carry) = 99,999,999 < INT32 ✓
160
+
161
+ fn bigint_mul() {
162
+ storage_set_array("rs:bigint", "c", "[0,0,0,0,0,0,0,0]");
163
+ let i: int = 0;
164
+ while (i < 8) {
165
+ let ai: int = storage_get_int("rs:bigint", "a", i);
166
+ if (ai != 0) {
167
+ let j: int = 0;
168
+ let carry: int = 0;
169
+ while (j < 8) {
170
+ let k: int = i + j;
171
+ if (k < 8) {
172
+ let p: int = ai * storage_get_int("rs:bigint", "b", j)
173
+ + storage_get_int("rs:bigint", "c", k)
174
+ + carry;
175
+ carry = p / 10000;
176
+ storage_set_int("rs:bigint", "c", k, p % 10000);
177
+ }
178
+ j = j + 1;
179
+ }
180
+ }
181
+ i = i + 1;
182
+ }
183
+ }
184
+
185
+ // ── Fibonacci: F(n) → register a (n ≤ 150 before 32-digit overflow) ─────────
186
+ //
187
+ // Invariant: a=F(k), b=F(k+1). After n iterations: a=F(n).
188
+ //
189
+ // F(50) = 12,586,269,025 → a[0]=9025, a[1]=8626, a[2]=125
190
+ // F(100) = 354,224,848,179,261,915,075 → 21 digits, fits in 6 limbs
191
+
192
+ fn bigint_fib(n: int) {
193
+ bigint_init(); // a=0=F(0), b=0
194
+ bigint_from_int_b(1); // b=1=F(1)
195
+ let i: int = 0;
196
+ while (i < n) {
197
+ // Invariant entering iteration: a=F(i), b=F(i+1)
198
+ bigint_add(); // c = F(i) + F(i+1) = F(i+2)
199
+ bigint_copy_b_to_a(); // a = F(i+1)
200
+ bigint_copy_c_to_b(); // b = F(i+2)
201
+ // Invariant restored: a=F(i+1), b=F(i+2)
202
+ i = i + 1;
203
+ }
204
+ // result: a = F(n)
205
+ }
@@ -71,16 +71,29 @@ fn lerp(a: int, b: int, t: int) -> int {
71
71
  // Uses Newton's method, converges in ≤16 iterations for all int32.
72
72
  // isqrt(9) == 3, isqrt(10) == 3, isqrt(0) == 0
73
73
  fn isqrt(n: int) -> int {
74
- if (n <= 0) {
75
- return 0;
74
+ if (n <= 0) { return 0; }
75
+ if (n == 1) { return 1; }
76
+ // Bit-length of n: how many bits needed to represent n.
77
+ let bits: int = 0;
78
+ let tmp: int = n;
79
+ while (tmp > 0) {
80
+ tmp = tmp / 2;
81
+ bits = bits + 1;
76
82
  }
77
- let x: int = n;
83
+ // Initial guess: 2^((bits+1)/2). Always sqrt(n), so Newton converges
84
+ // monotonically from above — the standard floor-sqrt Newton iteration.
85
+ let half: int = (bits + 1) / 2;
86
+ let x: int = 1;
87
+ let j: int = 0;
88
+ while (j < half) {
89
+ x = x * 2;
90
+ j = j + 1;
91
+ }
92
+ // Newton–Raphson floor-sqrt: converges in ≤ 8 iterations from a 2× overestimate.
78
93
  let i: int = 0;
79
94
  while (i < 16) {
80
95
  let next: int = (x + n / x) / 2;
81
- if (next >= x) {
82
- return x;
83
- }
96
+ if (next >= x) { return x; }
84
97
  x = next;
85
98
  i = i + 1;
86
99
  }