scrabble-solver 2.8.8 → 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 (190) hide show
  1. package/.eslintrc.prettier-only.js +1 -1
  2. package/.prettierignore +3 -0
  3. package/.prettierrc +3 -3
  4. package/babel.config.js +1 -1
  5. package/lerna.json +1 -1
  6. package/package.json +6 -6
  7. package/packages/configs/package.json +3 -3
  8. package/packages/constants/package.json +1 -1
  9. package/packages/dictionaries/package-lock.json +2 -2
  10. package/packages/dictionaries/package.json +4 -4
  11. package/packages/logger/package-lock.json +2 -2
  12. package/packages/logger/package.json +1 -1
  13. package/packages/scrabble-solver/.next/BUILD_ID +1 -1
  14. package/packages/scrabble-solver/.next/build-manifest.json +10 -10
  15. package/packages/scrabble-solver/.next/cache/.tsbuildinfo +1 -1
  16. package/packages/scrabble-solver/.next/cache/eslint/.cache_8dgz12 +1 -1
  17. package/packages/scrabble-solver/.next/cache/next-server.js.nft.json +1 -1
  18. package/packages/scrabble-solver/.next/cache/webpack/client-production/0.pack +0 -0
  19. package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack +0 -0
  20. package/packages/scrabble-solver/.next/cache/webpack/server-production/0.pack +0 -0
  21. package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack +0 -0
  22. package/packages/scrabble-solver/.next/next-server.js.nft.json +1 -1
  23. package/packages/scrabble-solver/.next/prerender-manifest.json +1 -1
  24. package/packages/scrabble-solver/.next/routes-manifest.json +1 -1
  25. package/packages/scrabble-solver/.next/server/chunks/413.js +179 -44
  26. package/packages/scrabble-solver/.next/server/chunks/44.js +802 -0
  27. package/packages/scrabble-solver/.next/server/chunks/515.js +359 -95
  28. package/packages/scrabble-solver/.next/server/chunks/911.js +53 -23
  29. package/packages/scrabble-solver/.next/server/middleware-build-manifest.js +1 -1
  30. package/packages/scrabble-solver/.next/server/pages/404.html +2 -2
  31. package/packages/scrabble-solver/.next/server/pages/404.js.nft.json +1 -1
  32. package/packages/scrabble-solver/.next/server/pages/500.html +2 -2
  33. package/packages/scrabble-solver/.next/server/pages/_app.js.nft.json +1 -1
  34. package/packages/scrabble-solver/.next/server/pages/_error.js.nft.json +1 -1
  35. package/packages/scrabble-solver/.next/server/pages/api/solve.js +193 -927
  36. package/packages/scrabble-solver/.next/server/pages/api/solve.js.nft.json +1 -1
  37. package/packages/scrabble-solver/.next/server/pages/api/verify.js +217 -0
  38. package/packages/scrabble-solver/.next/server/pages/api/verify.js.nft.json +1 -0
  39. package/packages/scrabble-solver/.next/server/pages/index.html +3 -3
  40. package/packages/scrabble-solver/.next/server/pages/index.js +7 -1
  41. package/packages/scrabble-solver/.next/server/pages/index.js.nft.json +1 -1
  42. package/packages/scrabble-solver/.next/server/pages/index.json +1 -1
  43. package/packages/scrabble-solver/.next/server/pages-manifest.json +1 -0
  44. package/packages/scrabble-solver/.next/static/chunks/317-a33dd38e9b9a17ed.js +1 -0
  45. package/packages/scrabble-solver/.next/static/chunks/pages/{404-8eb3ba4f0ba17e08.js → 404-90c624da3c83fd17.js} +1 -1
  46. package/packages/scrabble-solver/.next/static/chunks/pages/_app-f8f360878e1c2aff.js +1 -0
  47. package/packages/scrabble-solver/.next/static/chunks/pages/index-ecea697d3e5d8a6f.js +1 -0
  48. package/packages/scrabble-solver/.next/static/css/64dc2ce1811912f1.css +1 -0
  49. package/packages/scrabble-solver/.next/static/css/{cdbc9e0afcff5473.css → ad2a08918868cad8.css} +1 -1
  50. package/packages/scrabble-solver/.next/static/yCxjzzYpw5JjJE53PO_s6/_buildManifest.js +1 -0
  51. package/packages/scrabble-solver/.next/static/{z3J3qmq1nazbDv_ENIkCo → yCxjzzYpw5JjJE53PO_s6}/_ssgManifest.js +0 -0
  52. package/packages/scrabble-solver/.next/trace +42 -41
  53. package/packages/scrabble-solver/package-lock.json +2 -2
  54. package/packages/scrabble-solver/package.json +8 -8
  55. package/packages/scrabble-solver/src/components/Badge/Badge.module.scss +13 -0
  56. package/packages/scrabble-solver/src/components/Badge/Badge.tsx +15 -0
  57. package/packages/scrabble-solver/src/components/Badge/index.ts +1 -0
  58. package/packages/scrabble-solver/src/components/NavButtons/NavButtons.tsx +33 -14
  59. package/packages/scrabble-solver/src/components/RemainingTiles/RemainingTiles.module.scss +8 -7
  60. package/packages/scrabble-solver/src/components/RemainingTiles/RemainingTiles.tsx +13 -4
  61. package/packages/scrabble-solver/src/components/Sidebar/Sidebar.tsx +2 -2
  62. package/packages/scrabble-solver/src/components/Sidebar/components/Section/Section.module.scss +0 -1
  63. package/packages/scrabble-solver/src/components/Sidebar/components/Section/Section.tsx +1 -1
  64. package/packages/scrabble-solver/src/components/SquareButton/SquareButton.module.scss +5 -0
  65. package/packages/scrabble-solver/src/components/Words/Words.module.scss +35 -0
  66. package/packages/scrabble-solver/src/components/Words/Words.tsx +57 -0
  67. package/packages/scrabble-solver/src/components/Words/index.ts +1 -0
  68. package/packages/scrabble-solver/src/components/index.ts +2 -0
  69. package/packages/scrabble-solver/src/i18n/de.json +4 -1
  70. package/packages/scrabble-solver/src/i18n/en.json +4 -1
  71. package/packages/scrabble-solver/src/i18n/es.json +4 -1
  72. package/packages/scrabble-solver/src/i18n/fr.json +4 -1
  73. package/packages/scrabble-solver/src/i18n/pl.json +4 -1
  74. package/packages/scrabble-solver/src/icons/BookHalf.svg +4 -0
  75. package/packages/scrabble-solver/src/icons/Check.svg +4 -0
  76. package/packages/scrabble-solver/src/icons/Cross.svg +2 -2
  77. package/packages/scrabble-solver/src/icons/CrossFill.svg +4 -0
  78. package/packages/scrabble-solver/src/icons/index.ts +3 -0
  79. package/packages/scrabble-solver/src/pages/api/solve.ts +2 -3
  80. package/packages/scrabble-solver/src/pages/api/verify.ts +71 -0
  81. package/packages/scrabble-solver/src/pages/index.tsx +5 -0
  82. package/packages/scrabble-solver/src/sdk/index.ts +1 -0
  83. package/packages/scrabble-solver/src/sdk/verify.ts +24 -0
  84. package/packages/scrabble-solver/src/state/rootReducer.ts +2 -0
  85. package/packages/scrabble-solver/src/state/sagas.ts +33 -6
  86. package/packages/scrabble-solver/src/state/selectors.ts +8 -0
  87. package/packages/scrabble-solver/src/state/slices/index.ts +2 -0
  88. package/packages/scrabble-solver/src/state/slices/verifyInitialState.ts +12 -0
  89. package/packages/scrabble-solver/src/state/slices/verifySlice.ts +31 -0
  90. package/packages/scrabble-solver/src/styles/variables.scss +2 -1
  91. package/packages/scrabble-solver/src/types/index.ts +4 -1
  92. package/packages/solver/build/fillPattern.d.ts +5 -0
  93. package/packages/solver/build/fillPattern.js +52 -0
  94. package/packages/solver/build/generateEndIndices.d.ts +3 -0
  95. package/packages/solver/build/generateEndIndices.js +9 -0
  96. package/packages/solver/build/generateHorizontalPatterns.d.ts +3 -0
  97. package/packages/solver/build/generateHorizontalPatterns.js +18 -0
  98. package/packages/solver/build/generatePattern.d.ts +8 -0
  99. package/packages/solver/build/generatePattern.js +22 -0
  100. package/packages/solver/build/generatePatterns.d.ts +3 -0
  101. package/packages/solver/build/generatePatterns.js +13 -0
  102. package/packages/solver/build/generateStartIndices.d.ts +3 -0
  103. package/packages/solver/build/generateStartIndices.js +9 -0
  104. package/packages/solver/build/generateVectors.d.ts +6 -0
  105. package/packages/solver/build/generateVectors.js +8 -0
  106. package/packages/solver/build/generateVerticalPatterns.d.ts +3 -0
  107. package/packages/solver/build/generateVerticalPatterns.js +18 -0
  108. package/packages/solver/build/getCellsScore.d.ts +3 -0
  109. package/packages/solver/build/getCellsScore.js +16 -0
  110. package/packages/solver/build/getPatternHash.d.ts +3 -0
  111. package/packages/solver/build/getPatternHash.js +13 -0
  112. package/packages/solver/build/getPatternScore.d.ts +3 -0
  113. package/packages/solver/build/getPatternScore.js +15 -0
  114. package/packages/solver/build/getUniquePatterns.js +5 -11
  115. package/packages/solver/build/index.d.ts +1 -1
  116. package/packages/solver/build/index.js +3 -3
  117. package/packages/solver/build/solve.d.ts +4 -0
  118. package/packages/solver/build/solve.js +23 -0
  119. package/packages/solver/package-lock.json +2 -2
  120. package/packages/solver/package.json +5 -5
  121. package/packages/solver/src/fillPattern.test.ts +107 -0
  122. package/packages/solver/src/fillPattern.ts +70 -0
  123. package/packages/solver/src/generateEndIndices.test.ts +18 -0
  124. package/packages/solver/src/generateEndIndices.ts +10 -0
  125. package/packages/solver/src/generateHorizontalPatterns.test.ts +31 -0
  126. package/packages/solver/src/generateHorizontalPatterns.ts +17 -0
  127. package/packages/solver/src/generatePattern.ts +35 -0
  128. package/packages/solver/src/generatePatterns.ts +13 -0
  129. package/packages/solver/src/generateStartIndices.test.ts +14 -0
  130. package/packages/solver/src/generateStartIndices.ts +10 -0
  131. package/packages/solver/src/generateVectors.test.ts +12 -0
  132. package/packages/solver/src/generateVectors.ts +15 -0
  133. package/packages/solver/src/generateVerticalPatterns.test.ts +32 -0
  134. package/packages/solver/src/generateVerticalPatterns.ts +17 -0
  135. package/packages/solver/src/getCellsScore.ts +22 -0
  136. package/packages/solver/src/getPatternHash.test.ts +17 -0
  137. package/packages/solver/src/getPatternHash.ts +14 -0
  138. package/packages/solver/src/getPatternScore.test.ts +60 -0
  139. package/packages/solver/src/getPatternScore.ts +15 -0
  140. package/packages/solver/src/getUniquePatterns.ts +1 -10
  141. package/packages/solver/src/index.ts +1 -1
  142. package/packages/solver/src/{Solver.test.ts → solve.test.ts} +14 -13
  143. package/packages/solver/src/solve.ts +25 -0
  144. package/packages/types/build/Board.d.ts +1 -0
  145. package/packages/types/build/Board.js +17 -1
  146. package/packages/types/build/Config.js +1 -1
  147. package/packages/types/build/FinalPattern.d.ts +7 -0
  148. package/packages/types/build/FinalPattern.js +16 -0
  149. package/packages/types/build/HorizontalPattern.js +2 -5
  150. package/packages/types/build/Pattern.d.ts +1 -4
  151. package/packages/types/build/Pattern.js +2 -5
  152. package/packages/types/build/Result.js +3 -5
  153. package/packages/types/build/Tile.d.ts +2 -2
  154. package/packages/types/build/VerticalPattern.js +2 -5
  155. package/packages/types/build/index.d.ts +1 -0
  156. package/packages/types/build/index.js +3 -1
  157. package/packages/types/package.json +2 -2
  158. package/packages/types/src/Board.ts +23 -1
  159. package/packages/types/src/Config.ts +1 -1
  160. package/packages/types/src/FinalPattern.ts +16 -0
  161. package/packages/types/src/HorizontalPattern.ts +5 -5
  162. package/packages/types/src/Pattern.ts +5 -5
  163. package/packages/types/src/Result.ts +3 -5
  164. package/packages/types/src/Tile.ts +2 -2
  165. package/packages/types/src/VerticalPattern.ts +5 -5
  166. package/packages/types/src/index.ts +1 -0
  167. package/packages/word-definitions/package-lock.json +2 -2
  168. package/packages/word-definitions/package.json +2 -2
  169. package/packages/word-lists/package-lock.json +2 -2
  170. package/packages/word-lists/package.json +2 -2
  171. package/packages/scrabble-solver/.next/static/chunks/615-d258f6c528c18622.js +0 -1
  172. package/packages/scrabble-solver/.next/static/chunks/pages/_app-4a663fd3d5ca4524.js +0 -1
  173. package/packages/scrabble-solver/.next/static/chunks/pages/index-1a9826d740cc8830.js +0 -1
  174. package/packages/scrabble-solver/.next/static/css/180c6c26317ac90f.css +0 -1
  175. package/packages/scrabble-solver/.next/static/z3J3qmq1nazbDv_ENIkCo/_buildManifest.js +0 -1
  176. package/packages/solver/build/PatternsFiller.d.ts +0 -12
  177. package/packages/solver/build/PatternsFiller.js +0 -70
  178. package/packages/solver/build/PatternsGenerator.d.ts +0 -32
  179. package/packages/solver/build/PatternsGenerator.js +0 -66
  180. package/packages/solver/build/ScoresCalculator.d.ts +0 -17
  181. package/packages/solver/build/ScoresCalculator.js +0 -37
  182. package/packages/solver/build/Solver.d.ts +0 -10
  183. package/packages/solver/build/Solver.js +0 -30
  184. package/packages/solver/src/PatternsFiller.test.ts +0 -92
  185. package/packages/solver/src/PatternsFiller.ts +0 -91
  186. package/packages/solver/src/PatternsGenerator.test.ts +0 -89
  187. package/packages/solver/src/PatternsGenerator.ts +0 -103
  188. package/packages/solver/src/ScoresCalculator.test.ts +0 -70
  189. package/packages/solver/src/ScoresCalculator.ts +0 -52
  190. package/packages/solver/src/Solver.ts +0 -39
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@scrabble-solver/scrabble-solver",
3
- "version": "2.8.8",
3
+ "version": "2.8.9",
4
4
  "lockfileVersion": 2,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@scrabble-solver/scrabble-solver",
9
- "version": "2.8.8",
9
+ "version": "2.8.9",
10
10
  "license": "CC BY-NC-ND 4.0",
11
11
  "dependencies": {
12
12
  "@popperjs/core": "^2.11.6",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scrabble-solver/scrabble-solver",
3
- "version": "2.8.8",
3
+ "version": "2.8.9",
4
4
  "description": "Scrabble Solver 2 - App",
5
5
  "engines": {
6
6
  "node": ">=16"
@@ -30,13 +30,13 @@
30
30
  "dependencies": {
31
31
  "@popperjs/core": "^2.11.6",
32
32
  "@reduxjs/toolkit": "^1.8.5",
33
- "@scrabble-solver/configs": "^2.8.8",
34
- "@scrabble-solver/constants": "^2.8.8",
35
- "@scrabble-solver/dictionaries": "^2.8.8",
36
- "@scrabble-solver/logger": "^2.8.8",
37
- "@scrabble-solver/solver": "^2.8.8",
38
- "@scrabble-solver/types": "^2.8.8",
39
- "@scrabble-solver/word-definitions": "^2.8.8",
33
+ "@scrabble-solver/configs": "^2.8.9",
34
+ "@scrabble-solver/constants": "^2.8.9",
35
+ "@scrabble-solver/dictionaries": "^2.8.9",
36
+ "@scrabble-solver/logger": "^2.8.9",
37
+ "@scrabble-solver/solver": "^2.8.9",
38
+ "@scrabble-solver/types": "^2.8.9",
39
+ "@scrabble-solver/word-definitions": "^2.8.9",
40
40
  "classnames": "^2.3.2",
41
41
  "next": "^12.3.1",
42
42
  "normalize.css": "^8.0.1",
@@ -0,0 +1,13 @@
1
+ .badge {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ justify-content: center;
5
+ padding: var(--spacing--xs) var(--spacing--s);
6
+ border-radius: var(--border--radius);
7
+ background-color: var(--color--violet--light);
8
+ box-shadow: va(--box-shadow);
9
+ line-height: var(--line-height);
10
+ font-size: var(--font--size--s);
11
+ font-weight: bold;
12
+ color: white;
13
+ }
@@ -0,0 +1,15 @@
1
+ import classNames from 'classnames';
2
+ import { FunctionComponent, ReactNode } from 'react';
3
+
4
+ import styles from './Badge.module.scss';
5
+
6
+ interface Props {
7
+ children?: ReactNode;
8
+ className?: string;
9
+ }
10
+
11
+ const Badge: FunctionComponent<Props> = ({ children, className }) => (
12
+ <div className={classNames(styles.badge, className)}>{children}</div>
13
+ );
14
+
15
+ export default Badge;
@@ -0,0 +1 @@
1
+ export { default } from './Badge';
@@ -1,9 +1,9 @@
1
1
  import classNames from 'classnames';
2
2
  import { FunctionComponent } from 'react';
3
3
 
4
- import { Cog, Eraser, Github, Keyboard, Sack } from 'icons';
4
+ import { BookHalf, Cog, Eraser, Github, Keyboard, Sack } from 'icons';
5
5
  import { GITHUB_PROJECT_URL } from 'parameters';
6
- import { selectHasOverusedTiles, useTranslate, useTypedSelector } from 'state';
6
+ import { selectHasInvalidWords, selectHasOverusedTiles, useTranslate, useTypedSelector } from 'state';
7
7
 
8
8
  import SquareButton from '../SquareButton';
9
9
 
@@ -14,27 +14,26 @@ interface Props {
14
14
  onShowKeyMap: () => void;
15
15
  onShowRemainingTiles: () => void;
16
16
  onShowSettings: () => void;
17
+ onShowWords: () => void;
17
18
  }
18
19
 
19
- const NavButtons: FunctionComponent<Props> = ({ onClear, onShowKeyMap, onShowRemainingTiles, onShowSettings }) => {
20
+ const NavButtons: FunctionComponent<Props> = ({
21
+ onClear,
22
+ onShowKeyMap,
23
+ onShowRemainingTiles,
24
+ onShowSettings,
25
+ onShowWords,
26
+ }) => {
20
27
  const translate = useTranslate();
21
28
  const hasOverusedTiles = useTypedSelector(selectHasOverusedTiles);
29
+ const hasInvalidWords = useTypedSelector(selectHasInvalidWords);
22
30
 
23
31
  return (
24
32
  <div className={styles.navButtons}>
25
- <SquareButton.Link
26
- className={styles.button}
27
- href={GITHUB_PROJECT_URL}
28
- Icon={Github}
29
- rel="noopener noreferrer"
30
- target="_blank"
31
- tooltip={translate('github')}
32
- />
33
+ <SquareButton className={styles.button} Icon={Eraser} tooltip={translate('common.clear')} onClick={onClear} />
33
34
 
34
35
  <div className={styles.separator} />
35
36
 
36
- <SquareButton className={styles.button} Icon={Keyboard} tooltip={translate('keyMap')} onClick={onShowKeyMap} />
37
-
38
37
  <SquareButton
39
38
  className={classNames(styles.button, {
40
39
  [styles.error]: hasOverusedTiles,
@@ -44,9 +43,29 @@ const NavButtons: FunctionComponent<Props> = ({ onClear, onShowKeyMap, onShowRem
44
43
  onClick={onShowRemainingTiles}
45
44
  />
46
45
 
46
+ <SquareButton
47
+ className={classNames(styles.button, {
48
+ [styles.error]: hasInvalidWords,
49
+ })}
50
+ Icon={BookHalf}
51
+ tooltip={translate('words')}
52
+ onClick={onShowWords}
53
+ />
54
+
47
55
  <div className={styles.separator} />
48
56
 
49
- <SquareButton className={styles.button} Icon={Eraser} tooltip={translate('common.clear')} onClick={onClear} />
57
+ <SquareButton.Link
58
+ className={styles.button}
59
+ href={GITHUB_PROJECT_URL}
60
+ Icon={Github}
61
+ rel="noopener noreferrer"
62
+ target="_blank"
63
+ tooltip={translate('github')}
64
+ />
65
+
66
+ <div className={styles.separator} />
67
+
68
+ <SquareButton className={styles.button} Icon={Keyboard} tooltip={translate('keyMap')} onClick={onShowKeyMap} />
50
69
 
51
70
  <SquareButton className={styles.button} Icon={Cog} tooltip={translate('settings')} onClick={onShowSettings} />
52
71
  </div>
@@ -1,11 +1,8 @@
1
- .group {
2
- margin-bottom: var(--spacing--l);
3
- }
4
-
5
1
  .title {
6
- margin-bottom: var(--spacing--m);
7
- font-size: var(--font--size--h2);
8
- font-weight: 300;
2
+ display: flex;
3
+ align-items: center;
4
+ justify-content: space-between;
5
+ flex-wrap: wrap;
9
6
  }
10
7
 
11
8
  .content {
@@ -13,3 +10,7 @@
13
10
  grid-template-columns: repeat(5, 1fr);
14
11
  gap: var(--spacing--m);
15
12
  }
13
+
14
+ .badge {
15
+ margin-left: var(--spacing--m);
16
+ }
@@ -2,6 +2,7 @@ import { FunctionComponent } from 'react';
2
2
 
3
3
  import { selectRemainingTilesGroups, useTranslate, useTypedSelector } from 'state';
4
4
 
5
+ import Badge from '../Badge';
5
6
  import Sidebar from '../Sidebar';
6
7
 
7
8
  import Character from './Character';
@@ -20,15 +21,23 @@ const RemainingTiles: FunctionComponent<Props> = ({ className, isOpen, onClose }
20
21
  return (
21
22
  <Sidebar className={className} isOpen={isOpen} title={translate('remaining-tiles')} onClose={onClose}>
22
23
  {groups.map(({ remainingCount, tiles, translationKey, totalCount }) => (
23
- <div className={styles.group} key={translationKey}>
24
- <h2 className={styles.title}>{`${translate(translationKey)} (${remainingCount} / ${totalCount})`}</h2>
25
-
24
+ <Sidebar.Section
25
+ key={translationKey}
26
+ title={
27
+ <span className={styles.title}>
28
+ <span>{translate(translationKey)}</span>
29
+ <Badge className={styles.badge}>
30
+ {remainingCount} / {totalCount}
31
+ </Badge>
32
+ </span>
33
+ }
34
+ >
26
35
  <div className={styles.content}>
27
36
  {tiles.map((tile) => {
28
37
  return <Character key={tile.character} tile={tile} />;
29
38
  })}
30
39
  </div>
31
- </div>
40
+ </Sidebar.Section>
32
41
  ))}
33
42
  </Sidebar>
34
43
  );
@@ -2,7 +2,7 @@ import classNames from 'classnames';
2
2
  import { FunctionComponent, ReactNode } from 'react';
3
3
  import Modal from 'react-modal';
4
4
 
5
- import { Cross } from 'icons';
5
+ import { CrossFill } from 'icons';
6
6
  import { TRANSITION_DURATION_LONG } from 'parameters';
7
7
  import { useTranslate } from 'state';
8
8
 
@@ -41,7 +41,7 @@ const Sidebar: FunctionComponent<Props> = ({ children, className, isOpen, title,
41
41
 
42
42
  <SquareButton
43
43
  className={styles.closeButton}
44
- Icon={Cross}
44
+ Icon={CrossFill}
45
45
  tooltip={translate('common.close')}
46
46
  onClick={onClose}
47
47
  />
@@ -7,6 +7,5 @@
7
7
  .heading {
8
8
  margin-bottom: var(--spacing--m);
9
9
  font-size: var(--font--size--h2);
10
- font-family: var(--font--family--title);
11
10
  font-weight: 300;
12
11
  }
@@ -6,7 +6,7 @@ import styles from './Section.module.scss';
6
6
  interface Props {
7
7
  children: ReactNode;
8
8
  className?: string;
9
- title: string;
9
+ title: ReactNode;
10
10
  }
11
11
 
12
12
  const Section: FunctionComponent<Props> = ({ children, className, title }) => (
@@ -18,6 +18,11 @@
18
18
  &:active {
19
19
  color: var(--color--foreground);
20
20
  }
21
+
22
+ &[disabled] {
23
+ pointer-events: none;
24
+ opacity: 0.25;
25
+ }
21
26
  }
22
27
 
23
28
  .content {
@@ -0,0 +1,35 @@
1
+ .title {
2
+ display: flex;
3
+ align-items: center;
4
+ justify-content: space-between;
5
+ flex-wrap: wrap;
6
+ }
7
+
8
+ .badge {
9
+ margin-left: var(--spacing--m);
10
+ }
11
+
12
+ .word {
13
+ display: flex;
14
+ align-items: center;
15
+
16
+ & + & {
17
+ margin-top: var(--spacing--s);
18
+ }
19
+ }
20
+
21
+ .invalid {
22
+ color: var(--color--error);
23
+ }
24
+
25
+ .valid {
26
+ color: var(--color--success);
27
+ }
28
+
29
+ .icon {
30
+ $size: 24px;
31
+
32
+ width: $size;
33
+ height: $size;
34
+ margin-right: var(--spacing--s);
35
+ }
@@ -0,0 +1,57 @@
1
+ import classNames from 'classnames';
2
+ import { FunctionComponent } from 'react';
3
+
4
+ import { Check, Cross } from 'icons';
5
+ import { selectVerify, useTranslate, useTypedSelector } from 'state';
6
+
7
+ import Badge from '../Badge';
8
+ import Sidebar from '../Sidebar';
9
+
10
+ import styles from './Words.module.scss';
11
+
12
+ interface Props {
13
+ className?: string;
14
+ isOpen: boolean;
15
+ onClose: () => void;
16
+ }
17
+
18
+ const Words: FunctionComponent<Props> = ({ className, isOpen, onClose }) => {
19
+ const translate = useTranslate();
20
+ const { invalidWords, validWords } = useTypedSelector(selectVerify);
21
+
22
+ return (
23
+ <Sidebar className={className} isOpen={isOpen} title={translate('words')} onClose={onClose}>
24
+ <Sidebar.Section
25
+ title={
26
+ <span className={styles.title}>
27
+ <span>{translate('words.invalid')}</span>
28
+ <Badge className={styles.badge}>{invalidWords.length}</Badge>
29
+ </span>
30
+ }
31
+ >
32
+ {invalidWords.map((word, index) => (
33
+ <div className={styles.word} key={index}>
34
+ <Cross className={classNames(styles.icon, styles.invalid)} /> {word}
35
+ </div>
36
+ ))}
37
+ </Sidebar.Section>
38
+
39
+ <Sidebar.Section
40
+ title={
41
+ <span className={styles.title}>
42
+ <span>{translate('words.valid')}</span>
43
+ <Badge className={styles.badge}>{validWords.length}</Badge>
44
+ </span>
45
+ }
46
+ >
47
+ {validWords.map((word, index) => (
48
+ <div className={styles.word} key={index}>
49
+ <Check className={classNames(styles.icon, styles.valid)} /> {word}
50
+ </div>
51
+ ))}
52
+ </Sidebar.Section>
53
+ </Sidebar>
54
+ );
55
+ };
56
+
57
+ export default Words;
@@ -0,0 +1 @@
1
+ export { default } from './Words';
@@ -1,3 +1,4 @@
1
+ export { default as Badge } from './Badge';
1
2
  export { default as Board } from './Board';
2
3
  export { default as Button } from './Button';
3
4
  export { default as Checkbox } from './Checkbox';
@@ -25,3 +26,4 @@ export { default as SvgFontCss } from './SvgFontCss';
25
26
  export { default as Tile } from './Tile';
26
27
  export { useTooltip } from './Tooltip';
27
28
  export { default as Well } from './Well';
29
+ export { default as Words } from './Words';
@@ -49,5 +49,8 @@
49
49
  "settings.autoGroupTiles.right": "Rechte Seite",
50
50
  "settings.autoGroupTiles.null": "Nicht gruppieren",
51
51
  "settings.game": "Spiel",
52
- "settings.language": "Sprache"
52
+ "settings.language": "Sprache",
53
+ "words": "Gebildete Wörter",
54
+ "words.invalid": "Falsch",
55
+ "words.valid": "Korrekt"
53
56
  }
@@ -49,5 +49,8 @@
49
49
  "settings.autoGroupTiles.right": "On the right",
50
50
  "settings.autoGroupTiles.null": "Do not group",
51
51
  "settings.game": "Game",
52
- "settings.language": "Language"
52
+ "settings.language": "Language",
53
+ "words": "Created words",
54
+ "words.invalid": "Invalid",
55
+ "words.valid": "Valid"
53
56
  }
@@ -49,5 +49,8 @@
49
49
  "settings.autoGroupTiles.right": "A la derecha",
50
50
  "settings.autoGroupTiles.null": "No agrupar",
51
51
  "settings.game": "Juego",
52
- "settings.language": "Idioma"
52
+ "settings.language": "Idioma",
53
+ "words": "Palabras creadas",
54
+ "words.invalid": "Incorrecto",
55
+ "words.valid": "Correcto"
53
56
  }
@@ -49,5 +49,8 @@
49
49
  "settings.autoGroupTiles.right": "Vers la gauche",
50
50
  "settings.autoGroupTiles.null": "Ne pas grouper",
51
51
  "settings.game": "Jeu",
52
- "settings.language": "Langue"
52
+ "settings.language": "Langue",
53
+ "words": "Mots créés",
54
+ "words.invalid": "Incorrect",
55
+ "words.valid": "Corriger"
53
56
  }
@@ -49,5 +49,8 @@
49
49
  "settings.autoGroupTiles.right": "Po prawej",
50
50
  "settings.autoGroupTiles.null": "Nie grupuj",
51
51
  "settings.game": "Gra",
52
- "settings.language": "Język"
52
+ "settings.language": "Język",
53
+ "words": "Utworzone słowa",
54
+ "words.invalid": "Niepoprawne",
55
+ "words.valid": "Poprawne"
53
56
  }
@@ -0,0 +1,4 @@
1
+ <!-- https://icons.getbootstrap.com/icons/book-half/ -->
2
+ <svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
3
+ <path d="M8.5 2.687c.654-.689 1.782-.886 3.112-.752 1.234.124 2.503.523 3.388.893v9.923c-.918-.35-2.107-.692-3.287-.81-1.094-.111-2.278-.039-3.213.492V2.687zM8 1.783C7.015.936 5.587.81 4.287.94c-1.514.153-3.042.672-3.994 1.105A.5.5 0 0 0 0 2.5v11a.5.5 0 0 0 .707.455c.882-.4 2.303-.881 3.68-1.02 1.409-.142 2.59.087 3.223.877a.5.5 0 0 0 .78 0c.633-.79 1.814-1.019 3.222-.877 1.378.139 2.8.62 3.681 1.02A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.293-.455c-.952-.433-2.48-.952-3.994-1.105C10.413.809 8.985.936 8 1.783z" fill="currentColor" />
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <!-- https://icons.getbootstrap.com/icons/check/ -->
2
+ <svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
3
+ <path d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.267.267 0 0 1 .02-.022z" fill="currentColor" />
4
+ </svg>
@@ -1,4 +1,4 @@
1
- <!-- https://icons.getbootstrap.com/icons/x-square-fill/ -->
1
+ <!-- https://icons.getbootstrap.com/icons/x/ -->
2
2
  <svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
3
- <path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm3.354 4.646L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 1 1 .708-.708z" fill="currentColor" />
3
+ <path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z" fill="currentColor" />
4
4
  </svg>
@@ -0,0 +1,4 @@
1
+ <!-- https://icons.getbootstrap.com/icons/x-square-fill/ -->
2
+ <svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
3
+ <path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm3.354 4.646L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 1 1 .708-.708z" fill="currentColor" />
4
+ </svg>
@@ -2,10 +2,13 @@ export { default as ArrowDown } from './ArrowDown.svg';
2
2
  export { default as ArrowLeft } from './ArrowLeft.svg';
3
3
  export { default as ArrowRight } from './ArrowRight.svg';
4
4
  export { default as ArrowUp } from './ArrowUp.svg';
5
+ export { default as BookHalf } from './BookHalf.svg';
6
+ export { default as Check } from './Check.svg';
5
7
  export { default as CheckboxChecked } from './CheckboxChecked.svg';
6
8
  export { default as CheckboxEmpty } from './CheckboxEmpty.svg';
7
9
  export { default as Cog } from './Cog.svg';
8
10
  export { default as Cross } from './Cross.svg';
11
+ export { default as CrossFill } from './CrossFill.svg';
9
12
  export { default as DashCircleFill } from './DashCircleFill.svg';
10
13
  export { default as Eraser } from './Eraser.svg';
11
14
  export { default as Flag } from './Flag.svg';
@@ -2,7 +2,7 @@ import { getLocaleConfig, isConfigId } from '@scrabble-solver/configs';
2
2
  import { BLANK } from '@scrabble-solver/constants';
3
3
  import { dictionaries } from '@scrabble-solver/dictionaries';
4
4
  import logger from '@scrabble-solver/logger';
5
- import Solver from '@scrabble-solver/solver';
5
+ import { solve as solveScrabble } from '@scrabble-solver/solver';
6
6
  import { Board, Config, isBoardJson, isLocale, Locale, Tile } from '@scrabble-solver/types';
7
7
  import { NextApiRequest, NextApiResponse } from 'next';
8
8
 
@@ -36,8 +36,7 @@ const solve = async (request: NextApiRequest, response: NextApiResponse): Promis
36
36
 
37
37
  const trie = await dictionaries.get(locale);
38
38
  const tiles = characters.map((character) => new Tile({ character, isBlank: character === BLANK }));
39
- const solver = new Solver(config, trie);
40
- const results = solver.solve(board, tiles);
39
+ const results = solveScrabble(trie, config, board, tiles);
41
40
  response.status(200).send(results.map((result) => result.toJson()));
42
41
  } catch (error) {
43
42
  const message = error instanceof Error ? error.message : 'Unknown error';
@@ -0,0 +1,71 @@
1
+ import { getLocaleConfig, isConfigId } from '@scrabble-solver/configs';
2
+ import { dictionaries } from '@scrabble-solver/dictionaries';
3
+ import logger from '@scrabble-solver/logger';
4
+ import { Board, Config, isBoardJson, isLocale, Locale } from '@scrabble-solver/types';
5
+ import { NextApiRequest, NextApiResponse } from 'next';
6
+
7
+ import { getServerLoggingData, isBoardValid } from 'api';
8
+
9
+ interface RequestData {
10
+ board: Board;
11
+ config: Config;
12
+ locale: Locale;
13
+ }
14
+
15
+ const verify = async (request: NextApiRequest, response: NextApiResponse): Promise<void> => {
16
+ const meta = getServerLoggingData(request);
17
+
18
+ try {
19
+ const { board, locale } = parseRequest(request);
20
+
21
+ logger.info('verify - request', {
22
+ meta,
23
+ payload: {
24
+ board: board.toString(),
25
+ boardBlanksCount: board.getBlanksCount(),
26
+ boardTilesCount: board.getTilesCount(),
27
+ configId: request.body.configId,
28
+ locale,
29
+ },
30
+ });
31
+
32
+ const trie = await dictionaries.get(locale);
33
+ const words = board.getWords().sort((a, b) => a.localeCompare(b));
34
+ const invalidWords = words.filter((word) => !trie.has(word));
35
+ const validWords = words.filter((word) => trie.has(word));
36
+ response.status(200).send({ invalidWords, validWords });
37
+ } catch (error) {
38
+ const message = error instanceof Error ? error.message : 'Unknown error';
39
+ logger.error('verify - error', { error, meta });
40
+ response.status(500).send({ error: 'Server error', message });
41
+ throw error;
42
+ }
43
+ };
44
+
45
+ const parseRequest = (request: NextApiRequest): RequestData => {
46
+ const { board: boardJson, configId, locale } = request.body;
47
+
48
+ if (!isLocale(locale)) {
49
+ throw new Error('Invalid "locale" parameter');
50
+ }
51
+
52
+ if (!isConfigId(configId)) {
53
+ throw new Error('Invalid "configId" parameter');
54
+ }
55
+
56
+ const config = getLocaleConfig(configId, locale);
57
+
58
+ if (!isBoardJson(boardJson) || !isBoardValid(boardJson, config)) {
59
+ throw new Error('Invalid "board" parameter');
60
+ }
61
+
62
+ const board = Board.fromJson(boardJson);
63
+
64
+ return {
65
+ board,
66
+ config,
67
+ locale,
68
+ };
69
+ };
70
+
71
+ export default verify;
@@ -19,6 +19,7 @@ import {
19
19
  Settings,
20
20
  Splash,
21
21
  Well,
22
+ Words,
22
23
  } from 'components';
23
24
  import { useIsTablet, useLocalStorage } from 'hooks';
24
25
  import { getCellSize } from 'lib';
@@ -39,6 +40,7 @@ const Index: FunctionComponent<Props> = ({ version }) => {
39
40
  const [showKeyMap, setShowKeyMap] = useState(false);
40
41
  const [showRemainingTiles, setShowRemainingTiles] = useState(false);
41
42
  const [showSettings, setShowSettings] = useState(false);
43
+ const [showWords, setShowWords] = useState(false);
42
44
  const [boardRef, { height: boardHeight }] = useMeasure<HTMLDivElement>();
43
45
  const [contentRef, { height: contentHeight, width: contentWidth }] = useMeasure<HTMLDivElement>();
44
46
  const [resultsContainerRef, { height: resultsContainerHeight, width: resultsContainerWidth }] =
@@ -85,6 +87,7 @@ const Index: FunctionComponent<Props> = ({ version }) => {
85
87
  onShowKeyMap={() => setShowKeyMap(true)}
86
88
  onShowRemainingTiles={() => setShowRemainingTiles(true)}
87
89
  onShowSettings={() => setShowSettings(true)}
90
+ onShowWords={() => setShowWords(true)}
88
91
  />
89
92
  </div>
90
93
 
@@ -122,6 +125,8 @@ const Index: FunctionComponent<Props> = ({ version }) => {
122
125
 
123
126
  <KeyMap isOpen={showKeyMap} onClose={() => setShowKeyMap(false)} />
124
127
 
128
+ <Words isOpen={showWords} onClose={() => setShowWords(false)} />
129
+
125
130
  <RemainingTiles isOpen={showRemainingTiles} onClose={() => setShowRemainingTiles(false)} />
126
131
 
127
132
  <Splash forceShow={!isInitialized} onAnimationEnd={handleSplashAnimationEnd} />
@@ -1,3 +1,4 @@
1
1
  export { default as findWordDefinitions } from './findWordDefinitions';
2
2
  export { default as solve } from './solve';
3
+ export { default as verify } from './verify';
3
4
  export { default as visit } from './visit';
@@ -0,0 +1,24 @@
1
+ import { BoardJson, Locale } from '@scrabble-solver/types';
2
+
3
+ interface Payload {
4
+ board: BoardJson;
5
+ configId: string;
6
+ locale: Locale;
7
+ }
8
+
9
+ interface Response {
10
+ invalidWords: string[];
11
+ validWords: string[];
12
+ }
13
+
14
+ const verify = ({ board, configId, locale }: Payload): Promise<Response> => {
15
+ return fetch('/api/verify', {
16
+ method: 'POST',
17
+ headers: {
18
+ 'Content-Type': 'application/json',
19
+ },
20
+ body: JSON.stringify({ board, configId, locale }),
21
+ }).then((response) => response.json());
22
+ };
23
+
24
+ export default verify;
@@ -8,6 +8,7 @@ import {
8
8
  resultsSlice,
9
9
  settingsSlice,
10
10
  solveSlice,
11
+ verifySlice,
11
12
  } from './slices';
12
13
 
13
14
  const rootReducer = combineReducers({
@@ -18,6 +19,7 @@ const rootReducer = combineReducers({
18
19
  results: resultsSlice.reducer,
19
20
  settings: settingsSlice.reducer,
20
21
  solve: solveSlice.reducer,
22
+ verify: verifySlice.reducer,
21
23
  });
22
24
 
23
25
  export default rootReducer;