@dicelette/core 1.25.1 → 1.27.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 +113 -73
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +109 -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,73 @@ 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) {
|
|
214
|
+
const exact = normalizedStats.get(searchTerm);
|
|
215
|
+
if (exact) return exact;
|
|
216
|
+
const candidates = [];
|
|
217
|
+
for (const [normalizedKey, original] of normalizedStats) {
|
|
218
|
+
if (normalizedKey.startsWith(searchTerm))
|
|
219
|
+
candidates.push([original, calculateSimilarity(searchTerm, normalizedKey)]);
|
|
220
|
+
}
|
|
221
|
+
if (candidates.length === 1) return candidates[0][0];
|
|
222
|
+
if (candidates.length > 0) {
|
|
223
|
+
candidates.sort((a, b) => b[1] - a[1]);
|
|
224
|
+
if (candidates[0][1] >= similarityThreshold) return candidates[0][0];
|
|
225
|
+
}
|
|
226
|
+
let bestMatch;
|
|
227
|
+
let bestSimilarity = 0;
|
|
228
|
+
for (const [normalizedKey, original] of normalizedStats) {
|
|
229
|
+
const similarity = calculateSimilarity(searchTerm, normalizedKey);
|
|
230
|
+
if (similarity === 1) return original;
|
|
231
|
+
if (similarity > bestSimilarity && similarity >= similarityThreshold) {
|
|
232
|
+
bestSimilarity = similarity;
|
|
233
|
+
bestMatch = original;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return bestMatch;
|
|
237
|
+
}
|
|
238
|
+
function findBestRecord(record, searchTerm, similarityThreshold = MIN_THRESHOLD_MATCH) {
|
|
239
|
+
const normalizeRecord = /* @__PURE__ */ new Map();
|
|
240
|
+
for (const key of Object.keys(record)) {
|
|
241
|
+
normalizeRecord.set(key.standardize(), key);
|
|
242
|
+
}
|
|
243
|
+
return findBestStatMatch(
|
|
244
|
+
searchTerm.standardize(),
|
|
245
|
+
normalizeRecord,
|
|
246
|
+
similarityThreshold
|
|
247
|
+
) || null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/utils.ts
|
|
184
251
|
function escapeRegex(string) {
|
|
185
252
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
186
253
|
}
|
|
@@ -190,10 +257,34 @@ function standardizeDice(dice) {
|
|
|
190
257
|
(_match, insideBrackets, outsideText) => insideBrackets ? insideBrackets : outsideText.standardize().replaceAll("df", "dF")
|
|
191
258
|
);
|
|
192
259
|
}
|
|
193
|
-
function
|
|
260
|
+
function handleDiceAfterD(tokenStd, normalizedStats) {
|
|
261
|
+
const diceMatch = /^(\d*)d(.+)$/i.exec(tokenStd);
|
|
262
|
+
if (!diceMatch) return null;
|
|
263
|
+
const diceCount = diceMatch[1] || "";
|
|
264
|
+
const afterD = diceMatch[2];
|
|
265
|
+
const bestMatch = findBestStatMatch(afterD, normalizedStats, 1);
|
|
266
|
+
if (bestMatch) {
|
|
267
|
+
const [, value] = bestMatch;
|
|
268
|
+
return `${diceCount}d${value.toString()}`;
|
|
269
|
+
}
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
function handleSimpleToken(tokenStd, token, normalizedStats, minThreshold) {
|
|
273
|
+
const bestMatch = findBestStatMatch(tokenStd, normalizedStats, minThreshold);
|
|
274
|
+
if (bestMatch) {
|
|
275
|
+
const [, value] = bestMatch;
|
|
276
|
+
return value.toString();
|
|
277
|
+
}
|
|
278
|
+
return token;
|
|
279
|
+
}
|
|
280
|
+
function generateStatsDice(originalDice, stats, minThreshold = 0.6, dollarValue) {
|
|
194
281
|
let dice = originalDice.standardize();
|
|
195
282
|
if (stats && Object.keys(stats).length > 0) {
|
|
196
|
-
const
|
|
283
|
+
const normalizedStats = /* @__PURE__ */ new Map();
|
|
284
|
+
for (const [key, value] of Object.entries(stats)) {
|
|
285
|
+
const normalized = key.standardize();
|
|
286
|
+
normalizedStats.set(normalized, [key, value]);
|
|
287
|
+
}
|
|
197
288
|
const partsRegex = /(\[[^\]]+])|([^[]+)/g;
|
|
198
289
|
let result = "";
|
|
199
290
|
let match;
|
|
@@ -207,7 +298,7 @@ function generateStatsDice(originalDice, stats, dollarValue) {
|
|
|
207
298
|
if (!outsideText) {
|
|
208
299
|
continue;
|
|
209
300
|
}
|
|
210
|
-
const tokenRegex = /(\$?[\p{L}\p{N}_]+)/gu;
|
|
301
|
+
const tokenRegex = /(\$?[\p{L}\p{N}_.]+)/gu;
|
|
211
302
|
let lastIndex = 0;
|
|
212
303
|
let tokenMatch;
|
|
213
304
|
while ((tokenMatch = tokenRegex.exec(outsideText)) !== null) {
|
|
@@ -216,45 +307,13 @@ function generateStatsDice(originalDice, stats, dollarValue) {
|
|
|
216
307
|
const tokenHasDollar = token.startsWith("$");
|
|
217
308
|
const tokenForCompare = tokenHasDollar ? token.slice(1) : token;
|
|
218
309
|
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;
|
|
310
|
+
const diceReplacement = handleDiceAfterD(tokenStd, normalizedStats);
|
|
311
|
+
if (diceReplacement) {
|
|
312
|
+
result += diceReplacement;
|
|
313
|
+
lastIndex = tokenRegex.lastIndex;
|
|
314
|
+
continue;
|
|
257
315
|
}
|
|
316
|
+
result += handleSimpleToken(tokenStd, token, normalizedStats, minThreshold);
|
|
258
317
|
lastIndex = tokenRegex.lastIndex;
|
|
259
318
|
}
|
|
260
319
|
result += outsideText.slice(lastIndex);
|
|
@@ -298,33 +357,6 @@ function randomInt(min, max, engine = NumberGenerator2.engines.nodeCrypto, rng)
|
|
|
298
357
|
if (!rng) rng = new Random(engine || void 0);
|
|
299
358
|
return rng.integer(min, max);
|
|
300
359
|
}
|
|
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
360
|
function createCriticalCustom(dice, customCritical, template, engine = NumberGenerator2.engines.nodeCrypto) {
|
|
329
361
|
const compareRegex = dice.match(SIGN_REGEX_SPACE);
|
|
330
362
|
let customDice = dice;
|
|
@@ -1443,6 +1475,7 @@ export {
|
|
|
1443
1475
|
SortOrder,
|
|
1444
1476
|
TooManyDice,
|
|
1445
1477
|
TooManyStats,
|
|
1478
|
+
calculateSimilarity,
|
|
1446
1479
|
createCriticalCustom,
|
|
1447
1480
|
diceRandomParse,
|
|
1448
1481
|
diceTypeRandomParse,
|
|
@@ -1450,11 +1483,14 @@ export {
|
|
|
1450
1483
|
evalCombinaison,
|
|
1451
1484
|
evalOneCombinaison,
|
|
1452
1485
|
evalStatsDice,
|
|
1486
|
+
findBestRecord,
|
|
1487
|
+
findBestStatMatch,
|
|
1453
1488
|
generateRandomStat,
|
|
1454
1489
|
generateStatsDice,
|
|
1455
1490
|
getEngine,
|
|
1456
1491
|
getEngineId,
|
|
1457
1492
|
isNumber,
|
|
1493
|
+
levenshteinDistance,
|
|
1458
1494
|
randomInt,
|
|
1459
1495
|
replaceExpByRandom,
|
|
1460
1496
|
replaceFormulaInDice,
|