@datamoc/mw_games 0.5.4 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/README.md +8 -3
  2. package/dist/actors/Appearances.d.ts.map +1 -1
  3. package/dist/actors/Appearances.js.map +1 -1
  4. package/dist/actors/Aura.d.ts.map +1 -1
  5. package/dist/actors/Aura.js.map +1 -1
  6. package/dist/actors/EntityTemplate.d.ts.map +1 -1
  7. package/dist/actors/EntityTemplate.js.map +1 -1
  8. package/dist/actors/Inventory.d.ts.map +1 -1
  9. package/dist/actors/Inventory.js +6 -1
  10. package/dist/actors/Inventory.js.map +1 -1
  11. package/dist/actors/Support.d.ts.map +1 -1
  12. package/dist/actors/Support.js.map +1 -1
  13. package/dist/actors/index.d.ts +1 -1
  14. package/dist/actors/index.d.ts.map +1 -1
  15. package/dist/actors/index.js.map +1 -1
  16. package/dist/actors/skillCheck.d.ts.map +1 -1
  17. package/dist/actors/skillCheck.js.map +1 -1
  18. package/dist/assets/Streaming.d.ts.map +1 -1
  19. package/dist/assets/Streaming.js.map +1 -1
  20. package/dist/assets/binary.d.ts.map +1 -1
  21. package/dist/assets/binary.js.map +1 -1
  22. package/dist/assets/loader.d.ts +15 -4
  23. package/dist/assets/loader.d.ts.map +1 -1
  24. package/dist/assets/loader.js +8 -23
  25. package/dist/assets/loader.js.map +1 -1
  26. package/dist/audio/Midi.d.ts.map +1 -1
  27. package/dist/audio/Midi.js +17 -3
  28. package/dist/audio/Midi.js.map +1 -1
  29. package/dist/audio/Music.d.ts.map +1 -1
  30. package/dist/audio/Music.js +8 -1
  31. package/dist/audio/Music.js.map +1 -1
  32. package/dist/audio/Synth.d.ts.map +1 -1
  33. package/dist/audio/Synth.js +1 -1
  34. package/dist/audio/Synth.js.map +1 -1
  35. package/dist/battle/BattleAI.js.map +1 -1
  36. package/dist/board/Army.d.ts.map +1 -1
  37. package/dist/board/Army.js.map +1 -1
  38. package/dist/board/Classics.d.ts.map +1 -1
  39. package/dist/board/Classics.js +310 -133
  40. package/dist/board/Classics.js.map +1 -1
  41. package/dist/board/FogOfWar.d.ts.map +1 -1
  42. package/dist/board/FogOfWar.js +12 -4
  43. package/dist/board/FogOfWar.js.map +1 -1
  44. package/dist/board/HexSkirmish.d.ts.map +1 -1
  45. package/dist/board/HexSkirmish.js +12 -4
  46. package/dist/board/HexSkirmish.js.map +1 -1
  47. package/dist/board/Tactics.d.ts.map +1 -1
  48. package/dist/board/Tactics.js +71 -26
  49. package/dist/board/Tactics.js.map +1 -1
  50. package/dist/board/chess.d.ts.map +1 -1
  51. package/dist/board/chess.js +8 -4
  52. package/dist/board/chess.js.map +1 -1
  53. package/dist/board/index.d.ts +2 -2
  54. package/dist/board/index.d.ts.map +1 -1
  55. package/dist/board/index.js +1 -1
  56. package/dist/board/index.js.map +1 -1
  57. package/dist/{roguelike → core}/Blob.d.ts +22 -11
  58. package/dist/core/Blob.d.ts.map +1 -0
  59. package/dist/{roguelike → core}/Blob.js +51 -28
  60. package/dist/core/Blob.js.map +1 -0
  61. package/dist/core/Csv.d.ts.map +1 -1
  62. package/dist/core/Csv.js +8 -2
  63. package/dist/core/Csv.js.map +1 -1
  64. package/dist/core/Input.d.ts.map +1 -1
  65. package/dist/core/Input.js +17 -4
  66. package/dist/core/Input.js.map +1 -1
  67. package/dist/core/Loading.d.ts.map +1 -1
  68. package/dist/core/Loading.js.map +1 -1
  69. package/dist/core/Motion.d.ts +27 -0
  70. package/dist/core/Motion.d.ts.map +1 -0
  71. package/dist/core/Motion.js +102 -0
  72. package/dist/core/Motion.js.map +1 -0
  73. package/dist/core/Multiplayer.d.ts.map +1 -1
  74. package/dist/core/Multiplayer.js.map +1 -1
  75. package/dist/core/PlayerInput.d.ts.map +1 -1
  76. package/dist/core/PlayerInput.js.map +1 -1
  77. package/dist/core/Replay.d.ts.map +1 -1
  78. package/dist/core/Replay.js.map +1 -1
  79. package/dist/core/Sanitize.d.ts.map +1 -1
  80. package/dist/core/Sanitize.js.map +1 -1
  81. package/dist/core/Save.d.ts.map +1 -1
  82. package/dist/core/Save.js.map +1 -1
  83. package/dist/core/Tween.d.ts +22 -2
  84. package/dist/core/Tween.d.ts.map +1 -1
  85. package/dist/core/Tween.js +30 -7
  86. package/dist/core/Tween.js.map +1 -1
  87. package/dist/core/index.d.ts +4 -0
  88. package/dist/core/index.d.ts.map +1 -1
  89. package/dist/core/index.js +2 -0
  90. package/dist/core/index.js.map +1 -1
  91. package/dist/i18n/Content.js +1 -2
  92. package/dist/i18n/Content.js.map +1 -1
  93. package/dist/i18n/EditSession.d.ts.map +1 -1
  94. package/dist/i18n/EditSession.js.map +1 -1
  95. package/dist/i18n/Fluent.d.ts.map +1 -1
  96. package/dist/i18n/Fluent.js +5 -1
  97. package/dist/i18n/Fluent.js.map +1 -1
  98. package/dist/i18n/FormatSpec.d.ts.map +1 -1
  99. package/dist/i18n/FormatSpec.js +3 -1
  100. package/dist/i18n/FormatSpec.js.map +1 -1
  101. package/dist/i18n/Validate.d.ts.map +1 -1
  102. package/dist/i18n/Validate.js +10 -2
  103. package/dist/i18n/Validate.js.map +1 -1
  104. package/dist/i18n/index.d.ts +1 -1
  105. package/dist/i18n/index.d.ts.map +1 -1
  106. package/dist/i18n/index.js +27 -4
  107. package/dist/i18n/index.js.map +1 -1
  108. package/dist/mw_games.global.js +94 -94
  109. package/dist/roguelike/ContentRoll.d.ts.map +1 -1
  110. package/dist/roguelike/ContentRoll.js.map +1 -1
  111. package/dist/roguelike/Doors.d.ts.map +1 -1
  112. package/dist/roguelike/Doors.js.map +1 -1
  113. package/dist/roguelike/DungeonParity.d.ts.map +1 -1
  114. package/dist/roguelike/DungeonParity.js +13 -4
  115. package/dist/roguelike/DungeonParity.js.map +1 -1
  116. package/dist/roguelike/Features.d.ts.map +1 -1
  117. package/dist/roguelike/Features.js.map +1 -1
  118. package/dist/roguelike/FieldOfView.d.ts.map +1 -1
  119. package/dist/roguelike/FieldOfView.js.map +1 -1
  120. package/dist/roguelike/Level.d.ts.map +1 -1
  121. package/dist/roguelike/Level.js.map +1 -1
  122. package/dist/roguelike/Scheduler.d.ts.map +1 -1
  123. package/dist/roguelike/Scheduler.js +10 -2
  124. package/dist/roguelike/Scheduler.js.map +1 -1
  125. package/dist/roguelike/Targeting.d.ts.map +1 -1
  126. package/dist/roguelike/Targeting.js.map +1 -1
  127. package/dist/roguelike/index.d.ts +0 -1
  128. package/dist/roguelike/index.d.ts.map +1 -1
  129. package/dist/roguelike/index.js +0 -1
  130. package/dist/roguelike/index.js.map +1 -1
  131. package/dist/rpg/Collision.d.ts.map +1 -1
  132. package/dist/rpg/Collision.js.map +1 -1
  133. package/dist/rpg/EventRunner.d.ts.map +1 -1
  134. package/dist/rpg/EventRunner.js.map +1 -1
  135. package/dist/rpg/Marshal.d.ts.map +1 -1
  136. package/dist/rpg/Marshal.js +2 -2
  137. package/dist/rpg/Marshal.js.map +1 -1
  138. package/dist/rpg/Quest.js.map +1 -1
  139. package/dist/rpg/index.d.ts +1 -1
  140. package/dist/rpg/index.d.ts.map +1 -1
  141. package/dist/rpg/index.js.map +1 -1
  142. package/dist/simulation/Scenario.d.ts.map +1 -1
  143. package/dist/simulation/Scenario.js.map +1 -1
  144. package/dist/simulation/Turns.d.ts.map +1 -1
  145. package/dist/simulation/Turns.js.map +1 -1
  146. package/dist/simulation/index.d.ts +1 -1
  147. package/dist/simulation/index.d.ts.map +1 -1
  148. package/dist/simulation/index.js.map +1 -1
  149. package/dist/three-d/Character3D.d.ts.map +1 -1
  150. package/dist/three-d/Character3D.js +3 -1
  151. package/dist/three-d/Character3D.js.map +1 -1
  152. package/dist/three-d/Collision3D.d.ts.map +1 -1
  153. package/dist/three-d/Collision3D.js.map +1 -1
  154. package/dist/three-d/Heightmap.d.ts.map +1 -1
  155. package/dist/three-d/Heightmap.js.map +1 -1
  156. package/dist/three-d/Models.d.ts.map +1 -1
  157. package/dist/three-d/Models.js.map +1 -1
  158. package/dist/three-d/TileGrid3D.d.ts.map +1 -1
  159. package/dist/three-d/TileGrid3D.js +5 -4
  160. package/dist/three-d/TileGrid3D.js.map +1 -1
  161. package/dist/three-d/Vox.d.ts.map +1 -1
  162. package/dist/three-d/Vox.js +6 -3
  163. package/dist/three-d/Vox.js.map +1 -1
  164. package/dist/two-d/render/AnimatedSprite.d.ts +6 -0
  165. package/dist/two-d/render/AnimatedSprite.d.ts.map +1 -1
  166. package/dist/two-d/render/AnimatedSprite.js +6 -0
  167. package/dist/two-d/render/AnimatedSprite.js.map +1 -1
  168. package/dist/two-d/render/Camera.d.ts.map +1 -1
  169. package/dist/two-d/render/Camera.js +13 -5
  170. package/dist/two-d/render/Camera.js.map +1 -1
  171. package/dist/two-d/render/Capabilities.d.ts.map +1 -1
  172. package/dist/two-d/render/Capabilities.js +48 -8
  173. package/dist/two-d/render/Capabilities.js.map +1 -1
  174. package/dist/two-d/render/ColorBlindness.d.ts.map +1 -1
  175. package/dist/two-d/render/ColorBlindness.js +3 -18
  176. package/dist/two-d/render/ColorBlindness.js.map +1 -1
  177. package/dist/two-d/render/ColorTransformBatcher.d.ts.map +1 -1
  178. package/dist/two-d/render/ColorTransformBatcher.js +1 -1
  179. package/dist/two-d/render/ColorTransformBatcher.js.map +1 -1
  180. package/dist/two-d/render/Particles.d.ts.map +1 -1
  181. package/dist/two-d/render/Particles.js +32 -6
  182. package/dist/two-d/render/Particles.js.map +1 -1
  183. package/dist/two-d/render/ScreenEffects.d.ts.map +1 -1
  184. package/dist/two-d/render/ScreenEffects.js +11 -0
  185. package/dist/two-d/render/ScreenEffects.js.map +1 -1
  186. package/dist/two-d/render/StatusVisuals.d.ts.map +1 -1
  187. package/dist/two-d/render/StatusVisuals.js +3 -1
  188. package/dist/two-d/render/StatusVisuals.js.map +1 -1
  189. package/dist/two-d/render/TileMap.d.ts.map +1 -1
  190. package/dist/two-d/render/TileMap.js +20 -16
  191. package/dist/two-d/render/TileMap.js.map +1 -1
  192. package/dist/two-d/render/TiledMap.d.ts.map +1 -1
  193. package/dist/two-d/render/TiledMap.js.map +1 -1
  194. package/dist/two-d/render/index.d.ts +3 -3
  195. package/dist/two-d/render/index.d.ts.map +1 -1
  196. package/dist/two-d/render/index.js +1 -1
  197. package/dist/two-d/render/index.js.map +1 -1
  198. package/dist/two-d/stage/DialogueStage.d.ts.map +1 -1
  199. package/dist/two-d/stage/DialogueStage.js +8 -4
  200. package/dist/two-d/stage/DialogueStage.js.map +1 -1
  201. package/dist/two-d/stage/script.d.ts.map +1 -1
  202. package/dist/two-d/stage/script.js.map +1 -1
  203. package/dist/two-d/stage/twee.d.ts.map +1 -1
  204. package/dist/two-d/stage/twee.js.map +1 -1
  205. package/dist/two-d/ui/Button.d.ts.map +1 -1
  206. package/dist/two-d/ui/Button.js +5 -2
  207. package/dist/two-d/ui/Button.js.map +1 -1
  208. package/dist/two-d/ui/FloatingText.d.ts.map +1 -1
  209. package/dist/two-d/ui/FloatingText.js +4 -1
  210. package/dist/two-d/ui/FloatingText.js.map +1 -1
  211. package/dist/two-d/ui/IconGrid.d.ts.map +1 -1
  212. package/dist/two-d/ui/IconGrid.js.map +1 -1
  213. package/dist/two-d/ui/ListView.d.ts.map +1 -1
  214. package/dist/two-d/ui/ListView.js.map +1 -1
  215. package/dist/two-d/ui/LoadingScreen.d.ts.map +1 -1
  216. package/dist/two-d/ui/LoadingScreen.js +6 -2
  217. package/dist/two-d/ui/LoadingScreen.js.map +1 -1
  218. package/dist/two-d/ui/MessageBox.d.ts +6 -0
  219. package/dist/two-d/ui/MessageBox.d.ts.map +1 -1
  220. package/dist/two-d/ui/MessageBox.js +11 -1
  221. package/dist/two-d/ui/MessageBox.js.map +1 -1
  222. package/dist/two-d/ui/NinePatch.d.ts.map +1 -1
  223. package/dist/two-d/ui/NinePatch.js.map +1 -1
  224. package/dist/two-d/ui/RebindScreen.d.ts.map +1 -1
  225. package/dist/two-d/ui/RebindScreen.js.map +1 -1
  226. package/dist/two-d/ui/Window.d.ts.map +1 -1
  227. package/dist/two-d/ui/Window.js +3 -0
  228. package/dist/two-d/ui/Window.js.map +1 -1
  229. package/dist/two-d/ui/a11y.d.ts +43 -0
  230. package/dist/two-d/ui/a11y.d.ts.map +1 -0
  231. package/dist/two-d/ui/a11y.js +79 -0
  232. package/dist/two-d/ui/a11y.js.map +1 -0
  233. package/dist/two-d/ui/contrast.d.ts +30 -0
  234. package/dist/two-d/ui/contrast.d.ts.map +1 -0
  235. package/dist/two-d/ui/contrast.js +42 -0
  236. package/dist/two-d/ui/contrast.js.map +1 -0
  237. package/dist/two-d/ui/index.d.ts +3 -0
  238. package/dist/two-d/ui/index.d.ts.map +1 -1
  239. package/dist/two-d/ui/index.js +2 -0
  240. package/dist/two-d/ui/index.js.map +1 -1
  241. package/dist/two-d/ui/markdown.d.ts.map +1 -1
  242. package/dist/two-d/ui/markdown.js +3 -1
  243. package/dist/two-d/ui/markdown.js.map +1 -1
  244. package/dist/two-d/ui/themedText.js +1 -1
  245. package/dist/two-d/ui/themedText.js.map +1 -1
  246. package/dist/version.d.ts +1 -1
  247. package/dist/version.js +1 -1
  248. package/dist/world/Environment.d.ts.map +1 -1
  249. package/dist/world/Environment.js +17 -6
  250. package/dist/world/Environment.js.map +1 -1
  251. package/package.json +258 -239
  252. package/tools/compress-dist.mjs +15 -1
  253. package/dist/roguelike/Blob.d.ts.map +0 -1
  254. package/dist/roguelike/Blob.js.map +0 -1
@@ -31,9 +31,15 @@ export class BoardGrid {
31
31
  throw new Error(`cell (${x},${y}) is outside the board`);
32
32
  return y * this.width + x;
33
33
  }
34
- inside(x, y) { return x >= 0 && y >= 0 && x < this.width && y < this.height; }
35
- get(x, y) { return this.cells[this.index(x, y)]; }
36
- set(x, y, piece) { this.cells[this.index(x, y)] = piece; }
34
+ inside(x, y) {
35
+ return x >= 0 && y >= 0 && x < this.width && y < this.height;
36
+ }
37
+ get(x, y) {
38
+ return this.cells[this.index(x, y)];
39
+ }
40
+ set(x, y, piece) {
41
+ this.cells[this.index(x, y)] = piece;
42
+ }
37
43
  move(from, to) {
38
44
  const piece = this.get(from.x, from.y);
39
45
  if (piece === null)
@@ -77,9 +83,15 @@ export function startingCheckers() {
77
83
  * ```
78
84
  */
79
85
  export function checkersMoves(state) {
80
- const sources = state.forcedFrom === null ? state.board.map((piece, from) => piece?.side === state.turn ? from : -1).filter((from) => from >= 0) : [state.forcedFrom];
86
+ const sources = state.forcedFrom === null
87
+ ? state.board.map((piece, from) => (piece?.side === state.turn ? from : -1)).filter((from) => from >= 0)
88
+ : [state.forcedFrom];
81
89
  const captures = sources.flatMap((from) => checkersFrom(state, from, true));
82
- return captures.length > 0 ? captures : state.forcedFrom === null ? sources.flatMap((from) => checkersFrom(state, from, false)) : [];
90
+ return captures.length > 0
91
+ ? captures
92
+ : state.forcedFrom === null
93
+ ? sources.flatMap((from) => checkersFrom(state, from, false))
94
+ : [];
83
95
  }
84
96
  /**
85
97
  * @example
@@ -92,7 +104,9 @@ export function checkersMoves(state) {
92
104
  * ```
93
105
  */
94
106
  export function applyCheckersMove(state, move) {
95
- if (!checkersMoves(state).some((candidate) => candidate.from === move.from && candidate.to === move.to && candidate.captures.length === move.captures.length))
107
+ if (!checkersMoves(state).some((candidate) => candidate.from === move.from &&
108
+ candidate.to === move.to &&
109
+ candidate.captures.length === move.captures.length))
96
110
  throw new Error('illegal checkers move');
97
111
  const piece = state.board[move.from];
98
112
  if (!piece)
@@ -100,9 +114,15 @@ export function applyCheckersMove(state, move) {
100
114
  state.board[move.from] = null;
101
115
  for (const captured of move.captures)
102
116
  state.board[captured] = null;
103
- const promoted = !piece.king && ((piece.side === 'red' && Math.floor(move.to / 8) === 0) || (piece.side === 'black' && Math.floor(move.to / 8) === 7));
117
+ const promoted = !piece.king &&
118
+ ((piece.side === 'red' && Math.floor(move.to / 8) === 0) ||
119
+ (piece.side === 'black' && Math.floor(move.to / 8) === 7));
104
120
  state.board[move.to] = { ...piece, king: piece.king || promoted };
105
- const next = { board: state.board, turn: state.turn, forcedFrom: move.captures.length > 0 ? move.to : null };
121
+ const next = {
122
+ board: state.board,
123
+ turn: state.turn,
124
+ forcedFrom: move.captures.length > 0 ? move.to : null,
125
+ };
106
126
  if (move.captures.length === 0 || checkersFrom(next, move.to, true).length === 0) {
107
127
  state.turn = state.turn === 'red' ? 'black' : 'red';
108
128
  state.forcedFrom = null;
@@ -204,7 +224,11 @@ export function playGo(state, x, y) {
204
224
  * console.log(goResult(state)); // 'finished'
205
225
  * ```
206
226
  */
207
- export function passGo(state) { state.turn = state.turn === 'black' ? 'white' : 'black'; state.ko = null; state.passes++; }
227
+ export function passGo(state) {
228
+ state.turn = state.turn === 'black' ? 'white' : 'black';
229
+ state.ko = null;
230
+ state.passes++;
231
+ }
208
232
  /**
209
233
  * @example
210
234
  * ```ts
@@ -213,7 +237,9 @@ export function passGo(state) { state.turn = state.turn === 'black' ? 'white' :
213
237
  * console.log(goResult(startingGo(9))); // 'ongoing' - nobody has passed yet
214
238
  * ```
215
239
  */
216
- export function goResult(state) { return state.passes >= 2 ? 'finished' : 'ongoing'; }
240
+ export function goResult(state) {
241
+ return state.passes >= 2 ? 'finished' : 'ongoing';
242
+ }
217
243
  /**
218
244
  * Stones on the board plus surrounded empty territory, per side - area scoring, not the
219
245
  * Japanese rule set's separate prisoner count.
@@ -227,49 +253,88 @@ export function goResult(state) { return state.passes >= 2 ? 'finished' : 'ongoi
227
253
  * console.log(goScore(state)); // { black: 1, white: 0 } - one stone, no territory settled yet
228
254
  * ```
229
255
  */
230
- export function goScore(state) { const score = { black: 0, white: 0 }; const seen = new Set(); for (let i = 0; i < state.board.length; i++) {
231
- const stone = state.board[i];
232
- if (stone)
233
- score[stone]++;
234
- else if (!seen.has(i)) {
235
- const area = group(state.board, state.size, i);
236
- const borders = new Set();
237
- for (const cell of area)
238
- for (const neighbour of goNeighbours(state, cell))
239
- if (state.board[neighbour])
240
- borders.add(state.board[neighbour]);
241
- for (const cell of area)
242
- seen.add(cell);
243
- if (borders.size === 1)
244
- score[[...borders][0]] += area.length;
256
+ export function goScore(state) {
257
+ const score = { black: 0, white: 0 };
258
+ const seen = new Set();
259
+ for (let i = 0; i < state.board.length; i++) {
260
+ const stone = state.board[i];
261
+ if (stone)
262
+ score[stone]++;
263
+ else if (!seen.has(i)) {
264
+ const area = group(state.board, state.size, i);
265
+ const borders = new Set();
266
+ for (const cell of area)
267
+ for (const neighbour of goNeighbours(state, cell))
268
+ if (state.board[neighbour])
269
+ borders.add(state.board[neighbour]);
270
+ for (const cell of area)
271
+ seen.add(cell);
272
+ if (borders.size === 1)
273
+ score[[...borders][0]] += area.length;
274
+ }
245
275
  }
246
- } return score; }
276
+ return score;
277
+ }
247
278
  function goIndex(state, x, y) {
248
279
  if (x < 0 || y < 0 || x >= state.size || y >= state.size)
249
280
  throw new Error('Go cell is outside the board');
250
281
  return y * state.size + x;
251
282
  }
252
- function goNeighbours(state, index) { const x = index % state.size; const y = Math.floor(index / state.size); return [[x - 1, y], [x + 1, y], [x, y - 1], [x, y + 1]].filter(([nx, ny]) => nx >= 0 && ny >= 0 && nx < state.size && ny < state.size).map(([nx, ny]) => ny * state.size + nx); }
253
- function group(board, size, start) { const stone = board[start]; if (!stone)
254
- return []; const found = []; const todo = [start]; const seen = new Set(); while (todo.length) {
255
- const index = todo.pop();
256
- if (seen.has(index) || board[index] !== stone)
257
- continue;
258
- seen.add(index);
259
- found.push(index);
260
- const x = index % size;
261
- const y = Math.floor(index / size);
262
- for (const [nx, ny] of [[x - 1, y], [x + 1, y], [x, y - 1], [x, y + 1]])
263
- if (nx >= 0 && ny >= 0 && nx < size && ny < size)
264
- todo.push(ny * size + nx);
265
- } return found; }
266
- function liberties(board, size, start) { const cells = group(board, size, start); const empty = new Set(); for (const index of cells) {
267
- const x = index % size;
268
- const y = Math.floor(index / size);
269
- for (const [nx, ny] of [[x - 1, y], [x + 1, y], [x, y - 1], [x, y + 1]])
270
- if (nx >= 0 && ny >= 0 && nx < size && ny < size && board[ny * size + nx] === null)
271
- empty.add(ny * size + nx);
272
- } return empty.size; }
283
+ function goNeighbours(state, index) {
284
+ const x = index % state.size;
285
+ const y = Math.floor(index / state.size);
286
+ return [
287
+ [x - 1, y],
288
+ [x + 1, y],
289
+ [x, y - 1],
290
+ [x, y + 1],
291
+ ]
292
+ .filter(([nx, ny]) => nx >= 0 && ny >= 0 && nx < state.size && ny < state.size)
293
+ .map(([nx, ny]) => ny * state.size + nx);
294
+ }
295
+ function group(board, size, start) {
296
+ const stone = board[start];
297
+ if (!stone)
298
+ return [];
299
+ const found = [];
300
+ const todo = [start];
301
+ const seen = new Set();
302
+ while (todo.length) {
303
+ const index = todo.pop();
304
+ if (seen.has(index) || board[index] !== stone)
305
+ continue;
306
+ seen.add(index);
307
+ found.push(index);
308
+ const x = index % size;
309
+ const y = Math.floor(index / size);
310
+ for (const [nx, ny] of [
311
+ [x - 1, y],
312
+ [x + 1, y],
313
+ [x, y - 1],
314
+ [x, y + 1],
315
+ ])
316
+ if (nx >= 0 && ny >= 0 && nx < size && ny < size)
317
+ todo.push(ny * size + nx);
318
+ }
319
+ return found;
320
+ }
321
+ function liberties(board, size, start) {
322
+ const cells = group(board, size, start);
323
+ const empty = new Set();
324
+ for (const index of cells) {
325
+ const x = index % size;
326
+ const y = Math.floor(index / size);
327
+ for (const [nx, ny] of [
328
+ [x - 1, y],
329
+ [x + 1, y],
330
+ [x, y - 1],
331
+ [x, y + 1],
332
+ ])
333
+ if (nx >= 0 && ny >= 0 && nx < size && ny < size && board[ny * size + nx] === null)
334
+ empty.add(ny * size + nx);
335
+ }
336
+ return empty.size;
337
+ }
273
338
  /**
274
339
  * @example
275
340
  * ```ts
@@ -279,7 +344,18 @@ function liberties(board, size, start) { const cells = group(board, size, start)
279
344
  * console.log(state.points[0], state.points[23]); // 2 -2 - white's and black's back checkers
280
345
  * ```
281
346
  */
282
- export function startingBackgammon() { const points = new Array(24).fill(0); points[0] = 2; points[11] = 5; points[16] = 3; points[18] = 5; points[23] = -2; points[12] = -5; points[7] = -3; points[5] = -5; return { points, bar: { white: 0, black: 0 }, off: { white: 0, black: 0 }, turn: 'white' }; }
347
+ export function startingBackgammon() {
348
+ const points = new Array(24).fill(0);
349
+ points[0] = 2;
350
+ points[11] = 5;
351
+ points[16] = 3;
352
+ points[18] = 5;
353
+ points[23] = -2;
354
+ points[12] = -5;
355
+ points[7] = -3;
356
+ points[5] = -5;
357
+ return { points, bar: { white: 0, black: 0 }, off: { white: 0, black: 0 }, turn: 'white' };
358
+ }
283
359
  /** Doubles roll as four of the same die, not two - the standard backgammon rule.
284
360
  *
285
361
  * @example
@@ -290,7 +366,10 @@ export function startingBackgammon() { const points = new Array(24).fill(0); poi
290
366
  * console.log(dice.length === 2 || dice.length === 4); // true
291
367
  * ```
292
368
  */
293
- export function rollBackgammonDice() { const dice = rollDice(2, 6); return dice[0] === dice[1] ? [...dice, ...dice] : dice; }
369
+ export function rollBackgammonDice() {
370
+ const dice = rollDice(2, 6);
371
+ return dice[0] === dice[1] ? [...dice, ...dice] : dice;
372
+ }
294
373
  /**
295
374
  * @example
296
375
  * ```ts
@@ -300,22 +379,27 @@ export function rollBackgammonDice() { const dice = rollDice(2, 6); return dice[
300
379
  * console.log(moves.length, moves[0]); // 3 { from: 0, to: 1, die: 1 }
301
380
  * ```
302
381
  */
303
- export function backgammonMoves(state, dice) { const side = state.turn === 'white' ? 1 : -1; const out = []; for (const die of dice) {
304
- if (state.bar[state.turn] > 0) {
305
- const to = state.turn === 'white' ? die - 1 : 24 - die;
306
- if (canBackgammonLand(state, to, side))
307
- out.push({ from: 'bar', to, die });
308
- continue;
309
- }
310
- for (let from = 0; from < 24; from++)
311
- if (state.points[from] * side > 0) {
312
- const to = from + die * side;
313
- if (to < 0 || to >= 24)
314
- out.push({ from, to: 'off', die });
315
- else if (canBackgammonLand(state, to, side))
316
- out.push({ from, to, die });
382
+ export function backgammonMoves(state, dice) {
383
+ const side = state.turn === 'white' ? 1 : -1;
384
+ const out = [];
385
+ for (const die of dice) {
386
+ if (state.bar[state.turn] > 0) {
387
+ const to = state.turn === 'white' ? die - 1 : 24 - die;
388
+ if (canBackgammonLand(state, to, side))
389
+ out.push({ from: 'bar', to, die });
390
+ continue;
317
391
  }
318
- } return out; }
392
+ for (let from = 0; from < 24; from++)
393
+ if (state.points[from] * side > 0) {
394
+ const to = from + die * side;
395
+ if (to < 0 || to >= 24)
396
+ out.push({ from, to: 'off', die });
397
+ else if (canBackgammonLand(state, to, side))
398
+ out.push({ from, to, die });
399
+ }
400
+ }
401
+ return out;
402
+ }
319
403
  /**
320
404
  * @example
321
405
  * ```ts
@@ -326,21 +410,28 @@ export function backgammonMoves(state, dice) { const side = state.turn === 'whit
326
410
  * console.log(state.points[0], state.points[1]); // 1 1 - one checker moved from point 0 to point 1
327
411
  * ```
328
412
  */
329
- export function applyBackgammonMove(state, move) { if (!backgammonMoves(state, [move.die]).some((candidate) => candidate.from === move.from && candidate.to === move.to))
330
- throw new Error('illegal backgammon move'); const side = state.turn === 'white' ? 1 : -1; if (move.from === 'bar')
331
- state.bar[state.turn]--;
332
- else
333
- state.points[move.from] -= side; if (move.to === 'off')
334
- state.off[state.turn]++;
335
- else {
336
- if (state.points[move.to] * side < 0) {
337
- const enemy = state.turn === 'white' ? 'black' : 'white';
338
- state.points[move.to] = 0;
339
- state.bar[enemy]++;
413
+ export function applyBackgammonMove(state, move) {
414
+ if (!backgammonMoves(state, [move.die]).some((candidate) => candidate.from === move.from && candidate.to === move.to))
415
+ throw new Error('illegal backgammon move');
416
+ const side = state.turn === 'white' ? 1 : -1;
417
+ if (move.from === 'bar')
418
+ state.bar[state.turn]--;
419
+ else
420
+ state.points[move.from] -= side;
421
+ if (move.to === 'off')
422
+ state.off[state.turn]++;
423
+ else {
424
+ if (state.points[move.to] * side < 0) {
425
+ const enemy = state.turn === 'white' ? 'black' : 'white';
426
+ state.points[move.to] = 0;
427
+ state.bar[enemy]++;
428
+ }
429
+ state.points[move.to] += side;
340
430
  }
341
- state.points[move.to] += side;
342
- } }
343
- function canBackgammonLand(state, to, side) { return state.points[to] * side >= -1; }
431
+ }
432
+ function canBackgammonLand(state, to, side) {
433
+ return state.points[to] * side >= -1;
434
+ }
344
435
  /**
345
436
  * @example
346
437
  * ```ts
@@ -350,9 +441,14 @@ function canBackgammonLand(state, to, side) { return state.points[to] * side >=
350
441
  * console.log(createDeck(2).length); // 54 - two jokers added
351
442
  * ```
352
443
  */
353
- export function createDeck(jokers = 0) { const suits = ['clubs', 'diamonds', 'hearts', 'spades']; const cards = []; for (const suit of suits)
354
- for (let rank = 1; rank <= 13; rank++)
355
- cards.push({ suit, rank: rank }); return cards.concat(new Array(jokers).fill(null).map(() => ({ suit: 'spades', rank: 1 }))); }
444
+ export function createDeck(jokers = 0) {
445
+ const suits = ['clubs', 'diamonds', 'hearts', 'spades'];
446
+ const cards = [];
447
+ for (const suit of suits)
448
+ for (let rank = 1; rank <= 13; rank++)
449
+ cards.push({ suit, rank: rank });
450
+ return cards.concat(new Array(jokers).fill(null).map(() => ({ suit: 'spades', rank: 1 })));
451
+ }
356
452
  /**
357
453
  * @example
358
454
  * ```ts
@@ -363,7 +459,9 @@ export function createDeck(jokers = 0) { const suits = ['clubs', 'diamonds', 'he
363
459
  * console.log(shuffled[0]); // { suit: 'hearts', rank: 6 } - deterministic for seed 1
364
460
  * ```
365
461
  */
366
- export function shuffleDeck(deck) { return Random.shuffle(deck); }
462
+ export function shuffleDeck(deck) {
463
+ return Random.shuffle(deck);
464
+ }
367
465
  /**
368
466
  * @example
369
467
  * ```ts
@@ -373,8 +471,11 @@ export function shuffleDeck(deck) { return Random.shuffle(deck); }
373
471
  * console.log(hands.length, hands[0].length); // 4 13 - a full deck dealt to four players
374
472
  * ```
375
473
  */
376
- export function deal(deck, hands, cardsEach) { if (hands < 0 || cardsEach < 0 || deck.length < hands * cardsEach)
377
- throw new Error('not enough cards to deal'); return Array.from({ length: hands }, () => deck.splice(0, cardsEach)); }
474
+ export function deal(deck, hands, cardsEach) {
475
+ if (hands < 0 || cardsEach < 0 || deck.length < hands * cardsEach)
476
+ throw new Error('not enough cards to deal');
477
+ return Array.from({ length: hands }, () => deck.splice(0, cardsEach));
478
+ }
378
479
  /** Highest card of the trump suit wins; failing that, highest card of whoever led. Ace (rank 1) ranks lowest, as in belote/tarot/bridge follow-suit play.
379
480
  *
380
481
  * @example
@@ -391,8 +492,16 @@ export function deal(deck, hands, cardsEach) { if (hands < 0 || cardsEach < 0 ||
391
492
  * console.log(trickWinner(plays, 'spades')); // 'east' - the only spade played beats every heart
392
493
  * ```
393
494
  */
394
- export function trickWinner(plays, trumpSuit) { if (plays.length === 0)
395
- throw new Error('a trick needs at least one play'); const leadSuit = plays[0].card.suit; const rankValue = (card) => (card.rank === 1 ? 14 : card.rank); const contest = trumpSuit && plays.some((play) => play.card.suit === trumpSuit) ? plays.filter((play) => play.card.suit === trumpSuit) : plays.filter((play) => play.card.suit === leadSuit); return contest.reduce((best, play) => (rankValue(play.card) > rankValue(best.card) ? play : best)).owner; }
495
+ export function trickWinner(plays, trumpSuit) {
496
+ if (plays.length === 0)
497
+ throw new Error('a trick needs at least one play');
498
+ const leadSuit = plays[0].card.suit;
499
+ const rankValue = (card) => (card.rank === 1 ? 14 : card.rank);
500
+ const contest = trumpSuit && plays.some((play) => play.card.suit === trumpSuit)
501
+ ? plays.filter((play) => play.card.suit === trumpSuit)
502
+ : plays.filter((play) => play.card.suit === leadSuit);
503
+ return contest.reduce((best, play) => (rankValue(play.card) > rankValue(best.card) ? play : best)).owner;
504
+ }
396
505
  /**
397
506
  * A Klondike-shaped deal: seven tableau columns, each with one face-up card on a run of
398
507
  * face-down ones, the rest in the stock.
@@ -406,10 +515,18 @@ export function trickWinner(plays, trumpSuit) { if (plays.length === 0)
406
515
  * console.log(state.tableau[0].up); // [{ suit: 'clubs', rank: 12 }] - one face-up card to start
407
516
  * ```
408
517
  */
409
- export function dealSolitaire(seed) { const deck = createDeck(); if (seed === undefined)
410
- shuffleDeck(deck);
411
- else
412
- Random.withSeed(seed, () => shuffleDeck(deck)); const tableau = Array.from({ length: 7 }, (_, column) => ({ down: deck.splice(0, 6 - column), up: deck.splice(0, 1) })); return { stock: deck, waste: [], tableau, foundations: [[], [], [], []] }; }
518
+ export function dealSolitaire(seed) {
519
+ const deck = createDeck();
520
+ if (seed === undefined)
521
+ shuffleDeck(deck);
522
+ else
523
+ Random.withSeed(seed, () => shuffleDeck(deck));
524
+ const tableau = Array.from({ length: 7 }, (_, column) => ({
525
+ down: deck.splice(0, 6 - column),
526
+ up: deck.splice(0, 1),
527
+ }));
528
+ return { stock: deck, waste: [], tableau, foundations: [[], [], [], []] };
529
+ }
413
530
  /**
414
531
  * Draws from the stock to the waste; once the stock is empty, the waste is turned back over
415
532
  * into a new stock rather than the game being stuck.
@@ -424,11 +541,16 @@ else
424
541
  * console.log(state.waste.length, state.stock.length); // 1 23
425
542
  * ```
426
543
  */
427
- export function drawSolitaire(state) { if (state.stock.length === 0) {
428
- state.stock = state.waste.reverse();
429
- state.waste = [];
430
- } const card = state.stock.pop() ?? null; if (card)
431
- state.waste.push(card); return card; }
544
+ export function drawSolitaire(state) {
545
+ if (state.stock.length === 0) {
546
+ state.stock = state.waste.reverse();
547
+ state.waste = [];
548
+ }
549
+ const card = state.stock.pop() ?? null;
550
+ if (card)
551
+ state.waste.push(card);
552
+ return card;
553
+ }
432
554
  /**
433
555
  * Moves the top `count` face-up cards from one tableau column to another - legal only onto a
434
556
  * card one rank higher of the opposite colour, or onto an empty column with a king.
@@ -442,9 +564,20 @@ export function drawSolitaire(state) { if (state.stock.length === 0) {
442
564
  * console.log(state.tableau[0].up.at(-1)); // { suit: 'hearts', rank: 11 }
443
565
  * ```
444
566
  */
445
- export function moveSolitaireTableau(state, from, to, count = 1) { const source = state.tableau[from]; const target = state.tableau[to]; const moving = source?.up.slice(-count); if (!source || !target || !moving?.length || (target.up.length === 0 ? moving[0].rank !== 13 : !alternatingDescending(moving[0], target.up.at(-1))))
446
- throw new Error('illegal solitaire tableau move'); source.up.splice(-count, count); target.up.push(...moving); if (source.down.length && source.up.length === 0)
447
- source.up.push(source.down.pop()); }
567
+ export function moveSolitaireTableau(state, from, to, count = 1) {
568
+ const source = state.tableau[from];
569
+ const target = state.tableau[to];
570
+ const moving = source?.up.slice(-count);
571
+ if (!source ||
572
+ !target ||
573
+ !moving?.length ||
574
+ (target.up.length === 0 ? moving[0].rank !== 13 : !alternatingDescending(moving[0], target.up.at(-1))))
575
+ throw new Error('illegal solitaire tableau move');
576
+ source.up.splice(-count, count);
577
+ target.up.push(...moving);
578
+ if (source.down.length && source.up.length === 0)
579
+ source.up.push(source.down.pop());
580
+ }
448
581
  /**
449
582
  * Moves a card to its suit's foundation pile - legal only as the next rank up (an ace
450
583
  * starts a pile).
@@ -461,16 +594,23 @@ export function moveSolitaireTableau(state, from, to, count = 1) { const source
461
594
  * console.log(state.foundations.some((pile) => pile.length === 1)); // true
462
595
  * ```
463
596
  */
464
- export function moveSolitaireToFoundation(state, source) { const card = source === 'waste' ? state.waste.at(-1) : state.tableau[source].up.at(-1); if (!card)
465
- throw new Error('no solitaire card to move'); const foundation = state.foundations.find((pile) => pile[0]?.suit === card.suit || pile.length === 0); if (!foundation || card.rank !== foundation.length + 1)
466
- throw new Error('card cannot move to its foundation'); if (source === 'waste')
467
- state.waste.pop();
468
- else {
469
- const column = state.tableau[source];
470
- column.up.pop();
471
- if (column.down.length && column.up.length === 0)
472
- column.up.push(column.down.pop());
473
- } foundation.push(card); }
597
+ export function moveSolitaireToFoundation(state, source) {
598
+ const card = source === 'waste' ? state.waste.at(-1) : state.tableau[source].up.at(-1);
599
+ if (!card)
600
+ throw new Error('no solitaire card to move');
601
+ const foundation = state.foundations.find((pile) => pile[0]?.suit === card.suit || pile.length === 0);
602
+ if (!foundation || card.rank !== foundation.length + 1)
603
+ throw new Error('card cannot move to its foundation');
604
+ if (source === 'waste')
605
+ state.waste.pop();
606
+ else {
607
+ const column = state.tableau[source];
608
+ column.up.pop();
609
+ if (column.down.length && column.up.length === 0)
610
+ column.up.push(column.down.pop());
611
+ }
612
+ foundation.push(card);
613
+ }
474
614
  /**
475
615
  * @example
476
616
  * ```ts
@@ -479,8 +619,14 @@ else {
479
619
  * console.log(solitaireWon(dealSolitaire(1))); // false - a fresh deal is never already won
480
620
  * ```
481
621
  */
482
- export function solitaireWon(state) { return state.foundations.every((pile) => pile.length === 13); }
483
- function alternatingDescending(top, bottom) { const topRed = top.suit === 'diamonds' || top.suit === 'hearts'; const bottomRed = bottom.suit === 'diamonds' || bottom.suit === 'hearts'; return top.rank === bottom.rank - 1 && topRed !== bottomRed; }
622
+ export function solitaireWon(state) {
623
+ return state.foundations.every((pile) => pile.length === 13);
624
+ }
625
+ function alternatingDescending(top, bottom) {
626
+ const topRed = top.suit === 'diamonds' || top.suit === 'hearts';
627
+ const bottomRed = bottom.suit === 'diamonds' || bottom.suit === 'hearts';
628
+ return top.rank === bottom.rank - 1 && topRed !== bottomRed;
629
+ }
484
630
  /**
485
631
  * @example
486
632
  * ```ts
@@ -491,8 +637,11 @@ function alternatingDescending(top, bottom) { const topRed = top.suit === 'diamo
491
637
  * console.log(rolls); // [5, 2] - deterministic for seed 1
492
638
  * ```
493
639
  */
494
- export function rollDice(count, sides) { if (!Number.isInteger(count) || count < 0 || !Number.isInteger(sides) || sides < 1)
495
- throw new Error('dice need a non-negative count and positive sides'); return Array.from({ length: count }, () => Random.range(1, sides)); }
640
+ export function rollDice(count, sides) {
641
+ if (!Number.isInteger(count) || count < 0 || !Number.isInteger(sides) || sides < 1)
642
+ throw new Error('dice need a non-negative count and positive sides');
643
+ return Array.from({ length: count }, () => Random.range(1, sides));
644
+ }
496
645
  /**
497
646
  * Rolls a `"NdS+M"`-style expression (2d6+1: two six-sided dice, plus one).
498
647
  *
@@ -504,8 +653,12 @@ export function rollDice(count, sides) { if (!Number.isInteger(count) || count <
504
653
  * console.log(Random.withSeed(1, () => rollExpression('2d6+1'))); // 8 - rolls [5, 2], plus 1
505
654
  * ```
506
655
  */
507
- export function rollExpression(expression) { const match = /^(\d+)d(\d+)([+-]\d+)?$/i.exec(expression.trim()); if (!match)
508
- throw new Error(`bad dice expression: "${expression}"`); return rollDice(Number(match[1]), Number(match[2])).reduce((sum, roll) => sum + roll, Number(match[3] ?? 0)); }
656
+ export function rollExpression(expression) {
657
+ const match = /^(\d+)d(\d+)([+-]\d+)?$/i.exec(expression.trim());
658
+ if (!match)
659
+ throw new Error(`bad dice expression: "${expression}"`);
660
+ return rollDice(Number(match[1]), Number(match[2])).reduce((sum, roll) => sum + roll, Number(match[3] ?? 0));
661
+ }
509
662
  /**
510
663
  * A cup of dice a player can keep and re-roll - Yahtzee's own mechanic, generalised to any
511
664
  * count/sides.
@@ -529,12 +682,19 @@ export class DiceCup {
529
682
  this.sides = sides;
530
683
  this.values = rollDice(count, sides);
531
684
  }
532
- reRoll() { for (let i = 0; i < this.values.length; i++)
533
- if (!this.kept.has(i))
534
- this.values[i] = Random.range(1, this.sides); }
535
- keep(index) { if (index < 0 || index >= this.count)
536
- throw new Error('die index is outside the cup'); this.kept.add(index); }
537
- clearKept() { this.kept.clear(); }
685
+ reRoll() {
686
+ for (let i = 0; i < this.values.length; i++)
687
+ if (!this.kept.has(i))
688
+ this.values[i] = Random.range(1, this.sides);
689
+ }
690
+ keep(index) {
691
+ if (index < 0 || index >= this.count)
692
+ throw new Error('die index is outside the cup');
693
+ this.kept.add(index);
694
+ }
695
+ clearKept() {
696
+ this.kept.clear();
697
+ }
538
698
  }
539
699
  /**
540
700
  * Scores five d6 values against one Yahtzee category.
@@ -548,17 +708,34 @@ export class DiceCup {
548
708
  * console.log(scoreDice([4, 4, 4, 4, 4], 'fours')); // 20 - five fours, four points each
549
709
  * ```
550
710
  */
551
- export function scoreDice(values, category) { if (values.length !== 5 || values.some((value) => value < 1 || value > 6))
552
- throw new Error('a score sheet needs five d6 values'); const counts = new Map(); for (const value of values)
553
- counts.set(value, (counts.get(value) ?? 0) + 1); const total = values.reduce((sum, value) => sum + value, 0); if (category === 'chance')
554
- return total; if (category === 'yahtzee')
555
- return counts.size === 1 ? 50 : 0; if (category === 'fullHouse')
556
- return [...counts.values()].sort().join(',') === '2,3' ? 25 : 0; if (category === 'threeKind')
557
- return [...counts.values()].some((count) => count >= 3) ? total : 0; if (category === 'fourKind')
558
- return [...counts.values()].some((count) => count >= 4) ? total : 0; if (category === 'smallStraight' || category === 'largeStraight') {
559
- const runs = ['1234', '2345', '3456'];
560
- const large = ['12345', '23456'];
561
- const unique = [...counts.keys()].sort().join('');
562
- return (category === 'smallStraight' ? runs : large).some((run) => unique.includes(run)) ? category === 'smallStraight' ? 30 : 40 : 0;
563
- } const face = ['ones', 'twos', 'threes', 'fours', 'fives', 'sixes'].indexOf(category) + 1; return face > 0 ? (counts.get(face) ?? 0) * face : 0; }
711
+ export function scoreDice(values, category) {
712
+ if (values.length !== 5 || values.some((value) => value < 1 || value > 6))
713
+ throw new Error('a score sheet needs five d6 values');
714
+ const counts = new Map();
715
+ for (const value of values)
716
+ counts.set(value, (counts.get(value) ?? 0) + 1);
717
+ const total = values.reduce((sum, value) => sum + value, 0);
718
+ if (category === 'chance')
719
+ return total;
720
+ if (category === 'yahtzee')
721
+ return counts.size === 1 ? 50 : 0;
722
+ if (category === 'fullHouse')
723
+ return [...counts.values()].sort().join(',') === '2,3' ? 25 : 0;
724
+ if (category === 'threeKind')
725
+ return [...counts.values()].some((count) => count >= 3) ? total : 0;
726
+ if (category === 'fourKind')
727
+ return [...counts.values()].some((count) => count >= 4) ? total : 0;
728
+ if (category === 'smallStraight' || category === 'largeStraight') {
729
+ const runs = ['1234', '2345', '3456'];
730
+ const large = ['12345', '23456'];
731
+ const unique = [...counts.keys()].sort().join('');
732
+ return (category === 'smallStraight' ? runs : large).some((run) => unique.includes(run))
733
+ ? category === 'smallStraight'
734
+ ? 30
735
+ : 40
736
+ : 0;
737
+ }
738
+ const face = ['ones', 'twos', 'threes', 'fours', 'fives', 'sixes'].indexOf(category) + 1;
739
+ return face > 0 ? (counts.get(face) ?? 0) * face : 0;
740
+ }
564
741
  //# sourceMappingURL=Classics.js.map