Compare commits

...

16 Commits

Author SHA1 Message Date
Ean Milligan 83595482b7 V4.1.2 - increasing maxLoops to 2M to hopefully limit maxLoopsError in simnom 2025-08-06 15:18:51 -04:00
Ean Milligan cdc710385f add missing file 2025-08-06 15:12:31 -04:00
Ean Milligan 59d4435c32 improve logging 2025-08-06 15:04:30 -04:00
Ean Milligan 2cfa923093 -6 loops overall by only incrementing loop count when regex/str.replace is being called 2025-08-06 14:50:20 -04:00
Ean Milligan c58ebcc9f9 -1 loop per iteration by only asserting paren () balance when () exist in the mathConf 2025-08-06 14:47:32 -04:00
Ean Milligan e01097b1e3 improve loop log message 2025-08-06 14:45:07 -04:00
Ean Milligan 4efbed7424 deno fmt + fix spacing in log message 2025-08-06 14:43:26 -04:00
Ean Milligan 4a5e33c9a0 -3 loops per iteration by only incrementing loop count when math is actually being handled 2025-08-06 14:40:46 -04:00
Ean Milligan 6bf671f82d -1 loop per iteration by only counting when necessary 2025-08-06 14:35:33 -04:00
Ean Milligan 587d5aa19d -1 loop per iteration by only doing rollDist when needed 2025-08-06 14:30:41 -04:00
Ean Milligan 1bb8c1a308 fix loopCount debug to actually log 2025-08-05 17:59:03 -04:00
Ean Milligan e06abac9cf Add debug logging to loopCountCheck 2025-08-05 17:48:24 -04:00
Ean Milligan c80b7c2235 V4.1.1 ver bump 2025-08-05 16:53:02 -04:00
Ean Milligan 959fd6e120 fix simnom being able to be negative 2025-08-05 16:52:12 -04:00
Ean Milligan de02ebcc09 fix getModifiers errors being ignored 2025-08-05 16:51:59 -04:00
Ean Milligan f989be56db Make simnom iterations performed per roll more readable 2025-08-05 16:46:30 -04:00
25 changed files with 102 additions and 73 deletions

View File

@ -1,4 +1,4 @@
# The Artificer - A Dice Rolling Discord Bot | V4.1.0 - 2025/08/05
# The Artificer - A Dice Rolling Discord Bot | V4.1.2 - 2025/08/06
[![SonarCloud](https://sonarcloud.io/images/project_badges/sonarcloud-orange.svg)](https://sonarcloud.io/summary/new_code?id=TheArtificer)
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=TheArtificer&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=TheArtificer) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=TheArtificer&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=TheArtificer) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=TheArtificer&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=TheArtificer) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=TheArtificer&metric=bugs)](https://sonarcloud.io/summary/new_code?id=TheArtificer) [![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=TheArtificer&metric=duplicated_lines_density)](https://sonarcloud.io/summary/new_code?id=TheArtificer) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=TheArtificer&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=TheArtificer)

View File

@ -1,7 +1,7 @@
export const config = {
name: 'The Artificer', // Name of the bot
maxFileSize: 8_388_290, // Max file size bot can send
version: '4.1.0', // Version of the bot
version: '4.1.2', // Version of the bot
token: 'the_bot_token', // Discord API Token for this bot
localtoken: 'local_testing_token', // Discord API Token for a secondary OPTIONAL testing bot, THIS MUST BE DIFFERENT FROM "token"
prefix: '[[', // Prefix for all commands
@ -16,7 +16,7 @@ export const config = {
guild: 1_000, // Allows guilds to have 1000 aliased rolls for free
},
},
maxLoops: 1_000_000, // Determines how long the bot will attempt a roll, number of loops before it kills a roll. Increase this at your own risk.
maxLoops: 2_000_000, // Determines how long the bot will attempt a roll, number of loops before it kills a roll. Increase this at your own risk.
maxWorkers: 16, // Maximum number of worker threads to spawn at once (Set this to less than the number of threads your CPU has, Artificer will eat it all if too many rolls happen at once)
workerTimeout: 300_000, // Maximum time before the bot kills a worker thread in ms
defaultSimulatedNominal: 10_000, // Default number of loops to run for simulating a nominal

View File

@ -23,6 +23,7 @@ export interface SolvedRoll {
line1: string;
line2: string;
line3: string;
footer: string;
counts: CountDetails;
rollDistributions: RollDistributionMap;
}

View File

@ -5,12 +5,12 @@ import { tokenizeCmd } from 'artigen/cmdTokenizer.ts';
import { Modifiers } from 'artigen/dice/getModifiers.ts';
import { loopCountCheck } from 'artigen/managers/loopManager.ts';
import { getLoopCount, loopCountCheck } from 'artigen/managers/loopManager.ts';
import { QueuedRoll } from 'artigen/managers/manager.d.ts';
import { reduceCountDetails } from 'artigen/utils/counter.ts';
import { cmdSplitRegex, escapeCharacters, withYVarsDash } from 'artigen/utils/escape.ts';
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
import { loggingEnabled, showLoopCountDebug } from 'artigen/utils/logFlag.ts';
import { assertPrePostBalance } from 'artigen/utils/parenBalance.ts';
import { reduceRollDistMaps } from 'artigen/utils/rollDist.ts';
import { compareTotalRolls, compareTotalRollsReverse, sortYVars } from 'artigen/utils/sortFuncs.ts';
@ -26,6 +26,7 @@ export const runCmd = (rollRequest: QueuedRoll): SolvedRoll => {
line1: '',
line2: '',
line3: '',
footer: '',
counts: {
total: 0,
successful: 0,
@ -106,10 +107,10 @@ export const runCmd = (rollRequest: QueuedRoll): SolvedRoll => {
}
// List number of iterations on simulated nominals
if (rollRequest.modifiers.simulatedNominal) line2 += `Iterations performed per roll: \`${rollRequest.modifiers.simulatedNominal}\`\n`;
if (rollRequest.modifiers.simulatedNominal) line2 += `Iterations performed per roll: \`${rollRequest.modifiers.simulatedNominal.toLocaleString()}\`\n`;
// Reduce counts to a single object
returnMsg.counts = reduceCountDetails(tempCountDetails);
if (rollRequest.modifiers.count) returnMsg.counts = reduceCountDetails(tempCountDetails);
// If a regular nominal and roll looks somewhat complex, alert user simulatedNominal exists
if (rollRequest.modifiers.nominalRoll && tempReturnData.filter((data) => data.isComplex).length) {
@ -120,7 +121,7 @@ export const runCmd = (rollRequest: QueuedRoll): SolvedRoll => {
const line2Space = rollRequest.modifiers.noSpaces ? '' : ' ';
// Fill out all of the details and results now
tempReturnData.forEach((e, i) => {
loopCountCheck();
loopCountCheck('artigen.ts - tempReturnData');
loggingEnabled && log(LT.LOG, `Parsing roll ${rollRequest.rollCmd} | Making return text ${JSON.stringify(e)}`);
let preFormat = '';
@ -169,7 +170,7 @@ export const runCmd = (rollRequest: QueuedRoll): SolvedRoll => {
returnMsg.line3 = line3;
// Reduce rollDist maps into a single map
returnMsg.rollDistributions = reduceRollDistMaps(tempRollDists);
if (rollRequest.modifiers.rollDist) returnMsg.rollDistributions = reduceRollDistMaps(tempRollDists);
} catch (e) {
// Fill in the return block
const solverError = e as Error;
@ -178,5 +179,7 @@ export const runCmd = (rollRequest: QueuedRoll): SolvedRoll => {
[returnMsg.errorCode, returnMsg.errorMsg] = translateError(solverError);
}
if (showLoopCountDebug) returnMsg.footer = `Loop Count: ${getLoopCount()}`;
return returnMsg;
};

View File

@ -32,7 +32,7 @@ export const tokenizeCmd = (
// Wrapped commands still exist, unwrap them
while (cmd.includes(config.prefix)) {
loopCountCheck();
loopCountCheck('cmdTokenizer.ts - while cmd includes prefix');
const openIdx = cmd.indexOf(config.prefix);
const closeIdx = getMatchingPostfixIdx(cmd, openIdx);
@ -49,7 +49,7 @@ export const tokenizeCmd = (
const simulatedData: ReturnData[] = [];
for (let i = 0; i < simulatedLoopCount; i++) {
loopCountCheck();
loopCountCheck(`cmdTokenizer.ts - simulate nominal loop #${i}`);
loggingEnabled && log(LT.LOG, `In simLoop:${i} "${currentCmd}" of ${JSON.stringify(cmd)}`);
@ -78,7 +78,7 @@ export const tokenizeCmd = (
loggingEnabled && log(LT.LOG, `ConfirmCrit on ${JSON.stringify(currentCmd)}`);
let done = false;
while (!done) {
loopCountCheck();
loopCountCheck('cmdTokenizer.ts - confirming crit');
// Keep running the same roll again until its not successful
const [ccTempData, ccTempCounts, ccTempDists] = tokenizeCmd(
@ -194,7 +194,7 @@ export const tokenizeCmd = (
const tempInitConf = data.initConfig.split(internalGrpWrapRegex).filter((x) => x);
loggingEnabled && log(LT.LOG, `Split solved math into tempInitConf ${JSON.stringify(tempInitConf)}`);
while (tempInitConf.includes(openInternalGrp)) {
loopCountCheck();
loopCountCheck('cmdTokenizer.ts - handling internal group result merging');
const openIdx = tempInitConf.indexOf(openInternalGrp);
const closeIdx = getMatchingInternalGrpIdx(tempInitConf, openIdx);
@ -214,7 +214,7 @@ export const tokenizeCmd = (
.filter((x) => x);
loggingEnabled && log(LT.LOG, `Split tempInitConfig into initConf ${JSON.stringify(initConf)}`);
while (initConf.includes(openInternal)) {
loopCountCheck();
loopCountCheck('cmdTokenizer.ts - handling internal nested roll result merging');
const openIdx = initConf.indexOf(openInternal);
const closeIdx = getMatchingInternalIdx(initConf, openIdx);

View File

@ -80,7 +80,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
for (let i = 0; i < rollConf.dieCount; i++) {
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Initial rolling ${i} of ${JSON.stringify(rollConf)}`);
// If loopCount gets too high, stop trying to calculate infinity
loopCountCheck();
loopCountCheck('executeRoll.ts - handling initial rolling');
// Copy the template to fill out for this iteration
const rolling = getTemplateRoll();
@ -104,7 +104,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
for (let i = 0; i < rollSet.length; i++) {
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Handling rerolling and exploding ${JSON.stringify(rollSet[i])}`);
// If loopCount gets too high, stop trying to calculate infinity
loopCountCheck();
loopCountCheck('executeRoll.ts - handling rerolling and exploding');
// This big boolean statement first checks if reroll is on, if the roll is within the reroll range, and finally if ro is ON, make sure we haven't already rerolled the roll
if (rollConf.reroll.on && rollConf.reroll.nums.includes(rollSet[i].roll) && (!rollConf.reroll.once || !rollSet[i ? i - 1 : i].rerolled)) {
@ -117,7 +117,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
if (modifiers.maxRoll && !minMaxOverride) {
// If maximizeRoll is on and we've entered the reroll code, dieSize is not allowed, determine the next best option and always return that
mmMaxLoop: for (let m = rollConf.dieSize - 1; m > 0; m--) {
loopCountCheck();
loopCountCheck('executeRoll.ts - maximizeRoll');
if (!rollConf.reroll.nums.includes(m)) {
minMaxOverride = m;
@ -127,7 +127,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
} else if (modifiers.minRoll && !minMaxOverride) {
// If minimizeRoll is on and we've entered the reroll code, 1 is not allowed, determine the next best option and always return that
mmMinLoop: for (let m = rollConf.dPercent.on ? 1 : 2; m <= rollConf.dieSize; m++) {
loopCountCheck();
loopCountCheck('executeRoll.ts - minimizeRoll');
if (!rollConf.reroll.nums.includes(m)) {
minMaxOverride = m;
@ -181,7 +181,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
for (const penRoll of rollSet) {
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Handling penetrating explosions ${JSON.stringify(penRoll)}`);
// If loopCount gets too high, stop trying to calculate infinity
loopCountCheck();
loopCountCheck('executeRoll.ts - penetrating explosion');
// If the die was from an explosion, decrement it by one
if (penRoll.exploding) {
@ -195,7 +195,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
for (let i = 0; i < rollSet.length; i++) {
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Handling compounding explosions ${JSON.stringify(rollSet[i])}`);
// If loopCount gets too high, stop trying to calculate infinity
loopCountCheck();
loopCountCheck('executeRoll.ts - compounding explosion');
// Compound the exploding rolls, including the exploding flag and
if (rollSet[i].exploding) {
@ -215,8 +215,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
let rerollCount = 0;
if (rollConf.reroll.on) {
for (let j = 0; j < rollSet.length; j++) {
// If loopCount gets too high, stop trying to calculate infinity
loopCountCheck();
loopCountCheck('executeRoll.ts - count rerolls');
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Setting originalIdx on ${JSON.stringify(rollSet[j])}`);
rollSet[j].origIdx = j;
@ -265,8 +264,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
// Now its time to drop all dice needed
let i = 0;
while (dropCount > 0 && i < rollSet.length) {
// If loopCount gets too high, stop trying to calculate infinity
loopCountCheck();
loopCountCheck('executeRoll.ts - dropping/keeping');
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Dropping dice ${dropCount} ${JSON.stringify(rollSet[i])}`);
// Skip all rolls that were rerolled
@ -290,7 +288,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
// Drop all dice that are not a part of the max
for (const ovaRoll of rollSet) {
loopCountCheck();
loopCountCheck('executeRoll.ts - OVA');
loggingEnabled &&
log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | checking if this roll should be dropped ${ovaRoll.roll} | to keep: ${maxRoll}`);
@ -311,7 +309,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
const labels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
let labelIdx = 0;
const rollLabels: Array<string> = rollVals.map((count) => {
loopCountCheck();
loopCountCheck('executeRoll.ts - matching');
if (labelIdx >= labels.length) {
throw new Error(`TooManyLabels_${labels.length}`);
@ -327,7 +325,7 @@ export const executeRoll = (rollStr: string, modifiers: RollModifiers): Executed
// Apply labels
for (const roll of rollSet) {
loopCountCheck();
loopCountCheck('executeRoll.ts - labeling matches');
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | trying to add a label to ${JSON.stringify(roll)}`);
if (rollLabels[roll.roll - 1]) {

View File

@ -20,7 +20,7 @@ export const formatRoll = (executedRoll: ExecutedRoll, modifiers: RollModifiers)
// Loop thru all parts of the roll to document everything that was done to create the total roll
loggingEnabled && log(LT.LOG, `Formatting roll ${JSON.stringify(executedRoll)}`);
executedRoll.rollSet.forEach((e) => {
loopCountCheck();
loopCountCheck('generateFormattedRoll.ts - formatting executed roll');
loggingEnabled && log(LT.LOG, `At ${JSON.stringify(e)}`);
let preFormat = '';

View File

@ -14,7 +14,7 @@ export const getGroupConf = (groupStr: string, rawStr: string): GroupConf => {
let biggest = parseInt(numberMatches.length ? numberMatches[0] : '1');
for (const num of numberMatches) {
loopCountCheck();
loopCountCheck('getGroupConf.ts - finding biggest number for die size');
const curNum = parseInt(num);
loggingEnabled && log(LT.LOG, `Finding biggest number to use as die size, ${curNum} ${biggest}`);
@ -37,7 +37,7 @@ export const getGroupConf = (groupStr: string, rawStr: string): GroupConf => {
let maxFail: number | null = null;
while (groupSplit.length) {
loopCountCheck();
loopCountCheck('getGroupConf.ts - parsing groupConf');
const option = groupSplit.shift() ?? '';
const value = parseInt(groupSplit.shift() ?? '');

View File

@ -247,5 +247,11 @@ export const getModifiers = (args: string[]): [RollModifiers, string[]] => {
modifiers.valid = false;
}
if (modifiers.simulatedNominal < 0) {
modifiers.error.name = 'NegativeSimNominal';
modifiers.error.message = 'Number of iterations for `simulatedNominal` must be at least 1';
modifiers.valid = false;
}
return [modifiers, args];
};

View File

@ -126,7 +126,7 @@ export const getRollConf = (rollStr: string, customTypes: CustomDiceShapes = new
const difficulty = parseInt(tempDifficulty.slice(0, afterDifficultyIdx) || '10');
for (let i = difficulty; i <= rollConf.dieSize; i++) {
loopCountCheck();
loopCountCheck('getRollConf.ts - setting cwod difficulty');
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling cwod ${rollStr} | Parsing difficulty ${i}`);
rollConf.success.range.push(i);
@ -216,7 +216,7 @@ export const getRollConf = (rollStr: string, customTypes: CustomDiceShapes = new
// Loop until all remaining args are parsed
while (remains.length > 0) {
loopCountCheck();
loopCountCheck('getRollConf.ts - parsing rollConf');
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | Parsing remains ${remains}`);
// Find the next number in the remains to be able to cut out the rule name
@ -232,7 +232,7 @@ export const getRollConf = (rollStr: string, customTypes: CustomDiceShapes = new
let noNumberAfter = false;
if (!(Object.values(DiceOptions) as string[]).includes(tempSep)) {
NumberlessDiceOptions.some((opt) => {
loopCountCheck();
loopCountCheck('getRollConf.ts - parsing numberlessDiceOptions');
loggingEnabled && log(LT.LOG, `In NumberlessDiceOptions ${opt} ${tempSep.startsWith(opt) && tempSep !== opt}`);
if (tempSep.startsWith(opt) && tempSep !== opt) {
afterSepIdx = opt.length;

View File

@ -30,7 +30,7 @@ export const handleGroup = (
// Nested groups still exist, unwrap them
while (groupParts.includes('{')) {
loopCountCheck();
loopCountCheck('groupHandler.ts - handling nested groups');
loggingEnabled && log(LT.LOG, `Handling Nested Groups | Current cmd: ${JSON.stringify(groupParts)}`);
@ -72,7 +72,7 @@ export const handleGroup = (
const groupResults: ReturnData[] = [];
for (const part of commaParts) {
loopCountCheck();
loopCountCheck('groupHandler.ts - solving commaParts');
loggingEnabled && log(LT.LOG, `Solving commaPart: ${part}`);
const [tempData, tempCounts, tempDists] = tokenizeMath(part, modifiers, previousResults, prevGrpReturnData);
@ -131,7 +131,7 @@ export const handleGroup = (
let i = 0;
while (dropCount > 0 && i < groupResults.length) {
loopCountCheck();
loopCountCheck('groupHandler.ts - handling group drop/keep');
loggingEnabled && log(LT.LOG, `Handling group dropping | Dropping ${dropCount}`);
@ -148,7 +148,7 @@ export const handleGroup = (
let failCnt = 0;
if (groupConf.success.on || groupConf.fail.on) {
groupResults.forEach((rd, idx) => {
loopCountCheck();
loopCountCheck('groupHandler.ts - handling group success/fail');
if (!resultFlags[idx].dropped) {
if (
@ -265,7 +265,7 @@ export const handleGroup = (
const initConf = retData.initConfig.split(internalGrpWrapRegex).filter((x) => x);
loggingEnabled && log(LT.LOG, `Split retData into initConf ${JSON.stringify(initConf)}`);
while (initConf.includes(openInternalGrp)) {
loopCountCheck();
loopCountCheck('groupHandler.ts - handling merging nested groups up');
const openIdx = initConf.indexOf(openInternalGrp);
const closeIdx = getMatchingInternalGrpIdx(initConf, openIdx);

View File

@ -1,12 +1,13 @@
import { closeLog, initLog } from '@Log4Deno';
import { runCmd } from 'artigen/artigen.ts';
import { SolvedRoll } from 'artigen/artigen.d.ts';
import { QueuedRoll } from 'artigen/managers/manager.d.ts';
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
import { loggingEnabled, loopLoggingEnabled } from 'artigen/utils/logFlag.ts';
loggingEnabled && initLog('logs/worker', loggingEnabled);
if (loggingEnabled || loopLoggingEnabled) initLog('logs/worker', loggingEnabled || loopLoggingEnabled);
// Extend the BigInt prototype to support JSON.stringify
interface BigIntX extends BigInt {
@ -23,13 +24,14 @@ self.postMessage('ready');
// Handle the roll
self.onmessage = async (e: MessageEvent<QueuedRoll>) => {
const payload = e.data;
const returnMsg = runCmd(payload) || {
const returnMsg: SolvedRoll = runCmd(payload) || {
error: true,
errorCode: 'EmptyMessage',
errorMsg: 'Error: Empty message',
line1: '',
line2: '',
line3: '',
footer: '',
counts: {
total: 0,
successful: 0,
@ -37,9 +39,12 @@ self.onmessage = async (e: MessageEvent<QueuedRoll>) => {
rerolled: 0,
dropped: 0,
exploded: 0,
success: 0,
fail: 0,
matches: new Map<string, number>(),
},
};
self.postMessage(returnMsg);
loggingEnabled && (await closeLog());
if (loggingEnabled || loopLoggingEnabled) await closeLog();
self.close();
};

View File

@ -278,7 +278,7 @@ Please click on "<@${botId}> *Click to see attachment*" above this message to se
details: returnMsg.line3,
},
counts: rollRequest.modifiers.count ? returnMsg.counts : null,
rollDistributions: returnMsg.rollDistributions.entries().toArray(),
rollDistributions: rollRequest.modifiers.rollDist ? returnMsg.rollDistributions.entries().toArray() : null,
},
}),
{

View File

@ -1,10 +1,15 @@
import { log, LogTypes as LT } from '@Log4Deno';
import config from '~config';
import { loopLoggingEnabled } from 'artigen/utils/logFlag.ts';
let loopCount = 0;
// Will ensure if maxLoops is 10, 10 loops will be allowed, 11 will not.
export const loopCountCheck = (): void => {
export const loopCountCheck = (location = 'unset'): void => {
loopCount++;
loopLoggingEnabled && log(LT.LOG, `Loop #${loopCount} at "${location}"`);
if (loopCount > config.limits.maxLoops) {
throw new Error('MaxLoopsExceeded');
}

View File

@ -34,7 +34,7 @@ export const mathSolver = (conf: MathConf[], wrapDetails = false): SolvedStep =>
// Evaluate all parenthesis
while (conf.includes('(')) {
loopCountCheck();
loopCountCheck('mathSolver.ts - evaluating parens');
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Looking for (`);
// Get first open parenthesis
@ -75,7 +75,7 @@ export const mathSolver = (conf: MathConf[], wrapDetails = false): SolvedStep =>
// Start at index 1 as there will never be implicit multiplication before the first element
loggingEnabled && log(LT.LOG, `Checking for missing implicit multiplication ${JSON.stringify(conf)}`);
for (let i = 1; i < conf.length; i++) {
loopCountCheck();
loopCountCheck('mathSolver.ts - checking for implicit multiplication');
const prevConfAsStr = <string> conf[i - 1];
const curConfAsStr = <string> conf[i];
@ -94,14 +94,15 @@ export const mathSolver = (conf: MathConf[], wrapDetails = false): SolvedStep =>
['+', '-'],
];
allCurOps.forEach((curOps) => {
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Evaluating ${JSON.stringify(curOps)}`);
// No loopCountCheck here since its finite/will always be 3 loops
loggingEnabled && log(LT.LOG, `Cur Ops ${JSON.stringify(curOps)}`);
// Iterate thru all operators/operands in the conf
for (let i = 0; i < conf.length; i++) {
loopCountCheck();
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Evaluating ${JSON.stringify(curOps)} | Checking ${JSON.stringify(conf[i])}`);
loggingEnabled && log(LT.LOG, `Checking ${JSON.stringify(conf[i])}`);
// Check if the current index is in the active tier of operators
if (curOps.includes(conf[i].toString())) {
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} as ${JSON.stringify(conf[i])} is in curOps`);
loopCountCheck('mathSolver.ts - evaluating roll');
// Grab the operands from before and after the operator
const operand1 = conf[i - 1];
const operand2 = conf[i + 1];

View File

@ -45,11 +45,11 @@ export const tokenizeMath = (
loggingEnabled && log(LT.LOG, `Split roll into mathConf ${JSON.stringify(mathConf)}`);
// Verify balanced parens before doing anything
assertParenBalance(mathConf);
if (mathConf.includes('(') || mathConf.includes(')')) assertParenBalance(mathConf);
// Evaluate all rolls into stepSolve format and all numbers into floats
for (let i = 0; i < mathConf.length; i++) {
loopCountCheck();
loopCountCheck('mathTokenizer.ts - parsing all tokens into MathConf');
loggingEnabled && log(LT.LOG, `Parsing roll ${JSON.stringify(cmd)} | Evaluating rolls into math-able items ${JSON.stringify(mathConf[i])}`);
@ -201,7 +201,7 @@ export const tokenizeMath = (
const formattedRoll = formatRoll(executedRoll, modifiers);
mathConf[i] = formattedRoll.solvedStep;
countDetails.push(formattedRoll.countDetails);
rollDists.push(formattedRoll.rollDistributions);
if (modifiers.rollDist) rollDists.push(formattedRoll.rollDistributions);
}
}
@ -272,7 +272,7 @@ export const tokenizeMath = (
let i = 0;
while (dropCount > 0 && i < allRollSets.length) {
loopCountCheck();
loopCountCheck('mathTokenizer.ts - handling group dropping');
loggingEnabled && log(LT.LOG, `Handling group dropping | Dropping ${dropCount}, looking at ${JSON.stringify(allRollSets[i])}`);
@ -293,7 +293,7 @@ export const tokenizeMath = (
// Handle marking new successes/fails
if (groupConf.success.on || groupConf.fail.on) {
allRollSets.forEach((rs) => {
loopCountCheck();
loopCountCheck('mathTokenizer.ts - handling group success/fails');
if (!rs.dropped && !rs.rerolled) {
if (groupConf.success.on && groupConf.success.range.includes(rs.roll)) {
@ -318,7 +318,7 @@ export const tokenizeMath = (
const formattedRoll = formatRoll(executedRoll, modifiers);
mathConf[rollGroupIdx] = formattedRoll.solvedStep;
countDetails.push(formattedRoll.countDetails);
rollDists.push(formattedRoll.rollDistributions);
if (modifiers.rollDist) rollDists.push(formattedRoll.rollDistributions);
});
}

View File

@ -16,7 +16,7 @@ export const rollCounter = (rollSet: RollSet[]): CountDetails => {
};
rollSet.forEach((roll) => {
loopCountCheck();
loopCountCheck('counter.ts - summing RollSet into CountDetails');
countDetails.total++;
if (roll.critHit) countDetails.successful++;
if (roll.critFail) countDetails.failed++;
@ -34,9 +34,9 @@ export const rollCounter = (rollSet: RollSet[]): CountDetails => {
export const reduceCountDetails = (counts: CountDetails[]): CountDetails =>
counts.reduce(
(acc, cur) => {
loopCountCheck();
loopCountCheck('counter.ts - merging array of CountDetails down to single CountDetail');
cur.matches.forEach((cnt, label) => {
loopCountCheck();
loopCountCheck('counter.ts - merging matches');
acc.matches.set(label, (acc.matches.get(label) ?? 0) + cnt);
});
return {

View File

@ -259,13 +259,17 @@ export const generateRollEmbed = (
// Embed desc limit is 4096
// Discord only formats 200 items per message
if (fullDesc.length < 4_000 && formattingCount <= 200) {
const fullSize = fullDesc.length + returnDetails.footer.length;
if (fullSize < 4_000 && formattingCount <= 200) {
// Response is valid size
return {
charCount: fullDesc.length,
charCount: fullSize,
embed: {
color: infoColor2,
description: fullDesc,
footer: {
text: returnDetails.footer,
},
},
hasAttachment: false,
};

View File

@ -11,12 +11,14 @@ import { loggingEnabled } from 'artigen/utils/logFlag.ts';
export const escapeCharacters = (str: string, esc: string): string => {
// Loop thru each esc char one at a time
for (const e of esc) {
loopCountCheck();
loggingEnabled && log(LT.LOG, `Escaping character ${e} | ${str}, ${esc}`);
// Create a new regex to look for that char that needs replaced and escape it
const tempRgx = new RegExp(`[${e}]`, 'g');
str = str.replace(tempRgx, `\\${e}`);
if (str.includes(e)) {
loopCountCheck(`escape.ts - escaping character ${e}`);
// Create a new regex to look for that char that needs replaced and escape it
const tempRgx = new RegExp(`[${e}]`, 'g');
str = str.replace(tempRgx, `\\${e}`);
}
}
return str;
};

View File

@ -1 +1,3 @@
export const loggingEnabled = false;
export const loopLoggingEnabled = false;
export const showLoopCountDebug = false;

View File

@ -22,7 +22,8 @@ const checkBalance = (
// Verify there are equal numbers of opening and closing parenthesis by adding 1 for opening parens and subtracting 1 for closing parens
for (let i = openIdx; i < conf.length; i++) {
countLoops && loopCountCheck();
countLoops &&
loopCountCheck(`parenBalance.ts - ${getMatching ? 'Looking for matching' : 'Checking'} ${openStr}/${closeStr}${getMatching ? '' : ' balance'}`);
loggingEnabled &&
log(
LT.LOG,

View File

@ -14,7 +14,7 @@ export const addToRange = (tSep: string, range: Array<number>, tNum: number) =>
const internalAddMultipleToRange = (tSep: string, range: Array<number>, start: number, end: number) => {
for (let i = start; i <= end; i++) {
loopCountCheck();
loopCountCheck(`rangeAdder.ts - ${tSep} range adder`);
addToRange(tSep, range, i);
}
};

View File

@ -10,7 +10,7 @@ export const createRollDistMap = (rollSet: RollSet[]): RollDistributionMap => {
const rollDistMap = new Map<string, number[]>();
rollSet.forEach((roll) => {
loopCountCheck();
loopCountCheck('rollDist.ts - convert RollSet into RollDist');
const tempArr: number[] = rollDistMap.get(rollDistKey(roll.type, roll.size)) ?? new Array<number>(roll.type === 'fate' ? roll.size + 2 : roll.size).fill(0);
tempArr[roll.type === 'fate' ? roll.roll + 1 : roll.roll - 1]++;
rollDistMap.set(rollDistKey(roll.type, roll.size), tempArr);
@ -22,17 +22,17 @@ export const createRollDistMap = (rollSet: RollSet[]): RollDistributionMap => {
// Collapses an array of RollDistMaps into a single RollDistMap
export const reduceRollDistMaps = (rollDistArr: RollDistributionMap[]): RollDistributionMap =>
rollDistArr.reduce((acc, cur) => {
loopCountCheck();
loopCountCheck('rollDist.ts - merge array of RollDists into single RollDist');
cur
.entries()
.toArray()
.forEach(([key, value]) => {
loopCountCheck();
loopCountCheck('rollDist.ts - doing the merge on each item of current');
const tempArr = acc.get(key) ?? new Array<number>(value.length).fill(0);
for (let i = 0; i < tempArr.length; i++) {
loopCountCheck();
loopCountCheck('rollDist.ts - doing the merge');
tempArr[i] += value[i];
}

View File

@ -12,7 +12,7 @@ export const generateRollVals = (rollConf: RollConf, rollSet: RollSet[], rollStr
// Count up all rolls
for (const ovaRoll of rollSet) {
loopCountCheck();
loopCountCheck('rollValCounter.ts - counting roll vals');
loggingEnabled && log(LT.LOG, `${getLoopCount()} Handling ${rollConf.type} ${rollStr} | incrementing rollVals for ${JSON.stringify(ovaRoll)}`);
if (!ovaRoll.dropped && !ovaRoll.rerolled) {

View File

@ -61,6 +61,7 @@ export const roll = async (
// Return early if the modifiers were invalid
if (!modifiers.valid) {
m.edit(generateRollError('Modifiers invalid:', modifiers.error.name, modifiers.error.message)).catch((e) => utils.commonLoggers.messageEditError('roll.ts:50', m, e));
return;
}
let rollCmd = (hasOwnProperty(msgOrInt, 'token') ? args.join('') : msgOrInt.content).startsWith(`${config.prefix}r`) ? remainingArgs.join('') : `${command}${remainingArgs.join('')}`;