@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
148 lines (115 loc) • 4.56 kB
JavaScript
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;
});
}
}