@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/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__ */ ((SortOrder3) => {
97
- SortOrder3["Ascending"] = "sa";
98
- SortOrder3["Descending"] = "sd";
99
- SortOrder3["None"] = "none";
100
- return SortOrder3;
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 generateStatsDice(originalDice, stats, dollarValue) {
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 statKeys = Object.keys(stats);
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 diceMatch = /^(\d*)d(.+)$/i.exec(tokenStd);
220
- if (diceMatch) {
221
- const diceCount = diceMatch[1] || "";
222
- const afterD = diceMatch[2];
223
- let foundStatAfterD = false;
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,