Progress on action choices

This commit is contained in:
Thaum Rystra
2024-03-30 21:12:35 +02:00
parent 6c3d4b91eb
commit 6138be8083
33 changed files with 210 additions and 55 deletions

View File

@@ -8,7 +8,7 @@ import { loadCreature } from '/imports/api/engine/loadCreatures';
import EngineActions, { EngineAction } from '/imports/api/engine/action/EngineActions';
import applyAction from '/imports/api/engine/action/functions/applyAction';
import { LogContent, Mutation, Removal, Update } from '/imports/api/engine/action/tasks/TaskResult';
import inputProvider from '/imports/api/engine/action/functions/inputProviderForTests.testFn';
import inputProvider from './userInput/inputProviderForTests.testFn';
/**
* Removes all creatures, properties, and creatureVariable documents from the database
*/

View File

@@ -1,7 +1,8 @@
import { EngineAction } from '/imports/api/engine/action/EngineActions';
import { getSingleProperty } from '/imports/api/engine/loadCreatures';
import applyTask from '/imports/api/engine/action/tasks/applyTask'
import InputProvider from '/imports/api/engine/action/functions/InputProvider';
import InputProvider from '/imports/api/engine/action/functions/userInput/InputProvider';
import saveInputChoices from './userInput/saveInputChoices';
// TODO create a function to get the effective value of a property,
// simulating all the result updates in the action so far
@@ -17,6 +18,16 @@ export default async function applyAction(action: EngineAction, userInput: Input
if (!simulate && stepThrough) throw 'Cannot step through unless simulating';
if (simulate && !userInput) throw 'Must provide a function to get user input when simulating';
if (action._isSimulation || action._stepThrough) {
console.error('_isSimulation and _stepThrough should not be set on the action, rather call\
applyAction with the appropriate options');
}
// If we are simulating, save the user input choices
if (simulate) {
userInput = saveInputChoices(action, userInput);
}
action._stepThrough = stepThrough;
action._isSimulation = simulate;
action.taskCount = 0;

View File

@@ -4,7 +4,7 @@ import { getPropertyChildren, getSingleProperty } from '/imports/api/engine/load
import { EngineAction } from '/imports/api/engine/action/EngineActions';
import applyTask from '../tasks/applyTask';
import { PropTask } from '../tasks/Task';
import InputProvider from '/imports/api/engine/action/functions/InputProvider';
import InputProvider from '/imports/api/engine/action/functions/userInput/InputProvider';
/**
* Get all the child tasks of a given property

View File

@@ -9,7 +9,7 @@ import resolve from '/imports/parser/resolve';
import { getEffectiveActionScope } from '/imports/api/engine/action/functions/getEffectiveActionScope';
import { CalculatedField } from '/imports/api/properties/subSchemas/computedField';
import { ResolveLevel } from '/imports/parser/parseTree/NodeFactory';
import InputProvider from '/imports/api/engine/action/functions/InputProvider';
import InputProvider from '/imports/api/engine/action/functions/userInput/InputProvider';
import { EngineAction } from '/imports/api/engine/action/EngineActions';
// TODO Redo the work of

View File

@@ -3,7 +3,7 @@ import recalculateCalculation from './recalculateCalculation'
import { InlineCalculation } from '/imports/api/properties/subSchemas/inlineCalculationField';
import { EngineAction } from '/imports/api/engine/action/EngineActions';
import ResolveLevel from '/imports/parser/types/ResolveLevel';
import InputProvider from '/imports/api/engine/action/functions/InputProvider';
import InputProvider from '/imports/api/engine/action/functions/userInput/InputProvider';
export default async function recalculateInlineCalculations(
inlineCalcObj: InlineCalculation, action: EngineAction,

View File

@@ -30,7 +30,7 @@ type InputProvider = {
/**
* Get the details of a check or save
*/
check(suggestedParams: CheckParams): Promise<CheckParams>;
//check(suggestedParams: CheckParams): Promise<CheckParams>;
}
export type Advantage = 0 | 1 | -1;

View File

@@ -0,0 +1,28 @@
import Alea from 'alea';
/**
* Return a function that can be be used as InputProvider.rollDice
* this function instance must be used for the entire action
*/
export default function getDeterministicDiceRoller(
actionId: string
): (dice: { number: number, diceSize: number }[]) => Promise<number[][]> {
// Create a random number generator seeded on the ID of the action
if (!actionId) throw new Meteor.Error('Id Required', 'action ID can not be ' + actionId)
const randFrac = Alea(actionId);
return (dice) => {
const results: number[][] = [];
for (const diceRoll of dice) {
const values: number[] = [];
if (diceRoll.number > 100) {
throw new Meteor.Error('Too many dice', 'can only roll up to 100 dice at once');
}
for (let i = 0; i < diceRoll.number; i++) {
const rolledValue = ~~(randFrac() * diceRoll.diceSize) + 1
values.push(rolledValue);
}
results.push(values);
}
return Promise.resolve(results);
}
}

View File

@@ -0,0 +1,26 @@
import InputProvider from '/imports/api/engine/action/functions/userInput/InputProvider';
import getDeterministicDiceRoller from '/imports/api/engine/action/functions/userInput/getDeterministicDiceRoller';
// This assumes the user's choices are in exactly the order they will be requested
// Dice rolls are done fresh, no cheating
export default function getReplayChoicesInputProvider(actionId: string, decisions: any[]):
InputProvider {
const dRoller = getDeterministicDiceRoller(actionId);
const replaySavedInput: InputProvider = {
nextStep() {
return Promise.resolve();
},
// To roll dice, ignore the user and use the deterministic dice roller again
rollDice(dice) {
decisions.pop();
return dRoller(dice);
},
choose() {
return Promise.resolve(decisions.pop());
},
advantage() {
return Promise.resolve(decisions.pop());
}
}
return replaySavedInput;
}

View File

@@ -1,4 +1,4 @@
import InputProvider from '/imports/api/engine/action/functions/InputProvider';
import InputProvider from '/imports/api/engine/action/functions/userInput/InputProvider';
const inputProviderForTests: InputProvider = {
/**

View File

@@ -0,0 +1,28 @@
import { EngineAction } from '/imports/api/engine/action/EngineActions';
import InputProvider from '/imports/api/engine/action/functions/userInput/InputProvider';
/**
* Create a new version of the user input function, that saves the user's choices to an array
* before returning them
*/
export default function saveInputChoices(action: EngineAction, userInput: InputProvider): InputProvider {
const newInputProvider: Partial<InputProvider> = {};
if (!action._choices) {
action._choices = [];
}
// For every function in the given input provider
for (const key in userInput) {
const oldFn = userInput[key];
// Make a new function that does the same thing, but saves the result to action._choices
const newFn = (...args) => {
const result = oldFn(...args);
action._choices.push(result);
return result;
}
newInputProvider[key] = newFn;
}
return newInputProvider as InputProvider;
}