@scrabble-solver/scrabble-solver 2.12.2 → 2.12.4
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 +6 -6
- 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/routes-manifest.json +1 -1
- package/.next/server/chunks/131.js +478 -106
- package/.next/server/chunks/277.js +1132 -976
- package/.next/server/chunks/44.js +37 -9
- package/.next/server/chunks/865.js +597 -169
- 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/_document.js.nft.json +1 -1
- package/.next/server/pages/api/dictionary/[locale]/[word].js +4 -2
- package/.next/server/pages/api/solve.js +23 -19
- package/.next/server/pages/api/verify.js +8 -5
- package/.next/server/pages/index.html +1 -1
- package/.next/server/pages/index.js +42 -55
- 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/{9dmPfnTc_AQTHBPvL7xQe → 8oRk1nUMQYFWmhu4SExQI}/_buildManifest.js +1 -1
- 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/trace +51 -52
- package/package.json +9 -9
- 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/hooks/useBackgroundImage.tsx +23 -0
- package/src/components/PlainTiles/lib/createPlainTile.ts +4 -4
- 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/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 +3 -3
- 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/pages/_app-02851b06b95b19cb.js +0 -32
- package/.next/static/chunks/pages/index-0ba5607d1aad8a09.js +0 -1
- package/.next/static/css/09dfdea53eba31a9.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/{9dmPfnTc_AQTHBPvL7xQe → 8oRk1nUMQYFWmhu4SExQI}/_ssgManifest.js +0 -0
package/src/pages/api/solve.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getConfig, hasConfig } 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
5
|
import { solve as solveScrabble } from '@scrabble-solver/solver';
|
|
6
|
-
import { Board, Config,
|
|
6
|
+
import { Board, Config, Locale, Tile, isBoardJson, isGame, isLocale } from '@scrabble-solver/types';
|
|
7
7
|
import { NextApiRequest, NextApiResponse } from 'next';
|
|
8
8
|
|
|
9
9
|
import { getServerLoggingData, isBoardValid, isCharacterValid } from 'api';
|
|
@@ -29,7 +29,7 @@ const solve = async (request: NextApiRequest, response: NextApiResponse): Promis
|
|
|
29
29
|
boardBlanksCount: board.getBlanksCount(),
|
|
30
30
|
boardTilesCount: board.getTilesCount(),
|
|
31
31
|
characters: characters.join(''),
|
|
32
|
-
|
|
32
|
+
game: request.body.game,
|
|
33
33
|
locale,
|
|
34
34
|
},
|
|
35
35
|
});
|
|
@@ -46,21 +46,25 @@ const solve = async (request: NextApiRequest, response: NextApiResponse): Promis
|
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
const parseRequest = (request: NextApiRequest): RequestData => {
|
|
49
|
-
const { board: boardJson, characters,
|
|
49
|
+
const { board: boardJson, characters, game, locale } = request.body;
|
|
50
50
|
|
|
51
51
|
if (!isLocale(locale)) {
|
|
52
52
|
throw new Error('Invalid "locale" parameter');
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
if (!
|
|
56
|
-
throw new Error('Invalid "
|
|
55
|
+
if (!isGame(game)) {
|
|
56
|
+
throw new Error('Invalid "game" parameter');
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
if (!isStringArray(characters) || characters.length === 0) {
|
|
60
60
|
throw new Error('Invalid "characters" parameter');
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
if (!hasConfig(game, locale)) {
|
|
64
|
+
throw new Error(`No game "${game}" in "${locale}"`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const config = getConfig(game, locale);
|
|
64
68
|
|
|
65
69
|
for (const character of characters) {
|
|
66
70
|
if (!isCharacterValid(character)) {
|
package/src/pages/api/verify.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getConfig, hasConfig } from '@scrabble-solver/configs';
|
|
2
2
|
import { dictionaries } from '@scrabble-solver/dictionaries';
|
|
3
3
|
import logger from '@scrabble-solver/logger';
|
|
4
|
-
import { Board, Config, isBoardJson,
|
|
4
|
+
import { Board, Config, Locale, isBoardJson, isGame, isLocale } from '@scrabble-solver/types';
|
|
5
5
|
import { NextApiRequest, NextApiResponse } from 'next';
|
|
6
6
|
|
|
7
7
|
import { getServerLoggingData, isBoardValid } from 'api';
|
|
@@ -24,7 +24,7 @@ const verify = async (request: NextApiRequest, response: NextApiResponse): Promi
|
|
|
24
24
|
board: board.toString(),
|
|
25
25
|
boardBlanksCount: board.getBlanksCount(),
|
|
26
26
|
boardTilesCount: board.getTilesCount(),
|
|
27
|
-
|
|
27
|
+
game: request.body.game,
|
|
28
28
|
locale,
|
|
29
29
|
},
|
|
30
30
|
});
|
|
@@ -42,17 +42,21 @@ const verify = async (request: NextApiRequest, response: NextApiResponse): Promi
|
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
const parseRequest = (request: NextApiRequest): RequestData => {
|
|
45
|
-
const { board: boardJson,
|
|
45
|
+
const { board: boardJson, game, locale } = request.body;
|
|
46
46
|
|
|
47
47
|
if (!isLocale(locale)) {
|
|
48
48
|
throw new Error('Invalid "locale" parameter');
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
if (!
|
|
52
|
-
throw new Error('Invalid "
|
|
51
|
+
if (!isGame(game)) {
|
|
52
|
+
throw new Error('Invalid "game" parameter');
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
if (!hasConfig(game, locale)) {
|
|
56
|
+
throw new Error(`No game "${game}" in "${locale}"`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const config = getConfig(game, locale);
|
|
56
60
|
|
|
57
61
|
if (!isBoardJson(boardJson) || !isBoardValid(boardJson, config)) {
|
|
58
62
|
throw new Error('Invalid "board" parameter');
|
package/src/parameters/index.ts
CHANGED
|
@@ -25,9 +25,11 @@ export const COLOR_BONUS_CHARACTER_3 = '#dde4f6';
|
|
|
25
25
|
export const COLOR_BONUS_CHARACTER_5 = '#fbe0d4';
|
|
26
26
|
export const COLOR_BONUS_CHARACTER_MULTIPLIER_2 = '#b8d5ed';
|
|
27
27
|
export const COLOR_BONUS_CHARACTER_MULTIPLIER_3 = '#86aed1';
|
|
28
|
+
export const COLOR_BONUS_CHARACTER_MULTIPLIER_4 = '#3477b2';
|
|
28
29
|
export const COLOR_BONUS_START = '#b284b8';
|
|
29
30
|
export const COLOR_BONUS_WORD_MULTIPLIER_2 = '#fbc997';
|
|
30
31
|
export const COLOR_BONUS_WORD_MULTIPLIER_3 = '#f19393';
|
|
32
|
+
export const COLOR_BONUS_WORD_MULTIPLIER_4 = '#ed5e5e';
|
|
31
33
|
export const COLOR_FILTERED = '#444';
|
|
32
34
|
|
|
33
35
|
export const COLOR_BONUS_CHARACTER: Record<number, string> = {
|
|
@@ -40,11 +42,13 @@ export const COLOR_BONUS_CHARACTER: Record<number, string> = {
|
|
|
40
42
|
export const COLOR_BONUS_CHARACTER_MULTIPLIER: Record<number, string> = {
|
|
41
43
|
2: COLOR_BONUS_CHARACTER_MULTIPLIER_2,
|
|
42
44
|
3: COLOR_BONUS_CHARACTER_MULTIPLIER_3,
|
|
45
|
+
4: COLOR_BONUS_CHARACTER_MULTIPLIER_4,
|
|
43
46
|
};
|
|
44
47
|
|
|
45
48
|
export const COLOR_BONUS_WORD: Record<number, string> = {
|
|
46
49
|
2: COLOR_BONUS_WORD_MULTIPLIER_2,
|
|
47
50
|
3: COLOR_BONUS_WORD_MULTIPLIER_3,
|
|
51
|
+
4: COLOR_BONUS_WORD_MULTIPLIER_4,
|
|
48
52
|
};
|
|
49
53
|
|
|
50
54
|
export const SPACING_XS = 2;
|
|
@@ -61,12 +65,6 @@ export const BOARD_CELL_BORDER_WIDTH = 1;
|
|
|
61
65
|
export const BOARD_TILE_FONT_SIZE_MIN = 14;
|
|
62
66
|
export const BOARD_TILE_FONT_SIZE_POINTS_MIN = 10;
|
|
63
67
|
export const BOARD_TILE_SIZE_MAX = 64;
|
|
64
|
-
/**
|
|
65
|
-
* 20 - fits all board tiles without horizontal scrollbar on 360px viewport width (font-size: 14px)
|
|
66
|
-
* 21 - fits all board tiles without horizontal scrollbar on 375px viewport width (font-size: 14px)
|
|
67
|
-
* 26 - tiles start to look good (font-size: 16px)
|
|
68
|
-
*/
|
|
69
|
-
export const BOARD_TILE_SIZE_MIN = 20;
|
|
70
68
|
|
|
71
69
|
export const BORDER_COLOR = '#cdcdcd';
|
|
72
70
|
export const BORDER_COLOR_LIGHT = '#d9d9d9';
|
package/src/sdk/solve.ts
CHANGED
|
@@ -5,14 +5,14 @@ import fetchJson from './fetchJson';
|
|
|
5
5
|
interface Payload {
|
|
6
6
|
board: BoardJson;
|
|
7
7
|
characters: string[];
|
|
8
|
-
|
|
8
|
+
game: string;
|
|
9
9
|
locale: Locale;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const solve = async ({ board, characters,
|
|
12
|
+
const solve = async ({ board, characters, game, locale }: Payload): Promise<Result[]> => {
|
|
13
13
|
const json = await fetchJson<ResultJson[]>('/api/solve', {
|
|
14
14
|
method: 'POST',
|
|
15
|
-
body: JSON.stringify({ board, characters,
|
|
15
|
+
body: JSON.stringify({ board, characters, game, locale }),
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
return json.map(Result.fromJson);
|
package/src/sdk/verify.ts
CHANGED
|
@@ -4,7 +4,7 @@ import fetchJson from './fetchJson';
|
|
|
4
4
|
|
|
5
5
|
interface Payload {
|
|
6
6
|
board: BoardJson;
|
|
7
|
-
|
|
7
|
+
game: string;
|
|
8
8
|
locale: Locale;
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -13,10 +13,10 @@ interface Response {
|
|
|
13
13
|
validWords: string[];
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
const verify = async ({ board,
|
|
16
|
+
const verify = async ({ board, game, locale }: Payload): Promise<Response> => {
|
|
17
17
|
return fetchJson<Response>('/api/verify', {
|
|
18
18
|
method: 'POST',
|
|
19
|
-
body: JSON.stringify({ board,
|
|
19
|
+
body: JSON.stringify({ board, game, locale }),
|
|
20
20
|
});
|
|
21
21
|
};
|
|
22
22
|
|
|
@@ -2,7 +2,7 @@ import { Trie } from '@kamilmielnik/trie';
|
|
|
2
2
|
import { getConfig } from '@scrabble-solver/configs';
|
|
3
3
|
import { BLANK } from '@scrabble-solver/constants';
|
|
4
4
|
import { solve } from '@scrabble-solver/solver';
|
|
5
|
-
import { Board,
|
|
5
|
+
import { Board, Tile } from '@scrabble-solver/types';
|
|
6
6
|
import { registerRoute } from 'workbox-routing';
|
|
7
7
|
|
|
8
8
|
import average from './average';
|
|
@@ -33,10 +33,10 @@ const routeSolveRequests = () => {
|
|
|
33
33
|
registerRoute(
|
|
34
34
|
({ url }) => url.origin === location.origin && url.pathname === '/api/solve',
|
|
35
35
|
async ({ request }) => {
|
|
36
|
-
const { board, characters,
|
|
36
|
+
const { board, characters, game, locale } = await request.clone().json();
|
|
37
37
|
|
|
38
38
|
const solveLocal = async (trie: Trie) => {
|
|
39
|
-
const config = getConfig(
|
|
39
|
+
const config = getConfig(game, locale);
|
|
40
40
|
const tiles = characters.map((character: string) => new Tile({ character, isBlank: character === BLANK }));
|
|
41
41
|
const resultsJson = solve(trie, config, Board.fromJson(board), tiles);
|
|
42
42
|
const json = JSON.stringify(resultsJson);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { Board, Locale } from '@scrabble-solver/types';
|
|
1
|
+
import { Board, Game, Locale } from '@scrabble-solver/types';
|
|
2
2
|
import store2 from 'store2';
|
|
3
3
|
|
|
4
4
|
import { AutoGroupTiles, InputMode, Rack } from 'types';
|
|
5
5
|
|
|
6
6
|
const AUTO_GROUP_TILES = 'auto-group-tiles';
|
|
7
7
|
const BOARD = 'board';
|
|
8
|
-
const
|
|
8
|
+
const GAME_ID = 'config-id';
|
|
9
9
|
const INPUT_MODE = 'input-mode';
|
|
10
10
|
const LOCALE = 'locale';
|
|
11
11
|
const RACK = 'rack';
|
|
@@ -31,12 +31,12 @@ const localStorage = {
|
|
|
31
31
|
store.set(BOARD, serialized, true);
|
|
32
32
|
},
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
return store.get(
|
|
34
|
+
getGame(): Game | undefined {
|
|
35
|
+
return store.get(GAME_ID);
|
|
36
36
|
},
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
store.set(
|
|
38
|
+
setGame(game: Game | undefined): void {
|
|
39
|
+
store.set(GAME_ID, game, true);
|
|
40
40
|
},
|
|
41
41
|
|
|
42
42
|
getInputMode(): InputMode | undefined {
|
package/src/state/sagas.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/* eslint-disable max-lines */
|
|
2
2
|
|
|
3
3
|
import { PayloadAction } from '@reduxjs/toolkit';
|
|
4
|
-
import {
|
|
4
|
+
import { hasConfig, localesMap } from '@scrabble-solver/configs';
|
|
5
|
+
import { Board, Locale, Result } from '@scrabble-solver/types';
|
|
5
6
|
import { call, delay, put, select, takeEvery, takeLatest } from 'redux-saga/effects';
|
|
6
7
|
|
|
7
8
|
import { LOCALE_FEATURES } from 'i18n';
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
selectCharacters,
|
|
16
17
|
selectConfig,
|
|
17
18
|
selectDictionary,
|
|
19
|
+
selectGame,
|
|
18
20
|
selectLocale,
|
|
19
21
|
selectLocaleAutoGroupTiles,
|
|
20
22
|
selectRack,
|
|
@@ -44,7 +46,7 @@ export function* rootSaga(): AnyGenerator {
|
|
|
44
46
|
yield takeEvery([rackSlice.actions.changeCharacter.type, rackSlice.actions.changeCharacters.type], onRackValueChange);
|
|
45
47
|
yield takeEvery(resultsSlice.actions.applyResult.type, onApplyResult);
|
|
46
48
|
yield takeEvery(resultsSlice.actions.changeResultCandidate.type, onResultCandidateChange);
|
|
47
|
-
yield takeEvery(settingsSlice.actions.
|
|
49
|
+
yield takeEvery(settingsSlice.actions.changeGame.type, onGameChange);
|
|
48
50
|
yield takeEvery(settingsSlice.actions.changeLocale.type, onLocaleChange);
|
|
49
51
|
yield takeLatest(dictionarySlice.actions.submit.type, onDictionarySubmit);
|
|
50
52
|
yield takeLatest(initialize.type, onInitialize);
|
|
@@ -77,7 +79,7 @@ function* onApplyResult({ payload: result }: PayloadAction<Result>): AnyGenerato
|
|
|
77
79
|
yield put(verifySlice.actions.submit());
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
function*
|
|
82
|
+
function* onGameChange(): AnyGenerator {
|
|
81
83
|
const characters = yield select(selectCharacters);
|
|
82
84
|
|
|
83
85
|
if (characters.length > 0) {
|
|
@@ -119,7 +121,9 @@ function* onInitialize(): AnyGenerator {
|
|
|
119
121
|
}
|
|
120
122
|
|
|
121
123
|
function* onReset(): AnyGenerator {
|
|
122
|
-
yield
|
|
124
|
+
const config = yield select(selectConfig);
|
|
125
|
+
|
|
126
|
+
yield put(boardSlice.actions.init(Board.create(config.boardWidth, config.boardHeight)));
|
|
123
127
|
yield put(cellFilterSlice.actions.reset());
|
|
124
128
|
yield put(dictionarySlice.actions.reset());
|
|
125
129
|
yield put(rackSlice.actions.reset());
|
|
@@ -128,7 +132,14 @@ function* onReset(): AnyGenerator {
|
|
|
128
132
|
yield put(verifySlice.actions.submit());
|
|
129
133
|
}
|
|
130
134
|
|
|
131
|
-
function* onLocaleChange(): AnyGenerator {
|
|
135
|
+
function* onLocaleChange({ payload: locale }: PayloadAction<Locale>): AnyGenerator {
|
|
136
|
+
const game = yield select(selectGame);
|
|
137
|
+
|
|
138
|
+
if (!hasConfig(game, locale)) {
|
|
139
|
+
const defaultConfig = localesMap[locale][0];
|
|
140
|
+
yield put(settingsSlice.actions.changeGame(defaultConfig.game));
|
|
141
|
+
}
|
|
142
|
+
|
|
132
143
|
const characters = yield select(selectCharacters);
|
|
133
144
|
|
|
134
145
|
if (characters.length > 0) {
|
|
@@ -166,7 +177,7 @@ function* onSolve(): AnyGenerator {
|
|
|
166
177
|
const results = yield call(solve, {
|
|
167
178
|
board: board.toJson(),
|
|
168
179
|
characters,
|
|
169
|
-
|
|
180
|
+
game: config.game,
|
|
170
181
|
locale,
|
|
171
182
|
});
|
|
172
183
|
yield put(resultsSlice.actions.changeResults(results));
|
|
@@ -187,7 +198,7 @@ function* onVerify(): AnyGenerator {
|
|
|
187
198
|
try {
|
|
188
199
|
const { invalidWords, validWords } = yield call(verify, {
|
|
189
200
|
board: board.toJson(),
|
|
190
|
-
|
|
201
|
+
game: config.game,
|
|
191
202
|
locale,
|
|
192
203
|
});
|
|
193
204
|
yield put(verifySlice.actions.submitSuccess({ board, invalidWords, validWords }));
|
package/src/state/selectors.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable max-lines */
|
|
2
2
|
|
|
3
3
|
import { createSelector } from '@reduxjs/toolkit';
|
|
4
|
-
import {
|
|
4
|
+
import { getConfig } from '@scrabble-solver/configs';
|
|
5
5
|
import { BLANK } from '@scrabble-solver/constants';
|
|
6
6
|
import { Cell, Config, isError, Tile } from '@scrabble-solver/types';
|
|
7
7
|
|
|
@@ -67,9 +67,9 @@ export const selectBoard = selectBoardRoot;
|
|
|
67
67
|
|
|
68
68
|
export const selectInputMode = createSelector([selectSettingsRoot], (settings) => settings.inputMode);
|
|
69
69
|
|
|
70
|
-
export const
|
|
70
|
+
export const selectGame = createSelector([selectSettingsRoot], (settings) => settings.game);
|
|
71
71
|
|
|
72
|
-
export const selectConfig = createSelector([
|
|
72
|
+
export const selectConfig = createSelector([selectGame, selectLocale], getConfig);
|
|
73
73
|
|
|
74
74
|
export const selectFilteredCells = selectCellFilterRoot;
|
|
75
75
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getConfig } from '@scrabble-solver/configs';
|
|
2
2
|
import { Board } from '@scrabble-solver/types';
|
|
3
3
|
|
|
4
4
|
import localStorage from '../localStorage';
|
|
@@ -7,8 +7,8 @@ import settingsInitialState from './settingsInitialState';
|
|
|
7
7
|
|
|
8
8
|
export type BoardState = Board;
|
|
9
9
|
|
|
10
|
-
const {
|
|
11
|
-
const { boardHeight, boardWidth } =
|
|
10
|
+
const { game, locale } = settingsInitialState;
|
|
11
|
+
const { boardHeight, boardWidth } = getConfig(game, locale);
|
|
12
12
|
export const boardDefaultState = Board.create(boardWidth, boardHeight);
|
|
13
13
|
|
|
14
14
|
const boardInitialState: BoardState = localStorage.getBoard() || boardDefaultState;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
|
2
|
+
import { games } from '@scrabble-solver/configs';
|
|
2
3
|
import { EMPTY_CELL } from '@scrabble-solver/constants';
|
|
3
|
-
import { Board, Cell, Result, Tile } from '@scrabble-solver/types';
|
|
4
|
+
import { Board, Cell, Game, Result, Tile } from '@scrabble-solver/types';
|
|
4
5
|
|
|
5
|
-
import boardInitialState
|
|
6
|
+
import boardInitialState from './boardInitialState';
|
|
7
|
+
import settingsSlice from './settingsSlice';
|
|
6
8
|
|
|
7
9
|
const boardSlice = createSlice({
|
|
8
10
|
initialState: boardInitialState,
|
|
@@ -42,10 +44,6 @@ const boardSlice = createSlice({
|
|
|
42
44
|
return board;
|
|
43
45
|
},
|
|
44
46
|
|
|
45
|
-
reset: () => {
|
|
46
|
-
return boardDefaultState;
|
|
47
|
-
},
|
|
48
|
-
|
|
49
47
|
toggleCellIsBlank: (state, action: PayloadAction<{ x: number; y: number }>) => {
|
|
50
48
|
const newBoard = state.clone();
|
|
51
49
|
const { x, y } = action.payload;
|
|
@@ -58,6 +56,22 @@ const boardSlice = createSlice({
|
|
|
58
56
|
return newBoard;
|
|
59
57
|
},
|
|
60
58
|
},
|
|
59
|
+
extraReducers: {
|
|
60
|
+
[settingsSlice.actions.changeGame.type]: (state, action: PayloadAction<Game>) => {
|
|
61
|
+
const game = action.payload;
|
|
62
|
+
const config = Object.values(games).find((gameConfig) => gameConfig.game === game);
|
|
63
|
+
|
|
64
|
+
if (!config) {
|
|
65
|
+
throw new Error(`Cannot find config for game "${game}"`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (state.rows.length !== config.boardHeight || state.rows[0].length !== config.boardWidth) {
|
|
69
|
+
return Board.create(config.boardWidth, config.boardHeight);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return state;
|
|
73
|
+
},
|
|
74
|
+
},
|
|
61
75
|
});
|
|
62
76
|
|
|
63
77
|
export default boardSlice;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Locale } from '@scrabble-solver/types';
|
|
1
|
+
import { Game, Locale } from '@scrabble-solver/types';
|
|
3
2
|
|
|
4
3
|
import { guessLocale } from 'lib';
|
|
5
4
|
import { AutoGroupTiles, InputMode } from 'types';
|
|
@@ -8,7 +7,7 @@ import localStorage from '../localStorage';
|
|
|
8
7
|
|
|
9
8
|
export interface SettingsState {
|
|
10
9
|
autoGroupTiles: AutoGroupTiles;
|
|
11
|
-
|
|
10
|
+
game: Game;
|
|
12
11
|
inputMode: InputMode;
|
|
13
12
|
locale: Locale;
|
|
14
13
|
}
|
|
@@ -18,7 +17,7 @@ const isTouchScreen = typeof globalThis.matchMedia !== 'undefined' && globalThis
|
|
|
18
17
|
|
|
19
18
|
const settingsInitialState: SettingsState = {
|
|
20
19
|
autoGroupTiles: typeof localStorageAutoGroupTiles === 'undefined' ? 'left' : localStorageAutoGroupTiles,
|
|
21
|
-
|
|
20
|
+
game: localStorage.getGame() || Game.Scrabble,
|
|
22
21
|
inputMode: localStorage.getInputMode() || (isTouchScreen ? 'touchscreen' : 'keyboard'),
|
|
23
22
|
locale: localStorage.getLocale() || guessLocale(),
|
|
24
23
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
|
2
|
-
import { Locale } from '@scrabble-solver/types';
|
|
2
|
+
import { Game, Locale } from '@scrabble-solver/types';
|
|
3
3
|
|
|
4
4
|
import { AutoGroupTiles, InputMode } from 'types';
|
|
5
5
|
|
|
@@ -14,9 +14,9 @@ const settingsSlice = createSlice({
|
|
|
14
14
|
return { ...state, autoGroupTiles };
|
|
15
15
|
},
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
return { ...state,
|
|
17
|
+
changeGame: (state, action: PayloadAction<Game>) => {
|
|
18
|
+
const game = action.payload;
|
|
19
|
+
return { ...state, game };
|
|
20
20
|
},
|
|
21
21
|
|
|
22
22
|
changeInputMode: (state, action: PayloadAction<InputMode>) => {
|
|
@@ -29,7 +29,7 @@ const settingsSlice = createSlice({
|
|
|
29
29
|
return { ...state, locale };
|
|
30
30
|
},
|
|
31
31
|
|
|
32
|
-
init: (state, action: PayloadAction<Partial<Pick<typeof settingsInitialState, '
|
|
32
|
+
init: (state, action: PayloadAction<Partial<Pick<typeof settingsInitialState, 'game' | 'locale'>>>) => {
|
|
33
33
|
return { ...state, ...action.payload };
|
|
34
34
|
},
|
|
35
35
|
},
|
|
@@ -97,20 +97,11 @@ $easeOutSine: cubic-bezier(0.61, 1, 0.88, 1);
|
|
|
97
97
|
--z-index--close-button: 101;
|
|
98
98
|
--z-index--tooltip: 102;
|
|
99
99
|
|
|
100
|
-
--flag--de--aspect-ratio: 20 / 12;
|
|
101
|
-
--flag--es--aspect-ratio: 750 / 500;
|
|
102
|
-
--flag--fa--aspect-ratio: 630 / 360;
|
|
103
|
-
--flag--fr--aspect-ratio: 24 / 15;
|
|
104
|
-
--flag--gb--aspect-ratio: 60 / 30;
|
|
105
|
-
--flag--pl--aspect-ratio: 16 / 10;
|
|
106
|
-
--flag--us--aspect-ratio: 7410 / 3900;
|
|
107
|
-
|
|
108
100
|
--button--icon--size: 24px;
|
|
109
101
|
--dictionary--height: 260px;
|
|
110
102
|
--dictionary--height--mobile: 110px;
|
|
111
103
|
--key--height: 36px;
|
|
112
104
|
--key--icon--size: 16px;
|
|
113
|
-
--logo--aspect-ratio: 682 / 166;
|
|
114
105
|
--logo--height: 60px;
|
|
115
106
|
--modal--width: 370px;
|
|
116
107
|
--nav--height: calc(var(--logo--height) + var(--nav--padding));
|