@scrabble-solver/solver 2.8.2 → 2.8.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/build/PatternsFiller.js +38 -72
- package/build/PatternsGenerator.js +43 -72
- package/build/ScoresCalculator.js +20 -24
- package/build/Solver.js +19 -24
- package/package.json +8 -8
- package/src/PatternsFiller.ts +5 -10
- package/src/PatternsGenerator.ts +17 -16
- package/src/Solver.ts +4 -8
package/build/PatternsFiller.js
CHANGED
|
@@ -1,104 +1,70 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __read = (this && this.__read) || function (o, n) {
|
|
3
|
-
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
4
|
-
if (!m) return o;
|
|
5
|
-
var i = m.call(o), r, ar = [], e;
|
|
6
|
-
try {
|
|
7
|
-
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
8
|
-
}
|
|
9
|
-
catch (error) { e = { error: error }; }
|
|
10
|
-
finally {
|
|
11
|
-
try {
|
|
12
|
-
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
13
|
-
}
|
|
14
|
-
finally { if (e) throw e.error; }
|
|
15
|
-
}
|
|
16
|
-
return ar;
|
|
17
|
-
};
|
|
18
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
19
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
20
|
-
if (ar || !(i in from)) {
|
|
21
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
22
|
-
ar[i] = from[i];
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
26
|
-
};
|
|
27
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
3
|
+
const constants_1 = require("@scrabble-solver/constants");
|
|
4
|
+
const types_1 = require("@scrabble-solver/types");
|
|
5
|
+
class PatternsFiller {
|
|
6
|
+
constructor(config, trie) {
|
|
32
7
|
this.config = config;
|
|
33
8
|
this.trie = trie;
|
|
34
9
|
}
|
|
35
|
-
|
|
36
|
-
|
|
10
|
+
fill(pattern, tiles) {
|
|
11
|
+
const patterns = [];
|
|
37
12
|
if (pattern.getNumberOfEmptyCells() > tiles.length) {
|
|
38
13
|
return [];
|
|
39
14
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
for (
|
|
43
|
-
|
|
44
|
-
this.fillPattern(pattern,
|
|
15
|
+
const onPatternFound = (newPattern) => patterns.push(newPattern);
|
|
16
|
+
const tilesPermutations = this.generateBlankTilesPermutations(tiles);
|
|
17
|
+
for (let index = 0; index < tilesPermutations.length; ++index) {
|
|
18
|
+
const tilesPermutation = tilesPermutations[index];
|
|
19
|
+
this.fillPattern(pattern, pattern.toString(), tilesPermutation, onPatternFound);
|
|
45
20
|
}
|
|
46
21
|
return patterns;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
22
|
+
}
|
|
23
|
+
fillPattern(pattern, word, tiles, onPatternFound) {
|
|
24
|
+
const indexOfFirstCellWithoutTile = pattern.getIndexOfFirstCellWithoutTile();
|
|
50
25
|
if (indexOfFirstCellWithoutTile === -1) {
|
|
51
26
|
if (this.canAddPattern(pattern, word)) {
|
|
52
27
|
onPatternFound(pattern.clone());
|
|
53
28
|
}
|
|
54
29
|
}
|
|
55
30
|
else {
|
|
56
|
-
for (
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
var previousTile = pattern.cells[indexOfFirstCellWithoutTile].tile;
|
|
31
|
+
for (let index = 0; index < tiles.length; ++index) {
|
|
32
|
+
const tile = tiles[index];
|
|
33
|
+
const previousTile = pattern.cells[indexOfFirstCellWithoutTile].tile;
|
|
60
34
|
pattern.cells[indexOfFirstCellWithoutTile].tile = tile;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
35
|
+
const indexOfNextCellWithoutTile = pattern.getIndexOfFirstCellWithoutTile();
|
|
36
|
+
const indexOfFirstEmptyLetter = word.indexOf(constants_1.EMPTY_CELL);
|
|
37
|
+
const newWordPrefix = word.substring(0, indexOfFirstEmptyLetter) + tile.character;
|
|
38
|
+
const newWord = newWordPrefix + word.substring(indexOfFirstEmptyLetter + 1);
|
|
65
39
|
if (indexOfNextCellWithoutTile === -1) {
|
|
66
40
|
if (this.canAddPattern(pattern, newWord)) {
|
|
67
41
|
onPatternFound(pattern.clone());
|
|
68
42
|
}
|
|
69
43
|
}
|
|
70
44
|
else if (this.trie.hasPrefix(newWordPrefix)) {
|
|
71
|
-
|
|
45
|
+
tiles.splice(index, 1);
|
|
46
|
+
this.fillPattern(pattern, newWord, tiles, onPatternFound);
|
|
47
|
+
tiles.splice(index, 0, tile);
|
|
72
48
|
}
|
|
73
49
|
pattern.cells[indexOfFirstCellWithoutTile].tile = previousTile;
|
|
74
50
|
}
|
|
75
51
|
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
.every(function (collision) { return _this.trie.has(collision); }));
|
|
84
|
-
};
|
|
85
|
-
PatternsFiller.prototype.generateBlankTilesPermutations = function (tiles) {
|
|
86
|
-
var _this = this;
|
|
87
|
-
var alphabet = this.config.alphabet;
|
|
88
|
-
var firstBlankIndex = tiles.findIndex(function (_a) {
|
|
89
|
-
var character = _a.character, isBlank = _a.isBlank;
|
|
90
|
-
return isBlank && !alphabet.includes(character);
|
|
91
|
-
});
|
|
52
|
+
}
|
|
53
|
+
canAddPattern(pattern, word) {
|
|
54
|
+
return this.trie.has(word) && pattern.getCollisions().every((collision) => this.trie.has(collision.toString()));
|
|
55
|
+
}
|
|
56
|
+
generateBlankTilesPermutations(tiles) {
|
|
57
|
+
const { alphabet } = this.config;
|
|
58
|
+
const firstBlankIndex = tiles.findIndex(({ character, isBlank }) => isBlank && !alphabet.includes(character));
|
|
92
59
|
if (firstBlankIndex === -1) {
|
|
93
60
|
return [tiles];
|
|
94
61
|
}
|
|
95
|
-
|
|
96
|
-
return this.config.alphabet.reduce(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return permutations.concat(
|
|
62
|
+
const remainingTiles = tiles.slice(0, firstBlankIndex).concat(tiles.slice(firstBlankIndex + 1));
|
|
63
|
+
return this.config.alphabet.reduce((permutations, character) => {
|
|
64
|
+
const newTile = new types_1.Tile({ character, isBlank: true });
|
|
65
|
+
const newTiles = [...remainingTiles, newTile];
|
|
66
|
+
return permutations.concat(this.generateBlankTilesPermutations(newTiles));
|
|
100
67
|
}, []);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
}());
|
|
68
|
+
}
|
|
69
|
+
}
|
|
104
70
|
exports.default = PatternsFiller;
|
|
@@ -1,95 +1,66 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __read = (this && this.__read) || function (o, n) {
|
|
3
|
-
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
4
|
-
if (!m) return o;
|
|
5
|
-
var i = m.call(o), r, ar = [], e;
|
|
6
|
-
try {
|
|
7
|
-
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
8
|
-
}
|
|
9
|
-
catch (error) { e = { error: error }; }
|
|
10
|
-
finally {
|
|
11
|
-
try {
|
|
12
|
-
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
13
|
-
}
|
|
14
|
-
finally { if (e) throw e.error; }
|
|
15
|
-
}
|
|
16
|
-
return ar;
|
|
17
|
-
};
|
|
18
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
19
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
20
|
-
if (ar || !(i in from)) {
|
|
21
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
22
|
-
ar[i] = from[i];
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
26
|
-
};
|
|
27
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
3
|
+
const types_1 = require("@scrabble-solver/types");
|
|
4
|
+
class PatternsGenerator {
|
|
5
|
+
constructor(config) {
|
|
31
6
|
this.config = config;
|
|
32
7
|
}
|
|
33
|
-
|
|
34
|
-
return
|
|
35
|
-
}
|
|
36
|
-
|
|
8
|
+
generate(board) {
|
|
9
|
+
return [...this.generateHorizontal(board), ...this.generateVertical(board)];
|
|
10
|
+
}
|
|
11
|
+
generateHorizontal(board) {
|
|
37
12
|
return this.generatePatterns({
|
|
38
|
-
board
|
|
39
|
-
getNthVector:
|
|
13
|
+
board,
|
|
14
|
+
getNthVector: (index) => board.getRow(index),
|
|
40
15
|
numberOfVectors: this.config.boardHeight,
|
|
41
16
|
PatternModel: types_1.HorizontalPattern,
|
|
42
17
|
});
|
|
43
|
-
}
|
|
44
|
-
|
|
18
|
+
}
|
|
19
|
+
generateVertical(board) {
|
|
45
20
|
return this.generatePatterns({
|
|
46
|
-
board
|
|
47
|
-
getNthVector:
|
|
21
|
+
board,
|
|
22
|
+
getNthVector: (index) => board.getColumn(index),
|
|
48
23
|
numberOfVectors: this.config.boardWidth,
|
|
49
24
|
PatternModel: types_1.VerticalPattern,
|
|
50
25
|
});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return this.generateVectors({ getNthVector: getNthVector, numberOfVectors: numberOfVectors }).reduce(function (patterns, cells) {
|
|
56
|
-
return patterns.concat(_this.generateCellsPatterns({ board: board, PatternModel: PatternModel, cells: cells }));
|
|
26
|
+
}
|
|
27
|
+
generatePatterns({ board, getNthVector, numberOfVectors, PatternModel, }) {
|
|
28
|
+
return this.generateVectors({ getNthVector, numberOfVectors }).reduce((patterns, cells) => {
|
|
29
|
+
return patterns.concat(this.generateCellsPatterns({ board, PatternModel, cells }));
|
|
57
30
|
}, []);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
var getNthVector = _a.getNthVector, numberOfVectors = _a.numberOfVectors;
|
|
31
|
+
}
|
|
32
|
+
generateVectors({ getNthVector, numberOfVectors, }) {
|
|
61
33
|
return Array(numberOfVectors)
|
|
62
34
|
.fill(0)
|
|
63
|
-
.map(
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
board
|
|
35
|
+
.map((_, index) => getNthVector(index));
|
|
36
|
+
}
|
|
37
|
+
generateCellsPatterns({ board, cells, PatternModel, }) {
|
|
38
|
+
return this.generateStartIndices(cells).flatMap((startIndex) => {
|
|
39
|
+
const endIndices = this.generateEndIndices(cells, startIndex);
|
|
40
|
+
const patterns = [];
|
|
41
|
+
for (const endIndex of endIndices) {
|
|
42
|
+
const pattern = new PatternModel({
|
|
43
|
+
board,
|
|
72
44
|
cells: cells.slice(startIndex, endIndex + 1),
|
|
73
45
|
});
|
|
74
|
-
if (pattern.canBePlaced(
|
|
75
|
-
|
|
46
|
+
if (pattern.canBePlaced(this.config)) {
|
|
47
|
+
patterns.push(pattern);
|
|
76
48
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
49
|
+
}
|
|
50
|
+
return patterns;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
generateStartIndices(cells) {
|
|
82
54
|
return Array(cells.length - 1)
|
|
83
55
|
.fill(0)
|
|
84
|
-
.map(
|
|
85
|
-
.filter(
|
|
86
|
-
}
|
|
87
|
-
|
|
56
|
+
.map((_, startIndex) => startIndex)
|
|
57
|
+
.filter((startIndex) => startIndex === 0 || !cells[startIndex - 1].hasTile());
|
|
58
|
+
}
|
|
59
|
+
generateEndIndices(cells, startIndex) {
|
|
88
60
|
return Array(cells.length - startIndex - 1)
|
|
89
61
|
.fill(0)
|
|
90
|
-
.map(
|
|
91
|
-
.filter(
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
}());
|
|
62
|
+
.map((_, endIndex) => endIndex + startIndex + 1)
|
|
63
|
+
.filter((endIndex) => endIndex >= cells.length - 1 || !cells[endIndex + 1].hasTile());
|
|
64
|
+
}
|
|
65
|
+
}
|
|
95
66
|
exports.default = PatternsGenerator;
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
var _b = bonus && bonus.canApply(_this.config, cell) ? bonus.value : constants_1.NO_BONUS, characterMultiplier = _b.characterMultiplier, wordMultiplier = _b.wordMultiplier;
|
|
11
|
-
var characterScore = cell.tile.isBlank ? _this.config.blankScore : _this.config.pointsMap[cell.tile.character];
|
|
3
|
+
const constants_1 = require("@scrabble-solver/constants");
|
|
4
|
+
class ScoresCalculator {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
this.reduceCellScore = ({ multiplier, score }, cell) => {
|
|
7
|
+
const bonus = this.config.getCellBonus(cell);
|
|
8
|
+
const { characterMultiplier, wordMultiplier } = bonus && bonus.canApply(this.config, cell) ? bonus.value : constants_1.NO_BONUS;
|
|
9
|
+
const characterScore = cell.tile.isBlank ? this.config.blankScore : this.config.pointsMap[cell.tile.character];
|
|
12
10
|
return {
|
|
13
11
|
multiplier: multiplier * wordMultiplier,
|
|
14
12
|
score: score + characterScore * characterMultiplier,
|
|
@@ -16,26 +14,24 @@ var ScoresCalculator = /** @class */ (function () {
|
|
|
16
14
|
};
|
|
17
15
|
this.config = config;
|
|
18
16
|
}
|
|
19
|
-
|
|
17
|
+
calculate(pattern) {
|
|
20
18
|
return this.calculatePatternScoreWithCollisions(pattern) + this.calculateBonusScore(pattern);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
}
|
|
20
|
+
calculateBonusScore(pattern) {
|
|
21
|
+
const areAllTilesUsed = pattern.getNumberOfEmptyCells() === this.config.maximumNumberOfCharacters;
|
|
24
22
|
return areAllTilesUsed ? this.config.allTilesBonusScore : 0;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
var _this = this;
|
|
23
|
+
}
|
|
24
|
+
calculatePatternScoreWithCollisions(pattern) {
|
|
28
25
|
return pattern
|
|
29
26
|
.getCollisions()
|
|
30
|
-
.reduce(
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
.reduce((patternsScore, collisionPattern) => patternsScore + this.calculatePatternScore(collisionPattern), this.calculatePatternScore(pattern));
|
|
28
|
+
}
|
|
29
|
+
calculatePatternScore(pattern) {
|
|
30
|
+
const { multiplier, score } = pattern.cells.reduce(this.reduceCellScore, {
|
|
34
31
|
multiplier: 1,
|
|
35
32
|
score: 0,
|
|
36
|
-
})
|
|
33
|
+
});
|
|
37
34
|
return score * multiplier;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
}());
|
|
35
|
+
}
|
|
36
|
+
}
|
|
41
37
|
exports.default = ScoresCalculator;
|
package/build/Solver.js
CHANGED
|
@@ -3,33 +3,28 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
const types_1 = require("@scrabble-solver/types");
|
|
7
|
+
const uniqBy_1 = __importDefault(require("lodash/uniqBy"));
|
|
8
|
+
const PatternsFiller_1 = __importDefault(require("./PatternsFiller"));
|
|
9
|
+
const PatternsGenerator_1 = __importDefault(require("./PatternsGenerator"));
|
|
10
|
+
const ScoresCalculator_1 = __importDefault(require("./ScoresCalculator"));
|
|
11
|
+
class Solver {
|
|
12
|
+
constructor(config, trie) {
|
|
13
13
|
this.patternsFiller = new PatternsFiller_1.default(config, trie);
|
|
14
14
|
this.patternsGenerator = new PatternsGenerator_1.default(config);
|
|
15
15
|
this.scoresCalculator = new ScoresCalculator_1.default(config);
|
|
16
16
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
numberOfCollisions: pattern.getCollisions().length,
|
|
28
|
-
points: _this.scoresCalculator.calculate(pattern),
|
|
29
|
-
});
|
|
30
|
-
});
|
|
17
|
+
solve(board, tiles) {
|
|
18
|
+
const patterns = this.patternsGenerator.generate(board);
|
|
19
|
+
const filledPatterns = patterns.flatMap((pattern) => this.patternsFiller.fill(pattern, tiles));
|
|
20
|
+
const uniquePatterns = (0, uniqBy_1.default)(filledPatterns, (pattern) => JSON.stringify(pattern.toJson()));
|
|
21
|
+
const results = uniquePatterns.map((pattern, index) => new types_1.Result({
|
|
22
|
+
cells: pattern.cells,
|
|
23
|
+
id: index,
|
|
24
|
+
numberOfCollisions: pattern.getCollisions().length,
|
|
25
|
+
points: this.scoresCalculator.calculate(pattern),
|
|
26
|
+
}));
|
|
31
27
|
return results;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
}());
|
|
28
|
+
}
|
|
29
|
+
}
|
|
35
30
|
exports.default = Solver;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrabble-solver/solver",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.4",
|
|
4
4
|
"description": "Scrabble Solver 2 - Solver",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -24,15 +24,15 @@
|
|
|
24
24
|
"clean:force": "npm run clean && rimraf package-lock.json"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@kamilmielnik/trie": "^
|
|
28
|
-
"@scrabble-solver/types": "^2.8.
|
|
27
|
+
"@kamilmielnik/trie": "^2.0.0",
|
|
28
|
+
"@scrabble-solver/types": "^2.8.4",
|
|
29
29
|
"lodash": "^4.17.21"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@scrabble-solver/configs": "^2.8.
|
|
33
|
-
"@scrabble-solver/constants": "^2.8.
|
|
34
|
-
"@scrabble-solver/dictionaries": "^2.8.
|
|
35
|
-
"@types/lodash": "^4.14.
|
|
32
|
+
"@scrabble-solver/configs": "^2.8.4",
|
|
33
|
+
"@scrabble-solver/constants": "^2.8.4",
|
|
34
|
+
"@scrabble-solver/dictionaries": "^2.8.4",
|
|
35
|
+
"@types/lodash": "^4.14.186"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "3b0c4c9d7987e456f34f7e2918931b0024b8a393"
|
|
38
38
|
}
|
package/src/PatternsFiller.ts
CHANGED
|
@@ -24,7 +24,7 @@ class PatternsFiller {
|
|
|
24
24
|
|
|
25
25
|
for (let index = 0; index < tilesPermutations.length; ++index) {
|
|
26
26
|
const tilesPermutation = tilesPermutations[index];
|
|
27
|
-
this.fillPattern(pattern,
|
|
27
|
+
this.fillPattern(pattern, pattern.toString(), tilesPermutation, onPatternFound);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
return patterns;
|
|
@@ -45,7 +45,6 @@ class PatternsFiller {
|
|
|
45
45
|
} else {
|
|
46
46
|
for (let index = 0; index < tiles.length; ++index) {
|
|
47
47
|
const tile = tiles[index];
|
|
48
|
-
const remainingTiles = tiles.slice(0, index).concat(tiles.slice(index + 1));
|
|
49
48
|
const previousTile = pattern.cells[indexOfFirstCellWithoutTile].tile;
|
|
50
49
|
pattern.cells[indexOfFirstCellWithoutTile].tile = tile;
|
|
51
50
|
const indexOfNextCellWithoutTile = pattern.getIndexOfFirstCellWithoutTile();
|
|
@@ -57,7 +56,9 @@ class PatternsFiller {
|
|
|
57
56
|
onPatternFound(pattern.clone());
|
|
58
57
|
}
|
|
59
58
|
} else if (this.trie.hasPrefix(newWordPrefix)) {
|
|
60
|
-
|
|
59
|
+
tiles.splice(index, 1);
|
|
60
|
+
this.fillPattern(pattern, newWord, tiles, onPatternFound);
|
|
61
|
+
tiles.splice(index, 0, tile);
|
|
61
62
|
}
|
|
62
63
|
pattern.cells[indexOfFirstCellWithoutTile].tile = previousTile;
|
|
63
64
|
}
|
|
@@ -65,13 +66,7 @@ class PatternsFiller {
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
public canAddPattern(pattern: Pattern, word: string): boolean {
|
|
68
|
-
return (
|
|
69
|
-
this.trie.has(word) &&
|
|
70
|
-
pattern
|
|
71
|
-
.getCollisions()
|
|
72
|
-
.map(String)
|
|
73
|
-
.every((collision) => this.trie.has(collision))
|
|
74
|
-
);
|
|
69
|
+
return this.trie.has(word) && pattern.getCollisions().every((collision) => this.trie.has(collision.toString()));
|
|
75
70
|
}
|
|
76
71
|
|
|
77
72
|
public generateBlankTilesPermutations(tiles: Tile[]): Tile[][] {
|
package/src/PatternsGenerator.ts
CHANGED
|
@@ -66,22 +66,23 @@ class PatternsGenerator {
|
|
|
66
66
|
cells: Cell[];
|
|
67
67
|
PatternModel: new (parameters: { board: Board; cells: Cell[] }) => P;
|
|
68
68
|
}): P[] {
|
|
69
|
-
return this.generateStartIndices(cells).
|
|
70
|
-
(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
69
|
+
return this.generateStartIndices(cells).flatMap((startIndex) => {
|
|
70
|
+
const endIndices = this.generateEndIndices(cells, startIndex);
|
|
71
|
+
const patterns: P[] = [];
|
|
72
|
+
|
|
73
|
+
for (const endIndex of endIndices) {
|
|
74
|
+
const pattern = new PatternModel({
|
|
75
|
+
board,
|
|
76
|
+
cells: cells.slice(startIndex, endIndex + 1),
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (pattern.canBePlaced(this.config)) {
|
|
80
|
+
patterns.push(pattern);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return patterns;
|
|
85
|
+
});
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
public generateStartIndices(cells: Cell[]): number[] {
|
package/src/Solver.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Trie } from '@kamilmielnik/trie';
|
|
2
|
-
import { Board, Config,
|
|
2
|
+
import { Board, Config, Result, Tile } from '@scrabble-solver/types';
|
|
3
3
|
import uniqBy from 'lodash/uniqBy';
|
|
4
4
|
|
|
5
5
|
import PatternsFiller from './PatternsFiller';
|
|
@@ -20,13 +20,9 @@ class Solver {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
public solve(board: Board, tiles: Tile[]): Result[] {
|
|
23
|
-
const patterns = this.patternsGenerator
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
(filledPatterns, pattern) => filledPatterns.concat(this.patternsFiller.fill(pattern, tiles)),
|
|
27
|
-
[],
|
|
28
|
-
);
|
|
29
|
-
const uniquePatterns = uniqBy(patterns, (pattern) => JSON.stringify(pattern.toJson()));
|
|
23
|
+
const patterns = this.patternsGenerator.generate(board);
|
|
24
|
+
const filledPatterns = patterns.flatMap((pattern) => this.patternsFiller.fill(pattern, tiles));
|
|
25
|
+
const uniquePatterns = uniqBy(filledPatterns, (pattern) => JSON.stringify(pattern.toJson()));
|
|
30
26
|
const results = uniquePatterns.map(
|
|
31
27
|
(pattern, index) =>
|
|
32
28
|
new Result({
|