UNPKG

lotto-draw

Version:

A simple tool used to pick random elements from a mutable collection of weighted participants

158 lines (157 loc) 7.96 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Lotto = void 0; var Participant_1 = require("./Participant"); var Utilities_1 = require("./Utilities"); /** * Represents a lotto consisting of a number of pickable ticket-holding participants. */ var Lotto = /** @class */ (function () { /** * Creates a new instance of Lotto. * @param customRandom The custom RNG to use in place of Math.random(). */ function Lotto(customRandom) { /** The array of participants that are holding tickets in the lotto. */ this._participants = []; this._customRandom = customRandom; } /** * Adds a participant with the specified number of tickets, or adds to the participant ticket count if the participant already holds tickets. * @param participant The participant to add or to increase the ticket count for if they already hold tickets. * @param tickets The number of tickets, defaults to 1. * @returns The Lotto instance. */ Lotto.prototype.add = function (participant, tickets) { if (tickets === void 0) { tickets = 1; } // Check that we have a valid ticket count. if (!(0, Utilities_1.isNaturalNumber)(tickets)) { throw new Error("tickets value must be a natural number"); } // Check whether this participant has already been added. var existingParticipant = this._participants.find(function (part) { return part.participant === participant; }); if (existingParticipant) { // The participant has already been added to the lotto so just add to their ticket count. existingParticipant.tickets += tickets; } else { // The participant is not part of the lotto so we should add them. this._participants.push(new Participant_1.Participant(participant, tickets)); } return this; }; /** * Removes the specified number of tickets for the given participant from the draw, or all tickets if a ticket number is not defined. * @param participant The participant to remove tickets for. * @param tickets The number of tickets to remove, or undefined if all tickets are to be removed. * @returns The Lotto instance. */ Lotto.prototype.remove = function (participant, tickets) { // Attempt to get the existing participant. var existingParticipant = this._participants.find(function (part) { return part.participant === participant; }); // There is nothing to do if the specified participant isn't even part of the lotto. if (!existingParticipant) { return this; } // Check whether a tickets value was given. if (tickets !== undefined) { // Check that we have a valid ticket count. if (!(0, Utilities_1.isNaturalNumber)(tickets)) { throw new Error("tickets value must be a natural number"); } existingParticipant.tickets -= tickets; // If the participant no longer holds any tickets then they should be removed. if (existingParticipant.tickets < 1) { this._participants = this._participants.filter(function (part) { return part !== existingParticipant; }); } } else { // We are removing all tickets for the participant so just remove them from the lotto. this._participants = this._participants.filter(function (part) { return part !== existingParticipant; }); } return this; }; /** * Draw a winning ticket and return the participant that holds the ticket. * @param options The draw options. * @returns The participant that holds the winning ticket. */ Lotto.prototype.draw = function (options) { if (options === void 0) { options = {}; } // If we have no participants then just return null. if (this._participants.length === 0) { return null; } var redrawable = (0, Utilities_1.isNullOrUndefined)(options.redrawable) ? true : options.redrawable; var pickable = []; this._participants.forEach(function (_a) { var participant = _a.participant, tickets = _a.tickets; for (var ticketCount = 0; ticketCount < tickets; ticketCount++) { pickable.push(participant); } }); var random; // We need a random floating-point number between 0 (inclusive) and 1 to scale up to pick our winner. // If a custom random function exists then we should use that or fall back to Math.random(). if (this._customRandom) { // Call our custom random function to get a random floating-point number. random = this._customRandom(); // Verify that the result of calling our custom random function is a number between 0 (inclusive) and 1. if (typeof random !== "number" || random < 0 || random >= 1) { throw new Error("the 'random' function provided did not return a number between 0 (inclusive) and 1"); } } else { // No custom random function was defined so just use good ol' Math.random(). random = Math.random(); } // Pick a winning participant. var winner = pickable[Math.floor(random * pickable.length)]; // If the ticket isn't redrawable then we should remove a ticket from the winning participants ticket count. if (!redrawable) { this.remove(winner, 1); } // Return the winning participant. return winner; }; /** * Draws multiple winning tickets and return an array of the participants that hold the winning tickets. * @param tickets The number of winning tickets to draw. * @param options The draw multiple options. * @returns An array of the participants that hold the winning tickets. */ Lotto.prototype.drawMultiple = function (tickets, options) { if (options === void 0) { options = {}; } var uniqueResults = (0, Utilities_1.isNullOrUndefined)(options.unique) ? false : options.unique; // Handle cases where the user has asked for zero tickets (no idea why they would do this be we should trust them). if (tickets === 0) { return []; } // Now that we know out tickets value is not zero we should check that it is a valid natural number. if (!(0, Utilities_1.isNaturalNumber)(tickets)) { throw new Error("tickets value must be a natural number"); } var result = []; // Keep drawing tickets until we either reach the number of required tickets or we simply run out of tickets to draw. // We can run out of tickets to draw if 'options.redrawable' is explicity 'false' or we just had no participants when 'drawMultiple' was called. while (result.length < tickets && this._participants.length > 0) { result.push(this.draw(options)); } // If the 'unique' draw option is set then we need to remove duplicates from the result list. if (uniqueResults) { // Create an array to store our unique results. var unique = []; // Iterate over all of our participants (with potential duplicates) and populate our array of unique values. for (var _i = 0, result_1 = result; _i < result_1.length; _i++) { var participant = result_1[_i]; if (unique.indexOf(participant) === -1) { unique.push(participant); } } result = unique; } return result; }; return Lotto; }()); exports.Lotto = Lotto;