Began implementing dice rolls in the maths parser

This commit is contained in:
Stefan Zermatten
2020-06-30 14:40:20 +02:00
parent 56f9e82326
commit 7be4280508
5 changed files with 116 additions and 7 deletions

View File

@@ -1,4 +1,4 @@
import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js';
import evaluateAndRollString from '/imports/api/creature/computation/afterComputation/evaluateAndRollString.js';
export default function applyDamage({
prop,
@@ -11,9 +11,9 @@ export default function applyDamage({
...creature.variables,
...actionContext,
};
let {result, errors} = evaluateString(prop.amount, scope);
if (Meteor.isClient) errors.forEach(e => console.error(e));
if (Number.isFinite(result)) {
damageTargets.forEach()
let {result, errors} = evaluateAndRollString(prop.amount, scope);
if (Meteor.isClient){
errors.forEach(e => console.error(e));
console.log(result);
}
}

View File

@@ -1,5 +1,5 @@
import applyAction from '/imports/api/creature/actions/applyAction.js';
//import applyDamage from '/imports/api/creature/actions/applyDamage.js';
import applyDamage from '/imports/api/creature/actions/applyDamage.js';
import applyBuff from '/imports/api/creature/actions/applyBuff.js';
function applyProperty(options){
@@ -19,7 +19,7 @@ function applyProperty(options){
applyAction(options);
return true;
case 'damage':
// applyDamage(options);
applyDamage(options);
return true;
case 'adjustment':
// applyAdjustment(options);

View File

@@ -0,0 +1,77 @@
import math from '/imports/math.js';
import bareSymbolSubtitutor from '/imports/api/creature/computation/utility/bareSymbolSubtitutor.js';
import substituteRollsWithFunctions from '/imports/api/creature/computation/afterComputation/substituteRollsWithFunctions.js'
export default function evaluateAndRollString(string, scope){
let errors = [];
if (!string){
errors.push('No string provided');
return {result: string, errors};
}
if (!scope) errors.push('No scope provided');
// Parse the string using mathjs
let calc;
try {
calc = math.parse(string);
} catch (e) {
errors.push(e);
return {result: string, errors};
}
// Replace all bare symbols with symbol.value
let transformedCalc = calc.transform(bareSymbolSubtitutor(scope));
// Replace all rolls with the function to call them
transformedCalc = calc.transform(substituteRollsWithFunctions);
// Evaluate the expression to a number or return with substitutions
try {
let result = transformedCalc.evaluate(scope);
return {result, errors};
} catch (e1){
errors.push(e1);
try {
let result = simplifyWithAccessors(transformedCalc, scope).toHTML();
return {result, errors};
} catch (e2){
errors.push(e2);
return {result: transformedCalc.toHTML(), errors};
}
}
}
function simplifyWithAccessors(calc, scope){
let noAccessorCalc = calc.transform(substituteAccessors(scope));
return math.simplify(noAccessorCalc);
}
// returns a function to replace all accessors with either their resolved value
// or a symbol to simplify with
function substituteAccessors(scope){
return function(node){
if (node.isAccessorNode){
try {
return evaluateAccessor(node, scope);
} catch (e) {
return replaceAccessorWithSymbol(node);
}
} else {
return node;
}
}
}
// Throws error if symbol is undefined in scope
function evaluateAccessor(node, scope){
let value = node.evaluate(scope);
if (value === undefined){
throw 'Undefined symbol'
}
return new math.ConstantNode(value);
}
function replaceAccessorWithSymbol(node){
let symbolNode = new math.SymbolNode(node.toString());
return symbolNode;
}

View File

@@ -0,0 +1,20 @@
import math from '/imports/math.js';
const diceRegex = /d\d+/;
export default function substituteRollsWithFunctions(node){
// TODO also replace dx as 1dx
if (
node.isOperatorNode &&
node.fn === 'multiply' &&
node.implicit &&
node.args[1].isSymbolNode &&
diceRegex.test(node.args[1].name)
){
let diceSize = node.args[1].name.slice(1);
let diceSizeNode = new math.ConstantNode(diceSize);
return new math.FunctionNode('roll', [node.args[0], diceSizeNode]);
} else {
return node;
}
}

View File

@@ -1,8 +1,20 @@
import { create, all } from 'mathjs';
import { Random } from 'meteor/random'
const math = create(all);
math.import({
'if': function(pred, a, b) {
return pred ? a : b;
},
'roll': function(number, diceSize){
if (number > 100) throw 'Can only roll 100 dice at a time';
let rollTotal = 0;
let i, roll;
for (i = 0; i < number; i++){
roll = ~~(Random.fraction() * diceSize) + 1
rollTotal += roll;
}
return rollTotal;
}
});