@dicelette/core 1.25.1 → 1.26.0
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 +19 -0
- package/dist/index.d.mts +23 -2
- package/dist/index.d.ts +23 -2
- package/dist/index.js +119 -73
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +115 -73
- package/dist/index.mjs.map +1 -1
- package/package.json +59 -59
package/dist/index.mjs
CHANGED
|
@@ -93,11 +93,11 @@ var NoStatisticsError = class extends Error {
|
|
|
93
93
|
};
|
|
94
94
|
|
|
95
95
|
// src/interfaces/index.ts
|
|
96
|
-
var SortOrder = /* @__PURE__ */ ((
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return
|
|
96
|
+
var SortOrder = /* @__PURE__ */ ((SortOrder2) => {
|
|
97
|
+
SortOrder2["Ascending"] = "sa";
|
|
98
|
+
SortOrder2["Descending"] = "sd";
|
|
99
|
+
SortOrder2["None"] = "none";
|
|
100
|
+
return SortOrder2;
|
|
101
101
|
})(SortOrder || {});
|
|
102
102
|
|
|
103
103
|
// src/interfaces/constant.ts
|
|
@@ -181,6 +181,79 @@ import { evaluate } from "mathjs";
|
|
|
181
181
|
import "uniformize";
|
|
182
182
|
import { NumberGenerator as NumberGenerator2 } from "@dice-roller/rpg-dice-roller";
|
|
183
183
|
import { Random } from "random-js";
|
|
184
|
+
|
|
185
|
+
// src/similarity.ts
|
|
186
|
+
var MIN_THRESHOLD_MATCH = 0.5;
|
|
187
|
+
function calculateSimilarity(str1, str2) {
|
|
188
|
+
const longer = str1.length > str2.length ? str1 : str2;
|
|
189
|
+
const shorter = str1.length > str2.length ? str2 : str1;
|
|
190
|
+
if (longer.length === 0) return 1;
|
|
191
|
+
const distance = levenshteinDistance(longer, shorter);
|
|
192
|
+
return (longer.length - distance) / longer.length;
|
|
193
|
+
}
|
|
194
|
+
function levenshteinDistance(str1, str2) {
|
|
195
|
+
const matrix = Array(str2.length + 1).fill(null).map(() => Array(str1.length + 1).fill(null));
|
|
196
|
+
for (let i = 0; i <= str1.length; i++) matrix[0][i] = i;
|
|
197
|
+
for (let j = 0; j <= str2.length; j++) matrix[j][0] = j;
|
|
198
|
+
for (let j = 1; j <= str2.length; j++) {
|
|
199
|
+
for (let i = 1; i <= str1.length; i++) {
|
|
200
|
+
const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
|
|
201
|
+
matrix[j][i] = Math.min(
|
|
202
|
+
matrix[j][i - 1] + 1,
|
|
203
|
+
// insertion
|
|
204
|
+
matrix[j - 1][i] + 1,
|
|
205
|
+
// deletion
|
|
206
|
+
matrix[j - 1][i - 1] + cost
|
|
207
|
+
// substitution
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return matrix[str2.length][str1.length];
|
|
212
|
+
}
|
|
213
|
+
function findBestStatMatch(searchTerm, normalizedStats, similarityThreshold = MIN_THRESHOLD_MATCH, partialSearch = true) {
|
|
214
|
+
const exact = normalizedStats.get(searchTerm);
|
|
215
|
+
if (exact) return exact;
|
|
216
|
+
if (partialSearch) {
|
|
217
|
+
const candidates = [];
|
|
218
|
+
for (const [normalizedKey, original] of normalizedStats) {
|
|
219
|
+
if (normalizedKey.startsWith(searchTerm))
|
|
220
|
+
candidates.push([original, normalizedKey.length]);
|
|
221
|
+
else if (normalizedKey.endsWith(searchTerm))
|
|
222
|
+
candidates.push([original, normalizedKey.length]);
|
|
223
|
+
else if (normalizedKey.includes(searchTerm))
|
|
224
|
+
candidates.push([original, normalizedKey.length]);
|
|
225
|
+
}
|
|
226
|
+
if (candidates.length > 0) {
|
|
227
|
+
candidates.sort((a, b) => a[1] - b[1]);
|
|
228
|
+
return candidates[0][0];
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
let bestMatch;
|
|
232
|
+
let bestSimilarity = 0;
|
|
233
|
+
for (const [normalizedKey, original] of normalizedStats) {
|
|
234
|
+
const similarity = calculateSimilarity(searchTerm, normalizedKey);
|
|
235
|
+
if (similarity === 1) return original;
|
|
236
|
+
if (similarity > bestSimilarity && similarity >= similarityThreshold) {
|
|
237
|
+
bestSimilarity = similarity;
|
|
238
|
+
bestMatch = original;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return bestMatch;
|
|
242
|
+
}
|
|
243
|
+
function findBestRecord(record, searchTerm, similarityThreshold = MIN_THRESHOLD_MATCH) {
|
|
244
|
+
const normalizeRecord = /* @__PURE__ */ new Map();
|
|
245
|
+
for (const key of Object.keys(record)) {
|
|
246
|
+
normalizeRecord.set(key.standardize(), key);
|
|
247
|
+
}
|
|
248
|
+
return findBestStatMatch(
|
|
249
|
+
searchTerm.standardize(),
|
|
250
|
+
normalizeRecord,
|
|
251
|
+
similarityThreshold,
|
|
252
|
+
false
|
|
253
|
+
) || null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// src/utils.ts
|
|
184
257
|
function escapeRegex(string) {
|
|
185
258
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
186
259
|
}
|
|
@@ -190,10 +263,34 @@ function standardizeDice(dice) {
|
|
|
190
263
|
(_match, insideBrackets, outsideText) => insideBrackets ? insideBrackets : outsideText.standardize().replaceAll("df", "dF")
|
|
191
264
|
);
|
|
192
265
|
}
|
|
193
|
-
function
|
|
266
|
+
function handleDiceAfterD(tokenStd, normalizedStats) {
|
|
267
|
+
const diceMatch = /^(\d*)d(.+)$/i.exec(tokenStd);
|
|
268
|
+
if (!diceMatch) return null;
|
|
269
|
+
const diceCount = diceMatch[1] || "";
|
|
270
|
+
const afterD = diceMatch[2];
|
|
271
|
+
const bestMatch = findBestStatMatch(afterD, normalizedStats, 1, false);
|
|
272
|
+
if (bestMatch) {
|
|
273
|
+
const [, value] = bestMatch;
|
|
274
|
+
return `${diceCount}d${value.toString()}`;
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
function handleSimpleToken(tokenStd, token, normalizedStats, minThreshold) {
|
|
279
|
+
const bestMatch = findBestStatMatch(tokenStd, normalizedStats, minThreshold, false);
|
|
280
|
+
if (bestMatch) {
|
|
281
|
+
const [, value] = bestMatch;
|
|
282
|
+
return value.toString();
|
|
283
|
+
}
|
|
284
|
+
return token;
|
|
285
|
+
}
|
|
286
|
+
function generateStatsDice(originalDice, stats, minThreshold = 0.6, dollarValue) {
|
|
194
287
|
let dice = originalDice.standardize();
|
|
195
288
|
if (stats && Object.keys(stats).length > 0) {
|
|
196
|
-
const
|
|
289
|
+
const normalizedStats = /* @__PURE__ */ new Map();
|
|
290
|
+
for (const [key, value] of Object.entries(stats)) {
|
|
291
|
+
const normalized = key.standardize();
|
|
292
|
+
normalizedStats.set(normalized, [key, value]);
|
|
293
|
+
}
|
|
197
294
|
const partsRegex = /(\[[^\]]+])|([^[]+)/g;
|
|
198
295
|
let result = "";
|
|
199
296
|
let match;
|
|
@@ -207,7 +304,7 @@ function generateStatsDice(originalDice, stats, dollarValue) {
|
|
|
207
304
|
if (!outsideText) {
|
|
208
305
|
continue;
|
|
209
306
|
}
|
|
210
|
-
const tokenRegex = /(\$?[\p{L}\p{N}_]+)/gu;
|
|
307
|
+
const tokenRegex = /(\$?[\p{L}\p{N}_.]+)/gu;
|
|
211
308
|
let lastIndex = 0;
|
|
212
309
|
let tokenMatch;
|
|
213
310
|
while ((tokenMatch = tokenRegex.exec(outsideText)) !== null) {
|
|
@@ -216,45 +313,13 @@ function generateStatsDice(originalDice, stats, dollarValue) {
|
|
|
216
313
|
const tokenHasDollar = token.startsWith("$");
|
|
217
314
|
const tokenForCompare = tokenHasDollar ? token.slice(1) : token;
|
|
218
315
|
const tokenStd = tokenForCompare.standardize();
|
|
219
|
-
const
|
|
220
|
-
if (
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
for (const key of statKeys) {
|
|
225
|
-
const keyStd = key.standardize();
|
|
226
|
-
if (afterD === keyStd) {
|
|
227
|
-
result += `${diceCount}d${stats[key].toString()}`;
|
|
228
|
-
foundStatAfterD = true;
|
|
229
|
-
break;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
if (foundStatAfterD) {
|
|
233
|
-
lastIndex = tokenRegex.lastIndex;
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
let bestKey = null;
|
|
238
|
-
let bestScore = 0;
|
|
239
|
-
for (const key of statKeys) {
|
|
240
|
-
const keyStd = key.standardize();
|
|
241
|
-
if (tokenStd === keyStd) {
|
|
242
|
-
bestKey = key;
|
|
243
|
-
bestScore = 1;
|
|
244
|
-
break;
|
|
245
|
-
}
|
|
246
|
-
const score = similarityScore(tokenStd, keyStd);
|
|
247
|
-
if (score > bestScore) {
|
|
248
|
-
bestScore = score;
|
|
249
|
-
bestKey = key;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
if (bestKey && bestScore >= 0.6) {
|
|
253
|
-
const statValue = stats[bestKey];
|
|
254
|
-
result += statValue.toString();
|
|
255
|
-
} else {
|
|
256
|
-
result += token;
|
|
316
|
+
const diceReplacement = handleDiceAfterD(tokenStd, normalizedStats);
|
|
317
|
+
if (diceReplacement) {
|
|
318
|
+
result += diceReplacement;
|
|
319
|
+
lastIndex = tokenRegex.lastIndex;
|
|
320
|
+
continue;
|
|
257
321
|
}
|
|
322
|
+
result += handleSimpleToken(tokenStd, token, normalizedStats, minThreshold);
|
|
258
323
|
lastIndex = tokenRegex.lastIndex;
|
|
259
324
|
}
|
|
260
325
|
result += outsideText.slice(lastIndex);
|
|
@@ -298,33 +363,6 @@ function randomInt(min, max, engine = NumberGenerator2.engines.nodeCrypto, rng)
|
|
|
298
363
|
if (!rng) rng = new Random(engine || void 0);
|
|
299
364
|
return rng.integer(min, max);
|
|
300
365
|
}
|
|
301
|
-
function levenshteinDistance(a, b) {
|
|
302
|
-
if (a === b) return 0;
|
|
303
|
-
const al = a.length;
|
|
304
|
-
const bl = b.length;
|
|
305
|
-
if (al === 0) return bl;
|
|
306
|
-
if (bl === 0) return al;
|
|
307
|
-
const v0 = new Array(bl + 1);
|
|
308
|
-
const v1 = new Array(bl + 1);
|
|
309
|
-
for (let i = 0; i <= bl; i++) v0[i] = i;
|
|
310
|
-
for (let i = 0; i < al; i++) {
|
|
311
|
-
v1[0] = i + 1;
|
|
312
|
-
for (let j = 0; j < bl; j++) {
|
|
313
|
-
const cost = a[i] === b[j] ? 0 : 1;
|
|
314
|
-
v1[j + 1] = Math.min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost);
|
|
315
|
-
}
|
|
316
|
-
for (let j = 0; j <= bl; j++) v0[j] = v1[j];
|
|
317
|
-
}
|
|
318
|
-
return v1[bl];
|
|
319
|
-
}
|
|
320
|
-
function similarityScore(a, b) {
|
|
321
|
-
const la = a.length;
|
|
322
|
-
const lb = b.length;
|
|
323
|
-
if (la === 0 && lb === 0) return 1;
|
|
324
|
-
const dist = levenshteinDistance(a, b);
|
|
325
|
-
const max = Math.max(la, lb);
|
|
326
|
-
return 1 - dist / max;
|
|
327
|
-
}
|
|
328
366
|
function createCriticalCustom(dice, customCritical, template, engine = NumberGenerator2.engines.nodeCrypto) {
|
|
329
367
|
const compareRegex = dice.match(SIGN_REGEX_SPACE);
|
|
330
368
|
let customDice = dice;
|
|
@@ -1443,6 +1481,7 @@ export {
|
|
|
1443
1481
|
SortOrder,
|
|
1444
1482
|
TooManyDice,
|
|
1445
1483
|
TooManyStats,
|
|
1484
|
+
calculateSimilarity,
|
|
1446
1485
|
createCriticalCustom,
|
|
1447
1486
|
diceRandomParse,
|
|
1448
1487
|
diceTypeRandomParse,
|
|
@@ -1450,11 +1489,14 @@ export {
|
|
|
1450
1489
|
evalCombinaison,
|
|
1451
1490
|
evalOneCombinaison,
|
|
1452
1491
|
evalStatsDice,
|
|
1492
|
+
findBestRecord,
|
|
1493
|
+
findBestStatMatch,
|
|
1453
1494
|
generateRandomStat,
|
|
1454
1495
|
generateStatsDice,
|
|
1455
1496
|
getEngine,
|
|
1456
1497
|
getEngineId,
|
|
1457
1498
|
isNumber,
|
|
1499
|
+
levenshteinDistance,
|
|
1458
1500
|
randomInt,
|
|
1459
1501
|
replaceExpByRandom,
|
|
1460
1502
|
replaceFormulaInDice,
|