UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

148 lines (115 loc) 4.56 kB
import { assert } from "../../../core/assert.js"; import { array_push_if_unique } from "../../../core/collection/array/array_push_if_unique.js"; import { buildPromiseChain } from "../../../core/process/buildPromiseChain.js"; import { ResourceAllocationSolver } from "./ResourceAllocationSolver.js"; /** * @template A * @example * const allocator = new StrategicResourceAllocator(); * * allocator.addTacticalModule(module); // add some tactical modules * * const resources = [new Resource(1, "a")]; * const actions = await allocator.allocate(resources); // list of actions that use resources * @author Alex Goldring * @copyright Company Named Limited (c) 2025 */ export class StrategicResourceAllocator { /** * * @type {TacticalModule[]} */ modules = []; /** * @private * @type {Array<function(ActionSequence[]):Promise<ActionSequence[]>>} */ sequenceTransformers = []; /** * Add a sequence transformer, these are applied as a final step during resource allocation * @param {function(ActionSequence[]):Promise<ActionSequence[]>} t */ addTransformer(t) { assert.isFunction(t, 't'); this.sequenceTransformers.push(t); } /** * * @param {TacticalModule} module * @returns {boolean} */ addTacticalModule(module) { assert.defined(module, 'module'); assert.notNull(module, 'module'); assert.equal(module.isTacticalModule, true, 'module.TacticalModule !== true'); return array_push_if_unique(this.modules, module); } /** * @template A * @param {Resource[]} resources * @returns {Promise<A[]>} actions */ allocate(resources) { assert.isArray(resources, 'resources'); /** * * @type {Map<ResourceAllocationBid, TacticalModule>} */ const bids = new Map(); return new Promise((resolve, reject) => { const moduleResults = this.modules.map(m => { const promise = m.collectBids(resources); assert.defined(promise, 'promise'); assert.notNull(promise, 'promise'); assert.isFunction(promise.then, "promise.then"); promise.then(moduleBids => { assert.isArray(moduleBids, 'moduleBids'); moduleBids.forEach(b => bids.set(b, m)); }); return promise; }); resolve(moduleResults); }) .then(moduleResults => Promise.all(moduleResults)) .then(() => { /** * * @type {ResourceAllocationSolver} */ const solver = new ResourceAllocationSolver(); //set resources solver.addResources(resources); //set bids bids.forEach((m, b) => solver.addBid(b)); /** * * @type {ResourceAllocationBid[]} */ const allocations = solver.solve(); const actionSequences = allocations.map(a => a.actions); //sort action sequences based on their priorities, higher priority sequences first actionSequences.sort((a, b) => { const aPriority = a.getPriority(); const bPriority = b.getPriority(); return bPriority - aPriority }); return actionSequences; }) .then(sequences => { // Apply sequence transformers const sequenceTransformers = this.sequenceTransformers; const nSequenceTransformers = sequenceTransformers.length; const factories = []; for (let i = 0; i < nSequenceTransformers; i++) { const sequenceProcessor = sequenceTransformers[i]; factories.push(input => sequenceProcessor(input)); } return buildPromiseChain({ factories, head: Promise.resolve(sequences) }); }) .then(sequences => { //extract actions from sequences in order const actions = sequences.map(s => s.actions).flat(); return actions; }); } }