scrabble-solver 2.8.5 → 2.8.7
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/README.md +31 -24
- package/lerna.json +1 -1
- package/package.json +2 -2
- package/packages/configs/build/literaki/literaki.js +2 -2
- package/packages/configs/build/scrabble/scrabble.js +2 -2
- package/packages/configs/package.json +3 -3
- package/packages/configs/src/literaki/literaki.ts +2 -2
- package/packages/configs/src/scrabble/scrabble.ts +2 -2
- 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 +9 -9
- 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 +183 -94
- package/packages/scrabble-solver/.next/server/chunks/{206.js → 429.js} +2 -4137
- package/packages/scrabble-solver/.next/server/chunks/515.js +111 -78
- package/packages/scrabble-solver/.next/server/chunks/{907.js → 911.js} +134 -367
- package/packages/scrabble-solver/.next/server/chunks/939.js +218 -0
- 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 +3 -2
- package/packages/scrabble-solver/.next/server/pages/_app.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/_document.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/dictionary/[locale]/[word].js +33 -17
- package/packages/scrabble-solver/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/solve.js +404 -58
- package/packages/scrabble-solver/.next/server/pages/api/solve.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/api/visit.js +3 -2
- package/packages/scrabble-solver/.next/server/pages/api/visit.js.nft.json +1 -1
- package/packages/scrabble-solver/.next/server/pages/index.html +3 -7
- package/packages/scrabble-solver/.next/server/pages/index.js +12 -14
- 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/static/chunks/56-cf37c430261bbea5.js +1 -0
- package/packages/scrabble-solver/.next/static/chunks/pages/_app-0b12a65bea70a0df.js +1 -0
- package/packages/scrabble-solver/.next/static/chunks/pages/index-fcb69802550afb81.js +1 -0
- package/packages/scrabble-solver/.next/static/css/1f39b55d50f5b30b.css +1 -0
- package/packages/scrabble-solver/.next/static/css/751e8a14776d05d8.css +1 -0
- package/packages/scrabble-solver/.next/static/z_0_lqfmiI_ISokr6NNRq/_buildManifest.js +1 -0
- package/packages/scrabble-solver/.next/static/{TzKQ3IntkvaYmHBkWpfoi → z_0_lqfmiI_ISokr6NNRq}/_ssgManifest.js +0 -0
- package/packages/scrabble-solver/.next/trace +40 -42
- package/packages/scrabble-solver/package-lock.json +2 -2
- package/packages/scrabble-solver/package.json +8 -8
- package/packages/scrabble-solver/src/api/index.ts +3 -9
- package/packages/scrabble-solver/src/api/isBoardValid.ts +43 -0
- package/packages/scrabble-solver/src/api/isCellValid.ts +26 -0
- package/packages/scrabble-solver/src/api/isRowValid.ts +19 -0
- package/packages/scrabble-solver/src/components/Board/Board.tsx +3 -1
- package/packages/scrabble-solver/src/components/Board/BoardPure.tsx +4 -1
- package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.module.scss +28 -11
- package/packages/scrabble-solver/src/components/Board/components/Cell/Cell.tsx +12 -1
- package/packages/scrabble-solver/src/components/Board/components/Cell/CellPure.tsx +3 -1
- package/packages/scrabble-solver/src/components/Board/components/Cell/lib.ts +10 -2
- package/packages/scrabble-solver/src/components/Dictionary/Dictionary.module.scss +20 -0
- package/packages/scrabble-solver/src/components/Dictionary/Dictionary.tsx +40 -29
- package/packages/scrabble-solver/src/components/Results/Cell.tsx +3 -2
- package/packages/scrabble-solver/src/components/Results/Result.tsx +16 -6
- package/packages/scrabble-solver/src/components/ResultsInput/ResultsInput.tsx +11 -3
- package/packages/scrabble-solver/src/hooks/useIsTablet.ts +2 -2
- package/packages/scrabble-solver/src/lib/getRemainingTiles.ts +1 -1
- package/packages/scrabble-solver/src/lib/index.ts +2 -1
- package/packages/scrabble-solver/src/lib/isRegExp.ts +11 -0
- package/packages/scrabble-solver/src/lib/isStringArray.ts +5 -0
- package/packages/scrabble-solver/src/lib/sortResults.ts +5 -5
- package/packages/scrabble-solver/src/pages/_app.tsx +6 -3
- package/packages/scrabble-solver/src/pages/api/dictionary/[locale]/[word].ts +35 -11
- package/packages/scrabble-solver/src/pages/api/solve.ts +39 -19
- package/packages/scrabble-solver/src/pages/api/visit.ts +1 -0
- package/packages/scrabble-solver/src/pages/index.module.scss +5 -11
- package/packages/scrabble-solver/src/pages/index.tsx +5 -5
- package/packages/scrabble-solver/src/sdk/{findWordDefinition.ts → findWordDefinitions.ts} +3 -3
- package/packages/scrabble-solver/src/sdk/index.ts +1 -1
- package/packages/scrabble-solver/src/state/sagas.ts +11 -11
- package/packages/scrabble-solver/src/state/selectors.ts +6 -2
- package/packages/scrabble-solver/src/state/slices/dictionaryInitialState.ts +3 -3
- package/packages/scrabble-solver/src/state/slices/dictionarySlice.ts +4 -10
- package/packages/solver/build/PatternsFiller.js +1 -1
- package/packages/solver/build/PatternsGenerator.d.ts +4 -4
- package/packages/solver/build/PatternsGenerator.js +6 -6
- package/packages/solver/build/ScoresCalculator.js +1 -1
- package/packages/solver/build/Solver.js +3 -3
- package/packages/solver/build/getUniquePatterns.d.ts +3 -0
- package/packages/solver/build/getUniquePatterns.js +25 -0
- package/packages/solver/package-lock.json +3 -29
- package/packages/solver/package.json +5 -7
- package/packages/solver/src/PatternsFiller.ts +1 -1
- package/packages/solver/src/PatternsGenerator.test.ts +2 -2
- package/packages/solver/src/PatternsGenerator.ts +8 -8
- package/packages/solver/src/ScoresCalculator.ts +1 -1
- package/packages/solver/src/Solver.ts +3 -3
- package/packages/solver/src/getUniquePatterns.ts +30 -0
- package/packages/types/build/Board.d.ts +2 -2
- package/packages/types/build/Board.js +8 -8
- package/packages/types/build/BoardJson.d.ts +2 -1
- package/packages/types/build/BoardJson.js +19 -0
- package/packages/types/build/Cell.d.ts +2 -3
- package/packages/types/build/Cell.js +0 -15
- package/packages/types/build/CellJson.d.ts +1 -0
- package/packages/types/build/CellJson.js +14 -0
- package/packages/types/build/Collision.d.ts +3 -0
- package/packages/types/build/Collision.js +2 -0
- package/packages/types/build/CollisionJson.d.ts +3 -0
- package/packages/types/build/CollisionJson.js +2 -0
- package/packages/types/build/Config.d.ts +2 -2
- package/packages/types/build/Config.js +5 -5
- package/packages/types/build/ConfigJson.d.ts +2 -2
- package/packages/types/build/Locale.d.ts +1 -0
- package/packages/types/build/Locale.js +4 -0
- package/packages/types/build/Pattern.d.ts +1 -1
- package/packages/types/build/Pattern.js +4 -4
- package/packages/types/build/PatternJson.d.ts +1 -1
- package/packages/types/build/Result.d.ts +10 -8
- package/packages/types/build/Result.js +15 -10
- package/packages/types/build/ResultJson.d.ts +3 -2
- package/packages/types/build/TileJson.d.ts +1 -0
- package/packages/types/build/TileJson.js +9 -0
- package/packages/types/build/index.d.ts +7 -4
- package/packages/types/build/index.js +10 -1
- package/packages/types/build/isObject.d.ts +2 -0
- package/packages/types/build/isObject.js +6 -0
- package/packages/types/package.json +2 -2
- package/packages/types/src/Board.ts +11 -11
- package/packages/types/src/BoardJson.ts +22 -2
- package/packages/types/src/Cell.ts +2 -19
- package/packages/types/src/CellJson.ts +12 -1
- package/packages/types/src/Collision.ts +5 -0
- package/packages/types/src/CollisionJson.ts +5 -0
- package/packages/types/src/Config.ts +6 -6
- package/packages/types/src/ConfigJson.ts +2 -2
- package/packages/types/src/Locale.ts +4 -0
- package/packages/types/src/Pattern.ts +4 -5
- package/packages/types/src/PatternJson.ts +1 -1
- package/packages/types/src/Result.ts +33 -23
- package/packages/types/src/ResultJson.ts +3 -2
- package/packages/types/src/TileJson.ts +6 -0
- package/packages/types/src/index.ts +7 -4
- package/packages/types/src/isObject.ts +5 -0
- package/packages/word-definitions/package-lock.json +2 -2
- package/packages/word-definitions/package.json +2 -2
- package/packages/word-lists/build/getDeDeWordList.js +1 -1
- package/packages/word-lists/build/lib/extractWords.js +4 -1
- package/packages/word-lists/package-lock.json +2 -2
- package/packages/word-lists/package.json +2 -2
- package/packages/word-lists/src/getDeDeWordList.ts +1 -1
- package/packages/word-lists/src/lib/extractWords.ts +4 -1
- package/packages/scrabble-solver/.next/static/TzKQ3IntkvaYmHBkWpfoi/_buildManifest.js +0 -1
- package/packages/scrabble-solver/.next/static/chunks/56-2d34867599a0ac66.js +0 -1
- package/packages/scrabble-solver/.next/static/chunks/pages/_app-6ffa2ab900772b67.js +0 -1
- package/packages/scrabble-solver/.next/static/chunks/pages/index-13ea7770a65c69ee.js +0 -1
- package/packages/scrabble-solver/.next/static/css/3159cfe62ff742a3.css +0 -1
- package/packages/scrabble-solver/.next/static/css/551d09cac435debb.css +0 -1
- package/packages/scrabble-solver/src/api/validateBoard.ts +0 -45
- package/packages/scrabble-solver/src/api/validateCell.ts +0 -40
- package/packages/scrabble-solver/src/api/validateCharacter.ts +0 -14
- package/packages/scrabble-solver/src/api/validateCharacters.ts +0 -24
- package/packages/scrabble-solver/src/api/validateConfigId.ts +0 -9
- package/packages/scrabble-solver/src/api/validateLocale.ts +0 -15
- package/packages/scrabble-solver/src/api/validateRow.ts +0 -17
- package/packages/scrabble-solver/src/api/validateTile.ts +0 -21
- package/packages/scrabble-solver/src/api/validateWord.ts +0 -11
- package/packages/scrabble-solver/src/lib/isLocale.ts +0 -7
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrabble-solver/scrabble-solver",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.7",
|
|
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.7",
|
|
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.7",
|
|
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.7",
|
|
34
|
+
"@scrabble-solver/constants": "^2.8.7",
|
|
35
|
+
"@scrabble-solver/dictionaries": "^2.8.7",
|
|
36
|
+
"@scrabble-solver/logger": "^2.8.7",
|
|
37
|
+
"@scrabble-solver/solver": "^2.8.7",
|
|
38
|
+
"@scrabble-solver/types": "^2.8.7",
|
|
39
|
+
"@scrabble-solver/word-definitions": "^2.8.7",
|
|
40
40
|
"classnames": "^2.3.2",
|
|
41
41
|
"next": "^12.3.1",
|
|
42
42
|
"normalize.css": "^8.0.1",
|
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
export { default as getServerLoggingData } from './getServerLoggingData';
|
|
2
|
-
export { default as
|
|
3
|
-
export { default as
|
|
4
|
-
export { default as
|
|
5
|
-
export { default as validateCharacters } from './validateCharacters';
|
|
6
|
-
export { default as validateConfigId } from './validateConfigId';
|
|
7
|
-
export { default as validateLocale } from './validateLocale';
|
|
8
|
-
export { default as validateRow } from './validateRow';
|
|
9
|
-
export { default as validateTile } from './validateTile';
|
|
10
|
-
export { default as validateWord } from './validateWord';
|
|
2
|
+
export { default as isBoardValid } from './isBoardValid';
|
|
3
|
+
export { default as isCellValid } from './isCellValid';
|
|
4
|
+
export { default as isRowValid } from './isRowValid';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { BoardJson, CellJson, Config } from '@scrabble-solver/types';
|
|
2
|
+
|
|
3
|
+
import isRowValid from './isRowValid';
|
|
4
|
+
|
|
5
|
+
const isBoardValid = (board: BoardJson, config: Config): boolean => {
|
|
6
|
+
if (board.length !== config.boardHeight) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
for (const row of board) {
|
|
11
|
+
if (!isRowValid(row, config)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return areTwoCharacterTilesValid(board, config);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const areTwoCharacterTilesValid = (board: CellJson[][], config: Config): boolean => {
|
|
20
|
+
const cells: CellJson[] = board
|
|
21
|
+
.flat()
|
|
22
|
+
.filter((cell) => cell && cell.tile && config.isTwoCharacterTilePrefix(cell.tile.character));
|
|
23
|
+
|
|
24
|
+
for (const cell of cells) {
|
|
25
|
+
for (const characters of config.twoCharacterTiles) {
|
|
26
|
+
const canCheckDown = cell.y + 1 < board.length;
|
|
27
|
+
const canCheckRight = cell.x + 1 < board[0].length;
|
|
28
|
+
const cellDown = board[cell.y + 1][cell.x];
|
|
29
|
+
const cellRight = board[cell.y][cell.x + 1];
|
|
30
|
+
const collidesDown = canCheckDown && cellDown.tile && cellDown.tile.character === characters[1];
|
|
31
|
+
const collidesRight = canCheckRight && cellRight.tile && cellRight.tile.character === characters[1];
|
|
32
|
+
const collides = collidesDown || collidesRight;
|
|
33
|
+
|
|
34
|
+
if (cell.tile && characters.startsWith(cell.tile.character) && collides) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return true;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default isBoardValid;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { BLANK } from '@scrabble-solver/constants';
|
|
2
|
+
import { CellJson, Config } from '@scrabble-solver/types';
|
|
3
|
+
|
|
4
|
+
const isCellValid = (cell: CellJson, config: Config): boolean => {
|
|
5
|
+
const { isEmpty, tile, x, y } = cell;
|
|
6
|
+
|
|
7
|
+
if (x < 0 || x >= config.boardWidth) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (y < 0 || y >= config.boardHeight) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (isEmpty && tile !== null) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (tile !== null && !config.hasCharacter(tile.character) && tile.character !== BLANK) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return true;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default isCellValid;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CellJson, Config } from '@scrabble-solver/types';
|
|
2
|
+
|
|
3
|
+
import isCellValid from './isCellValid';
|
|
4
|
+
|
|
5
|
+
const isRowValid = (row: CellJson[], config: Config): boolean => {
|
|
6
|
+
if (row.length !== config.boardWidth) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
for (const cell of row) {
|
|
11
|
+
if (!isCellValid(cell, config)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return true;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default isRowValid;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FunctionComponent, Ref } from 'react';
|
|
2
2
|
|
|
3
|
-
import { selectRowsWithCandidate, useTypedSelector } from 'state';
|
|
3
|
+
import { selectBoard, selectRowsWithCandidate, useTypedSelector } from 'state';
|
|
4
4
|
|
|
5
5
|
import BoardPure from './BoardPure';
|
|
6
6
|
import { useGrid } from './hooks';
|
|
@@ -13,12 +13,14 @@ interface Props {
|
|
|
13
13
|
|
|
14
14
|
const Board: FunctionComponent<Props> = ({ cellSize, className, innerRef }) => {
|
|
15
15
|
const rows = useTypedSelector(selectRowsWithCandidate);
|
|
16
|
+
const board = useTypedSelector(selectBoard);
|
|
16
17
|
const [{ lastDirection, refs }, { onDirectionToggle, onFocus, onKeyDown }] = useGrid(rows);
|
|
17
18
|
|
|
18
19
|
return (
|
|
19
20
|
<BoardPure
|
|
20
21
|
className={className}
|
|
21
22
|
cellSize={cellSize}
|
|
23
|
+
center={board.center}
|
|
22
24
|
innerRef={innerRef}
|
|
23
25
|
lastDirection={lastDirection}
|
|
24
26
|
refs={refs}
|
|
@@ -8,6 +8,7 @@ import { Cell as CellComponent } from './components';
|
|
|
8
8
|
interface Props {
|
|
9
9
|
className?: string;
|
|
10
10
|
cellSize: number;
|
|
11
|
+
center: Cell;
|
|
11
12
|
innerRef?: Ref<HTMLDivElement>;
|
|
12
13
|
lastDirection: 'horizontal' | 'vertical';
|
|
13
14
|
refs: RefObject<HTMLInputElement>[][];
|
|
@@ -19,11 +20,12 @@ interface Props {
|
|
|
19
20
|
|
|
20
21
|
const BoardPure: FunctionComponent<Props> = ({
|
|
21
22
|
className,
|
|
23
|
+
cellSize,
|
|
24
|
+
center,
|
|
22
25
|
innerRef,
|
|
23
26
|
lastDirection,
|
|
24
27
|
refs,
|
|
25
28
|
rows,
|
|
26
|
-
cellSize,
|
|
27
29
|
onDirectionToggle,
|
|
28
30
|
onFocus,
|
|
29
31
|
onKeyDown,
|
|
@@ -37,6 +39,7 @@ const BoardPure: FunctionComponent<Props> = ({
|
|
|
37
39
|
cell={cell}
|
|
38
40
|
direction={lastDirection}
|
|
39
41
|
inputRef={refs[y][x]}
|
|
42
|
+
isCenter={center.x === x && center.y === y}
|
|
40
43
|
key={x}
|
|
41
44
|
size={cellSize}
|
|
42
45
|
onDirectionToggle={onDirectionToggle}
|
|
@@ -13,6 +13,34 @@ $icon-size: 16px;
|
|
|
13
13
|
transition: var(--transition);
|
|
14
14
|
background-clip: padding-box;
|
|
15
15
|
|
|
16
|
+
&.bonusStart,
|
|
17
|
+
&.bonusWord2,
|
|
18
|
+
&.bonusWord3 {
|
|
19
|
+
position: relative;
|
|
20
|
+
|
|
21
|
+
&:before {
|
|
22
|
+
display: flex;
|
|
23
|
+
justify-content: center;
|
|
24
|
+
align-items: center;
|
|
25
|
+
position: absolute;
|
|
26
|
+
top: 0;
|
|
27
|
+
bottom: 0;
|
|
28
|
+
left: 0;
|
|
29
|
+
right: 0;
|
|
30
|
+
font-weight: bold;
|
|
31
|
+
pointer-events: none;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&.bonusStart {
|
|
36
|
+
background-color: var(--color--red--light);
|
|
37
|
+
|
|
38
|
+
&:before {
|
|
39
|
+
content: '★';
|
|
40
|
+
color: var(--color--foreground--secondary);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
16
44
|
&.bonusCharacter1 {
|
|
17
45
|
background-color: var(--color--yellow--light);
|
|
18
46
|
}
|
|
@@ -39,22 +67,11 @@ $icon-size: 16px;
|
|
|
39
67
|
|
|
40
68
|
&.bonusWord2,
|
|
41
69
|
&.bonusWord3 {
|
|
42
|
-
position: relative;
|
|
43
70
|
background-color: var(--color--inactive);
|
|
44
71
|
|
|
45
72
|
&:before {
|
|
46
|
-
display: flex;
|
|
47
|
-
justify-content: center;
|
|
48
|
-
align-items: center;
|
|
49
|
-
position: absolute;
|
|
50
|
-
top: 0;
|
|
51
|
-
bottom: 0;
|
|
52
|
-
left: 0;
|
|
53
|
-
right: 0;
|
|
54
73
|
font-size: 75%;
|
|
55
|
-
font-weight: bold;
|
|
56
74
|
color: white;
|
|
57
|
-
pointer-events: none;
|
|
58
75
|
}
|
|
59
76
|
}
|
|
60
77
|
|
|
@@ -13,12 +13,22 @@ interface Props {
|
|
|
13
13
|
className?: string;
|
|
14
14
|
direction: 'horizontal' | 'vertical';
|
|
15
15
|
inputRef: RefObject<HTMLInputElement>;
|
|
16
|
+
isCenter: boolean;
|
|
16
17
|
size: number;
|
|
17
18
|
onDirectionToggle: () => void;
|
|
18
19
|
onFocus: (x: number, y: number) => void;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
const Cell: FunctionComponent<Props> = ({
|
|
22
|
+
const Cell: FunctionComponent<Props> = ({
|
|
23
|
+
cell,
|
|
24
|
+
className,
|
|
25
|
+
direction,
|
|
26
|
+
inputRef,
|
|
27
|
+
isCenter,
|
|
28
|
+
size,
|
|
29
|
+
onDirectionToggle,
|
|
30
|
+
onFocus,
|
|
31
|
+
}) => {
|
|
22
32
|
const { tile, x, y } = cell;
|
|
23
33
|
const dispatch = useDispatch();
|
|
24
34
|
const translate = useTranslate();
|
|
@@ -53,6 +63,7 @@ const Cell: FunctionComponent<Props> = ({ cell, className, direction, inputRef,
|
|
|
53
63
|
className={className}
|
|
54
64
|
direction={direction}
|
|
55
65
|
inputRef={inputRef}
|
|
66
|
+
isCenter={isCenter}
|
|
56
67
|
isEmpty={isEmpty}
|
|
57
68
|
points={points}
|
|
58
69
|
size={size}
|
|
@@ -17,6 +17,7 @@ interface Props {
|
|
|
17
17
|
className?: string;
|
|
18
18
|
direction: 'horizontal' | 'vertical';
|
|
19
19
|
inputRef: RefObject<HTMLInputElement>;
|
|
20
|
+
isCenter: boolean;
|
|
20
21
|
isEmpty: boolean;
|
|
21
22
|
points?: number;
|
|
22
23
|
size: number;
|
|
@@ -34,6 +35,7 @@ const CellPure: FunctionComponent<Props> = ({
|
|
|
34
35
|
className,
|
|
35
36
|
direction,
|
|
36
37
|
inputRef,
|
|
38
|
+
isCenter,
|
|
37
39
|
isEmpty,
|
|
38
40
|
points,
|
|
39
41
|
size,
|
|
@@ -45,7 +47,7 @@ const CellPure: FunctionComponent<Props> = ({
|
|
|
45
47
|
onToggleBlankClick,
|
|
46
48
|
}) => (
|
|
47
49
|
<div
|
|
48
|
-
className={classNames(styles.cell, getBonusClassname(cell, bonus), className, {
|
|
50
|
+
className={classNames(styles.cell, getBonusClassname(cell, bonus, isCenter), className, {
|
|
49
51
|
[styles.candidate]: cell.isCandidate(),
|
|
50
52
|
})}
|
|
51
53
|
style={style}
|
|
@@ -20,8 +20,16 @@ const CHARACTER_MULTIPLIER_CLASSNAMES: Record<number, string> = {
|
|
|
20
20
|
3: styles.bonusCharacterMultiplier3,
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
export const getBonusClassname = (cell: Cell, bonus: Bonus | undefined): string | undefined => {
|
|
24
|
-
if (!
|
|
23
|
+
export const getBonusClassname = (cell: Cell, bonus: Bonus | undefined, isCenter: boolean): string | undefined => {
|
|
24
|
+
if (!cell.isEmpty) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (isCenter) {
|
|
29
|
+
return styles.bonusStart;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!bonus) {
|
|
25
33
|
return undefined;
|
|
26
34
|
}
|
|
27
35
|
|
|
@@ -7,6 +7,26 @@
|
|
|
7
7
|
font-size: var(--font--size--s);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
.result {
|
|
11
|
+
transition: var(--transition);
|
|
12
|
+
|
|
13
|
+
&.isAllowed {
|
|
14
|
+
& + & {
|
|
15
|
+
.content {
|
|
16
|
+
padding-top: 0;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&.isNotAllowed {
|
|
22
|
+
& + & {
|
|
23
|
+
.content {
|
|
24
|
+
padding-top: 0;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
10
30
|
.content {
|
|
11
31
|
padding: var(--spacing--l);
|
|
12
32
|
}
|
|
@@ -14,44 +14,55 @@ interface Props {
|
|
|
14
14
|
|
|
15
15
|
const Dictionary: FunctionComponent<Props> = ({ className }) => {
|
|
16
16
|
const translate = useTranslate();
|
|
17
|
-
const {
|
|
17
|
+
const { results, isLoading } = useTypedSelector(selectDictionary);
|
|
18
|
+
const isFirstAllowed = results.length > 0 ? results[0].isAllowed : undefined;
|
|
18
19
|
|
|
19
20
|
return (
|
|
20
21
|
<div
|
|
21
22
|
className={classNames(styles.dictionary, className, {
|
|
22
|
-
[styles.isAllowed]:
|
|
23
|
-
[styles.isNotAllowed]:
|
|
23
|
+
[styles.isAllowed]: isFirstAllowed === true,
|
|
24
|
+
[styles.isNotAllowed]: isFirstAllowed === false,
|
|
24
25
|
})}
|
|
25
26
|
>
|
|
26
|
-
{
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
{
|
|
35
|
-
|
|
36
|
-
{isAllowed === true && (
|
|
37
|
-
<>
|
|
38
|
-
{definitions.length === 0 && <div>{translate('dictionary.empty-state.no-definitions')}</div>}
|
|
39
|
-
|
|
40
|
-
{definitions.length > 0 && (
|
|
41
|
-
<ul className={styles.definitions}>
|
|
42
|
-
{definitions.map((result, index) => (
|
|
43
|
-
<li key={index} className={styles.definition}>
|
|
44
|
-
{result}
|
|
45
|
-
</li>
|
|
46
|
-
))}
|
|
47
|
-
</ul>
|
|
48
|
-
)}
|
|
49
|
-
</>
|
|
27
|
+
{results.map(({ definitions, isAllowed, word }) => (
|
|
28
|
+
<div
|
|
29
|
+
className={classNames(styles.result, {
|
|
30
|
+
[styles.isAllowed]: isAllowed === true,
|
|
31
|
+
[styles.isNotAllowed]: isAllowed === false,
|
|
32
|
+
})}
|
|
33
|
+
key={word}
|
|
34
|
+
>
|
|
35
|
+
{typeof word === 'undefined' && (
|
|
36
|
+
<EmptyState type="info">{translate('dictionary.empty-state.uninitialized')}</EmptyState>
|
|
50
37
|
)}
|
|
51
38
|
|
|
52
|
-
{
|
|
39
|
+
{typeof word !== 'undefined' && (
|
|
40
|
+
<div className={styles.content}>
|
|
41
|
+
{word && <h2 className={styles.word}>{word}</h2>}
|
|
42
|
+
|
|
43
|
+
{isAllowed === false && <div>{translate('dictionary.empty-state.not-allowed')}</div>}
|
|
44
|
+
|
|
45
|
+
{isAllowed === true && (
|
|
46
|
+
<>
|
|
47
|
+
{definitions.length === 0 && <div>{translate('dictionary.empty-state.no-definitions')}</div>}
|
|
48
|
+
|
|
49
|
+
{definitions.length > 0 && (
|
|
50
|
+
<ul className={styles.definitions}>
|
|
51
|
+
{definitions.map((result, index) => (
|
|
52
|
+
<li key={index} className={styles.definition}>
|
|
53
|
+
{result}
|
|
54
|
+
</li>
|
|
55
|
+
))}
|
|
56
|
+
</ul>
|
|
57
|
+
)}
|
|
58
|
+
</>
|
|
59
|
+
)}
|
|
60
|
+
|
|
61
|
+
{!isLoading && isAllowed === null && <div>{translate('dictionary.empty-state.no-results')}</div>}
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
53
64
|
</div>
|
|
54
|
-
)}
|
|
65
|
+
))}
|
|
55
66
|
|
|
56
67
|
{isLoading && <Loading />}
|
|
57
68
|
</div>
|
|
@@ -11,12 +11,13 @@ import styles from './Results.module.scss';
|
|
|
11
11
|
interface Props {
|
|
12
12
|
className?: string;
|
|
13
13
|
translationKey: TranslationKey;
|
|
14
|
+
tooltip?: string | number;
|
|
14
15
|
value: string | number;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
const Cell: FunctionComponent<Props> = ({ className, translationKey, value }) => {
|
|
18
|
+
const Cell: FunctionComponent<Props> = ({ className, translationKey, tooltip, value }) => {
|
|
18
19
|
const translate = useTranslate();
|
|
19
|
-
const triggerProps = useTooltip(`${translate(translationKey)}: ${value}`);
|
|
20
|
+
const triggerProps = useTooltip(`${translate(translationKey)}: ${tooltip || value}`);
|
|
20
21
|
|
|
21
22
|
return (
|
|
22
23
|
<span className={classNames(styles.cell, className)} {...triggerProps}>
|
|
@@ -16,6 +16,7 @@ const Result = ({ index, style }: Props): ReactElement => {
|
|
|
16
16
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17
17
|
const results = useTypedSelector(selectSortedFilteredResults)!;
|
|
18
18
|
const result = results[index];
|
|
19
|
+
const otherWords = result.words.slice(1).join(' / ').toLocaleUpperCase();
|
|
19
20
|
|
|
20
21
|
const handleClick = () => {
|
|
21
22
|
dispatch(resultsSlice.actions.applyResult(result));
|
|
@@ -49,12 +50,21 @@ const Result = ({ index, style }: Props): ReactElement => {
|
|
|
49
50
|
onMouseLeave={handleMouseLeave}
|
|
50
51
|
>
|
|
51
52
|
<span className={styles.resultContent}>
|
|
52
|
-
<Cell
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<Cell className={styles.stat} translationKey="common.
|
|
53
|
+
<Cell
|
|
54
|
+
className={styles.word}
|
|
55
|
+
translationKey="common.word"
|
|
56
|
+
value={`${result.word.toLocaleUpperCase()}${otherWords.length > 0 ? ` (${otherWords})` : ''}`}
|
|
57
|
+
/>
|
|
58
|
+
<Cell className={styles.stat} translationKey="common.tiles" value={result.tilesCount} />
|
|
59
|
+
<Cell className={styles.stat} translationKey="common.consonants" value={result.consonantsCount} />
|
|
60
|
+
<Cell className={styles.stat} translationKey="common.vowels" value={result.vowelsCount} />
|
|
61
|
+
<Cell className={styles.stat} translationKey="common.blanks" value={result.blanksCount} />
|
|
62
|
+
<Cell
|
|
63
|
+
className={styles.stat}
|
|
64
|
+
translationKey="common.words"
|
|
65
|
+
tooltip={`${result.wordsCount} (${result.words.join(' / ').toLocaleUpperCase()})`}
|
|
66
|
+
value={result.wordsCount}
|
|
67
|
+
/>
|
|
58
68
|
<Cell className={styles.points} translationKey="common.points" value={result.points} />
|
|
59
69
|
</span>
|
|
60
70
|
</button>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
|
-
import { ChangeEvent, FunctionComponent } from 'react';
|
|
2
|
+
import { ChangeEvent, FunctionComponent, useState } from 'react';
|
|
3
3
|
import { useDispatch } from 'react-redux';
|
|
4
4
|
|
|
5
|
+
import { isRegExp } from 'lib';
|
|
5
6
|
import { resultsSlice, selectResultsQuery, useTranslate, useTypedSelector } from 'state';
|
|
6
7
|
|
|
7
8
|
import styles from './ResultsInput.module.scss';
|
|
@@ -14,9 +15,16 @@ const ResultsInput: FunctionComponent<Props> = ({ className }) => {
|
|
|
14
15
|
const dispatch = useDispatch();
|
|
15
16
|
const translate = useTranslate();
|
|
16
17
|
const value = useTypedSelector(selectResultsQuery);
|
|
18
|
+
const [localValue, setLocalValue] = useState(value);
|
|
17
19
|
|
|
18
20
|
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
19
|
-
|
|
21
|
+
const newValue = event.target.value;
|
|
22
|
+
|
|
23
|
+
setLocalValue(newValue);
|
|
24
|
+
|
|
25
|
+
if (isRegExp(newValue)) {
|
|
26
|
+
dispatch(resultsSlice.actions.changeQuery(newValue));
|
|
27
|
+
}
|
|
20
28
|
};
|
|
21
29
|
|
|
22
30
|
return (
|
|
@@ -25,7 +33,7 @@ const ResultsInput: FunctionComponent<Props> = ({ className }) => {
|
|
|
25
33
|
className={styles.input}
|
|
26
34
|
placeholder={translate('results.input.placeholder')}
|
|
27
35
|
type="text"
|
|
28
|
-
value={
|
|
36
|
+
value={localValue}
|
|
29
37
|
onChange={handleChange}
|
|
30
38
|
/>
|
|
31
39
|
</form>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useMedia } from 'react-use';
|
|
2
2
|
|
|
3
3
|
const useIsTablet = (): boolean => {
|
|
4
|
-
const isTabletHeight = useMedia('(max-height: 800px)');
|
|
5
|
-
const isTabletWidth = useMedia('(max-width: 1024px)');
|
|
4
|
+
const isTabletHeight = useMedia('(max-height: 800px)', false);
|
|
5
|
+
const isTabletWidth = useMedia('(max-width: 1024px)', false);
|
|
6
6
|
const isTablet = isTabletHeight || isTabletWidth;
|
|
7
7
|
return isTablet;
|
|
8
8
|
};
|
|
@@ -11,7 +11,7 @@ const getRemainingTiles = (config: Config, board: Board, characters: string[]):
|
|
|
11
11
|
const remainingTiles = Object.fromEntries(config.tiles.map((tile) => [tile.character, { ...tile, usedCount: 0 }]));
|
|
12
12
|
const blank: RemainingTile = {
|
|
13
13
|
character: BLANK,
|
|
14
|
-
count: config.
|
|
14
|
+
count: config.blanksCount,
|
|
15
15
|
score: config.blankScore,
|
|
16
16
|
usedCount:
|
|
17
17
|
nonEmptyCells.filter((cell) => cell.tile.isBlank).length +
|
|
@@ -15,8 +15,9 @@ export { default as getTotalRemainingTilesCount } from './getTotalRemainingTiles
|
|
|
15
15
|
export { default as getTileSizes } from './getTileSizes';
|
|
16
16
|
export { default as inverseDirection } from './inverseDirection';
|
|
17
17
|
export { default as isCtrl } from './isCtrl';
|
|
18
|
-
export { default as isLocale } from './isLocale';
|
|
19
18
|
export { default as isMac } from './isMac';
|
|
19
|
+
export { default as isRegExp } from './isRegExp';
|
|
20
|
+
export { default as isStringArray } from './isStringArray';
|
|
20
21
|
export { default as memoize } from './memoize';
|
|
21
22
|
export { default as noop } from './noop';
|
|
22
23
|
export { default as numberComparator } from './numberComparator';
|
|
@@ -6,13 +6,13 @@ import createKeyComparator from './createKeyComparator';
|
|
|
6
6
|
import reverseComparator from './reverseComparator';
|
|
7
7
|
|
|
8
8
|
const comparators: Record<ResultColumn, Comparator<Result>> = {
|
|
9
|
-
[ResultColumn.BlanksCount]: createKeyComparator('
|
|
10
|
-
[ResultColumn.ConsonantsCount]: createKeyComparator('
|
|
9
|
+
[ResultColumn.BlanksCount]: createKeyComparator('blanksCount'),
|
|
10
|
+
[ResultColumn.ConsonantsCount]: createKeyComparator('consonantsCount'),
|
|
11
11
|
[ResultColumn.Points]: createKeyComparator('points'),
|
|
12
|
-
[ResultColumn.TilesCount]: createKeyComparator('
|
|
13
|
-
[ResultColumn.VowelsCount]: createKeyComparator('
|
|
12
|
+
[ResultColumn.TilesCount]: createKeyComparator('tilesCount'),
|
|
13
|
+
[ResultColumn.VowelsCount]: createKeyComparator('vowelsCount'),
|
|
14
14
|
[ResultColumn.Word]: createKeyComparator('word'),
|
|
15
|
-
[ResultColumn.WordsCount]: createKeyComparator('
|
|
15
|
+
[ResultColumn.WordsCount]: createKeyComparator('wordsCount'),
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
const sortResults = (
|
|
@@ -7,7 +7,9 @@ import { createAppStore } from 'state';
|
|
|
7
7
|
|
|
8
8
|
import 'styles/global.scss';
|
|
9
9
|
|
|
10
|
-
const DESCRIPTION =
|
|
10
|
+
const DESCRIPTION =
|
|
11
|
+
// eslint-disable-next-line max-len
|
|
12
|
+
'Scrabble Solver 2 - Free and open-source analysis tool for Scrabble and Literaki. Quickly find top scoring words using given letters and board state. Available in English, French, German, Polish & Spanish.';
|
|
11
13
|
const KEYWORDS = [
|
|
12
14
|
'Scrabble',
|
|
13
15
|
'Solver',
|
|
@@ -54,8 +56,9 @@ const App: FunctionComponent<AppProps> = ({ Component, pageProps }) => (
|
|
|
54
56
|
|
|
55
57
|
<Provider store={store}>
|
|
56
58
|
<p style={{ fontSize: 0 }}>
|
|
57
|
-
Scrabble Solver 2 is
|
|
58
|
-
English, French, German, Polish &
|
|
59
|
+
Scrabble Solver 2 is a free and open-source analysis tool for Scrabble and Literaki. Quickly find top scoring
|
|
60
|
+
words using given letters and board state. Available in English, French, German, Polish & Spanish. Source code
|
|
61
|
+
is available on GitHub - contributions are welcome!
|
|
59
62
|
</p>
|
|
60
63
|
<Component {...pageProps} />
|
|
61
64
|
</Provider>
|