Skip to content

Instantly share code, notes, and snippets.

@TheNerdMan
Last active September 22, 2024 16:10
Show Gist options
  • Save TheNerdMan/2f50288de5c9a9fdccb303f54bef826a to your computer and use it in GitHub Desktop.
Save TheNerdMan/2f50288de5c9a9fdccb303f54bef826a to your computer and use it in GitHub Desktop.
A command night bot to output a random value but weight those values so some are rarer
/** @author=TheNerdMan */
/**
* Creates an array of shape [{outputStr: string, weight: number}]
*
* @param stringArray An array of strings, strings should be formated as such "\<String To Ouput\>, \<weight\>"
* an example is ["Foo, 0.5", "Bar, 0.25", "FooBar, 0.25"].
*
* Weights must add up to 1.0.
*/
function getWeights(stringArray /*: string[]*/) {
var recordArray = stringArray.map((c) => {
return { outputStr: c.split(",")[0]?.trim(), weight: c.split(",")[1]?.trim() }
});
validateWeights(recordArray);
return recordArray;
}
/**
* Makes sure weights add up to 1, throws a js error if not.
*
* @param mappedArray An array of shape [{outputStr: string, weight: number}],
*/
function validateWeights(mappedArray /*: [{outputStr: string, weight: number}]*/) {
// validate weights add up to 1.0
var weightSum = mappedArray
.map((c) => parseFloat(c.weight))
.reduce((a, b) => a + b, 0)
weightSum = parseFloat(weightSum.toFixed(1));
// throw error if weights don't add up to 1.0
if (weightSum !== 1.0) throw new Error("Weights must add up to 1.0");
}
/**
* Creates a "flat" array of ints that map to a index of a value from the weighted array.
*
* @param mappedArray An array of shape [{outputStr: string, weight: number}]
* @example
* [{ outputStr: "Foo", weight: 0.5 }, { outputStr: "Bar", weight: 0.25 }, { outputStr: "FooBar", weight: 0.25 }]
* outputs
* [1,1,1,1,2,2,3,3]
*/
function createFlatWeightedArray(mappedArray /*: [{outputStr: string, weight: number}]*/) {
// create a "flat" array of ints that map to a index of a value from the weighted array.
var flatWeightArray = [];
mappedArray.forEach((c) => {
var weight = parseFloat(c.weight);
var weightCount = Math.floor(weight * 10);
for (var i = 0; i < weightCount; i++) {
flatWeightArray.push(mappedArray.indexOf(c));
}
});
return flatWeightArray;
}
/**
* Gets the list from paste bin using api.allorigins.win to get around cors errors.
* If that service goes down then uh oh
*/
function retrieveListFromPasteBin(url /*: string*/) {
const request = new XMLHttpRequest();
request.open("GET", `https://api.allorigins.win/raw?url=${encodeURIComponent(url)}`, false); // `false` makes the request synchronous
request.send(null);
if (request.status === 200) {
console.log(request.responseText);
return request.responseText;
}
throw new Error(`Request Error: ${request.status}`);
}
function main(pasteBinUrl /*: string*/) {
if (!pasteBinUrl) throw new Error("Paste Bin Url missing");
// get the text
var pasteBinText = retrieveListFromPasteBin(pasteBinUrl);
// Remove line endings
var rawArray = pasteBinText.replace(/(\r\n|\n|\r)/gm, "").split(";");
// Drop the last item if user has put ; on every line
if (!rawArray[rawArray.length - 1]) rawArray.pop();
// Get the weights in a nice object
var weightedArray = getWeights(rawArray);
// now we do some maths to create an array of numbers, counting up to the amount of items we have
// but we add more numbers to make choosing a random number weighted to users needs.
// e.g.
// [{ outputStr: "Foo", weight: 0.5 }, { outputStr: "Bar", weight: 0.25 }, { outputStr: "FooBar", weight: 0.25 }]
// becomes
// [1,1,1,1,2,2,3,3]
var flatWeightArray = createFlatWeightedArray(weightedArray);
// now pick from that array, the answer will map to a value in the weighted array;
var weightedRandomIndex = flatWeightArray[Math.floor(Math.random() * flatWeightArray.length)]
var result = weightedArray[weightedRandomIndex];
return result.outputStr;
}
main(q)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment