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.
- package/.eslintrc.prettier-only.js +1 -1
- package/.prettierignore +3 -0
- package/.prettierrc +3 -3
- package/babel.config.js +1 -1
- package/lerna.json +1 -1
- package/package.json +6 -6
- package/packages/configs/package.json +3 -3
- package/packages/constants/package.json +1 -1
- package/packages/dictionaries/package-lock.json +2 -2
- package/packages/dictionaries/package.json +4 -4
- package/packages/logger/package-lock.json +2 -2
- package/packages/logger/package.json +1 -1
- package/packages/scrabble-solver/.next/BUILD_ID +1 -1
- package/packages/scrabble-solver/.next/build-manifest.json +10 -10
- package/packages/scrabble-solver/.next/cache/.tsbuildinfo +1 -1
- package/packages/scrabble-solver/.next/cache/eslint/.cache_8dgz12 +1 -1
- package/packages/scrabble-solver/.next/cache/next-server.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/cache/webpack/client-production/0.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/client-production/index.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/server-production/0.pack +0 -0
- package/packages/scrabble-solver/.next/cache/webpack/server-production/index.pack +0 -0
- package/packages/scrabble-solver/.next/next-server.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/prerender-manifest.json +1 -1
- package/packages/scrabble-solver/.next/routes-manifest.json +1 -1
- package/packages/scrabble-solver/.next/server/chunks/413.js +179 -44
- package/packages/scrabble-solver/.next/server/chunks/44.js +802 -0
- package/packages/scrabble-solver/.next/server/chunks/515.js +359 -95
- package/packages/scrabble-solver/.next/server/chunks/911.js +53 -23
- package/packages/scrabble-solver/.next/server/middleware-build-manifest.js +1 -1
- package/packages/scrabble-solver/.next/server/pages/404.html +2 -2
- package/packages/scrabble-solver/.next/server/pages/404.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/500.html +2 -2
- package/packages/scrabble-solver/.next/server/pages/_app.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/_error.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/solve.js +193 -927
- package/packages/scrabble-solver/.next/server/pages/api/solve.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/verify.js +217 -0
- package/packages/scrabble-solver/.next/server/pages/api/verify.js.nft.json +1 -0
- package/packages/scrabble-solver/.next/server/pages/index.html +3 -3
- package/packages/scrabble-solver/.next/server/pages/index.js +7 -1
- package/packages/scrabble-solver/.next/server/pages/index.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/index.json +1 -1
- package/packages/scrabble-solver/.next/server/pages-manifest.json +1 -0
- package/packages/scrabble-solver/.next/static/chunks/317-a33dd38e9b9a17ed.js +1 -0
- package/packages/scrabble-solver/.next/static/chunks/pages/{404-8eb3ba4f0ba17e08.js → 404-90c624da3c83fd17.js} +1 -1
- package/packages/scrabble-solver/.next/static/chunks/pages/_app-f8f360878e1c2aff.js +1 -0
- package/packages/scrabble-solver/.next/static/chunks/pages/index-ecea697d3e5d8a6f.js +1 -0
- package/packages/scrabble-solver/.next/static/css/64dc2ce1811912f1.css +1 -0
- package/packages/scrabble-solver/.next/static/css/{cdbc9e0afcff5473.css → ad2a08918868cad8.css} +1 -1
- package/packages/scrabble-solver/.next/static/yCxjzzYpw5JjJE53PO_s6/_buildManifest.js +1 -0
- package/packages/scrabble-solver/.next/static/{z3J3qmq1nazbDv_ENIkCo → yCxjzzYpw5JjJE53PO_s6}/_ssgManifest.js +0 -0
- package/packages/scrabble-solver/.next/trace +42 -41
- package/packages/scrabble-solver/package-lock.json +2 -2
- package/packages/scrabble-solver/package.json +8 -8
- package/packages/scrabble-solver/src/components/Badge/Badge.module.scss +13 -0
- package/packages/scrabble-solver/src/components/Badge/Badge.tsx +15 -0
- package/packages/scrabble-solver/src/components/Badge/index.ts +1 -0
- package/packages/scrabble-solver/src/components/NavButtons/NavButtons.tsx +33 -14
- package/packages/scrabble-solver/src/components/RemainingTiles/RemainingTiles.module.scss +8 -7
- package/packages/scrabble-solver/src/components/RemainingTiles/RemainingTiles.tsx +13 -4
- package/packages/scrabble-solver/src/components/Sidebar/Sidebar.tsx +2 -2
- package/packages/scrabble-solver/src/components/Sidebar/components/Section/Section.module.scss +0 -1
- package/packages/scrabble-solver/src/components/Sidebar/components/Section/Section.tsx +1 -1
- package/packages/scrabble-solver/src/components/SquareButton/SquareButton.module.scss +5 -0
- package/packages/scrabble-solver/src/components/Words/Words.module.scss +35 -0
- package/packages/scrabble-solver/src/components/Words/Words.tsx +57 -0
- package/packages/scrabble-solver/src/components/Words/index.ts +1 -0
- package/packages/scrabble-solver/src/components/index.ts +2 -0
- package/packages/scrabble-solver/src/i18n/de.json +4 -1
- package/packages/scrabble-solver/src/i18n/en.json +4 -1
- package/packages/scrabble-solver/src/i18n/es.json +4 -1
- package/packages/scrabble-solver/src/i18n/fr.json +4 -1
- package/packages/scrabble-solver/src/i18n/pl.json +4 -1
- package/packages/scrabble-solver/src/icons/BookHalf.svg +4 -0
- package/packages/scrabble-solver/src/icons/Check.svg +4 -0
- package/packages/scrabble-solver/src/icons/Cross.svg +2 -2
- package/packages/scrabble-solver/src/icons/CrossFill.svg +4 -0
- package/packages/scrabble-solver/src/icons/index.ts +3 -0
- package/packages/scrabble-solver/src/pages/api/solve.ts +2 -3
- package/packages/scrabble-solver/src/pages/api/verify.ts +71 -0
- package/packages/scrabble-solver/src/pages/index.tsx +5 -0
- package/packages/scrabble-solver/src/sdk/index.ts +1 -0
- package/packages/scrabble-solver/src/sdk/verify.ts +24 -0
- package/packages/scrabble-solver/src/state/rootReducer.ts +2 -0
- package/packages/scrabble-solver/src/state/sagas.ts +33 -6
- package/packages/scrabble-solver/src/state/selectors.ts +8 -0
- package/packages/scrabble-solver/src/state/slices/index.ts +2 -0
- package/packages/scrabble-solver/src/state/slices/verifyInitialState.ts +12 -0
- package/packages/scrabble-solver/src/state/slices/verifySlice.ts +31 -0
- package/packages/scrabble-solver/src/styles/variables.scss +2 -1
- package/packages/scrabble-solver/src/types/index.ts +4 -1
- package/packages/solver/build/fillPattern.d.ts +5 -0
- package/packages/solver/build/fillPattern.js +52 -0
- package/packages/solver/build/generateEndIndices.d.ts +3 -0
- package/packages/solver/build/generateEndIndices.js +9 -0
- package/packages/solver/build/generateHorizontalPatterns.d.ts +3 -0
- package/packages/solver/build/generateHorizontalPatterns.js +18 -0
- package/packages/solver/build/generatePattern.d.ts +8 -0
- package/packages/solver/build/generatePattern.js +22 -0
- package/packages/solver/build/generatePatterns.d.ts +3 -0
- package/packages/solver/build/generatePatterns.js +13 -0
- package/packages/solver/build/generateStartIndices.d.ts +3 -0
- package/packages/solver/build/generateStartIndices.js +9 -0
- package/packages/solver/build/generateVectors.d.ts +6 -0
- package/packages/solver/build/generateVectors.js +8 -0
- package/packages/solver/build/generateVerticalPatterns.d.ts +3 -0
- package/packages/solver/build/generateVerticalPatterns.js +18 -0
- package/packages/solver/build/getCellsScore.d.ts +3 -0
- package/packages/solver/build/getCellsScore.js +16 -0
- package/packages/solver/build/getPatternHash.d.ts +3 -0
- package/packages/solver/build/getPatternHash.js +13 -0
- package/packages/solver/build/getPatternScore.d.ts +3 -0
- package/packages/solver/build/getPatternScore.js +15 -0
- package/packages/solver/build/getUniquePatterns.js +5 -11
- package/packages/solver/build/index.d.ts +1 -1
- package/packages/solver/build/index.js +3 -3
- package/packages/solver/build/solve.d.ts +4 -0
- package/packages/solver/build/solve.js +23 -0
- package/packages/solver/package-lock.json +2 -2
- package/packages/solver/package.json +5 -5
- package/packages/solver/src/fillPattern.test.ts +107 -0
- package/packages/solver/src/fillPattern.ts +70 -0
- package/packages/solver/src/generateEndIndices.test.ts +18 -0
- package/packages/solver/src/generateEndIndices.ts +10 -0
- package/packages/solver/src/generateHorizontalPatterns.test.ts +31 -0
- package/packages/solver/src/generateHorizontalPatterns.ts +17 -0
- package/packages/solver/src/generatePattern.ts +35 -0
- package/packages/solver/src/generatePatterns.ts +13 -0
- package/packages/solver/src/generateStartIndices.test.ts +14 -0
- package/packages/solver/src/generateStartIndices.ts +10 -0
- package/packages/solver/src/generateVectors.test.ts +12 -0
- package/packages/solver/src/generateVectors.ts +15 -0
- package/packages/solver/src/generateVerticalPatterns.test.ts +32 -0
- package/packages/solver/src/generateVerticalPatterns.ts +17 -0
- package/packages/solver/src/getCellsScore.ts +22 -0
- package/packages/solver/src/getPatternHash.test.ts +17 -0
- package/packages/solver/src/getPatternHash.ts +14 -0
- package/packages/solver/src/getPatternScore.test.ts +60 -0
- package/packages/solver/src/getPatternScore.ts +15 -0
- package/packages/solver/src/getUniquePatterns.ts +1 -10
- package/packages/solver/src/index.ts +1 -1
- package/packages/solver/src/{Solver.test.ts → solve.test.ts} +14 -13
- package/packages/solver/src/solve.ts +25 -0
- package/packages/types/build/Board.d.ts +1 -0
- package/packages/types/build/Board.js +17 -1
- package/packages/types/build/Config.js +1 -1
- package/packages/types/build/FinalPattern.d.ts +7 -0
- package/packages/types/build/FinalPattern.js +16 -0
- package/packages/types/build/HorizontalPattern.js +2 -5
- package/packages/types/build/Pattern.d.ts +1 -4
- package/packages/types/build/Pattern.js +2 -5
- package/packages/types/build/Result.js +3 -5
- package/packages/types/build/Tile.d.ts +2 -2
- package/packages/types/build/VerticalPattern.js +2 -5
- package/packages/types/build/index.d.ts +1 -0
- package/packages/types/build/index.js +3 -1
- package/packages/types/package.json +2 -2
- package/packages/types/src/Board.ts +23 -1
- package/packages/types/src/Config.ts +1 -1
- package/packages/types/src/FinalPattern.ts +16 -0
- package/packages/types/src/HorizontalPattern.ts +5 -5
- package/packages/types/src/Pattern.ts +5 -5
- package/packages/types/src/Result.ts +3 -5
- package/packages/types/src/Tile.ts +2 -2
- package/packages/types/src/VerticalPattern.ts +5 -5
- package/packages/types/src/index.ts +1 -0
- package/packages/word-definitions/package-lock.json +2 -2
- package/packages/word-definitions/package.json +2 -2
- package/packages/word-lists/package-lock.json +2 -2
- package/packages/word-lists/package.json +2 -2
- package/packages/scrabble-solver/.next/static/chunks/615-d258f6c528c18622.js +0 -1
- package/packages/scrabble-solver/.next/static/chunks/pages/_app-4a663fd3d5ca4524.js +0 -1
- package/packages/scrabble-solver/.next/static/chunks/pages/index-1a9826d740cc8830.js +0 -1
- package/packages/scrabble-solver/.next/static/css/180c6c26317ac90f.css +0 -1
- package/packages/scrabble-solver/.next/static/z3J3qmq1nazbDv_ENIkCo/_buildManifest.js +0 -1
- package/packages/solver/build/PatternsFiller.d.ts +0 -12
- package/packages/solver/build/PatternsFiller.js +0 -70
- package/packages/solver/build/PatternsGenerator.d.ts +0 -32
- package/packages/solver/build/PatternsGenerator.js +0 -66
- package/packages/solver/build/ScoresCalculator.d.ts +0 -17
- package/packages/solver/build/ScoresCalculator.js +0 -37
- package/packages/solver/build/Solver.d.ts +0 -10
- package/packages/solver/build/Solver.js +0 -30
- package/packages/solver/src/PatternsFiller.test.ts +0 -92
- package/packages/solver/src/PatternsFiller.ts +0 -91
- package/packages/solver/src/PatternsGenerator.test.ts +0 -89
- package/packages/solver/src/PatternsGenerator.ts +0 -103
- package/packages/solver/src/ScoresCalculator.test.ts +0 -70
- package/packages/solver/src/ScoresCalculator.ts +0 -52
- 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.
|
|
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.
|
|
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.
|
|
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.
|
|
34
|
-
"@scrabble-solver/constants": "^2.8.
|
|
35
|
-
"@scrabble-solver/dictionaries": "^2.8.
|
|
36
|
-
"@scrabble-solver/logger": "^2.8.
|
|
37
|
-
"@scrabble-solver/solver": "^2.8.
|
|
38
|
-
"@scrabble-solver/types": "^2.8.
|
|
39
|
-
"@scrabble-solver/word-definitions": "^2.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> = ({
|
|
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.
|
|
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
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
<
|
|
24
|
-
|
|
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
|
-
</
|
|
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 {
|
|
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={
|
|
44
|
+
Icon={CrossFill}
|
|
45
45
|
tooltip={translate('common.close')}
|
|
46
46
|
onClick={onClose}
|
|
47
47
|
/>
|
|
@@ -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
|
|
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="
|
|
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
|
|
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
|
|
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} />
|
|
@@ -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;
|