0
0
mirror of https://github.com/sp-tarkov/server.git synced 2025-02-13 09:50:43 -05:00
server/project/src/helpers/WeightedRandomHelper.ts
Refringe 4ac12ef70a Formatting/Linting Changes (!168)
These are the formatting & linting configuration changes from the `3.8.0` branch and the changes that they make to the overall project.

The majority of these changes are from running two commands:

`npm run lint:fix`
`npm run style:fix`

This has already been run on the `3.8.0` branch and this PR should make `master` play nicer when it comes to merges going forward.

There are now four VSCode plugins recommended for server development. They've been added to the workspace file and a user should get a UI notification when the workspace is opened if they're not installed.

The four plugins are:
https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig
https://marketplace.visualstudio.com/items?itemName=dprint.dprint
https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint
https://marketplace.visualstudio.com/items?itemName=biomejs.biome

Once installed they should just work within the workspace.

Also, be sure to `npm i` to get the new dprint application.

Co-authored-by: Refringe <brownelltyler@gmail.com>
Reviewed-on: SPT-AKI/Server#168
2023-11-16 21:42:06 +00:00

85 lines
2.9 KiB
TypeScript

import { injectable } from "tsyringe";
@injectable()
export class WeightedRandomHelper
{
/**
* @deprecated USE getWeightedValue() WHERE POSSIBLE
* Gets a tplId from a weighted dictionary
* @param {tplId: weighting[]} itemArray
* @returns tplId
*/
public getWeightedInventoryItem(itemArray: { [tplId: string]: unknown; } | ArrayLike<unknown>): string
{
const itemKeys = Object.keys(itemArray);
const weights = Object.values(itemArray);
const chosenItem = this.weightedRandom(itemKeys, weights);
return chosenItem.item;
}
public getWeightedValue<T>(itemArray: { [key: string]: unknown; } | ArrayLike<unknown>): T
{
const itemKeys = Object.keys(itemArray);
const weights = Object.values(itemArray);
const chosenItem = this.weightedRandom(itemKeys, weights);
return chosenItem.item;
}
/**
* Picks the random item based on its weight.
* The items with higher weight will be picked more often (with a higher probability).
*
* For example:
* - items = ['banana', 'orange', 'apple']
* - weights = [0, 0.2, 0.8]
* - weightedRandom(items, weights) in 80% of cases will return 'apple', in 20% of cases will return
* 'orange' and it will never return 'banana' (because probability of picking the banana is 0%)
*
* @param {any[]} items
* @param {number[]} weights
* @returns {{item: any, index: number}}
*/
public weightedRandom(items: string | any[], weights: string | any[]): { item: any; index: number; }
{
if (items.length !== weights.length)
{
throw new Error("Items and weight inputs must be of the same length");
}
if (!items.length)
{
throw new Error("Items must not be empty");
}
// Preparing the cumulative weights array.
// For example:
// - weights = [1, 4, 3]
// - cumulativeWeights = [1, 5, 8]
const cumulativeWeights = [];
for (let i = 0; i < weights.length; i += 1)
{
cumulativeWeights[i] = weights[i] + (cumulativeWeights[i - 1] || 0);
}
// Getting the random number in a range of [0...sum(weights)]
// For example:
// - weights = [1, 4, 3]
// - maxCumulativeWeight = 8
// - range for the random number is [0...8]
const maxCumulativeWeight = cumulativeWeights[cumulativeWeights.length - 1];
const randomNumber = maxCumulativeWeight * Math.random();
// Picking the random item based on its weight.
// The items with higher weight will be picked more often.
for (let itemIndex = 0; itemIndex < items.length; itemIndex += 1)
{
if (cumulativeWeights[itemIndex] >= randomNumber)
{
return { item: items[itemIndex], index: itemIndex };
}
}
}
}