Add initial group support, only support SUM mode (ie no modifiers allowed)
This commit is contained in:
		
							parent
							
								
									cb3cb6777d
								
							
						
					
					
						commit
						cbac134f79
					
				| 
						 | 
				
			
			@ -5,15 +5,16 @@ import config from '~config';
 | 
			
		|||
import { ReturnData } from 'artigen/artigen.d.ts';
 | 
			
		||||
 | 
			
		||||
import { CountDetails, RollDistributionMap, RollModifiers } from 'artigen/dice/dice.d.ts';
 | 
			
		||||
import { handleGroup } from 'artigen/dice/groupHandler.ts';
 | 
			
		||||
 | 
			
		||||
import { loopCountCheck } from 'artigen/managers/loopManager.ts';
 | 
			
		||||
 | 
			
		||||
import { tokenizeMath } from 'artigen/math/mathTokenizer.ts';
 | 
			
		||||
 | 
			
		||||
import { reduceCountDetails } from 'artigen/utils/counter.ts';
 | 
			
		||||
import { closeInternal, cmdSplitRegex, internalWrapRegex, openInternal } from 'artigen/utils/escape.ts';
 | 
			
		||||
import { closeInternal, closeInternalGrp, internalGrpWrapRegex, internalWrapRegex, openInternal, openInternalGrp } from 'artigen/utils/escape.ts';
 | 
			
		||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
 | 
			
		||||
import { assertGroupBalance, getMatchingGroupIdx, getMatchingInternalIdx, getMatchingPostfixIdx } from 'artigen/utils/parenBalance.ts';
 | 
			
		||||
import { assertGroupBalance, getMatchingGroupIdx, getMatchingInternalGrpIdx, getMatchingInternalIdx, getMatchingPostfixIdx } from 'artigen/utils/parenBalance.ts';
 | 
			
		||||
import { basicReducer } from 'artigen/utils/reducers.ts';
 | 
			
		||||
 | 
			
		||||
// tokenizeCmd expects a string[] of items that are either config.prefix/config.postfix or some text that contains math and/or dice rolls
 | 
			
		||||
| 
						 | 
				
			
			@ -131,38 +132,78 @@ export const tokenizeCmd = (
 | 
			
		|||
    }
 | 
			
		||||
    return [returnData, countDetails, rollDists];
 | 
			
		||||
  } else {
 | 
			
		||||
    // Check for any groups and handle them?
 | 
			
		||||
    // Check for any groups and handle them
 | 
			
		||||
    const groupParts = cmd
 | 
			
		||||
      .join('')
 | 
			
		||||
      .split(/([{,}])/g)
 | 
			
		||||
      .split(/([{}])/g)
 | 
			
		||||
      .filter((x) => x);
 | 
			
		||||
    const groupResults: ReturnData[] = [];
 | 
			
		||||
    if (groupParts.includes('{')) {
 | 
			
		||||
      assertGroupBalance(groupParts);
 | 
			
		||||
    }
 | 
			
		||||
    while (groupParts.includes('{')) {
 | 
			
		||||
      loggingEnabled && log(LT.LOG, `Handling Groups | Current cmd: ${JSON.stringify(groupParts)}`);
 | 
			
		||||
      const openIdx = groupParts.indexOf('}');
 | 
			
		||||
      const closeIdx = getMatchingGroupIdx;
 | 
			
		||||
      const temp = cmd.join('').replaceAll('{', '').replaceAll('}', '').replaceAll(',', '');
 | 
			
		||||
      cmd = temp.split(cmdSplitRegex);
 | 
			
		||||
 | 
			
		||||
      const openIdx = groupParts.indexOf('{');
 | 
			
		||||
      const closeIdx = getMatchingGroupIdx(groupParts, openIdx);
 | 
			
		||||
 | 
			
		||||
      const currentGrp = groupParts.slice(openIdx + 1, closeIdx);
 | 
			
		||||
 | 
			
		||||
      const [tempData, tempCounts, tempDists] = handleGroup(currentGrp, '', modifiers, previousResults);
 | 
			
		||||
      const data = tempData[0];
 | 
			
		||||
      log(LT.LOG, `Solved Group is back ${JSON.stringify(data)} | ${JSON.stringify(returnData)} ${JSON.stringify(tempCounts)} ${JSON.stringify(tempDists)}`);
 | 
			
		||||
 | 
			
		||||
      countDetails.push(...tempCounts);
 | 
			
		||||
      rollDists.push(...tempDists);
 | 
			
		||||
 | 
			
		||||
      // Merge result back into groupParts
 | 
			
		||||
      groupParts.splice(openIdx, closeIdx - openIdx + 1, `${openInternalGrp}${groupResults.length}${closeInternalGrp}`);
 | 
			
		||||
      groupResults.push(data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const cmdForMath = groupParts.join('');
 | 
			
		||||
    loggingEnabled && log(LT.LOG, `Tokenizing math ${cmdForMath}`);
 | 
			
		||||
 | 
			
		||||
    // Solve the math and rolls for this cmd
 | 
			
		||||
    const [tempData, tempCounts, tempDists] = tokenizeMath(cmdForMath, modifiers, previousResults);
 | 
			
		||||
    const [tempData, tempCounts, tempDists] = tokenizeMath(cmdForMath, modifiers, previousResults, groupResults);
 | 
			
		||||
    const data = tempData[0];
 | 
			
		||||
    loggingEnabled &&
 | 
			
		||||
      log(LT.LOG, `Solved math is back ${JSON.stringify(data)} | ${JSON.stringify(returnData)} ${JSON.stringify(tempCounts)} ${JSON.stringify(tempDists)}`);
 | 
			
		||||
      log(
 | 
			
		||||
        LT.LOG,
 | 
			
		||||
        `Solved math is back ${JSON.stringify(data)} | ${JSON.stringify(returnData)} ${JSON.stringify(groupResults)} ${
 | 
			
		||||
          JSON.stringify(
 | 
			
		||||
            tempCounts,
 | 
			
		||||
          )
 | 
			
		||||
        } ${JSON.stringify(tempDists)}`,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    // Merge counts
 | 
			
		||||
    countDetails.push(...tempCounts);
 | 
			
		||||
    rollDists.push(...tempDists);
 | 
			
		||||
 | 
			
		||||
    // Handle merging group data into initConfig first since a group could "smuggle" a returnData in it
 | 
			
		||||
    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();
 | 
			
		||||
 | 
			
		||||
      const openIdx = tempInitConf.indexOf(openInternalGrp);
 | 
			
		||||
      const closeIdx = getMatchingInternalGrpIdx(tempInitConf, openIdx);
 | 
			
		||||
 | 
			
		||||
      // Take first groupResult out of array
 | 
			
		||||
      const dataToMerge = groupResults.shift();
 | 
			
		||||
 | 
			
		||||
      // Replace the found pair with the nested tempInitConfig and result
 | 
			
		||||
      tempInitConf.splice(openIdx, closeIdx - openIdx + 1, `${dataToMerge?.initConfig}`);
 | 
			
		||||
      loggingEnabled && log(LT.LOG, `Current tempInitConf state ${JSON.stringify(tempInitConf)}`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Handle merging returnData into tempData
 | 
			
		||||
    const initConf = data.initConfig.split(internalWrapRegex).filter((x) => x);
 | 
			
		||||
    loggingEnabled && log(LT.LOG, `Split solved math initConfig ${JSON.stringify(initConf)}`);
 | 
			
		||||
    const initConf = tempInitConf
 | 
			
		||||
      .join('')
 | 
			
		||||
      .split(internalWrapRegex)
 | 
			
		||||
      .filter((x) => x);
 | 
			
		||||
    loggingEnabled && log(LT.LOG, `Split tempInitConfig into initConf ${JSON.stringify(initConf)}`);
 | 
			
		||||
    while (initConf.includes(openInternal)) {
 | 
			
		||||
      loopCountCheck();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,112 @@
 | 
			
		|||
import { log, LogTypes as LT } from '@Log4Deno';
 | 
			
		||||
 | 
			
		||||
import { ReturnData } from 'artigen/artigen.d.ts';
 | 
			
		||||
 | 
			
		||||
import { CountDetails, RollDistributionMap, RollModifiers } from 'artigen/dice/dice.d.ts';
 | 
			
		||||
 | 
			
		||||
import { loopCountCheck } from 'artigen/managers/loopManager.ts';
 | 
			
		||||
 | 
			
		||||
import { tokenizeMath } from 'artigen/math/mathTokenizer.ts';
 | 
			
		||||
 | 
			
		||||
import { closeInternalGrp, openInternalGrp } from 'artigen/utils/escape.ts';
 | 
			
		||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
 | 
			
		||||
import { getMatchingGroupIdx } from 'artigen/utils/parenBalance.ts';
 | 
			
		||||
 | 
			
		||||
export const handleGroup = (
 | 
			
		||||
  groupParts: string[],
 | 
			
		||||
  groupModifiers: string,
 | 
			
		||||
  modifiers: RollModifiers,
 | 
			
		||||
  previousResults: number[],
 | 
			
		||||
): [ReturnData[], CountDetails[], RollDistributionMap[]] => {
 | 
			
		||||
  const returnData: ReturnData[] = [];
 | 
			
		||||
  const countDetails: CountDetails[] = [];
 | 
			
		||||
  const rollDists: RollDistributionMap[] = [];
 | 
			
		||||
 | 
			
		||||
  // Nested groups still exist, unwrap them
 | 
			
		||||
  while (groupParts.includes('{')) {
 | 
			
		||||
    loopCountCheck();
 | 
			
		||||
 | 
			
		||||
    loggingEnabled && log(LT.LOG, `Handling Nested Groups | Current cmd: ${JSON.stringify(groupParts)}`);
 | 
			
		||||
 | 
			
		||||
    const openIdx = groupParts.indexOf('}');
 | 
			
		||||
    const closeIdx = getMatchingGroupIdx(groupParts, openIdx);
 | 
			
		||||
 | 
			
		||||
    const currentGrp = groupParts.slice(openIdx + 1, closeIdx);
 | 
			
		||||
 | 
			
		||||
    const [tempData, tempCounts, tempDists] = handleGroup(currentGrp, '', modifiers, previousResults);
 | 
			
		||||
    const data = tempData[0];
 | 
			
		||||
    loggingEnabled && log(LT.LOG, `Solved Nested Group is back ${JSON.stringify(data)} | ${JSON.stringify(tempCounts)} ${JSON.stringify(tempDists)}`);
 | 
			
		||||
 | 
			
		||||
    countDetails.push(...tempCounts);
 | 
			
		||||
    rollDists.push(...tempDists);
 | 
			
		||||
 | 
			
		||||
    // Merge result back into groupParts
 | 
			
		||||
    groupParts.splice(openIdx, closeIdx - openIdx + 1, `${openInternalGrp}${data.rollTotal}${closeInternalGrp}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Handle the items in the groups
 | 
			
		||||
  const commaParts = groupParts
 | 
			
		||||
    .join('')
 | 
			
		||||
    .split(',')
 | 
			
		||||
    .filter((x) => x);
 | 
			
		||||
 | 
			
		||||
  if (commaParts.length > 1) {
 | 
			
		||||
    loggingEnabled && log(LT.LOG, `In multi-mode ${JSON.stringify(commaParts)} ${groupModifiers}`);
 | 
			
		||||
    // Handle "normal operation" of group
 | 
			
		||||
    const groupResults: ReturnData[] = [];
 | 
			
		||||
 | 
			
		||||
    for (const part of commaParts) {
 | 
			
		||||
      loopCountCheck();
 | 
			
		||||
 | 
			
		||||
      loggingEnabled && log(LT.LOG, `Solving commaPart: ${part}`);
 | 
			
		||||
      const [tempData, tempCounts, tempDists] = tokenizeMath(part, modifiers, previousResults, []);
 | 
			
		||||
      const data = tempData[0];
 | 
			
		||||
 | 
			
		||||
      loggingEnabled && log(LT.LOG, `Solved Math for Group is back ${JSON.stringify(data)} | ${JSON.stringify(tempCounts)} ${JSON.stringify(tempDists)}`);
 | 
			
		||||
 | 
			
		||||
      countDetails.push(...tempCounts);
 | 
			
		||||
      rollDists.push(...tempDists);
 | 
			
		||||
      groupResults.push(data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (groupModifiers) {
 | 
			
		||||
      // Handle the provided modifiers
 | 
			
		||||
    } else {
 | 
			
		||||
      // Sum mode
 | 
			
		||||
      const data = groupResults.reduce((prev, cur) => ({
 | 
			
		||||
        rollTotal: prev.rollTotal + cur.rollTotal,
 | 
			
		||||
        rollPreFormat: '',
 | 
			
		||||
        rollPostFormat: '',
 | 
			
		||||
        rollDetails: prev.rollDetails + ' + ' + cur.rollDetails,
 | 
			
		||||
        containsCrit: prev.containsCrit || cur.containsCrit,
 | 
			
		||||
        containsFail: prev.containsFail || cur.containsFail,
 | 
			
		||||
        initConfig: prev.initConfig + ', ' + cur.initConfig,
 | 
			
		||||
        isComplex: prev.isComplex || cur.isComplex,
 | 
			
		||||
      }));
 | 
			
		||||
      data.initConfig = `{${data.initConfig}}`;
 | 
			
		||||
      data.rollDetails = `{${data.rollDetails}}`;
 | 
			
		||||
      returnData.push(data);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    loggingEnabled && log(LT.LOG, `In single-mode ${JSON.stringify(commaParts)} ${groupModifiers}`);
 | 
			
		||||
    if (groupModifiers) {
 | 
			
		||||
      // Handle special case where the group modifiers are applied across the dice rolled
 | 
			
		||||
      // ex from roll20 docs: {4d6+3d8}k4 - Roll 4 d6's and 3 d8's, out of those 7 dice the highest 4 are kept and summed up.
 | 
			
		||||
    } else {
 | 
			
		||||
      // why did you put this in a group, that was entirely pointless
 | 
			
		||||
      loggingEnabled && log(LT.LOG, `Solving commaPart: ${commaParts[0]}`);
 | 
			
		||||
      const [tempData, tempCounts, tempDists] = tokenizeMath(commaParts[0], modifiers, previousResults, []);
 | 
			
		||||
      const data = tempData[0];
 | 
			
		||||
 | 
			
		||||
      loggingEnabled && log(LT.LOG, `Solved Math for Group is back ${JSON.stringify(data)} | ${JSON.stringify(tempCounts)} ${JSON.stringify(tempDists)}`);
 | 
			
		||||
 | 
			
		||||
      countDetails.push(...tempCounts);
 | 
			
		||||
      rollDists.push(...tempDists);
 | 
			
		||||
      data.initConfig = `{${data.initConfig}}`;
 | 
			
		||||
      data.rollDetails = `{${data.rollDetails}}`;
 | 
			
		||||
      returnData.push(data);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return [returnData, countDetails, rollDists];
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ import { loopCountCheck } from 'artigen/managers/loopManager.ts';
 | 
			
		|||
 | 
			
		||||
import { mathSolver } from 'artigen/math/mathSolver.ts';
 | 
			
		||||
 | 
			
		||||
import { cmdSplitRegex, internalWrapRegex } from 'artigen/utils/escape.ts';
 | 
			
		||||
import { closeInternalGrp, cmdSplitRegex, internalWrapRegex, openInternalGrp } from 'artigen/utils/escape.ts';
 | 
			
		||||
import { legalMathOperators } from 'artigen/utils/legalMath.ts';
 | 
			
		||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
 | 
			
		||||
import { assertParenBalance } from 'artigen/utils/parenBalance.ts';
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +20,12 @@ import { assertParenBalance } from 'artigen/utils/parenBalance.ts';
 | 
			
		|||
const minusOps = ['(', '^', '**', '*', '/', '%', '+', '-'];
 | 
			
		||||
const allOps = [...minusOps, ')'];
 | 
			
		||||
 | 
			
		||||
export const tokenizeMath = (cmd: string, modifiers: RollModifiers, previousResults: number[]): [ReturnData[], CountDetails[], RollDistributionMap[]] => {
 | 
			
		||||
export const tokenizeMath = (
 | 
			
		||||
  cmd: string,
 | 
			
		||||
  modifiers: RollModifiers,
 | 
			
		||||
  previousResults: number[],
 | 
			
		||||
  groupResults: ReturnData[],
 | 
			
		||||
): [ReturnData[], CountDetails[], RollDistributionMap[]] => {
 | 
			
		||||
  const countDetails: CountDetails[] = [];
 | 
			
		||||
  const rollDists: RollDistributionMap[] = [];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +59,18 @@ export const tokenizeMath = (cmd: string, modifiers: RollModifiers, previousResu
 | 
			
		|||
    } else if (mathConf[i] == parseFloat(curMathConfStr)) {
 | 
			
		||||
      // If its a number, parse the number out
 | 
			
		||||
      mathConf[i] = parseFloat(curMathConfStr);
 | 
			
		||||
    } else if (curMathConfStr.startsWith(openInternalGrp)) {
 | 
			
		||||
      const groupIdx = parseInt(curMathConfStr.substring(1, curMathConfStr.indexOf(closeInternalGrp)));
 | 
			
		||||
      if (groupIdx >= groupResults.length) {
 | 
			
		||||
        throw new Error('InternalGroupMachineBroke');
 | 
			
		||||
      }
 | 
			
		||||
      mathConf[i] = {
 | 
			
		||||
        total: groupResults[groupIdx].rollTotal,
 | 
			
		||||
        details: groupResults[groupIdx].rollDetails,
 | 
			
		||||
        containsCrit: groupResults[groupIdx].containsCrit,
 | 
			
		||||
        containsFail: groupResults[groupIdx].containsFail,
 | 
			
		||||
        isComplex: groupResults[groupIdx].isComplex,
 | 
			
		||||
      };
 | 
			
		||||
    } else if (curMathConfStr.toLowerCase() === 'e') {
 | 
			
		||||
      // If the operand is the constant e, create a SolvedStep for it
 | 
			
		||||
      mathConf[i] = {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,3 +30,8 @@ export const cmdSplitRegex = new RegExp(`(${escapePrefixPostfix(config.prefix)})
 | 
			
		|||
export const openInternal = '\u2045';
 | 
			
		||||
export const closeInternal = '\u2046';
 | 
			
		||||
export const internalWrapRegex = new RegExp(`([${openInternal}${closeInternal}])`, 'g');
 | 
			
		||||
 | 
			
		||||
// Internal Group is used for marking handled groups
 | 
			
		||||
export const openInternalGrp = '\u2e20';
 | 
			
		||||
export const closeInternalGrp = '\u2e21';
 | 
			
		||||
export const internalGrpWrapRegex = new RegExp(`([${openInternalGrp}${closeInternalGrp}])`, 'g');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,7 @@ import { loopCountCheck } from 'artigen/managers/loopManager.ts';
 | 
			
		|||
 | 
			
		||||
import { MathConf } from 'artigen/math/math.d.ts';
 | 
			
		||||
 | 
			
		||||
import { closeInternal, openInternal } from 'artigen/utils/escape.ts';
 | 
			
		||||
import { closeInternal, closeInternalGrp, openInternal, openInternalGrp } from 'artigen/utils/escape.ts';
 | 
			
		||||
import { loggingEnabled } from 'artigen/utils/logFlag.ts';
 | 
			
		||||
 | 
			
		||||
const checkBalance = (conf: MathConf[], openStr: string, closeStr: string, errorType: string, getMatching: boolean, openIdx: number): number => {
 | 
			
		||||
| 
						 | 
				
			
			@ -60,5 +60,6 @@ export const assertPrePostBalance = (conf: MathConf[]) => checkBalance(conf, con
 | 
			
		|||
// getMatchingXIdx gets the matching X, also partially verifies the conf has balanced X
 | 
			
		||||
export const getMatchingGroupIdx = (conf: MathConf[], openIdx: number): number => checkBalance(conf, '{', '}', 'Group', true, openIdx);
 | 
			
		||||
export const getMatchingInternalIdx = (conf: MathConf[], openIdx: number): number => checkBalance(conf, openInternal, closeInternal, 'Internal', true, openIdx);
 | 
			
		||||
export const getMatchingInternalGrpIdx = (conf: MathConf[], openIdx: number): number => checkBalance(conf, openInternalGrp, closeInternalGrp, 'InternalGrp', true, openIdx);
 | 
			
		||||
export const getMatchingParenIdx = (conf: MathConf[], openIdx: number): number => checkBalance(conf, '(', ')', 'Paren', true, openIdx);
 | 
			
		||||
export const getMatchingPostfixIdx = (conf: MathConf[], openIdx: number): number => checkBalance(conf, config.prefix, config.postfix, 'PrefixPostfix', true, openIdx);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue