dndbeyond_src/ddb_main/packages/gfs/lib/LoopResolutionCalculator.js
2025-05-28 15:36:51 -07:00

321 lines
14 KiB
JavaScript

var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
import pkg from 'lodash';
import { ExpressionOperators } from './types';
import { log, LOG_LEVEL } from './utils/log';
var get = pkg.get, set = pkg.set;
var ADDITIVE_IDENTITY = 0;
var MULTIPLICATIVE_IDENTITY = 1;
var ValueUnavailble = /** @class */ (function (_super) {
__extends(ValueUnavailble, _super);
function ValueUnavailble(message) {
return _super.call(this, message) || this;
}
return ValueUnavailble;
}(Error));
function add(state, operands) {
var sum = ADDITIVE_IDENTITY;
for (var _i = 0, operands_1 = operands; _i < operands_1.length; _i++) {
var operand = operands_1[_i];
var value = Number(run(state, operand));
log(LOG_LEVEL.INFO, ExpressionOperators.ADD + ": value " + value);
if (isNaN(value)) {
log(LOG_LEVEL.WARN, ExpressionOperators.ADD + ": This was not a number");
}
else {
sum += value;
}
}
log(LOG_LEVEL.INFO, ExpressionOperators.ADD + ": result " + sum);
return sum;
}
function multiply(state, operands) {
var product = MULTIPLICATIVE_IDENTITY;
for (var _i = 0, operands_2 = operands; _i < operands_2.length; _i++) {
var operand = operands_2[_i];
var value = Number(run(state, operand));
log(LOG_LEVEL.INFO, ExpressionOperators.ADD + ": value " + value);
if (isNaN(value)) {
log(LOG_LEVEL.WARN, ExpressionOperators.ADD + ": This was not a number");
}
else {
product *= value;
}
}
log(LOG_LEVEL.INFO, ExpressionOperators.MULTIPLY + ": result " + product);
return product;
}
function keepHighest() {
var rest = [];
for (var _i = 0; _i < arguments.length; _i++) {
rest[_i] = arguments[_i];
}
log(LOG_LEVEL.INFO, "KEEP HIGHEST: options " + String(rest));
var result = Math.max.apply(Math, rest.filter(function (x) { return !isNaN(x); }));
log(LOG_LEVEL.INFO, "KEEP HIGHEST: result " + result);
return result;
}
function min(state, operands) {
var options = operands.map(function (x) { return run(state, x); });
log(LOG_LEVEL.INFO, ExpressionOperators.MIN + ": options " + String(options));
var restult = Math.min.apply(Math, options);
log(LOG_LEVEL.INFO, ExpressionOperators.MIN + ": result " + restult);
return restult;
}
function max(state, operands) {
var options = operands.map(function (x) { return run(state, x); });
log(LOG_LEVEL.INFO, ExpressionOperators.MAX + ": options " + String(options));
var restult = Math.max.apply(Math, options);
log(LOG_LEVEL.INFO, ExpressionOperators.MAX + ": result " + restult);
return restult;
}
function getNestedValue(state, path) {
return get(state, path);
}
function setNestedValue(state, path, value) {
if (isNaN(value)) {
throw new Error("Value " + value + " is not a number");
}
set(state, path, value);
}
function dynamicValue(state, key) {
key = key.flatMap(function (x) { return x.split('.'); });
log(LOG_LEVEL.INFO, ExpressionOperators.DYNAMIC_VALUE + ": key " + key);
if (!getNestedValue(state, key)) {
throw new ValueUnavailble('key not available');
}
var result = getNestedValue(state, key);
log(LOG_LEVEL.INFO, ExpressionOperators.DYNAMIC_VALUE + ": result " + result);
return result;
}
function floor(state, operand) {
return Math.floor(run(state, operand[0]));
}
function run(state, operand) {
var result;
switch (operand.operator) {
case ExpressionOperators.STATIC_VALUE: {
var values = operand.values;
if (!Array.isArray(values)) {
throw new Error('STATIC_VALUE has to be an array');
}
if (!values[0]) {
throw new Error('STATIC_VALUE must have a value for index 0');
}
result = values[0];
break;
}
case ExpressionOperators.DYNAMIC_VALUE: {
var values = operand.values;
if (!Array.isArray(values) || values.some(function (v) { return typeof v !== 'string'; })) {
throw new Error('DYNAMIC_VALUE has to be array of string');
}
if (!values[0]) {
throw new Error('DYNAMIC_VALUE must have a value for index 0');
}
result = dynamicValue(state, values);
break;
}
case ExpressionOperators.ADD:
if (!Array.isArray(operand.operands)) {
throw new Error('ADD only supports an array');
}
result = add(state, operand.operands);
break;
case ExpressionOperators.MIN:
if (!Array.isArray(operand.operands)) {
throw new Error('MIN only supports an array');
}
result = min(state, operand.operands);
break;
case ExpressionOperators.MAX:
if (!Array.isArray(operand.operands)) {
throw new Error('MAX only supports an array');
}
result = max(state, operand.operands);
break;
case ExpressionOperators.FLOOR:
if (!Array.isArray(operand.operands)) {
throw new Error('FLOOR not correct format');
}
result = floor(state, operand.operands);
break;
case ExpressionOperators.MULTIPLY:
if (!Array.isArray(operand.operands)) {
throw new Error('MULTIPLY only supports an array');
}
result = multiply(state, operand.operands);
break;
default:
throw new Error("run: Unknown operator " + operand.operator);
}
log(LOG_LEVEL.INFO, "Performing " + operand.operator + " :> Result " + result);
return result;
}
function determineRequirements(operand) {
switch (operand.operator) {
case 'DYNAMIC_VALUE':
if (!operand.values) {
throw new Error('DYNAMIC_VALUE reqires values');
}
return [operand.values.join('.')];
case 'STATIC_VALUE':
return [];
case 'MAX':
case 'MIN':
case 'ADD':
case 'MULTIPLY':
case 'FLOOR':
if (!Array.isArray(operand.operands)) {
throw new Error('no operands array');
}
var dynamicValueKeys = [];
for (var _i = 0, _a = operand.operands; _i < _a.length; _i++) {
var childOperand = _a[_i];
dynamicValueKeys = dynamicValueKeys.concat(determineRequirements(childOperand));
}
return dynamicValueKeys;
default:
throw new Error("determineRequirements: Unknown operator " + operand.operator);
}
}
function getRequiremets(expression) {
if (!expression.operand) {
throw new Error('getRequiremets: no operands');
}
var requirements = determineRequirements(expression.operand);
return requirements.filter(function (x) { return x !== expression.target.join('.'); });
}
export function runProgram(_a) {
var _b;
var initState = _a.state, initExpressions = _a.expressions, initTargetResolutionPolicies = _a.targetResolutionPolicies, _c = _a.forceRun, forceRun = _c === void 0 ? false : _c;
var state = initState !== null && initState !== void 0 ? initState : {};
var expressions = initExpressions !== null && initExpressions !== void 0 ? initExpressions : [];
var targetResolutionPolicies = initTargetResolutionPolicies !== null && initTargetResolutionPolicies !== void 0 ? initTargetResolutionPolicies : {};
var reprocess = [];
var _loop_1 = function (expression) {
try {
var requirements_1 = getRequiremets(expression);
log(LOG_LEVEL.INFO, expression.target + " requires " + requirements_1);
var shouldWait = expressions.filter(function (x) { return x !== expression; }).some(function (x) { return requirements_1.includes(x.target.join('.')); }) ||
requirements_1.some(function (x) { return !getNestedValue(state, x.split('.')); });
log(LOG_LEVEL.ERROR, "Can process " + ((_b = expression.statementId) !== null && _b !== void 0 ? _b : 'id') + " -> " + expression.target + " :> " + (shouldWait ? 'Not yet' : 'Yes'));
if (!forceRun && shouldWait) {
log(LOG_LEVEL.WARN, "Forcing " + expression.target + " to process");
reprocess.push(expression);
}
else {
var result = run(state, expression.operand);
if (targetResolutionPolicies.hasOwnProperty(expression.target.join('.'))) {
switch (targetResolutionPolicies[expression.target.join('.')]) {
case 'KEEP_HIGHEST':
setNestedValue(state, expression.target, keepHighest(getNestedValue(state, expression.target), result));
break;
default:
throw new Error('resolution policy not implemented');
}
}
else {
log(LOG_LEVEL.INFO, "Applying " + expression.target + " = " + result);
setNestedValue(state, expression.target, result);
}
}
}
catch (err) {
if (err instanceof ValueUnavailble) {
reprocess.push(expression);
}
else {
}
}
};
for (var _i = 0, expressions_1 = expressions; _i < expressions_1.length; _i++) {
var expression = expressions_1[_i];
_loop_1(expression);
}
if (!forceRun && reprocess.length && reprocess.length === expressions.length) {
// throw new Error('cannot resolve'); // Tthis is a circular dependency so just run now
return runProgram({ state: state, expressions: reprocess, targetResolutionPolicies: targetResolutionPolicies, forceRun: true });
}
if (!forceRun && reprocess.length) {
return runProgram({ state: state, expressions: reprocess, targetResolutionPolicies: targetResolutionPolicies });
}
return state;
}
export function initProgram(_a) {
var _b;
var initState = _a.state, initExpressions = _a.expressions, initTargetResolutionPolicies = _a.targetResolutionPolicies;
var state = initState !== null && initState !== void 0 ? initState : {};
var statements = initExpressions !== null && initExpressions !== void 0 ? initExpressions : [];
var targetResolutionPolicies = initTargetResolutionPolicies !== null && initTargetResolutionPolicies !== void 0 ? initTargetResolutionPolicies : {};
//TODO: consider a init program to run once
// const sorted = expressions.sort((a: Statement, b: Statement) => (a?.precedence ?? 0) - (b?.precedence ?? 0));
var getSections = function () { return ({
noDynamicValues: [],
onlySelfDynamics: [],
complexDynamics: [],
}); };
var statementMap = {};
for (var i = 0; i < statements.length; i++) {
var statement = statements[i];
var reqs = determineRequirements(statement.operand);
statementMap[statement.precedence] = (_b = statementMap[statement.precedence]) !== null && _b !== void 0 ? _b : getSections();
var sections = statementMap[statement.precedence];
if (reqs.length === 0) {
sections.noDynamicValues.push(statement);
}
else if (reqs.length === 1 && reqs.includes(statement.target.join('.'))) {
sections.onlySelfDynamics.push(statement);
}
else if (reqs.length > 1 && reqs.includes(statement.target.join('.'))) {
sections.onlySelfDynamics.push(statement);
}
else {
sections.complexDynamics.push(statement);
}
}
// const sections = sorted.reduce(
// (sections, expression) => {
// const reqs = determineRequirements(expression.operand);
// if (reqs.length === 0) {
// sections.noDynamicValues.push(expression);
// } else if (reqs.length === 1 && reqs.includes(expression.target)) {
// sections.onlySelfDynamics.push(expression);
// } else {
// sections.complexDynamics.push(expression);
// }
// return sections;
// },
// {
// noDynamicValues: [] as Statement[],
// onlySelfDynamics: [] as Statement[],
// complexDynamics: [] as Statement[],
// },
// );
var precedences = Object.keys(statementMap).sort(function (a, b) { return Number(a) - Number(b); });
for (var i = 0; i < precedences.length; i++) {
var precedence = Number(precedences[i]);
var precedenceMap = statementMap[precedence];
state = runProgram({ state: state, expressions: precedenceMap.noDynamicValues, targetResolutionPolicies: targetResolutionPolicies });
state = runProgram({ state: state, expressions: precedenceMap.onlySelfDynamics, targetResolutionPolicies: targetResolutionPolicies });
state = runProgram({ state: state, expressions: precedenceMap.complexDynamics, targetResolutionPolicies: targetResolutionPolicies });
}
log(LOG_LEVEL.INFO, 'STATE:');
log(LOG_LEVEL.INFO, JSON.stringify(state, null, 2));
return state;
}
// runProgram(dynamicAddTest);