scrabble-solver 2.16.7 → 2.17.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 (276) hide show
  1. package/.nx/workspace-data/d/server-process.json +2 -2
  2. package/.nx/workspace-data/file-map.json +548 -572
  3. package/.nx/workspace-data/lockfile-dependencies.hash +1 -1
  4. package/.nx/workspace-data/lockfile-nodes.hash +1 -1
  5. package/.nx/workspace-data/nx_files.nxt +0 -0
  6. package/.nx/workspace-data/parsed-lock-file.dependencies.json +185 -160
  7. package/.nx/workspace-data/parsed-lock-file.nodes.json +406 -370
  8. package/.nx/workspace-data/project-graph.json +656 -552
  9. package/.nx/workspace-data/source-maps.json +96 -0
  10. package/.oxlintrc.json +6 -0
  11. package/CLAUDE.md +22 -17
  12. package/README.md +9 -0
  13. package/bun.lock +107 -133
  14. package/cypress/e2e/app.spec.cy.ts +4 -4
  15. package/cypress/e2e/features/solver-paths.ts +66 -0
  16. package/cypress/e2e/features.spec.cy.ts +1 -0
  17. package/cypress/support/lib/actions.ts +28 -9
  18. package/lerna.json +1 -1
  19. package/package.json +8 -7
  20. package/packages/configs/package.json +3 -3
  21. package/packages/constants/package.json +1 -1
  22. package/packages/dictionaries/README.md +1 -1
  23. package/packages/dictionaries/build/Dictionaries.d.ts +2 -2
  24. package/packages/dictionaries/build/Dictionaries.js +6 -6
  25. package/packages/dictionaries/build/lib/DiskCache.d.ts +11 -4
  26. package/packages/dictionaries/build/lib/DiskCache.js +27 -10
  27. package/packages/dictionaries/build/lib/LayeredCache.d.ts +4 -4
  28. package/packages/dictionaries/build/lib/LayeredCache.js +3 -3
  29. package/packages/dictionaries/build/lib/MemoryCache.d.ts +4 -4
  30. package/packages/dictionaries/build/lib/MemoryCache.js +2 -2
  31. package/packages/dictionaries/build/lib/createCacheTimestampComparator.d.ts +2 -2
  32. package/packages/dictionaries/build/lib/downloadDictionary.d.ts +2 -2
  33. package/packages/dictionaries/build/lib/downloadDictionary.js +4 -3
  34. package/packages/dictionaries/build/lib/getDictionaryFilepath.d.ts +3 -1
  35. package/packages/dictionaries/build/lib/getDictionaryFilepath.js +8 -3
  36. package/packages/dictionaries/package.json +7 -6
  37. package/packages/dictionaries/src/Dictionaries.ts +12 -12
  38. package/packages/dictionaries/src/lib/DiskCache.test.ts +85 -0
  39. package/packages/dictionaries/src/lib/DiskCache.ts +34 -14
  40. package/packages/dictionaries/src/lib/LayeredCache.ts +7 -7
  41. package/packages/dictionaries/src/lib/MemoryCache.ts +6 -6
  42. package/packages/dictionaries/src/lib/createCacheTimestampComparator.ts +2 -2
  43. package/packages/dictionaries/src/lib/downloadDictionary.ts +5 -4
  44. package/packages/dictionaries/src/lib/getDictionaryFilepath.ts +7 -2
  45. package/packages/gaddag/build/Gaddag.d.ts +39 -0
  46. package/packages/gaddag/build/Gaddag.js +128 -0
  47. package/packages/gaddag/build/buildGaddag.d.ts +10 -0
  48. package/packages/gaddag/build/buildGaddag.js +360 -0
  49. package/packages/gaddag/build/constants.d.ts +12 -0
  50. package/packages/gaddag/build/constants.js +15 -0
  51. package/packages/gaddag/build/index.d.ts +3 -0
  52. package/packages/gaddag/build/index.js +13 -0
  53. package/packages/logger/package.json +1 -1
  54. package/packages/scrabble-solver/.next/BUILD_ID +1 -1
  55. package/packages/scrabble-solver/.next/build-manifest.json +13 -13
  56. package/packages/scrabble-solver/.next/cache/.previewinfo +1 -1
  57. package/packages/scrabble-solver/.next/cache/.rscinfo +1 -1
  58. package/packages/scrabble-solver/.next/cache/.tsbuildinfo +1 -1
  59. package/packages/scrabble-solver/.next/cache/webpack/client-production/0.pack +0 -0
  60. package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack +0 -0
  61. package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack.old +0 -0
  62. package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/index.pack +0 -0
  63. package/packages/scrabble-solver/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
  64. package/packages/scrabble-solver/.next/cache/webpack/server-production/0.pack +0 -0
  65. package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack +0 -0
  66. package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack.old +0 -0
  67. package/packages/scrabble-solver/.next/diagnostics/framework.json +1 -1
  68. package/packages/scrabble-solver/.next/next-server.js.nft.json +1 -1
  69. package/packages/scrabble-solver/.next/prerender-manifest.json +4 -4
  70. package/packages/scrabble-solver/.next/required-server-files.js +3 -2
  71. package/packages/scrabble-solver/.next/required-server-files.json +3 -2
  72. package/packages/scrabble-solver/.next/routes-manifest.json +1 -1
  73. package/packages/scrabble-solver/.next/server/chunks/129.js +1 -0
  74. package/packages/scrabble-solver/.next/server/chunks/30.js +1 -1
  75. package/packages/scrabble-solver/.next/server/chunks/392.js +1 -1
  76. package/packages/scrabble-solver/.next/server/chunks/671.js +1 -1
  77. package/packages/scrabble-solver/.next/server/chunks/712.js +1 -1
  78. package/packages/scrabble-solver/.next/server/chunks/785.js +1 -0
  79. package/packages/scrabble-solver/.next/server/chunks/791.js +1 -1
  80. package/packages/scrabble-solver/.next/server/middleware-build-manifest.js +1 -1
  81. package/packages/scrabble-solver/.next/server/pages/404.html +1 -1
  82. package/packages/scrabble-solver/.next/server/pages/500.html +1 -1
  83. package/packages/scrabble-solver/.next/server/pages/_app.js +1 -1
  84. package/packages/scrabble-solver/.next/server/pages/_app.js.nft.json +1 -1
  85. package/packages/scrabble-solver/.next/server/pages/_error.js +1 -1
  86. package/packages/scrabble-solver/.next/server/pages/_error.js.nft.json +1 -1
  87. package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale]/[word].js +1 -1
  88. package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +1 -1
  89. package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale].js +1 -1
  90. package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale].js.nft.json +1 -1
  91. package/packages/scrabble-solver/.next/server/pages/api/solve.js +1 -1
  92. package/packages/scrabble-solver/.next/server/pages/api/solve.js.nft.json +1 -1
  93. package/packages/scrabble-solver/.next/server/pages/api/verify.js +1 -1
  94. package/packages/scrabble-solver/.next/server/pages/api/verify.js.nft.json +1 -1
  95. package/packages/scrabble-solver/.next/server/pages/index.html +1 -1
  96. package/packages/scrabble-solver/.next/server/pages/index.js +1 -1
  97. package/packages/scrabble-solver/.next/server/pages/index.js.nft.json +1 -1
  98. package/packages/scrabble-solver/.next/server/pages/index.json +1 -1
  99. package/packages/scrabble-solver/.next/server/pages/not-found.html +1 -1
  100. package/packages/scrabble-solver/.next/server/pages/not-found.js.nft.json +1 -1
  101. package/packages/scrabble-solver/.next/server/pages-manifest.json +1 -1
  102. package/packages/scrabble-solver/.next/server/webpack-runtime.js +1 -1
  103. package/packages/scrabble-solver/.next/static/{vYpMmuC_yEurZGcyGVnLf → OLE8_ZInlGH7ENU0NIOpf}/_buildManifest.js +1 -1
  104. package/packages/scrabble-solver/.next/static/chunks/583.7a46807be14c377e.js +1 -0
  105. package/packages/scrabble-solver/.next/static/chunks/830.661816bf0de8c2f1.js +1 -0
  106. package/packages/scrabble-solver/.next/static/chunks/{main-852475ca502b4e49.js → main-5b999f3f24ebfa84.js} +2 -2
  107. package/packages/scrabble-solver/.next/static/chunks/pages/_app-7024e7a27ffbb2fa.js +7 -0
  108. package/packages/scrabble-solver/.next/static/chunks/pages/{index-ec0f6aeeaf7be227.js → index-8b398b0a5d870773.js} +1 -1
  109. package/packages/scrabble-solver/.next/static/chunks/webpack-3943d624bbb47a64.js +1 -0
  110. package/packages/scrabble-solver/.next/static/css/aedeaada218d7280.css +2 -0
  111. package/packages/scrabble-solver/.next/trace +21 -21
  112. package/packages/scrabble-solver/.next/trace-build +1 -1
  113. package/packages/scrabble-solver/next.config.js +0 -6
  114. package/packages/scrabble-solver/package.json +10 -10
  115. package/packages/scrabble-solver/public/service-worker.js +2 -2
  116. package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.tsx +4 -1
  117. package/packages/scrabble-solver/src/components/Board/hooks/useGrid.ts +17 -6
  118. package/packages/scrabble-solver/src/components/Dictionary/Dictionary.module.scss +7 -22
  119. package/packages/scrabble-solver/src/components/Dictionary/Dictionary.tsx +1 -8
  120. package/packages/scrabble-solver/src/components/Results/Result.tsx +0 -3
  121. package/packages/scrabble-solver/src/components/Results/Results.module.scss +0 -1
  122. package/packages/scrabble-solver/src/components/Results/Results.tsx +10 -2
  123. package/packages/scrabble-solver/src/components/Results/types.ts +3 -3
  124. package/packages/scrabble-solver/src/components/Solver/Solver.tsx +4 -3
  125. package/packages/scrabble-solver/src/components/Tooltip/useTooltip.ts +1 -1
  126. package/packages/scrabble-solver/src/pages/api/dictionary/[locale]/[word].ts +2 -2
  127. package/packages/scrabble-solver/src/pages/api/dictionary/[locale]/index.ts +80 -3
  128. package/packages/scrabble-solver/src/pages/api/solve.ts +2 -2
  129. package/packages/scrabble-solver/src/pages/api/verify.ts +12 -3
  130. package/packages/scrabble-solver/src/sdk/solve.ts +13 -7
  131. package/packages/scrabble-solver/src/sdk/verify.ts +12 -6
  132. package/packages/scrabble-solver/src/service-worker/appShell.test.ts +86 -0
  133. package/packages/scrabble-solver/src/service-worker/appShell.ts +39 -0
  134. package/packages/scrabble-solver/src/service-worker/index.ts +4 -5
  135. package/packages/scrabble-solver/src/serviceWorkerManager.ts +1 -18
  136. package/packages/scrabble-solver/src/solver-worker/client.test.ts +151 -0
  137. package/packages/scrabble-solver/src/solver-worker/client.ts +106 -0
  138. package/packages/scrabble-solver/src/solver-worker/dictionaries/constants.ts +9 -0
  139. package/packages/scrabble-solver/src/solver-worker/dictionaries/getDictionary.ts +21 -0
  140. package/packages/scrabble-solver/src/solver-worker/dictionaries/index.ts +2 -0
  141. package/packages/scrabble-solver/src/solver-worker/dictionaries/revalidateDictionary.test.ts +199 -0
  142. package/packages/scrabble-solver/src/solver-worker/dictionaries/revalidateDictionary.ts +86 -0
  143. package/packages/scrabble-solver/src/solver-worker/dictionaries/revalidatedAt.ts +43 -0
  144. package/packages/scrabble-solver/src/solver-worker/getGaddag.test.ts +109 -0
  145. package/packages/scrabble-solver/src/solver-worker/getGaddag.ts +62 -0
  146. package/packages/scrabble-solver/src/solver-worker/index.ts +1 -0
  147. package/packages/scrabble-solver/src/solver-worker/index.worker.ts +124 -0
  148. package/packages/scrabble-solver/src/solver-worker/messages.ts +20 -0
  149. package/packages/scrabble-solver/src/state/index.ts +1 -0
  150. package/packages/scrabble-solver/src/state/sagas.ts +19 -9
  151. package/packages/scrabble-solver/src/state/useTypedStore.ts +6 -0
  152. package/packages/scrabble-solver/src/types/api.ts +1 -2
  153. package/packages/scrabble-solver/tsconfig.json +4 -0
  154. package/packages/scrabble-solver/tsconfig.tsbuildinfo +1 -1
  155. package/packages/solver/README.md +24 -0
  156. package/packages/solver/benchmarks/index.ts +15 -0
  157. package/packages/solver/benchmarks/lib.ts +31 -0
  158. package/packages/solver/benchmarks/renderChart.ts +165 -0
  159. package/packages/solver/benchmarks/results/chart.svg +65 -0
  160. package/packages/solver/benchmarks/runBenchmarks.ts +66 -0
  161. package/packages/solver/benchmarks/scenarios.ts +64 -0
  162. package/packages/solver/benchmarks/types.ts +16 -0
  163. package/packages/solver/benchmarks/updateReadme.ts +53 -0
  164. package/packages/solver/build/MoveGenerator.d.ts +116 -0
  165. package/packages/solver/build/MoveGenerator.js +686 -0
  166. package/packages/solver/build/index.d.ts +0 -13
  167. package/packages/solver/build/index.js +1 -27
  168. package/packages/solver/build/solve.d.ts +2 -2
  169. package/packages/solver/build/solve.js +3 -19
  170. package/packages/solver/package.json +7 -6
  171. package/packages/solver/src/MoveGenerator.test.ts +1009 -0
  172. package/packages/solver/src/MoveGenerator.ts +926 -0
  173. package/packages/solver/src/ReferenceSolver.ts +401 -0
  174. package/packages/solver/src/index.ts +0 -13
  175. package/packages/solver/src/solve.test.ts +21 -21
  176. package/packages/solver/src/solve.ts +4 -22
  177. package/packages/solver/tsconfig.json +1 -1
  178. package/packages/types/build/Cell.d.ts +1 -0
  179. package/packages/types/build/Cell.js +3 -0
  180. package/packages/types/build/Result.d.ts +3 -2
  181. package/packages/types/build/Result.js +11 -6
  182. package/packages/types/build/ResultJson.d.ts +5 -4
  183. package/packages/types/build/index.d.ts +0 -6
  184. package/packages/types/build/index.js +1 -9
  185. package/packages/types/build/readResultCells.d.ts +5 -0
  186. package/packages/types/build/readResultCells.js +75 -0
  187. package/packages/types/package.json +4 -3
  188. package/packages/types/src/Cell.ts +4 -0
  189. package/packages/types/src/Result.ts +15 -6
  190. package/packages/types/src/ResultJson.ts +5 -5
  191. package/packages/types/src/index.ts +0 -6
  192. package/packages/types/src/readResultCells.test.ts +140 -0
  193. package/packages/types/src/readResultCells.ts +90 -0
  194. package/packages/word-definitions/package.json +2 -2
  195. package/packages/word-lists/package.json +2 -2
  196. package/packages/scrabble-solver/.next/static/chunks/pages/_app-a5bbe02710f13d05.js +0 -7
  197. package/packages/scrabble-solver/.next/static/chunks/webpack-1a7a166b34ed8d24.js +0 -1
  198. package/packages/scrabble-solver/.next/static/css/0af35adb4bab56f0.css +0 -2
  199. package/packages/scrabble-solver/src/sdk/getDictionary.ts +0 -9
  200. package/packages/scrabble-solver/src/service-worker/average.ts +0 -7
  201. package/packages/scrabble-solver/src/service-worker/dictionaries/constants.ts +0 -5
  202. package/packages/scrabble-solver/src/service-worker/dictionaries/getDictionary.ts +0 -20
  203. package/packages/scrabble-solver/src/service-worker/dictionaries/index.ts +0 -2
  204. package/packages/scrabble-solver/src/service-worker/dictionaries/revalidateDictionary.ts +0 -33
  205. package/packages/scrabble-solver/src/service-worker/getTrie.ts +0 -24
  206. package/packages/scrabble-solver/src/service-worker/routeSolveRequests.ts +0 -91
  207. package/packages/scrabble-solver/src/service-worker/routeVerifyRequests.ts +0 -37
  208. package/packages/solver/build/areDigraphsValid.d.ts +0 -2
  209. package/packages/solver/build/areDigraphsValid.js +0 -17
  210. package/packages/solver/build/fillPattern.d.ts +0 -6
  211. package/packages/solver/build/fillPattern.js +0 -52
  212. package/packages/solver/build/generateEndIndices.d.ts +0 -2
  213. package/packages/solver/build/generateEndIndices.js +0 -17
  214. package/packages/solver/build/generateHorizontalPatterns.d.ts +0 -2
  215. package/packages/solver/build/generateHorizontalPatterns.js +0 -16
  216. package/packages/solver/build/generatePattern.d.ts +0 -7
  217. package/packages/solver/build/generatePattern.js +0 -20
  218. package/packages/solver/build/generatePatterns.d.ts +0 -2
  219. package/packages/solver/build/generatePatterns.js +0 -11
  220. package/packages/solver/build/generateStartIndices.d.ts +0 -2
  221. package/packages/solver/build/generateStartIndices.js +0 -16
  222. package/packages/solver/build/generateVectors.d.ts +0 -7
  223. package/packages/solver/build/generateVectors.js +0 -7
  224. package/packages/solver/build/generateVerticalPatterns.d.ts +0 -2
  225. package/packages/solver/build/generateVerticalPatterns.js +0 -16
  226. package/packages/solver/build/getCellsScore.d.ts +0 -2
  227. package/packages/solver/build/getCellsScore.js +0 -18
  228. package/packages/solver/build/getPatternHash.d.ts +0 -2
  229. package/packages/solver/build/getPatternHash.js +0 -14
  230. package/packages/solver/build/getPatternScore.d.ts +0 -2
  231. package/packages/solver/build/getPatternScore.js +0 -22
  232. package/packages/solver/build/getUniquePatterns.d.ts +0 -2
  233. package/packages/solver/build/getUniquePatterns.js +0 -17
  234. package/packages/solver/src/areDigraphsValid.ts +0 -18
  235. package/packages/solver/src/fillPattern.test.ts +0 -107
  236. package/packages/solver/src/fillPattern.ts +0 -68
  237. package/packages/solver/src/generateEndIndices.test.ts +0 -23
  238. package/packages/solver/src/generateEndIndices.ts +0 -19
  239. package/packages/solver/src/generateHorizontalPatterns.test.ts +0 -31
  240. package/packages/solver/src/generateHorizontalPatterns.ts +0 -15
  241. package/packages/solver/src/generatePattern.ts +0 -33
  242. package/packages/solver/src/generatePatterns.test.ts +0 -13
  243. package/packages/solver/src/generatePatterns.ts +0 -11
  244. package/packages/solver/src/generateStartIndices.test.ts +0 -19
  245. package/packages/solver/src/generateStartIndices.ts +0 -17
  246. package/packages/solver/src/generateVectors.test.ts +0 -12
  247. package/packages/solver/src/generateVectors.ts +0 -10
  248. package/packages/solver/src/generateVerticalPatterns.test.ts +0 -32
  249. package/packages/solver/src/generateVerticalPatterns.ts +0 -15
  250. package/packages/solver/src/getCellsScore.ts +0 -21
  251. package/packages/solver/src/getPatternHash.test.ts +0 -17
  252. package/packages/solver/src/getPatternHash.ts +0 -12
  253. package/packages/solver/src/getPatternScore.test.ts +0 -204
  254. package/packages/solver/src/getPatternScore.ts +0 -23
  255. package/packages/solver/src/getUniquePatterns.ts +0 -19
  256. package/packages/types/build/CollisionJson.d.ts +0 -2
  257. package/packages/types/build/CollisionJson.js +0 -2
  258. package/packages/types/build/FinalPattern.d.ts +0 -6
  259. package/packages/types/build/FinalPattern.js +0 -14
  260. package/packages/types/build/HorizontalPattern.d.ts +0 -5
  261. package/packages/types/build/HorizontalPattern.js +0 -34
  262. package/packages/types/build/Pattern.d.ts +0 -20
  263. package/packages/types/build/Pattern.js +0 -50
  264. package/packages/types/build/PatternJson.d.ts +0 -6
  265. package/packages/types/build/PatternJson.js +0 -2
  266. package/packages/types/build/VerticalPattern.d.ts +0 -5
  267. package/packages/types/build/VerticalPattern.js +0 -34
  268. package/packages/types/src/CollisionJson.ts +0 -3
  269. package/packages/types/src/FinalPattern.ts +0 -14
  270. package/packages/types/src/HorizontalPattern.ts +0 -37
  271. package/packages/types/src/Pattern.ts +0 -71
  272. package/packages/types/src/PatternJson.ts +0 -7
  273. package/packages/types/src/VerticalPattern.ts +0 -37
  274. /package/packages/scrabble-solver/.next/static/{vYpMmuC_yEurZGcyGVnLf → OLE8_ZInlGH7ENU0NIOpf}/_ssgManifest.js +0 -0
  275. /package/packages/scrabble-solver/src/{service-worker → solver-worker}/dictionaries/expirationManager.ts +0 -0
  276. /package/packages/scrabble-solver/src/{service-worker → solver-worker}/dictionaries/getDictionaryUrl.ts +0 -0
@@ -1,204 +0,0 @@
1
- /* eslint-disable max-lines */
2
-
3
- import { getConfig } from '@scrabble-solver/configs';
4
- import {
5
- type Bingo,
6
- Board,
7
- Cell,
8
- Config,
9
- Game,
10
- HorizontalPattern,
11
- Locale,
12
- Pattern,
13
- Tile,
14
- VerticalPattern,
15
- } from '@scrabble-solver/types';
16
-
17
- import { getPatternScore } from './getPatternScore';
18
-
19
- describe('getPatternScore', () => {
20
- describe('Literaki - PL_PL', () => {
21
- const config = getConfig(Game.Literaki, Locale.PL_PL);
22
- const board = Board.fromStringArray([
23
- ' kasom ',
24
- ' i ',
25
- ' napiją ',
26
- ' w i ',
27
- ' krabim ',
28
- ' z ',
29
- ' ę eh ',
30
- 'f s srać ',
31
- 'i z t s ',
32
- 'knebel e ',
33
- 'a ew warcz',
34
- 'ł żyło wody ',
35
- 'o pecyj chu',
36
- ' y ',
37
- ' t ',
38
- ]);
39
-
40
- it('gives proper score without collisions (źaaaaaaź)', () => {
41
- const pattern = new Pattern(board, [
42
- new Cell({ x: 0, y: 0, tile: new Tile({ character: 'ź' }), isEmpty: true }),
43
- new Cell({ x: 1, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
44
- new Cell({ x: 2, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
45
- new Cell({ x: 3, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
46
- new Cell({ x: 4, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
47
- new Cell({ x: 5, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
48
- new Cell({ x: 6, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
49
- new Cell({ x: 7, y: 0, tile: new Tile({ character: 'ź' }), isEmpty: false }),
50
- ]);
51
-
52
- expect(getPatternScore(config, pattern)).toBe(128);
53
- });
54
-
55
- it('gives proper score with collisions (źaaaaaaź)', () => {
56
- const pattern = new HorizontalPattern(board, [
57
- new Cell({ x: 0, y: 0, tile: new Tile({ character: 'ź' }), isEmpty: true }),
58
- new Cell({ x: 1, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
59
- new Cell({ x: 2, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
60
- new Cell({ x: 3, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
61
- new Cell({ x: 4, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
62
- new Cell({ x: 5, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
63
- new Cell({ x: 6, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
64
- new Cell({ x: 7, y: 0, tile: new Tile({ character: 'ź' }), isEmpty: false }),
65
- ]);
66
-
67
- expect(getPatternScore(config, pattern)).toBe(133);
68
- });
69
-
70
- it('gives proper score with collisions (lino)', () => {
71
- const pattern = new VerticalPattern(board, [
72
- new Cell({ x: 2, y: 11, tile: new Tile({ character: 'l' }), isEmpty: true }),
73
- new Cell({ x: 2, y: 12, tile: new Tile({ character: 'i' }), isEmpty: true }),
74
- new Cell({ x: 2, y: 13, tile: new Tile({ character: 'n' }), isEmpty: true }),
75
- new Cell({ x: 2, y: 14, tile: new Tile({ character: 'o' }), isEmpty: true }),
76
- ]);
77
-
78
- expect(getPatternScore(config, pattern)).toBe(44);
79
- });
80
-
81
- it('gives proper score for blank', () => {
82
- const pattern = new HorizontalPattern(board, [
83
- new Cell({
84
- x: 13,
85
- y: 14,
86
- tile: new Tile({ character: 'o', isBlank: true }),
87
- isEmpty: true,
88
- }),
89
- new Cell({ x: 12, y: 14, tile: new Tile({ character: 't' }), isEmpty: false }),
90
- ]);
91
-
92
- expect(getPatternScore(config, pattern)).toBe(2);
93
- });
94
-
95
- it('gives 0 score for unknown characters', () => {
96
- const pattern = new HorizontalPattern(board, [
97
- new Cell({
98
- x: 13,
99
- y: 14,
100
- tile: new Tile({ character: '?', isBlank: false }),
101
- isEmpty: true,
102
- }),
103
- new Cell({ x: 12, y: 14, tile: new Tile({ character: 't' }), isEmpty: false }),
104
- ]);
105
-
106
- expect(getPatternScore(config, pattern)).toBe(2);
107
- });
108
-
109
- it('gives 50 points bonus for bingo', () => {
110
- const pattern = new HorizontalPattern(board, [
111
- new Cell({ x: 6, y: 14, tile: new Tile({ character: 'p' }), isEmpty: true }),
112
- new Cell({ x: 7, y: 14, tile: new Tile({ character: 'o' }), isEmpty: true }),
113
- new Cell({ x: 8, y: 14, tile: new Tile({ character: 'r' }), isEmpty: true }),
114
- new Cell({ x: 9, y: 14, tile: new Tile({ character: 'o' }), isEmpty: true }),
115
- new Cell({ x: 10, y: 14, tile: new Tile({ character: 'w' }), isEmpty: true }),
116
- new Cell({
117
- x: 11,
118
- y: 14,
119
- tile: new Tile({ character: 'a', isBlank: true }),
120
- isEmpty: true,
121
- }),
122
- new Cell({ x: 11, y: 14, tile: new Tile({ character: 't' }), isEmpty: false }),
123
- new Cell({ x: 13, y: 14, tile: new Tile({ character: 'e' }), isEmpty: true }),
124
- ]);
125
-
126
- expect(getPatternScore(config, pattern)).toBe(59);
127
- });
128
- });
129
-
130
- describe('Letter League - EN_US', () => {
131
- const config = getConfig(Game.LetterLeague, Locale.EN_US);
132
-
133
- it('gives double multiplier bonus for bingo without collisions', () => {
134
- const board = Board.create(config.boardWidth, config.boardHeight);
135
- const pattern = new HorizontalPattern(board, [
136
- new Cell({ x: 13, y: 6, tile: new Tile({ character: 'r' }), isEmpty: true }),
137
- new Cell({ x: 13, y: 7, tile: new Tile({ character: 'o' }), isEmpty: true }),
138
- new Cell({ x: 13, y: 8, tile: new Tile({ character: 'm' }), isEmpty: true }),
139
- new Cell({ x: 13, y: 9, tile: new Tile({ character: 'a' }), isEmpty: true }),
140
- new Cell({ x: 13, y: 10, tile: new Tile({ character: 'i' }), isEmpty: true }),
141
- new Cell({ x: 13, y: 11, tile: new Tile({ character: 'n' }), isEmpty: true }),
142
- new Cell({ x: 13, y: 12, tile: new Tile({ character: 'e' }), isEmpty: true }),
143
- ]);
144
-
145
- expect(getPatternScore(config, pattern)).toBe(18);
146
- });
147
-
148
- it('gives double multiplier bonus for bingo with collisions', () => {
149
- const board = Board.fromStringArray([
150
- ' ',
151
- ' ',
152
- ' ',
153
- ' ',
154
- ' ',
155
- ' ',
156
- ' ',
157
- ' ',
158
- ' ',
159
- ' ',
160
- ' ',
161
- ' ',
162
- ' on ',
163
- ' ',
164
- ' ',
165
- ' ',
166
- ' ',
167
- ' ',
168
- ' ',
169
- ]);
170
-
171
- const pattern = new VerticalPattern(board, [
172
- new Cell({ x: 13, y: 6, tile: new Tile({ character: 'r' }), isEmpty: true }),
173
- new Cell({ x: 13, y: 7, tile: new Tile({ character: 'o' }), isEmpty: true }),
174
- new Cell({ x: 13, y: 8, tile: new Tile({ character: 'm' }), isEmpty: true }),
175
- new Cell({ x: 13, y: 9, tile: new Tile({ character: 'a' }), isEmpty: true }),
176
- new Cell({ x: 13, y: 10, tile: new Tile({ character: 'i' }), isEmpty: true }),
177
- new Cell({ x: 13, y: 11, tile: new Tile({ character: 'n' }), isEmpty: true }),
178
- new Cell({ x: 13, y: 12, tile: new Tile({ character: 'e' }), isEmpty: true }),
179
- ]);
180
-
181
- expect(getPatternScore(config, pattern)).toBe(21);
182
- });
183
- });
184
-
185
- describe('invalid bingo config', () => {
186
- const baseConfig = getConfig(Game.LetterLeague, Locale.EN_US);
187
- const config = Config.fromJson({ ...baseConfig.config, bingo: {} as unknown as Bingo });
188
-
189
- it('ignores the bingo bonus when the config is neither a score nor a multiplier bingo', () => {
190
- const board = Board.create(config.boardWidth, config.boardHeight);
191
- const pattern = new HorizontalPattern(board, [
192
- new Cell({ x: 13, y: 6, tile: new Tile({ character: 'r' }), isEmpty: true }),
193
- new Cell({ x: 13, y: 7, tile: new Tile({ character: 'o' }), isEmpty: true }),
194
- new Cell({ x: 13, y: 8, tile: new Tile({ character: 'm' }), isEmpty: true }),
195
- new Cell({ x: 13, y: 9, tile: new Tile({ character: 'a' }), isEmpty: true }),
196
- new Cell({ x: 13, y: 10, tile: new Tile({ character: 'i' }), isEmpty: true }),
197
- new Cell({ x: 13, y: 11, tile: new Tile({ character: 'n' }), isEmpty: true }),
198
- new Cell({ x: 13, y: 12, tile: new Tile({ character: 'e' }), isEmpty: true }),
199
- ]);
200
-
201
- expect(getPatternScore(config, pattern)).toBe(9);
202
- });
203
- });
204
- });
@@ -1,23 +0,0 @@
1
- import { type Config, isMultiplierBingo, isScoreBingo, type Pattern } from '@scrabble-solver/types';
2
-
3
- import { getCellsScore } from './getCellsScore';
4
-
5
- export const getPatternScore = (config: Config, pattern: Pattern) => {
6
- const areAllTilesUsed = pattern.getEmptyCellsCount() === config.rackSize;
7
- const baseScore = getCellsScore(config, pattern.cells);
8
- const collisionsScore = pattern
9
- .getCollisions()
10
- .reduce((sum, collision) => sum + getCellsScore(config, collision.cells), 0);
11
-
12
- if (areAllTilesUsed) {
13
- if (isScoreBingo(config.bingo)) {
14
- return baseScore + collisionsScore + config.bingo.score;
15
- }
16
-
17
- if (isMultiplierBingo(config.bingo)) {
18
- return baseScore * config.bingo.multiplier + collisionsScore;
19
- }
20
- }
21
-
22
- return baseScore + collisionsScore;
23
- };
@@ -1,19 +0,0 @@
1
- import { type Pattern } from '@scrabble-solver/types';
2
-
3
- import { getPatternHash } from './getPatternHash';
4
-
5
- export const getUniquePatterns = (patterns: Pattern[]): Pattern[] => {
6
- const hashes = new Set<string>();
7
- const uniquePatterns: Pattern[] = [];
8
-
9
- for (const pattern of patterns) {
10
- const hash = getPatternHash(pattern);
11
-
12
- if (!hashes.has(hash)) {
13
- hashes.add(hash);
14
- uniquePatterns.push(pattern);
15
- }
16
- }
17
-
18
- return uniquePatterns;
19
- };
@@ -1,2 +0,0 @@
1
- import { type CellJson } from './CellJson';
2
- export type CollisionJson = CellJson[];
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,6 +0,0 @@
1
- import { Pattern } from './Pattern';
2
- export declare class FinalPattern extends Pattern {
3
- private readonly collisions;
4
- constructor(pattern: Pattern);
5
- getCollisions(): Pattern[];
6
- }
@@ -1,14 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FinalPattern = void 0;
4
- const Pattern_1 = require("./Pattern");
5
- class FinalPattern extends Pattern_1.Pattern {
6
- constructor(pattern) {
7
- super(pattern.board, pattern.cells);
8
- this.collisions = pattern.getCollisions();
9
- }
10
- getCollisions() {
11
- return this.collisions;
12
- }
13
- }
14
- exports.FinalPattern = FinalPattern;
@@ -1,5 +0,0 @@
1
- import { Pattern } from './Pattern';
2
- export declare class HorizontalPattern extends Pattern {
3
- clone(): Pattern;
4
- getCollisions(): Pattern[];
5
- }
@@ -1,34 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HorizontalPattern = void 0;
4
- const Pattern_1 = require("./Pattern");
5
- class HorizontalPattern extends Pattern_1.Pattern {
6
- clone() {
7
- return new HorizontalPattern(this.board, this.cells.map((cell) => cell.clone()));
8
- }
9
- getCollisions() {
10
- const collisions = [];
11
- this.cells
12
- .filter((cell) => cell.isEmpty && (this.board.collidesUp(cell) || this.board.collidesDown(cell)))
13
- .forEach((cell) => {
14
- const column = this.board.getColumn(cell.x);
15
- let y = cell.y - 1;
16
- while (y >= 0 && column[y].hasTile()) {
17
- --y;
18
- }
19
- const previousCells = column.slice(y + 1, cell.y);
20
- y = cell.y + 1;
21
- while (y < column.length && column[y].hasTile()) {
22
- ++y;
23
- }
24
- const nextCells = column.slice(cell.y + 1, y);
25
- const cells = [...previousCells, cell, ...nextCells];
26
- if (cells.length > 1) {
27
- const pattern = new Pattern_1.Pattern(this.board, cells);
28
- collisions.push(pattern);
29
- }
30
- });
31
- return collisions;
32
- }
33
- }
34
- exports.HorizontalPattern = HorizontalPattern;
@@ -1,20 +0,0 @@
1
- import { type Board } from './Board';
2
- import { type Cell } from './Cell';
3
- import { type Config } from './Config';
4
- import { type PatternJson } from './PatternJson';
5
- export declare class Pattern {
6
- readonly board: Board;
7
- readonly cells: Cell[];
8
- constructor(board: Board, cells: Cell[]);
9
- canBePlaced(config: Config): boolean;
10
- clone(): Pattern;
11
- collides(): boolean;
12
- getIndexOfFirstCellWithoutTile(): number;
13
- getEmptyCellsCount(): number;
14
- goesThroughBoardCenter(): boolean;
15
- hasAtLeast1EmptyCell(): boolean;
16
- hasAtLeast1NonEmptyCell(): boolean;
17
- getCollisions(): Pattern[];
18
- toJson(): PatternJson;
19
- toString(): string;
20
- }
@@ -1,50 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Pattern = void 0;
4
- class Pattern {
5
- constructor(board, cells) {
6
- this.board = board;
7
- this.cells = cells;
8
- }
9
- canBePlaced(config) {
10
- const emptyCellsCount = this.getEmptyCellsCount();
11
- const isUsedCellsCountInRange = emptyCellsCount >= 1 && emptyCellsCount <= config.rackSize;
12
- return (isUsedCellsCountInRange &&
13
- (this.hasAtLeast1NonEmptyCell() || this.collides() || (this.goesThroughBoardCenter() && this.board.isEmpty())));
14
- }
15
- clone() {
16
- return new Pattern(this.board, this.cells.map((cell) => cell.clone()));
17
- }
18
- collides() {
19
- return this.cells.some((cell) => cell.isEmpty && this.board.collides(cell));
20
- }
21
- getIndexOfFirstCellWithoutTile() {
22
- return this.cells.findIndex((cell) => !cell.hasTile());
23
- }
24
- getEmptyCellsCount() {
25
- return this.cells.filter((cell) => cell.isEmpty).length;
26
- }
27
- goesThroughBoardCenter() {
28
- return this.cells.some((cell) => cell.x === this.board.center.x && cell.y === this.board.center.y && cell.isEmpty);
29
- }
30
- hasAtLeast1EmptyCell() {
31
- return this.cells.some((cell) => cell.isEmpty);
32
- }
33
- hasAtLeast1NonEmptyCell() {
34
- return this.cells.some((cell) => !cell.isEmpty);
35
- }
36
- getCollisions() {
37
- return [];
38
- }
39
- toJson() {
40
- return {
41
- cells: this.cells.map((cell) => cell.toJson()),
42
- collisions: this.getCollisions().map((collision) => collision.toJson()),
43
- word: this.toString(),
44
- };
45
- }
46
- toString() {
47
- return this.cells.reduce((result, cell) => result + cell.toString(), '');
48
- }
49
- }
50
- exports.Pattern = Pattern;
@@ -1,6 +0,0 @@
1
- import { type CellJson } from './CellJson';
2
- export interface PatternJson {
3
- cells: CellJson[];
4
- collisions: PatternJson[];
5
- word: string;
6
- }
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,5 +0,0 @@
1
- import { Pattern } from './Pattern';
2
- export declare class VerticalPattern extends Pattern {
3
- clone(): Pattern;
4
- getCollisions(): Pattern[];
5
- }
@@ -1,34 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VerticalPattern = void 0;
4
- const Pattern_1 = require("./Pattern");
5
- class VerticalPattern extends Pattern_1.Pattern {
6
- clone() {
7
- return new VerticalPattern(this.board, this.cells.map((cell) => cell.clone()));
8
- }
9
- getCollisions() {
10
- const collisions = [];
11
- this.cells
12
- .filter((cell) => cell.isEmpty && (this.board.collidesLeft(cell) || this.board.collidesRight(cell)))
13
- .forEach((cell) => {
14
- const row = this.board.getRow(cell.y);
15
- let x = cell.x - 1;
16
- while (x >= 0 && row[x].hasTile()) {
17
- --x;
18
- }
19
- const previousCells = row.slice(x + 1, cell.x);
20
- x = cell.x + 1;
21
- while (x < row.length && row[x].hasTile()) {
22
- ++x;
23
- }
24
- const nextCells = row.slice(cell.x + 1, x);
25
- const cells = [...previousCells, cell, ...nextCells];
26
- if (cells.length > 1) {
27
- const pattern = new Pattern_1.Pattern(this.board, cells);
28
- collisions.push(pattern);
29
- }
30
- });
31
- return collisions;
32
- }
33
- }
34
- exports.VerticalPattern = VerticalPattern;
@@ -1,3 +0,0 @@
1
- import { type CellJson } from './CellJson';
2
-
3
- export type CollisionJson = CellJson[];
@@ -1,14 +0,0 @@
1
- import { Pattern } from './Pattern';
2
-
3
- export class FinalPattern extends Pattern {
4
- private readonly collisions: Pattern[];
5
-
6
- constructor(pattern: Pattern) {
7
- super(pattern.board, pattern.cells);
8
- this.collisions = pattern.getCollisions();
9
- }
10
-
11
- public getCollisions(): Pattern[] {
12
- return this.collisions;
13
- }
14
- }
@@ -1,37 +0,0 @@
1
- import { Pattern } from './Pattern';
2
-
3
- export class HorizontalPattern extends Pattern {
4
- public clone(): Pattern {
5
- return new HorizontalPattern(
6
- this.board,
7
- this.cells.map((cell) => cell.clone()),
8
- );
9
- }
10
-
11
- public getCollisions(): Pattern[] {
12
- const collisions: Pattern[] = [];
13
-
14
- this.cells
15
- .filter((cell) => cell.isEmpty && (this.board.collidesUp(cell) || this.board.collidesDown(cell)))
16
- .forEach((cell) => {
17
- const column = this.board.getColumn(cell.x);
18
- let y = cell.y - 1;
19
- while (y >= 0 && column[y].hasTile()) {
20
- --y;
21
- }
22
- const previousCells = column.slice(y + 1, cell.y);
23
- y = cell.y + 1;
24
- while (y < column.length && column[y].hasTile()) {
25
- ++y;
26
- }
27
- const nextCells = column.slice(cell.y + 1, y);
28
- const cells = [...previousCells, cell, ...nextCells];
29
- if (cells.length > 1) {
30
- const pattern = new Pattern(this.board, cells);
31
- collisions.push(pattern);
32
- }
33
- });
34
-
35
- return collisions;
36
- }
37
- }
@@ -1,71 +0,0 @@
1
- import { type Board } from './Board';
2
- import { type Cell } from './Cell';
3
- import { type Config } from './Config';
4
- import { type PatternJson } from './PatternJson';
5
-
6
- export class Pattern {
7
- public readonly board: Board;
8
-
9
- public readonly cells: Cell[];
10
-
11
- constructor(board: Board, cells: Cell[]) {
12
- this.board = board;
13
- this.cells = cells;
14
- }
15
-
16
- public canBePlaced(config: Config): boolean {
17
- const emptyCellsCount = this.getEmptyCellsCount();
18
- const isUsedCellsCountInRange = emptyCellsCount >= 1 && emptyCellsCount <= config.rackSize;
19
- return (
20
- isUsedCellsCountInRange &&
21
- (this.hasAtLeast1NonEmptyCell() || this.collides() || (this.goesThroughBoardCenter() && this.board.isEmpty()))
22
- );
23
- }
24
-
25
- public clone(): Pattern {
26
- return new Pattern(
27
- this.board,
28
- this.cells.map((cell) => cell.clone()),
29
- );
30
- }
31
-
32
- public collides(): boolean {
33
- return this.cells.some((cell) => cell.isEmpty && this.board.collides(cell));
34
- }
35
-
36
- public getIndexOfFirstCellWithoutTile(): number {
37
- return this.cells.findIndex((cell) => !cell.hasTile());
38
- }
39
-
40
- public getEmptyCellsCount(): number {
41
- return this.cells.filter((cell) => cell.isEmpty).length;
42
- }
43
-
44
- public goesThroughBoardCenter(): boolean {
45
- return this.cells.some((cell) => cell.x === this.board.center.x && cell.y === this.board.center.y && cell.isEmpty);
46
- }
47
-
48
- public hasAtLeast1EmptyCell(): boolean {
49
- return this.cells.some((cell) => cell.isEmpty);
50
- }
51
-
52
- public hasAtLeast1NonEmptyCell(): boolean {
53
- return this.cells.some((cell) => !cell.isEmpty);
54
- }
55
-
56
- public getCollisions(): Pattern[] {
57
- return [];
58
- }
59
-
60
- public toJson(): PatternJson {
61
- return {
62
- cells: this.cells.map((cell) => cell.toJson()),
63
- collisions: this.getCollisions().map((collision) => collision.toJson()),
64
- word: this.toString(),
65
- };
66
- }
67
-
68
- public toString(): string {
69
- return this.cells.reduce((result, cell) => result + cell.toString(), '');
70
- }
71
- }
@@ -1,7 +0,0 @@
1
- import { type CellJson } from './CellJson';
2
-
3
- export interface PatternJson {
4
- cells: CellJson[];
5
- collisions: PatternJson[];
6
- word: string;
7
- }
@@ -1,37 +0,0 @@
1
- import { Pattern } from './Pattern';
2
-
3
- export class VerticalPattern extends Pattern {
4
- public clone(): Pattern {
5
- return new VerticalPattern(
6
- this.board,
7
- this.cells.map((cell) => cell.clone()),
8
- );
9
- }
10
-
11
- public getCollisions(): Pattern[] {
12
- const collisions: Pattern[] = [];
13
-
14
- this.cells
15
- .filter((cell) => cell.isEmpty && (this.board.collidesLeft(cell) || this.board.collidesRight(cell)))
16
- .forEach((cell) => {
17
- const row = this.board.getRow(cell.y);
18
- let x = cell.x - 1;
19
- while (x >= 0 && row[x].hasTile()) {
20
- --x;
21
- }
22
- const previousCells = row.slice(x + 1, cell.x);
23
- x = cell.x + 1;
24
- while (x < row.length && row[x].hasTile()) {
25
- ++x;
26
- }
27
- const nextCells = row.slice(cell.x + 1, x);
28
- const cells = [...previousCells, cell, ...nextCells];
29
- if (cells.length > 1) {
30
- const pattern = new Pattern(this.board, cells);
31
- collisions.push(pattern);
32
- }
33
- });
34
-
35
- return collisions;
36
- }
37
- }