scrabble-solver 2.8.7 → 2.8.9

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 (200) hide show
  1. package/.eslintrc.prettier-only.js +1 -1
  2. package/.prettierignore +3 -0
  3. package/.prettierrc +3 -3
  4. package/README.md +8 -8
  5. package/babel.config.js +1 -1
  6. package/lerna.json +1 -1
  7. package/package.json +6 -6
  8. package/packages/configs/package.json +3 -3
  9. package/packages/constants/package.json +1 -1
  10. package/packages/dictionaries/package-lock.json +2 -2
  11. package/packages/dictionaries/package.json +4 -4
  12. package/packages/logger/package-lock.json +2 -2
  13. package/packages/logger/package.json +1 -1
  14. package/packages/scrabble-solver/.next/BUILD_ID +1 -1
  15. package/packages/scrabble-solver/.next/build-manifest.json +10 -10
  16. package/packages/scrabble-solver/.next/cache/.tsbuildinfo +1 -1
  17. package/packages/scrabble-solver/.next/cache/eslint/.cache_8dgz12 +1 -1
  18. package/packages/scrabble-solver/.next/cache/next-server.js.nft.json +1 -1
  19. package/packages/scrabble-solver/.next/cache/webpack/client-production/0.pack +0 -0
  20. package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack +0 -0
  21. package/packages/scrabble-solver/.next/cache/webpack/server-production/0.pack +0 -0
  22. package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack +0 -0
  23. package/packages/scrabble-solver/.next/next-server.js.nft.json +1 -1
  24. package/packages/scrabble-solver/.next/prerender-manifest.json +1 -1
  25. package/packages/scrabble-solver/.next/routes-manifest.json +1 -1
  26. package/packages/scrabble-solver/.next/server/chunks/413.js +257 -55
  27. package/packages/scrabble-solver/.next/server/chunks/44.js +802 -0
  28. package/packages/scrabble-solver/.next/server/chunks/515.js +452 -104
  29. package/packages/scrabble-solver/.next/server/chunks/911.js +53 -23
  30. package/packages/scrabble-solver/.next/server/middleware-build-manifest.js +1 -1
  31. package/packages/scrabble-solver/.next/server/pages/404.html +2 -2
  32. package/packages/scrabble-solver/.next/server/pages/404.js.nft.json +1 -1
  33. package/packages/scrabble-solver/.next/server/pages/500.html +2 -2
  34. package/packages/scrabble-solver/.next/server/pages/_app.js.nft.json +1 -1
  35. package/packages/scrabble-solver/.next/server/pages/_document.js.nft.json +1 -1
  36. package/packages/scrabble-solver/.next/server/pages/_error.js.nft.json +1 -1
  37. package/packages/scrabble-solver/.next/server/pages/api/solve.js +193 -927
  38. package/packages/scrabble-solver/.next/server/pages/api/solve.js.nft.json +1 -1
  39. package/packages/scrabble-solver/.next/server/pages/api/verify.js +217 -0
  40. package/packages/scrabble-solver/.next/server/pages/api/verify.js.nft.json +1 -0
  41. package/packages/scrabble-solver/.next/server/pages/index.html +3 -3
  42. package/packages/scrabble-solver/.next/server/pages/index.js +7 -1
  43. package/packages/scrabble-solver/.next/server/pages/index.js.nft.json +1 -1
  44. package/packages/scrabble-solver/.next/server/pages/index.json +1 -1
  45. package/packages/scrabble-solver/.next/server/pages-manifest.json +1 -0
  46. package/packages/scrabble-solver/.next/static/chunks/317-a33dd38e9b9a17ed.js +1 -0
  47. package/packages/scrabble-solver/.next/static/chunks/pages/{404-30c06e61d256c5b2.js → 404-90c624da3c83fd17.js} +1 -1
  48. package/packages/scrabble-solver/.next/static/chunks/pages/_app-f8f360878e1c2aff.js +1 -0
  49. package/packages/scrabble-solver/.next/static/chunks/pages/index-ecea697d3e5d8a6f.js +1 -0
  50. package/packages/scrabble-solver/.next/static/css/64dc2ce1811912f1.css +1 -0
  51. package/packages/scrabble-solver/.next/static/css/{cdbc9e0afcff5473.css → ad2a08918868cad8.css} +1 -1
  52. package/packages/scrabble-solver/.next/static/yCxjzzYpw5JjJE53PO_s6/_buildManifest.js +1 -0
  53. package/packages/scrabble-solver/.next/static/{z_0_lqfmiI_ISokr6NNRq → yCxjzzYpw5JjJE53PO_s6}/_ssgManifest.js +0 -0
  54. package/packages/scrabble-solver/.next/trace +42 -40
  55. package/packages/scrabble-solver/package-lock.json +2 -2
  56. package/packages/scrabble-solver/package.json +8 -8
  57. package/packages/scrabble-solver/src/components/Badge/Badge.module.scss +13 -0
  58. package/packages/scrabble-solver/src/components/Badge/Badge.tsx +15 -0
  59. package/packages/scrabble-solver/src/components/Badge/index.ts +1 -0
  60. package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.module.scss +34 -9
  61. package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.tsx +23 -4
  62. package/packages/scrabble-solver/src/components/Board/components/Cell/CellPure.tsx +29 -1
  63. package/packages/scrabble-solver/src/components/Board/hooks/useGrid.ts +1 -0
  64. package/packages/scrabble-solver/src/components/NavButtons/NavButtons.tsx +33 -14
  65. package/packages/scrabble-solver/src/components/RemainingTiles/RemainingTiles.module.scss +8 -7
  66. package/packages/scrabble-solver/src/components/RemainingTiles/RemainingTiles.tsx +13 -4
  67. package/packages/scrabble-solver/src/components/Sidebar/Sidebar.tsx +2 -2
  68. package/packages/scrabble-solver/src/components/Sidebar/components/Section/Section.module.scss +0 -1
  69. package/packages/scrabble-solver/src/components/Sidebar/components/Section/Section.tsx +1 -1
  70. package/packages/scrabble-solver/src/components/SquareButton/SquareButton.module.scss +5 -0
  71. package/packages/scrabble-solver/src/components/Words/Words.module.scss +35 -0
  72. package/packages/scrabble-solver/src/components/Words/Words.tsx +57 -0
  73. package/packages/scrabble-solver/src/components/Words/index.ts +1 -0
  74. package/packages/scrabble-solver/src/components/index.ts +2 -0
  75. package/packages/scrabble-solver/src/i18n/de.json +5 -1
  76. package/packages/scrabble-solver/src/i18n/en.json +5 -1
  77. package/packages/scrabble-solver/src/i18n/es.json +5 -1
  78. package/packages/scrabble-solver/src/i18n/fr.json +5 -1
  79. package/packages/scrabble-solver/src/i18n/pl.json +5 -1
  80. package/packages/scrabble-solver/src/icons/BookHalf.svg +4 -0
  81. package/packages/scrabble-solver/src/icons/Check.svg +4 -0
  82. package/packages/scrabble-solver/src/icons/Cross.svg +2 -2
  83. package/packages/scrabble-solver/src/icons/CrossFill.svg +4 -0
  84. package/packages/scrabble-solver/src/icons/Flag.svg +4 -0
  85. package/packages/scrabble-solver/src/icons/Star.svg +4 -0
  86. package/packages/scrabble-solver/src/icons/index.ts +5 -0
  87. package/packages/scrabble-solver/src/pages/api/solve.ts +2 -3
  88. package/packages/scrabble-solver/src/pages/api/verify.ts +71 -0
  89. package/packages/scrabble-solver/src/pages/index.tsx +5 -0
  90. package/packages/scrabble-solver/src/sdk/index.ts +1 -0
  91. package/packages/scrabble-solver/src/sdk/verify.ts +24 -0
  92. package/packages/scrabble-solver/src/state/rootReducer.ts +12 -1
  93. package/packages/scrabble-solver/src/state/sagas.ts +54 -7
  94. package/packages/scrabble-solver/src/state/selectors.ts +49 -11
  95. package/packages/scrabble-solver/src/state/slices/cellFilterInitialState.ts +7 -0
  96. package/packages/scrabble-solver/src/state/slices/cellFilterSlice.ts +24 -0
  97. package/packages/scrabble-solver/src/state/slices/index.ts +4 -0
  98. package/packages/scrabble-solver/src/state/slices/verifyInitialState.ts +12 -0
  99. package/packages/scrabble-solver/src/state/slices/verifySlice.ts +31 -0
  100. package/packages/scrabble-solver/src/styles/variables.scss +2 -1
  101. package/packages/scrabble-solver/src/types/index.ts +5 -1
  102. package/packages/solver/build/fillPattern.d.ts +5 -0
  103. package/packages/solver/build/fillPattern.js +52 -0
  104. package/packages/solver/build/generateEndIndices.d.ts +3 -0
  105. package/packages/solver/build/generateEndIndices.js +9 -0
  106. package/packages/solver/build/generateHorizontalPatterns.d.ts +3 -0
  107. package/packages/solver/build/generateHorizontalPatterns.js +18 -0
  108. package/packages/solver/build/generatePattern.d.ts +8 -0
  109. package/packages/solver/build/generatePattern.js +22 -0
  110. package/packages/solver/build/generatePatterns.d.ts +3 -0
  111. package/packages/solver/build/generatePatterns.js +13 -0
  112. package/packages/solver/build/generateStartIndices.d.ts +3 -0
  113. package/packages/solver/build/generateStartIndices.js +9 -0
  114. package/packages/solver/build/generateVectors.d.ts +6 -0
  115. package/packages/solver/build/generateVectors.js +8 -0
  116. package/packages/solver/build/generateVerticalPatterns.d.ts +3 -0
  117. package/packages/solver/build/generateVerticalPatterns.js +18 -0
  118. package/packages/solver/build/getCellsScore.d.ts +3 -0
  119. package/packages/solver/build/getCellsScore.js +16 -0
  120. package/packages/solver/build/getPatternHash.d.ts +3 -0
  121. package/packages/solver/build/getPatternHash.js +13 -0
  122. package/packages/solver/build/getPatternScore.d.ts +3 -0
  123. package/packages/solver/build/getPatternScore.js +15 -0
  124. package/packages/solver/build/getUniquePatterns.js +5 -11
  125. package/packages/solver/build/index.d.ts +1 -1
  126. package/packages/solver/build/index.js +3 -3
  127. package/packages/solver/build/solve.d.ts +4 -0
  128. package/packages/solver/build/solve.js +23 -0
  129. package/packages/solver/package-lock.json +2 -2
  130. package/packages/solver/package.json +5 -5
  131. package/packages/solver/src/fillPattern.test.ts +107 -0
  132. package/packages/solver/src/fillPattern.ts +70 -0
  133. package/packages/solver/src/generateEndIndices.test.ts +18 -0
  134. package/packages/solver/src/generateEndIndices.ts +10 -0
  135. package/packages/solver/src/generateHorizontalPatterns.test.ts +31 -0
  136. package/packages/solver/src/generateHorizontalPatterns.ts +17 -0
  137. package/packages/solver/src/generatePattern.ts +35 -0
  138. package/packages/solver/src/generatePatterns.ts +13 -0
  139. package/packages/solver/src/generateStartIndices.test.ts +14 -0
  140. package/packages/solver/src/generateStartIndices.ts +10 -0
  141. package/packages/solver/src/generateVectors.test.ts +12 -0
  142. package/packages/solver/src/generateVectors.ts +15 -0
  143. package/packages/solver/src/generateVerticalPatterns.test.ts +32 -0
  144. package/packages/solver/src/generateVerticalPatterns.ts +17 -0
  145. package/packages/solver/src/getCellsScore.ts +22 -0
  146. package/packages/solver/src/getPatternHash.test.ts +17 -0
  147. package/packages/solver/src/getPatternHash.ts +14 -0
  148. package/packages/solver/src/getPatternScore.test.ts +60 -0
  149. package/packages/solver/src/getPatternScore.ts +15 -0
  150. package/packages/solver/src/getUniquePatterns.ts +1 -10
  151. package/packages/solver/src/index.ts +1 -1
  152. package/packages/solver/src/{Solver.test.ts → solve.test.ts} +14 -13
  153. package/packages/solver/src/solve.ts +25 -0
  154. package/packages/types/build/Board.d.ts +1 -0
  155. package/packages/types/build/Board.js +17 -1
  156. package/packages/types/build/Config.js +1 -1
  157. package/packages/types/build/FinalPattern.d.ts +7 -0
  158. package/packages/types/build/FinalPattern.js +16 -0
  159. package/packages/types/build/HorizontalPattern.js +2 -5
  160. package/packages/types/build/Pattern.d.ts +1 -4
  161. package/packages/types/build/Pattern.js +2 -5
  162. package/packages/types/build/Result.js +3 -5
  163. package/packages/types/build/Tile.d.ts +2 -2
  164. package/packages/types/build/VerticalPattern.js +2 -5
  165. package/packages/types/build/index.d.ts +1 -0
  166. package/packages/types/build/index.js +3 -1
  167. package/packages/types/package.json +2 -2
  168. package/packages/types/src/Board.ts +23 -1
  169. package/packages/types/src/Config.ts +1 -1
  170. package/packages/types/src/FinalPattern.ts +16 -0
  171. package/packages/types/src/HorizontalPattern.ts +5 -5
  172. package/packages/types/src/Pattern.ts +5 -5
  173. package/packages/types/src/Result.ts +3 -5
  174. package/packages/types/src/Tile.ts +2 -2
  175. package/packages/types/src/VerticalPattern.ts +5 -5
  176. package/packages/types/src/index.ts +1 -0
  177. package/packages/word-definitions/package-lock.json +8 -8
  178. package/packages/word-definitions/package.json +2 -2
  179. package/packages/word-lists/package-lock.json +2 -2
  180. package/packages/word-lists/package.json +2 -2
  181. package/packages/scrabble-solver/.next/static/chunks/56-cf37c430261bbea5.js +0 -1
  182. package/packages/scrabble-solver/.next/static/chunks/pages/_app-0b12a65bea70a0df.js +0 -1
  183. package/packages/scrabble-solver/.next/static/chunks/pages/index-fcb69802550afb81.js +0 -1
  184. package/packages/scrabble-solver/.next/static/css/1f39b55d50f5b30b.css +0 -1
  185. package/packages/scrabble-solver/.next/static/z_0_lqfmiI_ISokr6NNRq/_buildManifest.js +0 -1
  186. package/packages/solver/build/PatternsFiller.d.ts +0 -12
  187. package/packages/solver/build/PatternsFiller.js +0 -70
  188. package/packages/solver/build/PatternsGenerator.d.ts +0 -32
  189. package/packages/solver/build/PatternsGenerator.js +0 -66
  190. package/packages/solver/build/ScoresCalculator.d.ts +0 -17
  191. package/packages/solver/build/ScoresCalculator.js +0 -37
  192. package/packages/solver/build/Solver.d.ts +0 -10
  193. package/packages/solver/build/Solver.js +0 -30
  194. package/packages/solver/src/PatternsFiller.test.ts +0 -92
  195. package/packages/solver/src/PatternsFiller.ts +0 -91
  196. package/packages/solver/src/PatternsGenerator.test.ts +0 -89
  197. package/packages/solver/src/PatternsGenerator.ts +0 -103
  198. package/packages/solver/src/ScoresCalculator.test.ts +0 -70
  199. package/packages/solver/src/ScoresCalculator.ts +0 -52
  200. package/packages/solver/src/Solver.ts +0 -39
@@ -0,0 +1,35 @@
1
+ import { Board, Cell, Config, Pattern } from '@scrabble-solver/types';
2
+
3
+ import generateEndIndices from './generateEndIndices';
4
+ import generateStartIndices from './generateStartIndices';
5
+
6
+ const generatePattern = <P extends Pattern>({
7
+ board,
8
+ cells,
9
+ config,
10
+ PatternModel,
11
+ }: {
12
+ board: Board;
13
+ cells: Cell[];
14
+ config: Config;
15
+ PatternModel: new (board: Board, cells: Cell[]) => P;
16
+ }): P[] => {
17
+ const startIndices = generateStartIndices(cells);
18
+
19
+ return startIndices.flatMap((startIndex) => {
20
+ const endIndices = generateEndIndices(cells, startIndex);
21
+ const patterns: P[] = [];
22
+
23
+ for (const endIndex of endIndices) {
24
+ const pattern = new PatternModel(board, cells.slice(startIndex, endIndex + 1));
25
+
26
+ if (pattern.canBePlaced(config)) {
27
+ patterns.push(pattern);
28
+ }
29
+ }
30
+
31
+ return patterns;
32
+ });
33
+ };
34
+
35
+ export default generatePattern;
@@ -0,0 +1,13 @@
1
+ import { Board, Config, Pattern } from '@scrabble-solver/types';
2
+
3
+ import generateHorizontalPatterns from './generateHorizontalPatterns';
4
+ import generateVerticalPatterns from './generateVerticalPatterns';
5
+
6
+ const generatePatterns = (config: Config, board: Board): Pattern[] => {
7
+ const horizontalPatterns = generateHorizontalPatterns(config, board);
8
+ const verticalPatterns = generateVerticalPatterns(config, board);
9
+
10
+ return horizontalPatterns.concat(verticalPatterns);
11
+ };
12
+
13
+ export default generatePatterns;
@@ -0,0 +1,14 @@
1
+ import { Cell } from '@scrabble-solver/types';
2
+
3
+ import generateStartIndices from './generateStartIndices';
4
+
5
+ describe('generateStartIndices', () => {
6
+ const emptyCell: Cell = { hasTile: () => false } as Cell;
7
+ const filledCell: Cell = { hasTile: () => true } as Cell;
8
+
9
+ it('finds indices where a new word can start', () => {
10
+ const cells = [filledCell, filledCell, emptyCell, emptyCell, emptyCell, filledCell];
11
+ const startIndices = generateStartIndices(cells);
12
+ expect(startIndices).toEqual([0, 3, 4]);
13
+ });
14
+ });
@@ -0,0 +1,10 @@
1
+ import { Cell } from '@scrabble-solver/types';
2
+
3
+ const generateStartIndices = (cells: Cell[]): number[] => {
4
+ return Array(cells.length - 1)
5
+ .fill(0)
6
+ .map((_, startIndex) => startIndex)
7
+ .filter((startIndex) => startIndex === 0 || !cells[startIndex - 1].hasTile());
8
+ };
9
+
10
+ export default generateStartIndices;
@@ -0,0 +1,12 @@
1
+ import generateVectors from './generateVectors';
2
+
3
+ describe('generateVectors', () => {
4
+ it('generates given number of vectors', () => {
5
+ const vectors = generateVectors({
6
+ getNthVector: () => [],
7
+ vectorsCount: 3,
8
+ });
9
+ expect(vectors.length).toBe(3);
10
+ expect(vectors).toEqual([[], [], []]);
11
+ });
12
+ });
@@ -0,0 +1,15 @@
1
+ import { Cell } from '@scrabble-solver/types';
2
+
3
+ const generateVectors = ({
4
+ getNthVector,
5
+ vectorsCount,
6
+ }: {
7
+ getNthVector: (index: number) => Cell[];
8
+ vectorsCount: number;
9
+ }): Cell[][] => {
10
+ return Array(vectorsCount)
11
+ .fill(0)
12
+ .map((_, index) => getNthVector(index));
13
+ };
14
+
15
+ export default generateVectors;
@@ -0,0 +1,32 @@
1
+ import { EMPTY_CELL } from '@scrabble-solver/constants';
2
+ import { Board, Config } from '@scrabble-solver/types';
3
+
4
+ import generateVerticalPatterns from './generateVerticalPatterns';
5
+
6
+ const board = Board.fromStringArray([' t ', 'do ', ' ']);
7
+
8
+ const config = {
9
+ boardHeight: 3,
10
+ boardWidth: 3,
11
+ maximumCharactersCount: 7,
12
+ } as Config;
13
+
14
+ describe('generateVerticalPatterns', () => {
15
+ it('generates some vertical patterns', () => {
16
+ expect(generateVerticalPatterns(config, board).length).toBeGreaterThan(0);
17
+ });
18
+
19
+ it('generates proper vertical patterns', () => {
20
+ const vertical = generateVerticalPatterns(config, board);
21
+
22
+ expect(vertical.map(({ cells }) => cells.map(String))).toEqual([
23
+ [EMPTY_CELL, 'd'],
24
+ [EMPTY_CELL, 'd', EMPTY_CELL],
25
+ ['d', EMPTY_CELL],
26
+ ['t', 'o', EMPTY_CELL],
27
+ [EMPTY_CELL, EMPTY_CELL],
28
+ [EMPTY_CELL, EMPTY_CELL, EMPTY_CELL],
29
+ [EMPTY_CELL, EMPTY_CELL],
30
+ ]);
31
+ });
32
+ });
@@ -0,0 +1,17 @@
1
+ import { Board, Config, Pattern, VerticalPattern } from '@scrabble-solver/types';
2
+
3
+ import generatePattern from './generatePattern';
4
+ import generateVectors from './generateVectors';
5
+
6
+ const generateVerticalPatterns = (config: Config, board: Board): Pattern[] => {
7
+ const getNthVector = (index: number) => board.getColumn(index);
8
+ const vectorsCount = config.boardWidth;
9
+ const verticalVectors = generateVectors({ getNthVector, vectorsCount });
10
+ const verticalPatterns = verticalVectors.flatMap((cells) => {
11
+ return generatePattern({ board, config, PatternModel: VerticalPattern, cells });
12
+ });
13
+
14
+ return verticalPatterns;
15
+ };
16
+
17
+ export default generateVerticalPatterns;
@@ -0,0 +1,22 @@
1
+ import { NO_BONUS } from '@scrabble-solver/constants';
2
+ import { Cell, Config } from '@scrabble-solver/types';
3
+
4
+ const getCellsScore = (config: Config, cells: Cell[]): number => {
5
+ const total = cells.reduce(
6
+ ({ multiplier, score }, cell: Cell): { multiplier: number; score: number } => {
7
+ const bonus = config.getCellBonus(cell);
8
+ const { characterMultiplier, wordMultiplier } = bonus && bonus.canApply(config, cell) ? bonus.value : NO_BONUS;
9
+ const characterScore = cell.tile.isBlank ? config.blankScore : config.pointsMap[cell.tile.character];
10
+
11
+ return {
12
+ multiplier: multiplier * wordMultiplier,
13
+ score: score + characterScore * characterMultiplier,
14
+ };
15
+ },
16
+ { multiplier: 1, score: 0 },
17
+ );
18
+
19
+ return total.score * total.multiplier;
20
+ };
21
+
22
+ export default getCellsScore;
@@ -0,0 +1,17 @@
1
+ import { Board, Cell, Pattern, Tile } from '@scrabble-solver/types';
2
+
3
+ import getPatternHash from './getPatternHash';
4
+
5
+ describe('getPatternHash', () => {
6
+ const board = Board.fromStringArray([' t ', 'do ', ' ']);
7
+
8
+ it('hashes blanks', () => {
9
+ const pattern = new Pattern(board, [
10
+ new Cell({ x: 0, y: 0, tile: new Tile({ character: 'a', isBlank: false }) }),
11
+ new Cell({ x: 0, y: 1, tile: new Tile({ character: 'b', isBlank: true }) }),
12
+ new Cell({ x: 0, y: 2, tile: new Tile({ character: 'c', isBlank: false }) }),
13
+ ]);
14
+
15
+ expect(getPatternHash(pattern)).toBe('0,0,a-0,1,b!-0,2,c');
16
+ });
17
+ });
@@ -0,0 +1,14 @@
1
+ import { Pattern } from '@scrabble-solver/types';
2
+
3
+ const getPatternHash = (pattern: Pattern): string => {
4
+ return pattern.cells
5
+ .map((cell) => {
6
+ const blank = cell.tile.isBlank ? '!' : '';
7
+ const tile = cell.tile.character + blank;
8
+ // eslint-disable-next-line prefer-template
9
+ return cell.x + ',' + cell.y + ',' + tile;
10
+ })
11
+ .join('-');
12
+ };
13
+
14
+ export default getPatternHash;
@@ -0,0 +1,60 @@
1
+ import { literaki } from '@scrabble-solver/configs';
2
+ import { Board, Cell, HorizontalPattern, Locale, Pattern, Tile, VerticalPattern } from '@scrabble-solver/types';
3
+
4
+ import getPatternScore from './getPatternScore';
5
+
6
+ const config = literaki[Locale.PL_PL];
7
+ const board = Board.fromStringArray([
8
+ ' kasom ',
9
+ ' i ',
10
+ ' napiją ',
11
+ ' w i ',
12
+ ' krabim ',
13
+ ' z ',
14
+ ' ę eh ',
15
+ 'f s srać ',
16
+ 'i z t s ',
17
+ 'knebel e ',
18
+ 'a ew warcz',
19
+ 'ł żyło wody ',
20
+ 'o pecyj chu',
21
+ ' y ',
22
+ ' t ',
23
+ ]);
24
+
25
+ describe('getPatternScore', () => {
26
+ it('gives proper score without collisions', () => {
27
+ const pattern = new Pattern(board, [
28
+ new Cell({ x: 0, y: 0, tile: new Tile({ character: 'ź' }), isEmpty: true }),
29
+ new Cell({ x: 1, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
30
+ new Cell({ x: 2, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
31
+ new Cell({ x: 3, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
32
+ new Cell({ x: 4, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
33
+ new Cell({ x: 5, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
34
+ new Cell({ x: 6, y: 0, tile: new Tile({ character: 'a' }), isEmpty: true }),
35
+ new Cell({ x: 7, y: 0, tile: new Tile({ character: 'ź' }), isEmpty: false }),
36
+ ]);
37
+
38
+ expect(getPatternScore(config, pattern)).toBe(128);
39
+ });
40
+
41
+ it('gives proper score with collisions', () => {
42
+ const pattern = new VerticalPattern(board, [
43
+ new Cell({ x: 2, y: 11, tile: new Tile({ character: 'l' }), isEmpty: true }),
44
+ new Cell({ x: 2, y: 12, tile: new Tile({ character: 'i' }), isEmpty: true }),
45
+ new Cell({ x: 2, y: 13, tile: new Tile({ character: 'n' }), isEmpty: true }),
46
+ new Cell({ x: 2, y: 14, tile: new Tile({ character: 'o' }), isEmpty: true }),
47
+ ]);
48
+
49
+ expect(getPatternScore(config, pattern)).toBe(44);
50
+ });
51
+
52
+ it('gives proper score for blank', () => {
53
+ const pattern = new HorizontalPattern(board, [
54
+ new Cell({ x: 13, y: 14, tile: new Tile({ character: 'o', isBlank: true }), isEmpty: true }),
55
+ new Cell({ x: 12, y: 14, tile: new Tile({ character: 't' }), isEmpty: false }),
56
+ ]);
57
+
58
+ expect(getPatternScore(config, pattern)).toBe(2);
59
+ });
60
+ });
@@ -0,0 +1,15 @@
1
+ import { Config, Pattern } from '@scrabble-solver/types';
2
+
3
+ import getCellsScore from './getCellsScore';
4
+
5
+ const getPatternScore = (config: Config, pattern: Pattern) => {
6
+ const areAllTilesUsed = pattern.getEmptyCellsCount() === config.maximumCharactersCount;
7
+ const bonusScore = areAllTilesUsed ? config.allTilesBonusScore : 0;
8
+ const score = pattern
9
+ .getCollisions()
10
+ .reduce((sum, collision) => sum + getCellsScore(config, collision.cells), getCellsScore(config, pattern.cells));
11
+
12
+ return score + bonusScore;
13
+ };
14
+
15
+ export default getPatternScore;
@@ -1,15 +1,6 @@
1
1
  import { Pattern } from '@scrabble-solver/types';
2
2
 
3
- const getPatternHash = (pattern: Pattern): string => {
4
- return pattern.cells
5
- .map((cell) => {
6
- const blank = cell.tile.isBlank ? '!' : '';
7
- const tile = cell.tile.character + blank;
8
- // eslint-disable-next-line prefer-template
9
- return cell.x + ',' + cell.y + ',' + tile;
10
- })
11
- .join('-');
12
- };
3
+ import getPatternHash from './getPatternHash';
13
4
 
14
5
  const getUniquePatterns = (patterns: Pattern[]): Pattern[] => {
15
6
  const hashes = new Set<string>();
@@ -1 +1 @@
1
- export { default } from './Solver';
1
+ export { default as solve } from './solve';
@@ -1,8 +1,9 @@
1
+ import { Trie } from '@kamilmielnik/trie';
1
2
  import { literaki, scrabble } from '@scrabble-solver/configs';
2
3
  import { dictionaries } from '@scrabble-solver/dictionaries';
3
4
  import { Board, Locale, Result, Tile } from '@scrabble-solver/types';
4
5
 
5
- import Solver from './Solver';
6
+ import solve from './solve';
6
7
 
7
8
  const generateTiles = (characters: string[]): Tile[] => {
8
9
  return characters.map((character) => new Tile({ character, isBlank: false }));
@@ -15,14 +16,14 @@ const getBestResult = ([firstResult, ...results]: Result[]): Result => {
15
16
  );
16
17
  };
17
18
 
18
- describe('Solver - PL', () => {
19
+ describe('solve - PL', () => {
19
20
  const locale = Locale.PL_PL;
20
21
  const config = literaki[locale];
21
- let solver: Solver | undefined;
22
+ let trie: Trie | undefined;
22
23
 
23
24
  beforeAll(() => {
24
- return dictionaries.get(locale).then((trie) => {
25
- solver = new Solver(config, trie);
25
+ return dictionaries.get(locale).then((loadedTrie) => {
26
+ trie = loadedTrie;
26
27
  });
27
28
  });
28
29
 
@@ -45,7 +46,7 @@ describe('Solver - PL', () => {
45
46
  ' ',
46
47
  ]);
47
48
  const tiles = generateTiles(['l', 'i', 'n', 'o']);
48
- const results = solver!.solve(board, tiles);
49
+ const results = solve(trie!, config, board, tiles);
49
50
  expect(results.length).toBe(61);
50
51
  });
51
52
 
@@ -68,7 +69,7 @@ describe('Solver - PL', () => {
68
69
  ' ',
69
70
  ]);
70
71
  const tiles = generateTiles(['a', 'a', 'ą', 'r', 't', 'w', 'z']);
71
- const results = solver!.solve(board, tiles);
72
+ const results = solve(trie!, config, board, tiles);
72
73
  const bestResult = getBestResult(results);
73
74
  expect(bestResult.word).toBe('zmartwychwstałą');
74
75
  expect(bestResult.points).toBe(682);
@@ -93,21 +94,21 @@ describe('Solver - PL', () => {
93
94
  'ar ń m ',
94
95
  ]);
95
96
  const tiles = generateTiles(['ą', 'c', 'h', 't', 'w', 'w', 'z']);
96
- const results = solver!.solve(board, tiles);
97
+ const results = solve(trie!, config, board, tiles);
97
98
  const bestResult = getBestResult(results);
98
99
  expect(bestResult.word).toBe('zmartwychwstałą');
99
100
  expect(bestResult.points).toBe(1157);
100
101
  });
101
102
  });
102
103
 
103
- describe('Solver - ES', () => {
104
+ describe('solve - ES', () => {
104
105
  const locale = Locale.ES_ES;
105
106
  const config = scrabble[locale];
106
- let solver: Solver | undefined;
107
+ let trie: Trie | undefined;
107
108
 
108
109
  beforeAll(() => {
109
- return dictionaries.get(locale).then((trie) => {
110
- solver = new Solver(config, trie);
110
+ return dictionaries.get(locale).then((loadedTrie) => {
111
+ trie = loadedTrie;
111
112
  });
112
113
  });
113
114
 
@@ -130,7 +131,7 @@ describe('Solver - ES', () => {
130
131
  ' ',
131
132
  ]);
132
133
  const tiles = generateTiles(['ll', 'a', 'n', 'a']);
133
- const results = solver!.solve(board, tiles);
134
+ const results = solve(trie!, config, board, tiles);
134
135
  const bestResult = getBestResult(results);
135
136
  expect(results.length).toBe(24);
136
137
  expect(bestResult.points).toBe(22);
@@ -0,0 +1,25 @@
1
+ import { Trie } from '@kamilmielnik/trie';
2
+ import { Board, Config, Result, Tile } from '@scrabble-solver/types';
3
+
4
+ import fillPattern from './fillPattern';
5
+ import generatePatterns from './generatePatterns';
6
+ import getPatternScore from './getPatternScore';
7
+ import getUniquePatterns from './getUniquePatterns';
8
+
9
+ const solve = (trie: Trie, config: Config, board: Board, tiles: Tile[]): Result[] => {
10
+ const patterns = generatePatterns(config, board);
11
+ const filledPatterns = patterns.flatMap((pattern) => fillPattern(trie, config, pattern, tiles));
12
+ const uniquePatterns = getUniquePatterns(filledPatterns);
13
+ const results = uniquePatterns.map(
14
+ (pattern, index) =>
15
+ new Result({
16
+ cells: pattern.cells,
17
+ collisions: pattern.getCollisions().map((collision) => collision.cells),
18
+ id: index,
19
+ points: getPatternScore(config, pattern),
20
+ }),
21
+ );
22
+ return results;
23
+ };
24
+
25
+ export default solve;
@@ -22,6 +22,7 @@ declare class Board {
22
22
  getColumn(index: number): Cell[];
23
23
  getRow(index: number): Cell[];
24
24
  getTilesCount(): number;
25
+ getWords(): string[];
25
26
  isEmpty(): boolean;
26
27
  toJson(): BoardJson;
27
28
  toString(): string;
@@ -79,6 +79,22 @@ class Board {
79
79
  return count + row.reduce((rowCount, cell) => (cell.hasTile() ? rowCount + 1 : rowCount), 0);
80
80
  }, 0);
81
81
  }
82
+ getWords() {
83
+ const columns = [];
84
+ for (let x = 0; x < this.columnsCount; ++x) {
85
+ const column = [];
86
+ for (let y = 0; y < this.rowsCount; ++y) {
87
+ column.push(this.rows[y][x]);
88
+ }
89
+ columns.push(column);
90
+ }
91
+ const columnsBoard = new Board({ rows: columns });
92
+ const lines = this.toString().split('\n').concat(columnsBoard.toString().split('\n'));
93
+ const words = lines
94
+ .flatMap((line) => line.replaceAll(/\s+/g, constants_1.EMPTY_CELL).split(' '))
95
+ .filter((word) => word.length > 1);
96
+ return words;
97
+ }
82
98
  isEmpty() {
83
99
  return this.rows.every((row) => row.every(({ isEmpty }) => isEmpty));
84
100
  }
@@ -86,7 +102,7 @@ class Board {
86
102
  return this.rows.map((row) => row.map((cell) => cell.toJson()));
87
103
  }
88
104
  toString() {
89
- return this.rows.map((row) => row.map(String)).join('\n');
105
+ return this.rows.map((row) => row.map(String).join('')).join('\n');
90
106
  }
91
107
  updateCell(x, y, updateCell) {
92
108
  this.rows[y][x] = updateCell(this.rows[y][x]);
@@ -64,7 +64,7 @@ class Config {
64
64
  return tile.isBlank ? this.blankScore : this.getCharacterPoints(tile.character);
65
65
  }
66
66
  hasCharacter(character) {
67
- return this.alphabet.includes(character);
67
+ return character in this.pointsMap;
68
68
  }
69
69
  isTwoCharacterTilePrefix(character) {
70
70
  return typeof this.getTwoCharacterTileByPrefix(character) !== 'undefined';
@@ -0,0 +1,7 @@
1
+ import Pattern from './Pattern';
2
+ declare class FinalPattern extends Pattern {
3
+ private readonly collisions;
4
+ constructor(pattern: Pattern);
5
+ getCollisions(): Pattern[];
6
+ }
7
+ export default FinalPattern;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const Pattern_1 = __importDefault(require("./Pattern"));
7
+ class FinalPattern extends Pattern_1.default {
8
+ constructor(pattern) {
9
+ super(pattern.board, pattern.cells);
10
+ this.collisions = pattern.getCollisions();
11
+ }
12
+ getCollisions() {
13
+ return this.collisions;
14
+ }
15
+ }
16
+ exports.default = FinalPattern;
@@ -6,10 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const Pattern_1 = __importDefault(require("./Pattern"));
7
7
  class HorizontalPattern extends Pattern_1.default {
8
8
  clone() {
9
- return new HorizontalPattern({
10
- board: this.board,
11
- cells: this.cells.map((cell) => cell.clone()),
12
- });
9
+ return new HorizontalPattern(this.board, this.cells.map((cell) => cell.clone()));
13
10
  }
14
11
  getCollisions() {
15
12
  const collisions = [];
@@ -29,7 +26,7 @@ class HorizontalPattern extends Pattern_1.default {
29
26
  const nextCells = column.slice(cell.y + 1, y);
30
27
  const cells = [...previousCells, cell, ...nextCells];
31
28
  if (cells.length > 1) {
32
- const pattern = new Pattern_1.default({ board: this.board, cells });
29
+ const pattern = new Pattern_1.default(this.board, cells);
33
30
  collisions.push(pattern);
34
31
  }
35
32
  });
@@ -5,10 +5,7 @@ import PatternJson from './PatternJson';
5
5
  declare class Pattern {
6
6
  readonly board: Board;
7
7
  readonly cells: Cell[];
8
- constructor({ board, cells }: {
9
- board: Board;
10
- cells: Cell[];
11
- });
8
+ constructor(board: Board, cells: Cell[]);
12
9
  canBePlaced(config: Config): boolean;
13
10
  clone(): Pattern;
14
11
  collides(): boolean;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  class Pattern {
4
- constructor({ board, cells }) {
4
+ constructor(board, cells) {
5
5
  this.board = board;
6
6
  this.cells = cells;
7
7
  }
@@ -12,10 +12,7 @@ class Pattern {
12
12
  (this.hasAtLeast1NonEmptyCell() || this.collides() || (this.goesThroughBoardCenter() && this.board.isEmpty())));
13
13
  }
14
14
  clone() {
15
- return new Pattern({
16
- board: this.board,
17
- cells: this.cells.map((cell) => cell.clone()),
18
- });
15
+ return new Pattern(this.board, this.cells.map((cell) => cell.clone()));
19
16
  }
20
17
  collides() {
21
18
  return this.cells.some((cell) => cell.isEmpty && this.board.collides(cell));
@@ -50,11 +50,9 @@ const getNonBlanks = (tiles) => tiles.filter(({ isBlank }) => !isBlank);
50
50
  const getPointsRatio = (tiles, points) => points / tiles.length;
51
51
  const getTiles = (cells) => cells.filter(({ isEmpty }) => isEmpty).map(({ tile }) => tile);
52
52
  const getTilesCharacters = (tiles) => getNonBlankCharacters(tiles).sort(charactersComparator).join('');
53
- const getWord = (cells) => cells.map(String).join('');
54
- const getWords = (cells, collisions) => [
55
- getWord(cells),
56
- ...collisions.map((collision) => collision.map(String).join('')),
57
- ];
53
+ // eslint-disable-next-line prefer-template
54
+ const getWord = (cells) => cells.reduce((word, cell) => word + cell.toString(), '');
55
+ const getWords = (cells, collisions) => [cells, ...collisions].map(getWord);
58
56
  const isConsonant = ({ character, isBlank }) => constants_1.CONSONANTS.includes(character) && !isBlank;
59
57
  const isVowel = ({ character, isBlank }) => constants_1.VOWELS.includes(character) && !isBlank;
60
58
  exports.default = Result;
@@ -2,8 +2,8 @@ import TileJson from './TileJson';
2
2
  declare class Tile {
3
3
  static fromJson(json: TileJson | null): Tile;
4
4
  static readonly Null: Tile;
5
- readonly character: string;
6
- readonly isBlank: boolean;
5
+ character: string;
6
+ isBlank: boolean;
7
7
  constructor({ character, isBlank }: {
8
8
  character: string;
9
9
  isBlank?: boolean;
@@ -6,10 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const Pattern_1 = __importDefault(require("./Pattern"));
7
7
  class VerticalPattern extends Pattern_1.default {
8
8
  clone() {
9
- return new VerticalPattern({
10
- board: this.board,
11
- cells: this.cells.map((cell) => cell.clone()),
12
- });
9
+ return new VerticalPattern(this.board, this.cells.map((cell) => cell.clone()));
13
10
  }
14
11
  getCollisions() {
15
12
  const collisions = [];
@@ -29,7 +26,7 @@ class VerticalPattern extends Pattern_1.default {
29
26
  const nextCells = row.slice(cell.x + 1, x);
30
27
  const cells = [...previousCells, cell, ...nextCells];
31
28
  if (cells.length > 1) {
32
- const pattern = new Pattern_1.default({ board: this.board, cells });
29
+ const pattern = new Pattern_1.default(this.board, cells);
33
30
  collisions.push(pattern);
34
31
  }
35
32
  });
@@ -10,6 +10,7 @@ export { default as Collision } from './Collision';
10
10
  export { default as CollisionJson } from './CollisionJson';
11
11
  export { default as Config } from './Config';
12
12
  export { default as ConfigJson } from './ConfigJson';
13
+ export { default as FinalPattern } from './FinalPattern';
13
14
  export { default as HorizontalPattern } from './HorizontalPattern';
14
15
  export { default as isObject } from './isObject';
15
16
  export { default as Locale, isLocale } from './Locale';
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.WordDefinition = exports.WordBonus = exports.VerticalPattern = exports.isTileJson = exports.Tile = exports.Result = exports.Pattern = exports.isLocale = exports.Locale = exports.isObject = exports.HorizontalPattern = exports.Config = exports.CharacterBonus = exports.isCellJson = exports.Cell = exports.Bonus = exports.isBoardJson = exports.Board = void 0;
6
+ exports.WordDefinition = exports.WordBonus = exports.VerticalPattern = exports.isTileJson = exports.Tile = exports.Result = exports.Pattern = exports.isLocale = exports.Locale = exports.isObject = exports.HorizontalPattern = exports.FinalPattern = exports.Config = exports.CharacterBonus = exports.isCellJson = exports.Cell = exports.Bonus = exports.isBoardJson = exports.Board = void 0;
7
7
  var Board_1 = require("./Board");
8
8
  Object.defineProperty(exports, "Board", { enumerable: true, get: function () { return __importDefault(Board_1).default; } });
9
9
  var BoardJson_1 = require("./BoardJson");
@@ -18,6 +18,8 @@ var CharacterBonus_1 = require("./CharacterBonus");
18
18
  Object.defineProperty(exports, "CharacterBonus", { enumerable: true, get: function () { return __importDefault(CharacterBonus_1).default; } });
19
19
  var Config_1 = require("./Config");
20
20
  Object.defineProperty(exports, "Config", { enumerable: true, get: function () { return __importDefault(Config_1).default; } });
21
+ var FinalPattern_1 = require("./FinalPattern");
22
+ Object.defineProperty(exports, "FinalPattern", { enumerable: true, get: function () { return __importDefault(FinalPattern_1).default; } });
21
23
  var HorizontalPattern_1 = require("./HorizontalPattern");
22
24
  Object.defineProperty(exports, "HorizontalPattern", { enumerable: true, get: function () { return __importDefault(HorizontalPattern_1).default; } });
23
25
  var isObject_1 = require("./isObject");
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scrabble-solver/types",
3
- "version": "2.8.7",
3
+ "version": "2.8.9",
4
4
  "description": "Scrabble Solver 2 - Types",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -24,7 +24,7 @@
24
24
  "clean:force": "npm run clean && rimraf package-lock.json"
25
25
  },
26
26
  "dependencies": {
27
- "@scrabble-solver/constants": "^2.8.7"
27
+ "@scrabble-solver/constants": "^2.8.9"
28
28
  },
29
29
  "gitHead": "1e2d1ad49a288d4a44196d8e3336740da132c637"
30
30
  }