@scrabble-solver/solver 2.8.1 → 2.8.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/build/PatternsFiller.js +37 -67
- package/build/PatternsGenerator.js +42 -75
- package/build/ScoresCalculator.js +20 -24
- package/build/Solver.js +19 -23
- package/package.json +6 -6
- package/src/PatternsFiller.ts +3 -2
package/build/PatternsFiller.js
CHANGED
|
@@ -1,104 +1,74 @@
|
|
|
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
|
-
|
|
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];
|
|
44
19
|
this.fillPattern(pattern, String(pattern), 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
|
-
var _this = this;
|
|
52
|
+
}
|
|
53
|
+
canAddPattern(pattern, word) {
|
|
79
54
|
return (this.trie.has(word) &&
|
|
80
55
|
pattern
|
|
81
56
|
.getCollisions()
|
|
82
57
|
.map(String)
|
|
83
|
-
.every(
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
var firstBlankIndex = tiles.findIndex(function (_a) {
|
|
89
|
-
var character = _a.character, isBlank = _a.isBlank;
|
|
90
|
-
return isBlank && !alphabet.includes(character);
|
|
91
|
-
});
|
|
58
|
+
.every((collision) => this.trie.has(collision)));
|
|
59
|
+
}
|
|
60
|
+
generateBlankTilesPermutations(tiles) {
|
|
61
|
+
const { alphabet } = this.config;
|
|
62
|
+
const firstBlankIndex = tiles.findIndex(({ character, isBlank }) => isBlank && !alphabet.includes(character));
|
|
92
63
|
if (firstBlankIndex === -1) {
|
|
93
64
|
return [tiles];
|
|
94
65
|
}
|
|
95
|
-
|
|
96
|
-
return this.config.alphabet.reduce(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return permutations.concat(
|
|
66
|
+
const remainingTiles = tiles.slice(0, firstBlankIndex).concat(tiles.slice(firstBlankIndex + 1));
|
|
67
|
+
return this.config.alphabet.reduce((permutations, character) => {
|
|
68
|
+
const newTile = new types_1.Tile({ character, isBlank: true });
|
|
69
|
+
const newTiles = [...remainingTiles, newTile];
|
|
70
|
+
return permutations.concat(this.generateBlankTilesPermutations(newTiles));
|
|
100
71
|
}, []);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
}());
|
|
72
|
+
}
|
|
73
|
+
}
|
|
104
74
|
exports.default = PatternsFiller;
|
|
@@ -1,95 +1,62 @@
|
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}, []));
|
|
79
|
-
}, []);
|
|
80
|
-
};
|
|
81
|
-
PatternsGenerator.prototype.generateStartIndices = function (cells) {
|
|
35
|
+
.map((_, index) => getNthVector(index));
|
|
36
|
+
}
|
|
37
|
+
generateCellsPatterns({ board, cells, PatternModel, }) {
|
|
38
|
+
return this.generateStartIndices(cells).reduce((patterns, startIndex) => patterns.concat(this.generateEndIndices(cells, startIndex).reduce((placeablePatterns, endIndex) => {
|
|
39
|
+
const pattern = new PatternModel({
|
|
40
|
+
board,
|
|
41
|
+
cells: cells.slice(startIndex, endIndex + 1),
|
|
42
|
+
});
|
|
43
|
+
if (pattern.canBePlaced(this.config)) {
|
|
44
|
+
placeablePatterns.push(pattern);
|
|
45
|
+
}
|
|
46
|
+
return placeablePatterns;
|
|
47
|
+
}, [])), []);
|
|
48
|
+
}
|
|
49
|
+
generateStartIndices(cells) {
|
|
82
50
|
return Array(cells.length - 1)
|
|
83
51
|
.fill(0)
|
|
84
|
-
.map(
|
|
85
|
-
.filter(
|
|
86
|
-
}
|
|
87
|
-
|
|
52
|
+
.map((_, startIndex) => startIndex)
|
|
53
|
+
.filter((startIndex) => startIndex === 0 || !cells[startIndex - 1].hasTile());
|
|
54
|
+
}
|
|
55
|
+
generateEndIndices(cells, startIndex) {
|
|
88
56
|
return Array(cells.length - startIndex - 1)
|
|
89
57
|
.fill(0)
|
|
90
|
-
.map(
|
|
91
|
-
.filter(
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
}());
|
|
58
|
+
.map((_, endIndex) => endIndex + startIndex + 1)
|
|
59
|
+
.filter((endIndex) => endIndex >= cells.length - 1 || !cells[endIndex + 1].hasTile());
|
|
60
|
+
}
|
|
61
|
+
}
|
|
95
62
|
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,29 @@ 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
|
-
var patterns = this.patternsGenerator
|
|
17
|
+
solve(board, tiles) {
|
|
18
|
+
const patterns = this.patternsGenerator
|
|
20
19
|
.generate(board)
|
|
21
|
-
.reduce(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
});
|
|
30
|
-
});
|
|
20
|
+
.reduce((filledPatterns, pattern) => filledPatterns.concat(this.patternsFiller.fill(pattern, tiles)), []);
|
|
21
|
+
const uniquePatterns = (0, uniqBy_1.default)(patterns, (pattern) => JSON.stringify(pattern.toJson()));
|
|
22
|
+
const results = uniquePatterns.map((pattern, index) => new types_1.Result({
|
|
23
|
+
cells: pattern.cells,
|
|
24
|
+
id: index,
|
|
25
|
+
numberOfCollisions: pattern.getCollisions().length,
|
|
26
|
+
points: this.scoresCalculator.calculate(pattern),
|
|
27
|
+
}));
|
|
31
28
|
return results;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
}());
|
|
29
|
+
}
|
|
30
|
+
}
|
|
35
31
|
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.3",
|
|
4
4
|
"description": "Scrabble Solver 2 - Solver",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -25,14 +25,14 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@kamilmielnik/trie": "^1.0.3",
|
|
28
|
-
"@scrabble-solver/types": "^2.8.
|
|
28
|
+
"@scrabble-solver/types": "^2.8.3",
|
|
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.
|
|
32
|
+
"@scrabble-solver/configs": "^2.8.3",
|
|
33
|
+
"@scrabble-solver/constants": "^2.8.3",
|
|
34
|
+
"@scrabble-solver/dictionaries": "^2.8.3",
|
|
35
35
|
"@types/lodash": "^4.14.185"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "116f214733e7ad071044f556bcf67e867da3d9ca"
|
|
38
38
|
}
|
package/src/PatternsFiller.ts
CHANGED
|
@@ -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
|
}
|