From 175af0bd28cf33b9ff33579785d6d2ab8686f57a Mon Sep 17 00:00:00 2001 From: catloversg <152669316+catloversg@users.noreply.github.com> Date: Sun, 19 May 2024 05:12:06 +0700 Subject: [PATCH] BUGFIX: Improve implementation of getRandomInt (#1282) --- src/Bladeburner/Actions/LevelableAction.ts | 6 +- src/Bladeburner/Bladeburner.ts | 36 ++++----- src/Bladeburner/City.ts | 9 ++- src/Bladeburner/data/Contracts.ts | 8 +- src/Bladeburner/data/Operations.ts | 14 ++-- src/CodingContractGenerator.ts | 29 ++++--- src/Corporation/Actions.ts | 4 +- src/Corporation/Division.ts | 6 +- src/Corporation/OfficeSpace.ts | 20 +++-- src/Corporation/Product.ts | 4 +- src/Gang/Gang.ts | 4 +- src/PersonObjects/Player/PlayerObject.ts | 4 +- src/Server/AllServers.ts | 4 +- src/StockMarket/Stock.ts | 6 +- src/StockMarket/StockMarket.tsx | 4 +- src/data/codingcontracttypes.ts | 92 +++++++++++----------- src/utils/EnumHelper.ts | 4 +- src/utils/helpers/getRandomByte.ts | 4 +- src/utils/helpers/getRandomInt.ts | 11 --- src/utils/helpers/getRandomIntInclusive.ts | 21 +++++ 20 files changed, 160 insertions(+), 130 deletions(-) delete mode 100644 src/utils/helpers/getRandomInt.ts create mode 100644 src/utils/helpers/getRandomIntInclusive.ts diff --git a/src/Bladeburner/Actions/LevelableAction.ts b/src/Bladeburner/Actions/LevelableAction.ts index d84cfe618..8b5215122 100644 --- a/src/Bladeburner/Actions/LevelableAction.ts +++ b/src/Bladeburner/Actions/LevelableAction.ts @@ -3,7 +3,7 @@ import type { IReviverValue } from "../../utils/JSONReviver"; import type { Availability } from "../Types"; import { ActionClass, ActionParams } from "./Action"; -import { getRandomInt } from "../../utils/helpers/getRandomInt"; +import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive"; import { clampInteger } from "../../utils/helpers/clampNumber"; export type LevelableActionParams = ActionParams & { @@ -37,7 +37,7 @@ export abstract class LevelableActionClass extends ActionClass { if (params.maxCount) this.maxCount = params.maxCount; if (params.difficultyFac) this.difficultyFac = params.difficultyFac; if (params.rewardFac) this.rewardFac = params.rewardFac; - this.count = getRandomInt(this.minCount, this.maxCount); + this.count = getRandomIntInclusive(this.minCount, this.maxCount); this.growthFunction = params.growthFunction; } @@ -65,7 +65,7 @@ export abstract class LevelableActionClass extends ActionClass { /** Reset a levelable action's tracked stats */ reset() { - this.count = getRandomInt(this.minCount, this.maxCount); + this.count = getRandomIntInclusive(this.minCount, this.maxCount); this.level = 1; this.maxLevel = 1; this.autoLevel = true; diff --git a/src/Bladeburner/Bladeburner.ts b/src/Bladeburner/Bladeburner.ts index 4b65ac0a9..ebbb3a240 100644 --- a/src/Bladeburner/Bladeburner.ts +++ b/src/Bladeburner/Bladeburner.ts @@ -24,7 +24,7 @@ import { Player } from "@player"; import { Router } from "../ui/GameRoot"; import { ConsoleHelpText } from "./data/Help"; import { exceptionAlert } from "../utils/helpers/exceptionAlert"; -import { getRandomInt } from "../utils/helpers/getRandomInt"; +import { getRandomIntInclusive } from "../utils/helpers/getRandomIntInclusive"; import { BladeburnerConstants } from "./data/Constants"; import { formatExp, formatMoney, formatPercent, formatBigNumber, formatStamina } from "../ui/formatNumber"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers"; @@ -64,7 +64,7 @@ export class Bladeburner { storedCycles = 0; - randomEventCounter: number = getRandomInt(240, 600); + randomEventCounter: number = getRandomIntInclusive(240, 600); actionTimeToComplete = 0; actionTimeCurrent = 0; @@ -522,11 +522,11 @@ export class Bladeburner { const sourceCity = this.cities[sourceCityName]; const rand = Math.random(); - let percentage = getRandomInt(3, 15) / 100; + let percentage = getRandomIntInclusive(3, 15) / 100; if (rand < 0.05 && sourceCity.comms > 0) { // 5% chance for community migration - percentage *= getRandomInt(2, 4); // Migration increases population change + percentage *= getRandomIntInclusive(2, 4); // Migration increases population change --sourceCity.comms; ++destCity.comms; } @@ -565,7 +565,7 @@ export class Bladeburner { if (chance <= 0.05) { // New Synthoid Community, 5% ++sourceCity.comms; - const percentage = getRandomInt(10, 20) / 100; + const percentage = getRandomIntInclusive(10, 20) / 100; const count = Math.round(sourceCity.pop * percentage); sourceCity.pop += count; if (sourceCity.pop < BladeburnerConstants.PopGrowthCeiling) { @@ -579,7 +579,7 @@ export class Bladeburner { if (sourceCity.comms <= 0) { // If no comms in source city, then instead trigger a new Synthoid community event ++sourceCity.comms; - const percentage = getRandomInt(10, 20) / 100; + const percentage = getRandomIntInclusive(10, 20) / 100; const count = Math.round(sourceCity.pop * percentage); sourceCity.pop += count; if (sourceCity.pop < BladeburnerConstants.PopGrowthCeiling) { @@ -593,7 +593,7 @@ export class Bladeburner { ++destCity.comms; // Change pop - const percentage = getRandomInt(10, 20) / 100; + const percentage = getRandomIntInclusive(10, 20) / 100; const count = Math.round(sourceCity.pop * percentage); sourceCity.pop -= count; destCity.pop += count; @@ -608,7 +608,7 @@ export class Bladeburner { } } else if (chance <= 0.3) { // New Synthoids (non community), 20% - const percentage = getRandomInt(8, 24) / 100; + const percentage = getRandomIntInclusive(8, 24) / 100; const count = Math.round(sourceCity.pop * percentage); sourceCity.pop += count; if (sourceCity.pop < BladeburnerConstants.PopGrowthCeiling) { @@ -632,13 +632,13 @@ export class Bladeburner { } else if (chance <= 0.7) { // Synthoid Riots (+chaos), 20% sourceCity.chaos += 1; - sourceCity.chaos *= 1 + getRandomInt(5, 20) / 100; + sourceCity.chaos *= 1 + getRandomIntInclusive(5, 20) / 100; if (this.logging.events) { this.log("Tensions between Synthoids and humans lead to riots in " + sourceCityName + "! Chaos increased"); } } else if (chance <= 0.9) { // Less Synthoids, 20% - const percentage = getRandomInt(8, 20) / 100; + const percentage = getRandomIntInclusive(8, 20) / 100; const count = Math.round(sourceCity.pop * percentage); sourceCity.pop -= count; if (this.logging.events) { @@ -753,7 +753,7 @@ export class Bladeburner { const teamCount = action.teamCount; if (teamCount >= 1) { const maxLosses = success ? Math.ceil(teamCount / 2) : Math.floor(teamCount); - const losses = getRandomInt(0, maxLosses); + const losses = getRandomIntInclusive(0, maxLosses); this.teamSize -= losses; if (this.teamSize < this.sleeveSize) { const sup = Player.sleeves.filter((x) => isSleeveSupportWork(x.currentWork)); @@ -803,13 +803,13 @@ export class Bladeburner { }); --city.comms; } else { - const change = getRandomInt(-10, -5) / 10; + const change = getRandomIntInclusive(-10, -5) / 10; city.changePopulationByPercentage(change, { nonZero: true, changeEstEqually: false, }); } - city.changeChaosByPercentage(getRandomInt(1, 5)); + city.changeChaosByPercentage(getRandomIntInclusive(1, 5)); break; case BladeOperationName.stealthRetirement: if (success) { @@ -818,13 +818,13 @@ export class Bladeburner { nonZero: true, }); } - city.changeChaosByPercentage(getRandomInt(-3, -1)); + city.changeChaosByPercentage(getRandomIntInclusive(-3, -1)); break; case BladeOperationName.assassination: if (success) { city.changePopulationByCount(-1, { estChange: -1, estOffset: 0 }); } - city.changeChaosByPercentage(getRandomInt(-5, 5)); + city.changeChaosByPercentage(getRandomIntInclusive(-5, 5)); break; default: throw new Error("Invalid Action name in completeOperation: " + this.action.name); @@ -838,7 +838,7 @@ export class Bladeburner { case BladeContractName.tracking: // Increase estimate accuracy by a relatively small amount city.improvePopulationEstimateByCount( - getRandomInt(100, 1e3) * this.getSkillMult(BladeMultName.successChanceEstimate), + getRandomIntInclusive(100, 1e3) * this.getSkillMult(BladeMultName.successChanceEstimate), ); break; case BladeContractName.bountyHunter: @@ -1012,7 +1012,7 @@ export class Bladeburner { // Calculate team losses if (teamCount >= 1) { - const losses = getRandomInt(1, teamLossMax); + const losses = getRandomIntInclusive(1, teamLossMax); this.teamSize -= losses; if (this.teamSize < this.sleeveSize) { const sup = Player.sleeves.filter((x) => isSleeveSupportWork(x.currentWork)); @@ -1332,7 +1332,7 @@ export class Bladeburner { if (this.randomEventCounter <= 0) { this.randomEvent(); // Add instead of setting because we might have gone over the required time for the event - this.randomEventCounter += getRandomInt(240, 600); + this.randomEventCounter += getRandomIntInclusive(240, 600); } this.processAction(seconds); diff --git a/src/Bladeburner/City.ts b/src/Bladeburner/City.ts index 8d31045b8..6de723ea5 100644 --- a/src/Bladeburner/City.ts +++ b/src/Bladeburner/City.ts @@ -1,6 +1,6 @@ import { CityName } from "@enums"; import { BladeburnerConstants } from "./data/Constants"; -import { getRandomInt } from "../utils/helpers/getRandomInt"; +import { getRandomIntInclusive } from "../utils/helpers/getRandomIntInclusive"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver"; import { addOffset } from "../utils/helpers/addOffset"; import { clampInteger, clampNumber } from "../utils/helpers/clampNumber"; @@ -16,11 +16,14 @@ export class City { this.name = name; // Synthoid population and estimate - this.pop = getRandomInt(BladeburnerConstants.PopulationThreshold, 1.5 * BladeburnerConstants.PopulationThreshold); + this.pop = getRandomIntInclusive( + BladeburnerConstants.PopulationThreshold, + 1.5 * BladeburnerConstants.PopulationThreshold, + ); this.popEst = this.pop * (Math.random() + 0.5); // Number of Synthoid communities population and estimate - this.comms = getRandomInt(5, 150); + this.comms = getRandomIntInclusive(5, 150); this.chaos = 0; } diff --git a/src/Bladeburner/data/Contracts.ts b/src/Bladeburner/data/Contracts.ts index 4c96c03a5..e6a71113a 100644 --- a/src/Bladeburner/data/Contracts.ts +++ b/src/Bladeburner/data/Contracts.ts @@ -1,6 +1,6 @@ import { BladeContractName } from "@enums"; import { Contract } from "../Actions/Contract"; -import { getRandomInt } from "../../utils/helpers/getRandomInt"; +import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive"; import { assertLoadingType } from "../../utils/TypeAssertion"; export function createContracts(): Record { @@ -36,7 +36,7 @@ export function createContracts(): Record { intelligence: 1, }, isStealth: true, - growthFunction: () => getRandomInt(5, 75) / 10, + growthFunction: () => getRandomIntInclusive(5, 75) / 10, minCount: 25, }), [BladeContractName.bountyHunter]: new Contract({ @@ -69,7 +69,7 @@ export function createContracts(): Record { intelligence: 0.9, }, isKill: true, - growthFunction: () => getRandomInt(5, 75) / 10, + growthFunction: () => getRandomIntInclusive(5, 75) / 10, minCount: 5, }), [BladeContractName.retirement]: new Contract({ @@ -102,7 +102,7 @@ export function createContracts(): Record { intelligence: 0.9, }, isKill: true, - growthFunction: () => getRandomInt(5, 75) / 10, + growthFunction: () => getRandomIntInclusive(5, 75) / 10, minCount: 5, }), }; diff --git a/src/Bladeburner/data/Operations.ts b/src/Bladeburner/data/Operations.ts index 61bfdfdd9..72d4e6513 100644 --- a/src/Bladeburner/data/Operations.ts +++ b/src/Bladeburner/data/Operations.ts @@ -1,6 +1,6 @@ import { BladeOperationName } from "@enums"; import { Operation } from "../Actions/Operation"; -import { getRandomInt } from "../../utils/helpers/getRandomInt"; +import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive"; import { LevelableActionClass } from "../Actions/LevelableAction"; import { assertLoadingType } from "../../utils/TypeAssertion"; @@ -36,7 +36,7 @@ export function createOperations(): Record { intelligence: 0.9, }, isStealth: true, - growthFunction: () => getRandomInt(10, 40) / 10, + growthFunction: () => getRandomIntInclusive(10, 40) / 10, maxCount: 100, }), [BladeOperationName.undercover]: new Operation({ @@ -69,7 +69,7 @@ export function createOperations(): Record { intelligence: 0.9, }, isStealth: true, - growthFunction: () => getRandomInt(10, 40) / 10, + growthFunction: () => getRandomIntInclusive(10, 40) / 10, maxCount: 100, }), [BladeOperationName.sting]: new Operation({ @@ -100,7 +100,7 @@ export function createOperations(): Record { intelligence: 0.9, }, isStealth: true, - growthFunction: () => getRandomInt(3, 40) / 10, + growthFunction: () => getRandomIntInclusive(3, 40) / 10, }), [BladeOperationName.raid]: new Operation({ name: BladeOperationName.raid, @@ -132,7 +132,7 @@ export function createOperations(): Record { intelligence: 0.9, }, isKill: true, - growthFunction: () => getRandomInt(2, 40) / 10, + growthFunction: () => getRandomIntInclusive(2, 40) / 10, getAvailability: function (bladeburner) { if (bladeburner.getCurrentCity().comms < 1) return { error: "No Synthoid communities in current city" }; return LevelableActionClass.prototype.getAvailability.call(this, bladeburner); @@ -169,7 +169,7 @@ export function createOperations(): Record { }, isStealth: true, isKill: true, - growthFunction: () => getRandomInt(1, 20) / 10, + growthFunction: () => getRandomIntInclusive(1, 20) / 10, }), [BladeOperationName.assassination]: new Operation({ name: BladeOperationName.assassination, @@ -202,7 +202,7 @@ export function createOperations(): Record { }, isStealth: true, isKill: true, - growthFunction: () => getRandomInt(1, 20) / 10, + growthFunction: () => getRandomIntInclusive(1, 20) / 10, }), }; } diff --git a/src/CodingContractGenerator.ts b/src/CodingContractGenerator.ts index a346217bc..8081ab25a 100644 --- a/src/CodingContractGenerator.ts +++ b/src/CodingContractGenerator.ts @@ -12,7 +12,7 @@ import { SpecialServers } from "./Server/data/SpecialServers"; import { Server } from "./Server/Server"; import { BaseServer } from "./Server/BaseServer"; -import { getRandomInt } from "./utils/helpers/getRandomInt"; +import { getRandomIntInclusive } from "./utils/helpers/getRandomIntInclusive"; import { ContractFilePath, resolveContractFilePath } from "./Paths/ContractFilePath"; export function generateRandomContract(): void { @@ -121,7 +121,7 @@ function sanitizeRewardType(rewardType: CodingContractRewardType): CodingContrac function getRandomProblemType(): string { const problemTypes = Object.keys(CodingContractTypes); - const randIndex = getRandomInt(0, problemTypes.length - 1); + const randIndex = getRandomIntInclusive(0, problemTypes.length - 1); return problemTypes[randIndex]; } @@ -130,7 +130,7 @@ function getRandomReward(): ICodingContractReward { // Don't offer money reward by default if BN multiplier is 0 (e.g. BN8) const rewardTypeUpperBound = currentNodeMults.CodingContractMoney === 0 ? CodingContractRewardType.Money - 1 : CodingContractRewardType.Money; - const rewardType = sanitizeRewardType(getRandomInt(0, rewardTypeUpperBound)); + const rewardType = sanitizeRewardType(getRandomIntInclusive(0, rewardTypeUpperBound)); // Add additional information based on the reward type const factionsThatAllowHacking = Player.factions.filter((fac) => Factions[fac].getInfo().offerHackingWork); @@ -140,13 +140,22 @@ function getRandomReward(): ICodingContractReward { // Get a random faction that player is a part of. That // faction must allow hacking contracts const numFactions = factionsThatAllowHacking.length; - const randFaction = factionsThatAllowHacking[getRandomInt(0, numFactions - 1)]; - return { type: rewardType, name: randFaction }; + // This check is unnecessary because sanitizeRewardType ensures that it won't happen. However, I'll still leave + // it here, just in case somebody else changes sanitizeRewardType without taking account of this check. + if (numFactions > 0) { + const randFaction = factionsThatAllowHacking[getRandomIntInclusive(0, numFactions - 1)]; + return { type: rewardType, name: randFaction }; + } + return { type: CodingContractRewardType.Money }; } case CodingContractRewardType.CompanyReputation: { const allJobs = Object.keys(Player.jobs); + // This check is also unnecessary. Check the comment above. if (allJobs.length > 0) { - return { type: CodingContractRewardType.CompanyReputation, name: allJobs[getRandomInt(0, allJobs.length - 1)] }; + return { + type: CodingContractRewardType.CompanyReputation, + name: allJobs[getRandomIntInclusive(0, allJobs.length - 1)], + }; } return { type: CodingContractRewardType.Money }; } @@ -157,7 +166,7 @@ function getRandomReward(): ICodingContractReward { function getRandomServer(): BaseServer { const servers = GetAllServers().filter((server: BaseServer) => server.serversOnNetwork.length !== 0); - let randIndex = getRandomInt(0, servers.length - 1); + let randIndex = getRandomIntInclusive(0, servers.length - 1); let randServer = servers[randIndex]; // An infinite loop shouldn't ever happen, but to be safe we'll use @@ -170,7 +179,7 @@ function getRandomServer(): BaseServer { ) { break; } - randIndex = getRandomInt(0, servers.length - 1); + randIndex = getRandomIntInclusive(0, servers.length - 1); randServer = servers[randIndex]; } @@ -181,7 +190,7 @@ function getRandomFilename( server: BaseServer, reward: ICodingContractReward = { type: CodingContractRewardType.Money }, ): ContractFilePath { - let contractFn = `contract-${getRandomInt(0, 1e6)}`; + let contractFn = `contract-${getRandomIntInclusive(0, 1e6)}`; for (let i = 0; i < 1000; ++i) { if ( @@ -191,7 +200,7 @@ function getRandomFilename( ) { break; } - contractFn = `contract-${getRandomInt(0, 1e6)}`; + contractFn = `contract-${getRandomIntInclusive(0, 1e6)}`; } if ("name" in reward) { diff --git a/src/Corporation/Actions.ts b/src/Corporation/Actions.ts index a5d4fdb86..47373d373 100644 --- a/src/Corporation/Actions.ts +++ b/src/Corporation/Actions.ts @@ -14,7 +14,7 @@ import { FactionName, IndustryType } from "@enums"; import { ResearchMap } from "./ResearchMap"; import { isRelevantMaterial } from "./ui/Helpers"; import { CityName } from "@enums"; -import { getRandomInt } from "../utils/helpers/getRandomInt"; +import { getRandomIntInclusive } from "../utils/helpers/getRandomIntInclusive"; import { getRecordValues } from "../Types/Record"; import { calculateOfficeSizeUpgradeCost, @@ -166,7 +166,7 @@ export function issueNewShares( const privateOwnedRatio = corporation.investorShares / corporation.totalShares; const maxPrivateShares = Math.round((amount / 2) * privateOwnedRatio); - const privateShares = Math.round(getRandomInt(0, maxPrivateShares) / 10e6) * 10e6; + const privateShares = Math.round(getRandomIntInclusive(0, maxPrivateShares) / 10e6) * 10e6; corporation.issuedShares += amount - privateShares; corporation.investorShares += privateShares; diff --git a/src/Corporation/Division.ts b/src/Corporation/Division.ts index a2837dc3b..ccc6e6d45 100644 --- a/src/Corporation/Division.ts +++ b/src/Corporation/Division.ts @@ -3,7 +3,7 @@ import { CityName, CorpEmployeeJob, IndustryType } from "@enums"; import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver"; import { IndustryResearchTrees, IndustriesData } from "./data/IndustryData"; import * as corpConstants from "./data/Constants"; -import { getRandomInt } from "../utils/helpers/getRandomInt"; +import { getRandomIntInclusive } from "../utils/helpers/getRandomIntInclusive"; import { calculateEffectWithFactors } from "../utils/calculateEffectWithFactors"; import { OfficeSpace } from "./OfficeSpace"; import { Product } from "./Product"; @@ -254,7 +254,7 @@ export class Division { processProductMarket(marketCycles = 1): void { // Demand gradually decreases, and competition gradually increases for (const product of this.products.values()) { - let change = getRandomInt(0, 3) * 0.0004; + let change = getRandomIntInclusive(0, 3) * 0.0004; if (change === 0) continue; if ( @@ -985,7 +985,7 @@ export class Division { const awareness = (this.awareness + 3 * advMult) * (1.005 * advMult); this.awareness = Math.min(awareness, Number.MAX_VALUE); - const popularity = (this.popularity + 1 * advMult) * ((1 + getRandomInt(1, 3) / 200) * advMult); + const popularity = (this.popularity + 1 * advMult) * ((1 + getRandomIntInclusive(1, 3) / 200) * advMult); this.popularity = Math.min(popularity, Number.MAX_VALUE); ++this.numAdVerts; diff --git a/src/Corporation/OfficeSpace.ts b/src/Corporation/OfficeSpace.ts index 7ba7ff1a8..6de7c114f 100644 --- a/src/Corporation/OfficeSpace.ts +++ b/src/Corporation/OfficeSpace.ts @@ -3,7 +3,7 @@ import * as corpConstants from "./data/Constants"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver"; import { Division } from "./Division"; import { Corporation } from "./Corporation"; -import { getRandomInt } from "../utils/helpers/getRandomInt"; +import { getRandomIntInclusive } from "../utils/helpers/getRandomIntInclusive"; import { createEnumKeyedRecord, getRecordKeys } from "../Types/Record"; interface IParams { @@ -178,15 +178,19 @@ export class OfficeSpace { hireRandomEmployee(position: CorpEmployeeJob): boolean { if (this.atCapacity()) return false; - this.totalExperience += getRandomInt(50, 100); + this.totalExperience += getRandomIntInclusive(50, 100); - this.avgMorale = (this.avgMorale * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1); - this.avgEnergy = (this.avgEnergy * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1); + this.avgMorale = (this.avgMorale * this.numEmployees + getRandomIntInclusive(50, 100)) / (this.numEmployees + 1); + this.avgEnergy = (this.avgEnergy * this.numEmployees + getRandomIntInclusive(50, 100)) / (this.numEmployees + 1); - this.avgIntelligence = (this.avgIntelligence * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1); - this.avgCharisma = (this.avgCharisma * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1); - this.avgCreativity = (this.avgCreativity * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1); - this.avgEfficiency = (this.avgEfficiency * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1); + this.avgIntelligence = + (this.avgIntelligence * this.numEmployees + getRandomIntInclusive(50, 100)) / (this.numEmployees + 1); + this.avgCharisma = + (this.avgCharisma * this.numEmployees + getRandomIntInclusive(50, 100)) / (this.numEmployees + 1); + this.avgCreativity = + (this.avgCreativity * this.numEmployees + getRandomIntInclusive(50, 100)) / (this.numEmployees + 1); + this.avgEfficiency = + (this.avgEfficiency * this.numEmployees + getRandomIntInclusive(50, 100)) / (this.numEmployees + 1); ++this.numEmployees; ++this.employeeJobs[position]; diff --git a/src/Corporation/Product.ts b/src/Corporation/Product.ts index 4da2181ca..5a551fcd3 100644 --- a/src/Corporation/Product.ts +++ b/src/Corporation/Product.ts @@ -6,7 +6,7 @@ import { IndustriesData } from "./data/IndustryData"; import { MaterialInfo } from "./MaterialInfo"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver"; -import { getRandomInt } from "../utils/helpers/getRandomInt"; +import { getRandomIntInclusive } from "../utils/helpers/getRandomIntInclusive"; import { PartialRecord, createEnumKeyedRecord, getRecordEntries, getRecordKeys } from "../Types/Record"; interface IConstructorParams { @@ -202,7 +202,7 @@ export class Product { this.demand = division.awareness === 0 ? 20 : Math.min(100, advMult * (100 * (division.popularity / division.awareness))); - this.competition = getRandomInt(0, 70); + this.competition = getRandomIntInclusive(0, 70); //Calculate the product's required materials and size this.size = 0; diff --git a/src/Gang/Gang.ts b/src/Gang/Gang.ts index e1a931813..49df55b06 100644 --- a/src/Gang/Gang.ts +++ b/src/Gang/Gang.ts @@ -11,7 +11,7 @@ import { dialogBoxCreate } from "../ui/React/DialogBox"; import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver"; import { exceptionAlert } from "../utils/helpers/exceptionAlert"; -import { getRandomInt } from "../utils/helpers/getRandomInt"; +import { getRandomIntInclusive } from "../utils/helpers/getRandomIntInclusive"; import { GangMemberUpgrade } from "./GangMemberUpgrade"; import { GangConstants } from "./data/Constants"; @@ -215,7 +215,7 @@ export class Gang { const others = gangs.filter((e) => { return e !== gangs[i]; }); - const other = getRandomInt(0, others.length - 1); + const other = getRandomIntInclusive(0, others.length - 1); const thisGang = gangs[i]; const otherGang = others[other]; diff --git a/src/PersonObjects/Player/PlayerObject.ts b/src/PersonObjects/Player/PlayerObject.ts index efdb97571..7a8bb5a3f 100644 --- a/src/PersonObjects/Player/PlayerObject.ts +++ b/src/PersonObjects/Player/PlayerObject.ts @@ -23,7 +23,7 @@ import { MoneySourceTracker } from "../../utils/MoneySourceTracker"; import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../../utils/JSONReviver"; import { JSONMap, JSONSet } from "../../Types/Jsonable"; import { cyrb53 } from "../../utils/StringHelperFunctions"; -import { getRandomInt } from "../../utils/helpers/getRandomInt"; +import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive"; import { CONSTANTS } from "../../Constants"; import { Person } from "../Person"; import { isMember } from "../../utils/EnumHelper"; @@ -142,7 +142,7 @@ export class PlayerObject extends Person implements IPlayer { navigator.userAgent + window.innerWidth + window.innerHeight + - getRandomInt(100, 999), + getRandomIntInclusive(100, 999), ); this.lastAugReset = this.lastNodeReset = Date.now(); } diff --git a/src/Server/AllServers.ts b/src/Server/AllServers.ts index d60a5ab97..5aa915a35 100644 --- a/src/Server/AllServers.ts +++ b/src/Server/AllServers.ts @@ -6,7 +6,7 @@ import { HacknetServer } from "../Hacknet/HacknetServer"; import { IMinMaxRange } from "../types"; import { createRandomIp } from "../utils/IPAddress"; -import { getRandomInt } from "../utils/helpers/getRandomInt"; +import { getRandomIntInclusive } from "../utils/helpers/getRandomIntInclusive"; import { Reviver } from "../utils/JSONReviver"; import { SpecialServers } from "./data/SpecialServers"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers"; @@ -133,7 +133,7 @@ export function initForeignServers(homeComputer: Server): void { const toNumber = (value: number | IMinMaxRange): number => { if (typeof value === "number") return value; - else return getRandomInt(value.min, value.max); + else return getRandomIntInclusive(value.min, value.max); }; for (const metadata of serverMetadata) { diff --git a/src/StockMarket/Stock.ts b/src/StockMarket/Stock.ts index 7c3e14993..717d6540b 100644 --- a/src/StockMarket/Stock.ts +++ b/src/StockMarket/Stock.ts @@ -1,6 +1,6 @@ import { IMinMaxRange } from "../types"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver"; -import { getRandomInt } from "../utils/helpers/getRandomInt"; +import { getRandomIntInclusive } from "../utils/helpers/getRandomIntInclusive"; export const StockForecastInfluenceLimit = 5; @@ -37,7 +37,7 @@ function toNumber(n: number | IMinMaxRange): number { } case "object": { const range = n; - value = getRandomInt(range.min, range.max); + value = getRandomIntInclusive(range.min, range.max); break; } default: @@ -136,7 +136,7 @@ export class Stock { this.b = p.b; this.otlkMag = p.otlkMag; this.otlkMagForecast = this.getAbsoluteForecast(); - this.cap = getRandomInt(this.price * 1e3, this.price * 25e3); + this.cap = getRandomIntInclusive(this.price * 1e3, this.price * 25e3); this.spreadPerc = toNumber(p.spreadPerc); this.shareTxForMovement = toNumber(p.shareTxForMovement); this.shareTxUntilMovement = this.shareTxForMovement; diff --git a/src/StockMarket/StockMarket.tsx b/src/StockMarket/StockMarket.tsx index afbef7a83..f9ec46679 100644 --- a/src/StockMarket/StockMarket.tsx +++ b/src/StockMarket/StockMarket.tsx @@ -15,7 +15,7 @@ import { dialogBoxCreate } from "../ui/React/DialogBox"; import { Reviver } from "../utils/JSONReviver"; import { NetscriptContext } from "../Netscript/APIWrapper"; import { helpers } from "../Netscript/NetscriptHelpers"; -import { getRandomInt } from "../utils/helpers/getRandomInt"; +import { getRandomIntInclusive } from "../utils/helpers/getRandomIntInclusive"; export let StockMarket: IStockMarket = { lastUpdate: 0, @@ -169,7 +169,7 @@ export function initStockMarket(): void { StockMarket.storedCycles = 0; StockMarket.lastUpdate = 0; - StockMarket.ticksUntilCycle = getRandomInt(1, StockMarketConstants.TicksPerCycle); + StockMarket.ticksUntilCycle = getRandomIntInclusive(1, StockMarketConstants.TicksPerCycle); initSymbolToStockMap(); } diff --git a/src/data/codingcontracttypes.ts b/src/data/codingcontracttypes.ts index 591be54f2..e17ec243c 100644 --- a/src/data/codingcontracttypes.ts +++ b/src/data/codingcontracttypes.ts @@ -1,4 +1,4 @@ -import { getRandomInt } from "../utils/helpers/getRandomInt"; +import { getRandomIntInclusive } from "../utils/helpers/getRandomIntInclusive"; import { MinHeap } from "../utils/Heap"; import { comprGenChar, comprLZGenerate, comprLZEncode, comprLZDecode } from "../utils/CompressionContracts"; @@ -69,7 +69,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ }, difficulty: 1, gen: (): number => { - return getRandomInt(500, 1e9); + return getRandomIntInclusive(500, 1e9); }, name: "Find Largest Prime Factor", numTries: 10, @@ -99,11 +99,11 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ }, difficulty: 1, gen: (): number[] => { - const len: number = getRandomInt(5, 40); + const len: number = getRandomIntInclusive(5, 40); const arr: number[] = []; arr.length = len; for (let i = 0; i < len; ++i) { - arr[i] = getRandomInt(-10, 10); + arr[i] = getRandomIntInclusive(-10, 10); } return arr; @@ -135,7 +135,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ }, difficulty: 1.5, gen: (): number => { - return getRandomInt(8, 100); + return getRandomIntInclusive(8, 100); }, name: "Total Ways to Sum", numTries: 10, @@ -167,8 +167,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ }, difficulty: 2, gen: (): [number, number[]] => { - const n: number = getRandomInt(12, 200); - const maxLen: number = getRandomInt(8, 12); + const n: number = getRandomIntInclusive(12, 200); + const maxLen: number = getRandomIntInclusive(8, 12); const s: number[] = []; // Bias towards small numbers is intentional to have much bigger answers in general // to force people better optimize their solutions @@ -241,8 +241,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ }, difficulty: 2, gen: (): number[][] => { - const m: number = getRandomInt(1, 15); - const n: number = getRandomInt(1, 15); + const m: number = getRandomIntInclusive(1, 15); + const n: number = getRandomIntInclusive(1, 15); const matrix: number[][] = []; matrix.length = m; for (let i = 0; i < m; ++i) { @@ -252,7 +252,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ for (let i = 0; i < m; ++i) { for (let j = 0; j < n; ++j) { - matrix[i][j] = getRandomInt(1, 50); + matrix[i][j] = getRandomIntInclusive(1, 50); } } @@ -345,14 +345,14 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ }, difficulty: 2.5, gen: (): number[] => { - const len: number = getRandomInt(3, 25); + const len: number = getRandomIntInclusive(3, 25); const arr: number[] = []; arr.length = len; for (let i = 0; i < arr.length; ++i) { if (Math.random() < 0.2) { arr[i] = 0; // 20% chance of being 0 } else { - arr[i] = getRandomInt(0, 10); + arr[i] = getRandomIntInclusive(0, 10); } } @@ -389,7 +389,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ }, difficulty: 3, gen: (): number[] => { - const len: number = getRandomInt(3, 25); + const len: number = getRandomIntInclusive(3, 25); const arr: number[] = []; arr.length = len; for (let i = 0; i < arr.length; i++) { @@ -447,10 +447,10 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ difficulty: 3, gen: (): number[][] => { const intervals: number[][] = []; - const numIntervals: number = getRandomInt(3, 20); + const numIntervals: number = getRandomIntInclusive(3, 20); for (let i = 0; i < numIntervals; ++i) { - const start: number = getRandomInt(1, 25); - const end: number = start + getRandomInt(1, 10); + const start: number = getRandomIntInclusive(1, 25); + const end: number = start + getRandomIntInclusive(1, 10); intervals.push([start, end]); } @@ -503,7 +503,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ gen: (): string => { let str = ""; for (let i = 0; i < 4; ++i) { - const num: number = getRandomInt(0, 255); + const num: number = getRandomIntInclusive(0, 255); const convNum: string = num.toString(); str += convNum; } @@ -567,11 +567,11 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ }, difficulty: 1, gen: (): number[] => { - const len: number = getRandomInt(3, 50); + const len: number = getRandomIntInclusive(3, 50); const arr: number[] = []; arr.length = len; for (let i = 0; i < len; ++i) { - arr[i] = getRandomInt(1, 200); + arr[i] = getRandomIntInclusive(1, 200); } return arr; @@ -607,11 +607,11 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ }, difficulty: 2, gen: (): number[] => { - const len: number = getRandomInt(3, 50); + const len: number = getRandomIntInclusive(3, 50); const arr: number[] = []; arr.length = len; for (let i = 0; i < len; ++i) { - arr[i] = getRandomInt(1, 200); + arr[i] = getRandomIntInclusive(1, 200); } return arr; @@ -645,11 +645,11 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ }, difficulty: 5, gen: (): number[] => { - const len: number = getRandomInt(3, 50); + const len: number = getRandomIntInclusive(3, 50); const arr: number[] = []; arr.length = len; for (let i = 0; i < len; ++i) { - arr[i] = getRandomInt(1, 200); + arr[i] = getRandomIntInclusive(1, 200); } return arr; @@ -693,12 +693,12 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ }, difficulty: 8, gen: (): [number, number[]] => { - const k = getRandomInt(2, 10); - const len = getRandomInt(3, 50); + const k = getRandomIntInclusive(2, 10); + const len = getRandomIntInclusive(3, 50); const prices: number[] = []; prices.length = len; for (let i = 0; i < len; ++i) { - prices[i] = getRandomInt(1, 200); + prices[i] = getRandomIntInclusive(1, 200); } return [k, prices]; @@ -785,14 +785,14 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ difficulty: 5, gen: (): number[][] => { const triangle: number[][] = []; - const levels: number = getRandomInt(3, 12); + const levels: number = getRandomIntInclusive(3, 12); triangle.length = levels; for (let row = 0; row < levels; ++row) { triangle[row] = []; triangle[row].length = row + 1; for (let i = 0; i < triangle[row].length; ++i) { - triangle[row][i] = getRandomInt(1, 9); + triangle[row][i] = getRandomIntInclusive(1, 9); } } @@ -832,8 +832,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ }, difficulty: 3, gen: (): number[] => { - const numRows: number = getRandomInt(2, 14); - const numColumns: number = getRandomInt(2, 14); + const numRows: number = getRandomIntInclusive(2, 14); + const numColumns: number = getRandomIntInclusive(2, 14); return [numRows, numColumns]; }, @@ -878,8 +878,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ }, difficulty: 5, gen: (): number[][] => { - const numRows: number = getRandomInt(2, 12); - const numColumns: number = getRandomInt(2, 12); + const numRows: number = getRandomIntInclusive(2, 12); + const numColumns: number = getRandomIntInclusive(2, 12); const grid: number[][] = []; grid.length = numRows; @@ -961,8 +961,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ difficulty: 7, numTries: 10, gen: (): number[][] => { - const height = getRandomInt(6, 12); - const width = getRandomInt(6, 12); + const height = getRandomIntInclusive(6, 12); + const width = getRandomIntInclusive(6, 12); const dstY = height - 1; const dstX = width - 1; const minPathLength = dstY + dstX; // Math.abs(dstY - srcY) + Math.abs(dstX - srcX) @@ -1087,7 +1087,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ }, difficulty: 10, gen: (): string => { - const len: number = getRandomInt(6, 20); + const len: number = getRandomIntInclusive(6, 20); const chars: string[] = []; chars.length = len; @@ -1205,18 +1205,18 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ }, difficulty: 10, gen: (): [string, number] => { - const numDigits = getRandomInt(4, 12); + const numDigits = getRandomIntInclusive(4, 12); const digitsArray: string[] = []; digitsArray.length = numDigits; for (let i = 0; i < digitsArray.length; ++i) { if (i === 0) { - digitsArray[i] = String(getRandomInt(1, 9)); + digitsArray[i] = String(getRandomIntInclusive(1, 9)); } else { - digitsArray[i] = String(getRandomInt(0, 9)); + digitsArray[i] = String(getRandomIntInclusive(0, 9)); } } - const target: number = getRandomInt(-100, 100); + const target: number = getRandomIntInclusive(-100, 100); const digits: string = digitsArray.join(""); return [digits, target]; @@ -1316,7 +1316,9 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ ].join(" "); }, gen: (): number => { - return getRandomInt(Math.pow(2, 4), Math.pow(2, getRandomInt(1, 57))); + const x = Math.pow(2, 4); + const y = Math.pow(2, getRandomIntInclusive(1, 57)); + return getRandomIntInclusive(Math.min(x, y), Math.max(x, y)); }, solver: (data: unknown, ans: string): boolean => { if (typeof data !== "number") throw new Error("solver expected number"); @@ -1351,11 +1353,13 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ }, gen: (): string => { const _alteredBit = Math.round(Math.random()); - const _buildArray: string[] = HammingEncodeProperly( - getRandomInt(Math.pow(2, 4), Math.pow(2, getRandomInt(1, 57))), - ).split(""); + const x = Math.pow(2, 4); + const y = Math.pow(2, getRandomIntInclusive(1, 57)); + const _buildArray: string[] = HammingEncodeProperly(getRandomIntInclusive(Math.min(x, y), Math.max(x, y))).split( + "", + ); if (_alteredBit) { - const _randomIndex: number = getRandomInt(0, _buildArray.length - 1); + const _randomIndex: number = getRandomIntInclusive(0, _buildArray.length - 1); _buildArray[_randomIndex] = _buildArray[_randomIndex] == "0" ? "1" : "0"; } return _buildArray.join(""); diff --git a/src/utils/EnumHelper.ts b/src/utils/EnumHelper.ts index f7f05c5c3..97f657441 100644 --- a/src/utils/EnumHelper.ts +++ b/src/utils/EnumHelper.ts @@ -4,7 +4,7 @@ import type { NetscriptContext } from "../Netscript/APIWrapper"; import * as allEnums from "../Enums"; import { assertString } from "../Netscript/TypeAssertion"; import { errorMessage } from "../Netscript/ErrorMessages"; -import { getRandomInt } from "./helpers/getRandomInt"; +import { getRandomIntInclusive } from "./helpers/getRandomIntInclusive"; interface GetMemberOptions { /** Whether to use fuzzy matching on the input (case insensitive, ignore spaces and dashes) */ @@ -69,7 +69,7 @@ class EnumHelper & st } // Get a random enum member random() { - const index = getRandomInt(0, this.valueArray.length - 1); + const index = getRandomIntInclusive(0, this.valueArray.length - 1); return this.valueArray[index]; } } diff --git a/src/utils/helpers/getRandomByte.ts b/src/utils/helpers/getRandomByte.ts index 46d91839b..b68b688e8 100644 --- a/src/utils/helpers/getRandomByte.ts +++ b/src/utils/helpers/getRandomByte.ts @@ -1,4 +1,4 @@ -import { getRandomInt } from "./getRandomInt"; +import { getRandomIntInclusive } from "./getRandomIntInclusive"; /** * Gets a random value in the range of a byte (0 - 255), or up to the maximum. @@ -9,5 +9,5 @@ export function getRandomByte(max: number): number { const byteMaximum = 255; const upper: number = Math.max(Math.min(max, byteMaximum), 0); - return getRandomInt(0, upper); + return getRandomIntInclusive(0, upper); } diff --git a/src/utils/helpers/getRandomInt.ts b/src/utils/helpers/getRandomInt.ts deleted file mode 100644 index 23e42ed6b..000000000 --- a/src/utils/helpers/getRandomInt.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Gets a random integer bounded by the values passed in. - * @param min The minimum value in the range. - * @param max The maximum value in the range. - */ -export function getRandomInt(min: number, max: number): number { - const lower: number = Math.min(min, max); - const upper: number = Math.max(min, max); - - return Math.floor(Math.random() * (upper - lower + 1)) + lower; -} diff --git a/src/utils/helpers/getRandomIntInclusive.ts b/src/utils/helpers/getRandomIntInclusive.ts new file mode 100644 index 000000000..df65da75c --- /dev/null +++ b/src/utils/helpers/getRandomIntInclusive.ts @@ -0,0 +1,21 @@ +/** + * + * Gets a random integer between min (inclusive) and max (inclusive). + * + * Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random + * + * @param min The minimum value in the range. + * @param max The maximum value in the range. + */ +export function getRandomIntInclusive(min: number, max: number): number { + if (!Number.isInteger(min)) { + throw new Error(`Min is not an integer. Min: ${min}.`); + } + if (!Number.isInteger(max)) { + throw new Error(`Max is not an integer. Max: ${max}.`); + } + if (min > max) { + throw new Error(`Min is greater than max. Min: ${min}. Max: ${max}.`); + } + return Math.floor(Math.random() * (max - min + 1) + min); +}