@scrabble-solver/scrabble-solver 2.12.1 → 2.12.3
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/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +14 -14
- package/.next/cache/.tsbuildinfo +1 -1
- package/.next/cache/eslint/.cache_8dgz12 +1 -1
- package/.next/cache/next-server.js.nft.json +1 -1
- package/.next/cache/webpack/client-production/0.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +1 -1
- package/.next/required-server-files.json +1 -1
- package/.next/routes-manifest.json +1 -1
- package/.next/server/chunks/131.js +478 -106
- package/.next/server/chunks/277.js +1202 -1013
- package/.next/server/chunks/44.js +36 -6
- package/.next/server/chunks/675.js +35 -13
- package/.next/server/chunks/859.js +21 -4
- package/.next/server/chunks/865.js +478 -106
- package/.next/server/chunks/911.js +894 -773
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/404.js.nft.json +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/pages/_app.js +3 -1
- package/.next/server/pages/_app.js.nft.json +1 -1
- package/.next/server/pages/api/dictionary/[locale]/[word].js +4 -2
- package/.next/server/pages/api/solve.js +51 -20
- package/.next/server/pages/api/verify.js +8 -5
- package/.next/server/pages/index.html +1 -1
- package/.next/server/pages/index.js +55 -65
- package/.next/server/pages/index.js.nft.json +1 -1
- package/.next/server/pages/index.json +1 -1
- package/.next/server/pages-manifest.json +2 -2
- package/.next/static/chunks/{framework-2c5cac93e8c637b5.js → framework-2c29dc3cd933590b.js} +2 -2
- package/.next/static/chunks/main-4dcb7f9b52833aba.js +1 -0
- package/.next/static/chunks/pages/_app-e7f3d1c9c09c8f91.js +32 -0
- package/.next/static/chunks/pages/index-82b2939158c7729f.js +1 -0
- package/.next/static/css/4e8b47fe382a8a8f.css +2 -0
- package/.next/static/css/cfae5256f1689f57.css +1 -0
- package/.next/static/{_yG9K-PD5kWT_p4sMdCSV → wNIKOJJzkMSJYb2nOL21o}/_buildManifest.js +1 -1
- package/.next/trace +51 -52
- package/package.json +14 -14
- package/src/components/Board/Board.module.scss +33 -0
- package/src/components/Board/Board.tsx +3 -0
- package/src/components/Board/BoardPure.tsx +25 -1
- package/src/components/Board/components/Cell/Cell.module.scss +4 -12
- package/src/components/Board/components/Cell/Cell.tsx +4 -0
- package/src/components/Board/components/InputPrompt/InputPrompt.tsx +1 -0
- package/src/components/Board/hooks/useBackgroundImage.tsx +24 -6
- package/src/components/PlainTiles/lib/createPlainTile.ts +4 -4
- package/src/components/Rack/components/InputPrompt/InputPrompt.tsx +1 -0
- package/src/components/Radio/Radio.module.scss +4 -0
- package/src/components/Radio/Radio.tsx +1 -0
- package/src/components/SeoMessage/SeoMessage.tsx +3 -3
- package/src/hooks/useAppLayout.ts +1 -2
- package/src/hooks/useLocalStorage.ts +5 -5
- package/src/i18n/constants.ts +31 -61
- package/src/lib/extractCharacters.test.ts +3 -3
- package/src/lib/extractCharactersByCase.test.ts +3 -3
- package/src/lib/getCellSize.ts +2 -2
- package/src/modals/MenuModal/MenuModal.module.scss +0 -1
- package/src/modals/MenuModal/MenuModal.tsx +3 -3
- package/src/modals/SettingsModal/SettingsModal.tsx +7 -3
- package/src/modals/SettingsModal/components/ConfigSetting/ConfigSetting.tsx +16 -7
- package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.module.scss +1 -4
- package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.tsx +8 -8
- package/src/pages/_app.tsx +3 -1
- package/src/pages/api/dictionary/[locale]/[word].ts +6 -3
- package/src/pages/api/solve.ts +11 -7
- package/src/pages/api/verify.ts +11 -7
- package/src/pages/index.module.scss +0 -1
- package/src/parameters/index.ts +4 -6
- package/src/sdk/solve.ts +3 -3
- package/src/sdk/verify.ts +3 -3
- package/src/service-worker/routeSolveRequests.ts +3 -3
- package/src/state/localStorage.ts +6 -6
- package/src/state/sagas.ts +18 -7
- package/src/state/selectors.ts +10 -10
- package/src/state/slices/boardInitialState.ts +3 -3
- package/src/state/slices/boardSlice.ts +20 -6
- package/src/state/slices/settingsInitialState.ts +3 -4
- package/src/state/slices/settingsSlice.ts +5 -5
- package/src/styles/variables.scss +0 -9
- package/.next/static/chunks/main-0ecb9ccfcb6c9b24.js +0 -1
- package/.next/static/chunks/pages/_app-bea4539a6b8042de.js +0 -32
- package/.next/static/chunks/pages/index-4e8566409753e1c3.js +0 -1
- package/.next/static/css/58053f9594647860.css +0 -2
- package/.next/static/css/60e8258da7362a1a.css +0 -1
- package/src/i18n/i18n.module.scss +0 -27
- package/src/modals/SettingsModal/components/ConfigSetting/options.ts +0 -19
- package/src/modals/SettingsModal/components/ConfigSetting/types.ts +0 -9
- package/src/modals/SettingsModal/components/InputModeSetting/types.ts +0 -7
- package/src/modals/SettingsModal/components/LocaleSetting/types.ts +0 -9
- /package/.next/static/{_yG9K-PD5kWT_p4sMdCSV → wNIKOJJzkMSJYb2nOL21o}/_ssgManifest.js +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrabble-solver/scrabble-solver",
|
|
3
|
-
"version": "2.12.
|
|
3
|
+
"version": "2.12.3",
|
|
4
4
|
"description": "Scrabble Solver 2 - App",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=16"
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"bugs": {
|
|
19
19
|
"url": "https://github.com/kamilmielnik/scrabble-solver/issues"
|
|
20
20
|
},
|
|
21
|
-
"homepage": "https://
|
|
21
|
+
"homepage": "https://scrabble-solver.org",
|
|
22
22
|
"scripts": {
|
|
23
23
|
"build": "env-cmd next build",
|
|
24
24
|
"clean": "rimraf .next/ node_modules/",
|
|
@@ -28,20 +28,20 @@
|
|
|
28
28
|
"start": "env-cmd next start -p 3333"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@floating-ui/react": "^0.22.
|
|
31
|
+
"@floating-ui/react": "^0.22.3",
|
|
32
32
|
"@kamilmielnik/trie": "^2.0.1",
|
|
33
33
|
"@reduxjs/toolkit": "^1.9.3",
|
|
34
|
-
"@scrabble-solver/configs": "^2.12.
|
|
35
|
-
"@scrabble-solver/constants": "^2.12.
|
|
36
|
-
"@scrabble-solver/dictionaries": "^2.12.
|
|
37
|
-
"@scrabble-solver/logger": "^2.12.
|
|
38
|
-
"@scrabble-solver/solver": "^2.12.
|
|
39
|
-
"@scrabble-solver/types": "^2.12.
|
|
40
|
-
"@scrabble-solver/word-definitions": "^2.12.
|
|
34
|
+
"@scrabble-solver/configs": "^2.12.3",
|
|
35
|
+
"@scrabble-solver/constants": "^2.12.3",
|
|
36
|
+
"@scrabble-solver/dictionaries": "^2.12.3",
|
|
37
|
+
"@scrabble-solver/logger": "^2.12.3",
|
|
38
|
+
"@scrabble-solver/solver": "^2.12.3",
|
|
39
|
+
"@scrabble-solver/types": "^2.12.3",
|
|
40
|
+
"@scrabble-solver/word-definitions": "^2.12.3",
|
|
41
41
|
"classnames": "^2.3.2",
|
|
42
42
|
"include-media": "^2.0.0",
|
|
43
43
|
"include-media-query-builder": "^1.1.0",
|
|
44
|
-
"next": "^13.
|
|
44
|
+
"next": "^13.3.0",
|
|
45
45
|
"normalize.css": "^8.0.1",
|
|
46
46
|
"react": "^18.2.0",
|
|
47
47
|
"react-cool-onclickoutside": "^1.7.0",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@svgr/webpack": "^7.0.0",
|
|
63
63
|
"@types/classnames": "^2.3.0",
|
|
64
|
-
"@types/react": "^18.0.
|
|
64
|
+
"@types/react": "^18.0.33",
|
|
65
65
|
"@types/react-dom": "^18.0.11",
|
|
66
66
|
"@types/react-highlight-words": "^0.16.4",
|
|
67
67
|
"@types/react-modal": "^3.13.1",
|
|
@@ -71,8 +71,8 @@
|
|
|
71
71
|
"@types/redux": "^3.6.31",
|
|
72
72
|
"@types/redux-saga": "^0.10.5",
|
|
73
73
|
"env-cmd": "^10.1.0",
|
|
74
|
-
"sass": "^1.
|
|
74
|
+
"sass": "^1.61.0",
|
|
75
75
|
"workbox-webpack-plugin": "^6.5.4"
|
|
76
76
|
},
|
|
77
|
-
"gitHead": "
|
|
77
|
+
"gitHead": "5400e276feee01557044e4e758b11e86b83fb085"
|
|
78
78
|
}
|
|
@@ -33,3 +33,36 @@
|
|
|
33
33
|
user-select: none;
|
|
34
34
|
animation: var(--transition--duration) var(--transition--easing) hide;
|
|
35
35
|
}
|
|
36
|
+
|
|
37
|
+
.iconContainer {
|
|
38
|
+
position: absolute;
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
justify-content: center;
|
|
42
|
+
pointer-events: none;
|
|
43
|
+
user-select: none;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.iconBackground,
|
|
47
|
+
.icon {
|
|
48
|
+
position: absolute;
|
|
49
|
+
top: calc((100% - var(--size)) / 2);
|
|
50
|
+
right: calc((100% - var(--size)) / 2);
|
|
51
|
+
bottom: calc((100% - var(--size)) / 2);
|
|
52
|
+
left: calc((100% - var(--size)) / 2);
|
|
53
|
+
width: var(--size);
|
|
54
|
+
height: var(--size);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.iconBackground {
|
|
58
|
+
--size: 80%;
|
|
59
|
+
|
|
60
|
+
border-radius: var(--border--radius);
|
|
61
|
+
background-color: var(--color--foreground--secondary);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.icon {
|
|
65
|
+
--size: 40%;
|
|
66
|
+
|
|
67
|
+
color: var(--color--white);
|
|
68
|
+
}
|
|
@@ -12,6 +12,7 @@ import { TRANSITION } from 'parameters';
|
|
|
12
12
|
import {
|
|
13
13
|
boardSlice,
|
|
14
14
|
cellFilterSlice,
|
|
15
|
+
selectFilteredCells,
|
|
15
16
|
selectInputMode,
|
|
16
17
|
selectLocale,
|
|
17
18
|
selectRowsWithCandidate,
|
|
@@ -33,6 +34,7 @@ const Board: FunctionComponent<Props> = ({ className }) => {
|
|
|
33
34
|
const locale = useTypedSelector(selectLocale);
|
|
34
35
|
const rows = useTypedSelector(selectRowsWithCandidate);
|
|
35
36
|
const inputMode = useTypedSelector(selectInputMode);
|
|
37
|
+
const filteredCells = useTypedSelector(selectFilteredCells);
|
|
36
38
|
const { cellSize } = useAppLayout();
|
|
37
39
|
const [
|
|
38
40
|
{ activeIndex, direction, inputRefs },
|
|
@@ -147,6 +149,7 @@ const Board: FunctionComponent<Props> = ({ className }) => {
|
|
|
147
149
|
<BoardPure
|
|
148
150
|
className={className}
|
|
149
151
|
cellSize={cellSize}
|
|
152
|
+
filteredCells={filteredCells}
|
|
150
153
|
inputRefs={inputRefs}
|
|
151
154
|
ref={ref}
|
|
152
155
|
rows={rows}
|
|
@@ -12,12 +12,17 @@ import {
|
|
|
12
12
|
RefObject,
|
|
13
13
|
} from 'react';
|
|
14
14
|
|
|
15
|
+
import { FlagFill } from 'icons';
|
|
16
|
+
import { BORDER_WIDTH } from 'parameters';
|
|
17
|
+
import { Point } from 'types';
|
|
18
|
+
|
|
15
19
|
import styles from './Board.module.scss';
|
|
16
20
|
import { Cell } from './components';
|
|
17
21
|
|
|
18
22
|
interface Props {
|
|
19
23
|
className?: string;
|
|
20
24
|
cellSize: number;
|
|
25
|
+
filteredCells: Point[];
|
|
21
26
|
inputRefs: RefObject<HTMLInputElement>[][];
|
|
22
27
|
rows: CellModel[][];
|
|
23
28
|
style?: CSSProperties;
|
|
@@ -29,7 +34,10 @@ interface Props {
|
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
const BoardPure = forwardRef<HTMLDivElement, Props>(
|
|
32
|
-
(
|
|
37
|
+
(
|
|
38
|
+
{ className, cellSize, filteredCells, inputRefs, rows, style, onBlur, onChange, onFocus, onKeyDown, onPaste },
|
|
39
|
+
ref,
|
|
40
|
+
) => (
|
|
33
41
|
<div
|
|
34
42
|
className={classNames(styles.board, className)}
|
|
35
43
|
ref={ref}
|
|
@@ -57,6 +65,22 @@ const BoardPure = forwardRef<HTMLDivElement, Props>(
|
|
|
57
65
|
))}
|
|
58
66
|
</Fragment>
|
|
59
67
|
))}
|
|
68
|
+
|
|
69
|
+
{filteredCells.map(({ x, y }) => (
|
|
70
|
+
<div
|
|
71
|
+
className={styles.iconContainer}
|
|
72
|
+
key={[x, y].join('-')}
|
|
73
|
+
style={{
|
|
74
|
+
height: cellSize,
|
|
75
|
+
width: cellSize,
|
|
76
|
+
left: x * (cellSize + BORDER_WIDTH),
|
|
77
|
+
top: y * (cellSize + BORDER_WIDTH),
|
|
78
|
+
}}
|
|
79
|
+
>
|
|
80
|
+
<div className={styles.iconBackground} />
|
|
81
|
+
<FlagFill className={styles.icon} />
|
|
82
|
+
</div>
|
|
83
|
+
))}
|
|
60
84
|
</div>
|
|
61
85
|
),
|
|
62
86
|
);
|
|
@@ -17,18 +17,14 @@
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
[dir='ltr'] & {
|
|
20
|
-
|
|
21
|
-
&:nth-child(15n + 2),
|
|
22
|
-
&:nth-child(15n + 3) {
|
|
20
|
+
&.first3 {
|
|
23
21
|
input {
|
|
24
22
|
left: 0;
|
|
25
23
|
clip-path: polygon(0 (100% / 3), (100% / 3) (100% / 3), (100% / 3) (200% / 3), 0 (200% / 3));
|
|
26
24
|
}
|
|
27
25
|
}
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
&:nth-last-child(15n + 2),
|
|
31
|
-
&:nth-last-child(15n + 3) {
|
|
27
|
+
&.last3 {
|
|
32
28
|
input {
|
|
33
29
|
left: -200%;
|
|
34
30
|
clip-path: polygon((200% / 3) (100% / 3), 100% (100% / 3), 100% (200% / 3), (200% / 3) (200% / 3));
|
|
@@ -37,9 +33,7 @@
|
|
|
37
33
|
}
|
|
38
34
|
|
|
39
35
|
[dir='rtl'] & {
|
|
40
|
-
|
|
41
|
-
&:nth-child(15n + 2),
|
|
42
|
-
&:nth-child(15n + 3) {
|
|
36
|
+
&.first3 {
|
|
43
37
|
input {
|
|
44
38
|
left: -200%;
|
|
45
39
|
right: 0;
|
|
@@ -47,9 +41,7 @@
|
|
|
47
41
|
}
|
|
48
42
|
}
|
|
49
43
|
|
|
50
|
-
|
|
51
|
-
&:nth-last-child(15n + 2),
|
|
52
|
-
&:nth-last-child(15n + 3) {
|
|
44
|
+
&.last3 {
|
|
53
45
|
input {
|
|
54
46
|
left: 0;
|
|
55
47
|
right: -200%;
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
|
|
14
14
|
import {
|
|
15
15
|
selectCellIsValid,
|
|
16
|
+
selectConfig,
|
|
16
17
|
selectInputMode,
|
|
17
18
|
selectLocale,
|
|
18
19
|
selectTilePoints,
|
|
@@ -52,6 +53,7 @@ const Cell: FunctionComponent<Props> = ({
|
|
|
52
53
|
const { tile, x, y } = cell;
|
|
53
54
|
const translate = useTranslate();
|
|
54
55
|
const locale = useTypedSelector(selectLocale);
|
|
56
|
+
const config = useTypedSelector(selectConfig);
|
|
55
57
|
const inputMode = useTypedSelector(selectInputMode);
|
|
56
58
|
const points = useTypedSelector((state) => selectTilePoints(state, cell.tile));
|
|
57
59
|
const isValid = useTypedSelector((state) => selectCellIsValid(state, cell));
|
|
@@ -98,6 +100,8 @@ const Cell: FunctionComponent<Props> = ({
|
|
|
98
100
|
y: (y + 1).toLocaleString(locale),
|
|
99
101
|
})}
|
|
100
102
|
className={classNames(styles.tile, className, {
|
|
103
|
+
[styles.first3]: x < 3,
|
|
104
|
+
[styles.last3]: config.boardWidth - x - 1 < 3,
|
|
101
105
|
[styles.sharpTopLeft]: cellTop?.hasTile() || cellLeft?.hasTile(),
|
|
102
106
|
[styles.sharpTopRight]: cellTop?.hasTile() || cellRight?.hasTile(),
|
|
103
107
|
[styles.sharpBottomLeft]: cellBottom?.hasTile() || cellLeft?.hasTile(),
|
|
@@ -9,7 +9,7 @@ import { useAppLayout, useMediaQueries } from 'hooks';
|
|
|
9
9
|
import { FlagFill, Star } from 'icons';
|
|
10
10
|
import { dataUrlToBlob, getTileSizes } from 'lib';
|
|
11
11
|
import { BORDER_COLOR_LIGHT, BORDER_RADIUS, BORDER_WIDTH, COLOR_BONUS_START, COLOR_FILTERED } from 'parameters';
|
|
12
|
-
import {
|
|
12
|
+
import { selectConfig, store, useTypedSelector } from 'state';
|
|
13
13
|
import { Point } from 'types';
|
|
14
14
|
|
|
15
15
|
import { getBonusColor } from '../lib';
|
|
@@ -21,6 +21,7 @@ const VERTICAL_LINE = 'v';
|
|
|
21
21
|
const BONUS = 'b';
|
|
22
22
|
const BONUS_WORD_2 = 'b2';
|
|
23
23
|
const BONUS_WORD_3 = 'b3';
|
|
24
|
+
const BONUS_WORD_4 = 'b4';
|
|
24
25
|
const CELL_FILTER = 'c';
|
|
25
26
|
|
|
26
27
|
const useBackgroundImage = () => {
|
|
@@ -29,7 +30,6 @@ const useBackgroundImage = () => {
|
|
|
29
30
|
const borderRadius = isLessThanXs ? BORDER_RADIUS_XS : BORDER_RADIUS;
|
|
30
31
|
const config = useTypedSelector(selectConfig);
|
|
31
32
|
const center = { x: Math.floor(config.boardWidth / 2), y: Math.floor(config.boardHeight / 2) };
|
|
32
|
-
const cellFilter = useTypedSelector(selectCellFilter);
|
|
33
33
|
const viewBoxHeight = boardSize;
|
|
34
34
|
const viewBoxWidth = boardSize;
|
|
35
35
|
const bonusSize = cellSize * 0.8;
|
|
@@ -42,6 +42,7 @@ const useBackgroundImage = () => {
|
|
|
42
42
|
const characterBonuses = config.bonuses.filter((bonus) => bonus.type === BONUS_CHARACTER);
|
|
43
43
|
const word2Bonuses = config.bonuses.filter((bonus) => bonus.type === BONUS_WORD && bonus.multiplier === 2);
|
|
44
44
|
const word3Bonuses = config.bonuses.filter((bonus) => bonus.type === BONUS_WORD && bonus.multiplier === 3);
|
|
45
|
+
const word4Bonuses = config.bonuses.filter((bonus) => bonus.type === BONUS_WORD && bonus.multiplier === 4);
|
|
45
46
|
|
|
46
47
|
const getX = (point: Point): number => point.x * (cellSize + BORDER_WIDTH);
|
|
47
48
|
|
|
@@ -102,6 +103,23 @@ const useBackgroundImage = () => {
|
|
|
102
103
|
</text>
|
|
103
104
|
</symbol>
|
|
104
105
|
|
|
106
|
+
<symbol id={BONUS_WORD_4}>
|
|
107
|
+
<rect height={bonusSize} rx={borderRadius} width={bonusSize} x={bonusOffset} y={bonusOffset} />
|
|
108
|
+
|
|
109
|
+
<text
|
|
110
|
+
dominantBaseline="central"
|
|
111
|
+
fill="white"
|
|
112
|
+
fontFamily="system-ui, sans-serif"
|
|
113
|
+
fontSize={fontSize}
|
|
114
|
+
fontWeight="bold"
|
|
115
|
+
textAnchor="middle"
|
|
116
|
+
x={fontOffset}
|
|
117
|
+
y={fontOffset}
|
|
118
|
+
>
|
|
119
|
+
x4
|
|
120
|
+
</text>
|
|
121
|
+
</symbol>
|
|
122
|
+
|
|
105
123
|
<symbol id={CELL_FILTER}>
|
|
106
124
|
<rect
|
|
107
125
|
fill={COLOR_FILTERED}
|
|
@@ -138,6 +156,10 @@ const useBackgroundImage = () => {
|
|
|
138
156
|
<use fill={getBonusColor(bonus)} key={index} href={`#${BONUS_WORD_3}`} x={getX(bonus)} y={getY(bonus)} />
|
|
139
157
|
))}
|
|
140
158
|
|
|
159
|
+
{word4Bonuses.map((bonus, index) => (
|
|
160
|
+
<use fill={getBonusColor(bonus)} key={index} href={`#${BONUS_WORD_4}`} x={getX(bonus)} y={getY(bonus)} />
|
|
161
|
+
))}
|
|
162
|
+
|
|
141
163
|
<rect
|
|
142
164
|
fill={COLOR_BONUS_START}
|
|
143
165
|
height={bonusSize}
|
|
@@ -154,10 +176,6 @@ const useBackgroundImage = () => {
|
|
|
154
176
|
x={getX(center) + iconOffset}
|
|
155
177
|
y={getY(center) + iconOffset}
|
|
156
178
|
/>
|
|
157
|
-
|
|
158
|
-
{cellFilter.map((cell, index) => (
|
|
159
|
-
<use key={index} href={`#${CELL_FILTER}`} x={getX(cell)} y={getY(cell)} />
|
|
160
|
-
))}
|
|
161
179
|
</svg>
|
|
162
180
|
</Provider>,
|
|
163
181
|
);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Locale } from '@scrabble-solver/types';
|
|
1
|
+
import { getConfig } from '@scrabble-solver/configs';
|
|
2
|
+
import { Game, Locale } from '@scrabble-solver/types';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
PLAIN_TILES_COLOR_DEFAULT,
|
|
6
6
|
PLAIN_TILES_POINTS_COLORS,
|
|
7
|
-
PLAIN_TILES_TILE_MAX_SCATTER,
|
|
8
7
|
PLAIN_TILES_TILE_MAX_ROTATE,
|
|
8
|
+
PLAIN_TILES_TILE_MAX_SCATTER,
|
|
9
9
|
PLAIN_TILES_TILE_SIZE,
|
|
10
10
|
} from 'parameters';
|
|
11
11
|
|
|
@@ -16,7 +16,7 @@ import getY from './getY';
|
|
|
16
16
|
import randomize from './randomize';
|
|
17
17
|
|
|
18
18
|
const createPlainTile = ({ cellIndex, character, color, rowIndex, showPoints }: CreatePlainTileOptions): PlainTile => {
|
|
19
|
-
const configPoints =
|
|
19
|
+
const configPoints = getConfig(Game.Literaki, Locale.EN_US).getCharacterPoints(character.toLowerCase());
|
|
20
20
|
const points = showPoints ? configPoints : undefined;
|
|
21
21
|
const defaultColor =
|
|
22
22
|
typeof configPoints === 'number' ? PLAIN_TILES_POINTS_COLORS[configPoints] : PLAIN_TILES_COLOR_DEFAULT;
|
|
@@ -10,9 +10,9 @@ const INVISIBLE_STYLE: CSSProperties = {
|
|
|
10
10
|
|
|
11
11
|
const SeoMessage: FunctionComponent = () => (
|
|
12
12
|
<p style={INVISIBLE_STYLE}>
|
|
13
|
-
Scrabble Solver 2 is a free and open-source analysis tool for Scrabble
|
|
14
|
-
using given letters and board state. Available in English, French, German, Polish & Spanish.
|
|
15
|
-
available on GitHub - contributions are welcome!
|
|
13
|
+
Scrabble Solver 2 is a free and open-source analysis tool for Scrabble, Super Scrabble & Literaki. Quickly find
|
|
14
|
+
top scoring words using given letters and board state. Available in English, French, German, Polish & Spanish.
|
|
15
|
+
Source code is available on GitHub - contributions are welcome!
|
|
16
16
|
</p>
|
|
17
17
|
);
|
|
18
18
|
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
BOARD_TILE_SIZE_MAX,
|
|
5
|
-
BOARD_TILE_SIZE_MIN,
|
|
6
5
|
BORDER_WIDTH,
|
|
7
6
|
BUTTON_HEIGHT,
|
|
8
7
|
COMPONENTS_SPACING,
|
|
@@ -47,7 +46,7 @@ const useAppLayout = () => {
|
|
|
47
46
|
: Math.max(solverHeight - bottomContainerHeight, 0);
|
|
48
47
|
const cellWidth = (maxBoardWidth - (config.boardWidth + 1) * BORDER_WIDTH) / config.boardWidth;
|
|
49
48
|
const cellHeight = (maxBoardHeight - (config.boardHeight + 1) * BORDER_WIDTH) / config.boardHeight;
|
|
50
|
-
const cellSize = Math.min(Math.
|
|
49
|
+
const cellSize = Math.min(Math.min(cellWidth, cellHeight), BOARD_TILE_SIZE_MAX);
|
|
51
50
|
const boardSize = (cellSize + BORDER_WIDTH) * config.boardWidth + BORDER_WIDTH;
|
|
52
51
|
const maxControlsWidth = tileSize * config.maximumCharactersCount + 2 * BORDER_WIDTH;
|
|
53
52
|
const showResultsInModal = isLessThanL;
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
localStorage,
|
|
5
5
|
selectAutoGroupTiles,
|
|
6
6
|
selectBoard,
|
|
7
|
-
|
|
7
|
+
selectGame,
|
|
8
8
|
selectInputMode,
|
|
9
9
|
selectLocale,
|
|
10
10
|
selectRack,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
const useLocalStorage = () => {
|
|
15
15
|
const autoGroupTiles = useTypedSelector(selectAutoGroupTiles);
|
|
16
16
|
const board = useTypedSelector(selectBoard);
|
|
17
|
-
const
|
|
17
|
+
const game = useTypedSelector(selectGame);
|
|
18
18
|
const inputMode = useTypedSelector(selectInputMode);
|
|
19
19
|
const locale = useTypedSelector(selectLocale);
|
|
20
20
|
const rack = useTypedSelector(selectRack);
|
|
@@ -32,10 +32,10 @@ const useLocalStorage = () => {
|
|
|
32
32
|
}, [board]);
|
|
33
33
|
|
|
34
34
|
useEffect(() => {
|
|
35
|
-
if (
|
|
36
|
-
localStorage.
|
|
35
|
+
if (game) {
|
|
36
|
+
localStorage.setGame(game);
|
|
37
37
|
}
|
|
38
|
-
}, [
|
|
38
|
+
}, [game]);
|
|
39
39
|
|
|
40
40
|
useEffect(() => {
|
|
41
41
|
if (inputMode) {
|
package/src/i18n/constants.ts
CHANGED
|
@@ -4,12 +4,14 @@ import { FunctionComponent, SVGAttributes } from 'react';
|
|
|
4
4
|
|
|
5
5
|
import { FlagDe, FlagEs, FlagFa, FlagFr, FlagGb, FlagPl, FlagUs } from 'icons';
|
|
6
6
|
|
|
7
|
-
import styles from './i18n.module.scss';
|
|
8
|
-
|
|
9
7
|
interface LocaleFeatures {
|
|
10
8
|
comma: string;
|
|
11
9
|
consonants: boolean;
|
|
12
10
|
direction: 'ltr' | 'rtl';
|
|
11
|
+
Icon: FunctionComponent<SVGAttributes<SVGElement>>;
|
|
12
|
+
label: string;
|
|
13
|
+
locale: Locale;
|
|
14
|
+
name: string;
|
|
13
15
|
separator: string;
|
|
14
16
|
vowels: boolean;
|
|
15
17
|
}
|
|
@@ -19,6 +21,10 @@ export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
|
|
|
19
21
|
comma: COMMA_LATIN,
|
|
20
22
|
consonants: true,
|
|
21
23
|
direction: 'ltr',
|
|
24
|
+
Icon: FlagDe,
|
|
25
|
+
label: 'Deutsch',
|
|
26
|
+
locale: Locale.DE_DE,
|
|
27
|
+
name: 'German',
|
|
22
28
|
separator: `${COMMA_LATIN} `,
|
|
23
29
|
vowels: true,
|
|
24
30
|
},
|
|
@@ -26,6 +32,10 @@ export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
|
|
|
26
32
|
comma: COMMA_LATIN,
|
|
27
33
|
consonants: true,
|
|
28
34
|
direction: 'ltr',
|
|
35
|
+
Icon: FlagGb,
|
|
36
|
+
label: 'English (GB)',
|
|
37
|
+
locale: Locale.EN_GB,
|
|
38
|
+
name: 'English (GB)',
|
|
29
39
|
separator: `${COMMA_LATIN} `,
|
|
30
40
|
vowels: true,
|
|
31
41
|
},
|
|
@@ -33,6 +43,10 @@ export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
|
|
|
33
43
|
comma: COMMA_LATIN,
|
|
34
44
|
consonants: true,
|
|
35
45
|
direction: 'ltr',
|
|
46
|
+
Icon: FlagUs,
|
|
47
|
+
label: 'English (US)',
|
|
48
|
+
locale: Locale.EN_US,
|
|
49
|
+
name: 'English (US)',
|
|
36
50
|
separator: `${COMMA_LATIN} `,
|
|
37
51
|
vowels: true,
|
|
38
52
|
},
|
|
@@ -40,6 +54,10 @@ export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
|
|
|
40
54
|
comma: COMMA_LATIN,
|
|
41
55
|
consonants: true,
|
|
42
56
|
direction: 'ltr',
|
|
57
|
+
Icon: FlagEs,
|
|
58
|
+
label: 'Español',
|
|
59
|
+
locale: Locale.ES_ES,
|
|
60
|
+
name: 'Spanish',
|
|
43
61
|
separator: `${COMMA_LATIN} `,
|
|
44
62
|
vowels: true,
|
|
45
63
|
},
|
|
@@ -47,6 +65,10 @@ export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
|
|
|
47
65
|
comma: COMMA_ARABIC,
|
|
48
66
|
consonants: false,
|
|
49
67
|
direction: 'rtl',
|
|
68
|
+
Icon: FlagFa,
|
|
69
|
+
label: 'فارسی',
|
|
70
|
+
locale: Locale.FA_IR,
|
|
71
|
+
name: 'Persian',
|
|
50
72
|
separator: `${COMMA_ARABIC} `,
|
|
51
73
|
vowels: false,
|
|
52
74
|
},
|
|
@@ -54,6 +76,10 @@ export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
|
|
|
54
76
|
comma: COMMA_LATIN,
|
|
55
77
|
consonants: true,
|
|
56
78
|
direction: 'ltr',
|
|
79
|
+
Icon: FlagFr,
|
|
80
|
+
label: 'Français',
|
|
81
|
+
locale: Locale.FR_FR,
|
|
82
|
+
name: 'French',
|
|
57
83
|
separator: `${COMMA_LATIN} `,
|
|
58
84
|
vowels: true,
|
|
59
85
|
},
|
|
@@ -61,67 +87,11 @@ export const LOCALE_FEATURES: Record<Locale, LocaleFeatures> = {
|
|
|
61
87
|
comma: COMMA_LATIN,
|
|
62
88
|
consonants: true,
|
|
63
89
|
direction: 'ltr',
|
|
64
|
-
separator: `${COMMA_LATIN} `,
|
|
65
|
-
vowels: true,
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
interface Flag {
|
|
70
|
-
className: string;
|
|
71
|
-
Icon: FunctionComponent<SVGAttributes<SVGElement>>;
|
|
72
|
-
label: string;
|
|
73
|
-
name: string;
|
|
74
|
-
value: Locale;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export const LOCALE_FLAGS: Record<Locale, Flag> = {
|
|
78
|
-
[Locale.DE_DE]: {
|
|
79
|
-
className: styles.de,
|
|
80
|
-
Icon: FlagDe,
|
|
81
|
-
label: 'Deutsch',
|
|
82
|
-
name: 'German',
|
|
83
|
-
value: Locale.DE_DE,
|
|
84
|
-
},
|
|
85
|
-
[Locale.EN_GB]: {
|
|
86
|
-
className: styles.gb,
|
|
87
|
-
Icon: FlagGb,
|
|
88
|
-
label: 'English (GB)',
|
|
89
|
-
name: 'English (GB)',
|
|
90
|
-
value: Locale.EN_GB,
|
|
91
|
-
},
|
|
92
|
-
[Locale.EN_US]: {
|
|
93
|
-
className: styles.us,
|
|
94
|
-
Icon: FlagUs,
|
|
95
|
-
label: 'English (US)',
|
|
96
|
-
name: 'English (US)',
|
|
97
|
-
value: Locale.EN_US,
|
|
98
|
-
},
|
|
99
|
-
[Locale.ES_ES]: {
|
|
100
|
-
className: styles.es,
|
|
101
|
-
Icon: FlagEs,
|
|
102
|
-
label: 'Español',
|
|
103
|
-
name: 'Spanish',
|
|
104
|
-
value: Locale.ES_ES,
|
|
105
|
-
},
|
|
106
|
-
[Locale.FA_IR]: {
|
|
107
|
-
className: styles.fa,
|
|
108
|
-
Icon: FlagFa,
|
|
109
|
-
label: 'فارسی',
|
|
110
|
-
name: 'Persian',
|
|
111
|
-
value: Locale.FA_IR,
|
|
112
|
-
},
|
|
113
|
-
[Locale.FR_FR]: {
|
|
114
|
-
className: styles.fr,
|
|
115
|
-
Icon: FlagFr,
|
|
116
|
-
label: 'Français',
|
|
117
|
-
name: 'French',
|
|
118
|
-
value: Locale.FR_FR,
|
|
119
|
-
},
|
|
120
|
-
[Locale.PL_PL]: {
|
|
121
|
-
className: styles.pl,
|
|
122
90
|
Icon: FlagPl,
|
|
123
91
|
label: 'Polski',
|
|
92
|
+
locale: Locale.PL_PL,
|
|
124
93
|
name: 'Polish',
|
|
125
|
-
|
|
94
|
+
separator: `${COMMA_LATIN} `,
|
|
95
|
+
vowels: true,
|
|
126
96
|
},
|
|
127
97
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getConfig } from '@scrabble-solver/configs';
|
|
2
2
|
import { BLANK } from '@scrabble-solver/constants';
|
|
3
|
-
import { Locale } from '@scrabble-solver/types';
|
|
3
|
+
import { Game, Locale } from '@scrabble-solver/types';
|
|
4
4
|
|
|
5
5
|
import extractCharacters from './extractCharacters';
|
|
6
6
|
|
|
@@ -16,7 +16,7 @@ const tests = [
|
|
|
16
16
|
|
|
17
17
|
describe('extractCharacters', () => {
|
|
18
18
|
const locale = Locale.ES_ES;
|
|
19
|
-
const config =
|
|
19
|
+
const config = getConfig(Game.Scrabble, locale);
|
|
20
20
|
|
|
21
21
|
for (const { input, expected } of tests) {
|
|
22
22
|
it(`[${locale}] "${input}"`, () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getConfig } from '@scrabble-solver/configs';
|
|
2
2
|
import { BLANK } from '@scrabble-solver/constants';
|
|
3
|
-
import { Locale } from '@scrabble-solver/types';
|
|
3
|
+
import { Game, Locale } from '@scrabble-solver/types';
|
|
4
4
|
|
|
5
5
|
import extractCharactersByCase from './extractCharactersByCase';
|
|
6
6
|
|
|
@@ -21,7 +21,7 @@ const tests = [
|
|
|
21
21
|
|
|
22
22
|
describe('extractCharactersByCase', () => {
|
|
23
23
|
const locale = Locale.ES_ES;
|
|
24
|
-
const config =
|
|
24
|
+
const config = getConfig(Game.Scrabble, locale);
|
|
25
25
|
|
|
26
26
|
for (const { input, expected } of tests) {
|
|
27
27
|
it(`[${locale}] "${input}"`, () => {
|
package/src/lib/getCellSize.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Config } from '@scrabble-solver/types';
|
|
2
2
|
|
|
3
|
-
import { BOARD_CELL_BORDER_WIDTH, BOARD_TILE_SIZE_MAX
|
|
3
|
+
import { BOARD_CELL_BORDER_WIDTH, BOARD_TILE_SIZE_MAX } from 'parameters';
|
|
4
4
|
|
|
5
5
|
const getCellSize = (config: Config, width: number, height: number): number => {
|
|
6
6
|
const maxWidth = (width - BOARD_CELL_BORDER_WIDTH) / config.boardWidth - BOARD_CELL_BORDER_WIDTH;
|
|
7
7
|
const maxHeight = (height - BOARD_CELL_BORDER_WIDTH) / config.boardHeight - BOARD_CELL_BORDER_WIDTH;
|
|
8
8
|
const cellSize = Math.min(maxWidth, maxHeight);
|
|
9
|
-
return Math.floor(Math.min(
|
|
9
|
+
return Math.floor(Math.min(cellSize, BOARD_TILE_SIZE_MAX));
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
export default getCellSize;
|