mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-21 16:52:55 +02:00
DARKNET: Darkweb Expansion Project & Bitnode (#2139)
This is BN15. It is a really big change; see the PR for all the details.
This commit is contained in:
committed by
GitHub
parent
a674633f6c
commit
6073964768
@@ -0,0 +1,706 @@
|
||||
import { DnetServerBuilder } from "../models/DarknetServerOptions";
|
||||
import {
|
||||
commonPasswordDictionary,
|
||||
defaultSettingsDictionary,
|
||||
dogNameDictionary,
|
||||
EUCountries,
|
||||
filler,
|
||||
letters,
|
||||
lettersUppercase,
|
||||
numbers,
|
||||
} from "../models/dictionaryData";
|
||||
import { DarknetServer } from "../../Server/DarknetServer";
|
||||
import { ModelIds, MinigamesType } from "../Enums";
|
||||
import { MAX_PASSWORD_LENGTH } from "../Constants";
|
||||
import { clampNumber } from "../../utils/helpers/clampNumber";
|
||||
import { hasFullDarknetAccess } from "../effects/effects";
|
||||
|
||||
const getRandomServerConfigBuilder = (difficulty: number) => {
|
||||
const tier0Servers = [getNoPasswordConfig];
|
||||
const tier1Servers = [getEchoVulnConfig, getDefaultPasswordConfig, getCaptchaConfig];
|
||||
const tier2Servers = [getDogNameConfig, getYesn_tConfig, getBufferOverflowConfig];
|
||||
const sf15UnlockedServers = hasFullDarknetAccess() ? [getKingOfTheHillConfig, getSpiceLevelConfig] : [];
|
||||
const tier3Servers = [
|
||||
getSortedEchoVulnConfig,
|
||||
getMastermindHintConfig,
|
||||
getRomanNumeralConfig,
|
||||
getGuessNumberConfig,
|
||||
getConvertToBase10Config,
|
||||
getDivisibilityTestConfig,
|
||||
getPacketSnifferConfig,
|
||||
...sf15UnlockedServers,
|
||||
];
|
||||
const tier4Servers = [
|
||||
getLargestPrimeFactorConfig,
|
||||
getLargeDictionaryConfig,
|
||||
getEuCountryDictionaryConfig,
|
||||
getTimingAttackConfig,
|
||||
getBinaryEncodedConfig,
|
||||
getParseArithmeticExpressionConfig,
|
||||
getXorMaskEncryptedPasswordConfig,
|
||||
getTripleModuloConfig,
|
||||
];
|
||||
|
||||
if (difficulty <= 2) {
|
||||
const serverBuilders = [...tier0Servers, ...tier1Servers];
|
||||
return serverBuilders[Math.floor(Math.random() * serverBuilders.length)];
|
||||
}
|
||||
if (difficulty <= 4) {
|
||||
const serverBuilders = [...tier0Servers, ...tier1Servers, ...tier1Servers, ...tier2Servers, ...tier3Servers];
|
||||
return serverBuilders[Math.floor(Math.random() * serverBuilders.length)];
|
||||
}
|
||||
if (difficulty <= 8) {
|
||||
const serverBuilders = [...tier1Servers, ...tier2Servers, ...tier3Servers];
|
||||
return serverBuilders[Math.floor(Math.random() * serverBuilders.length)];
|
||||
}
|
||||
if (difficulty <= 18) {
|
||||
const serverBuilders = [...tier2Servers, ...tier3Servers, ...tier4Servers];
|
||||
return serverBuilders[Math.floor(Math.random() * serverBuilders.length)];
|
||||
}
|
||||
const serverBuilders = [...tier3Servers, ...tier4Servers];
|
||||
return serverBuilders[Math.floor(Math.random() * serverBuilders.length)];
|
||||
};
|
||||
|
||||
export const createDarknetServer = (difficulty: number, depth: number, leftOffset: number): DarknetServer => {
|
||||
const cappedDifficulty = clampNumber(difficulty, 0, MAX_PASSWORD_LENGTH);
|
||||
return serverFactory(getRandomServerConfigBuilder(cappedDifficulty), difficulty, depth, leftOffset);
|
||||
};
|
||||
|
||||
export type ServerConfig = {
|
||||
modelId: MinigamesType;
|
||||
password: string;
|
||||
staticPasswordHint: string;
|
||||
passwordHintData?: string;
|
||||
};
|
||||
|
||||
export const serverFactory = (
|
||||
serverConfigBuilder: (n: number) => ServerConfig,
|
||||
difficulty: number,
|
||||
depth: number,
|
||||
leftOffset: number,
|
||||
): DarknetServer => {
|
||||
return DnetServerBuilder({
|
||||
...serverConfigBuilder(difficulty),
|
||||
difficulty,
|
||||
depth,
|
||||
leftOffset: leftOffset,
|
||||
});
|
||||
};
|
||||
|
||||
export const getEchoVulnConfig = (__difficulty: number): ServerConfig => {
|
||||
const hintTemplates = [
|
||||
"The password is",
|
||||
"The PIN is",
|
||||
"Remember to use",
|
||||
"It's set to",
|
||||
"The key is",
|
||||
"The secret is",
|
||||
];
|
||||
const password = getPassword(3);
|
||||
const hint = `${hintTemplates[Math.floor(Math.random() * hintTemplates.length)]} ${password}`;
|
||||
return {
|
||||
modelId: ModelIds.EchoVuln,
|
||||
password,
|
||||
staticPasswordHint: hint,
|
||||
};
|
||||
};
|
||||
|
||||
export const getSortedEchoVulnConfig = (difficulty: number): ServerConfig => {
|
||||
const hintTemplates = [
|
||||
"The password is shuffled",
|
||||
"The key is made from",
|
||||
"I accidentally sorted the password:",
|
||||
"The PIN uses",
|
||||
];
|
||||
const password = getPassword(Math.min(2 + difficulty / 7, 9));
|
||||
const sortedPassword = password.split("").sort().join("");
|
||||
const hint = `${hintTemplates[Math.floor(Math.random() * hintTemplates.length)]} ${sortedPassword}`;
|
||||
return {
|
||||
modelId: ModelIds.SortedEchoVuln,
|
||||
password,
|
||||
staticPasswordHint: hint,
|
||||
passwordHintData: sortedPassword,
|
||||
};
|
||||
};
|
||||
|
||||
export const getDictionaryAttackConfig = (
|
||||
__difficulty: number,
|
||||
dictionary: readonly string[],
|
||||
hintTemplates: string[],
|
||||
minigameType: MinigamesType,
|
||||
): ServerConfig => {
|
||||
return {
|
||||
modelId: minigameType,
|
||||
password: dictionary[Math.floor(Math.random() * dictionary.length)],
|
||||
staticPasswordHint: hintTemplates[Math.floor(Math.random() * hintTemplates.length)],
|
||||
};
|
||||
};
|
||||
|
||||
export const getNoPasswordConfig = (difficulty: number): ServerConfig => {
|
||||
const hintTemplates = [
|
||||
"The password is not set",
|
||||
"There is no password",
|
||||
"The PIN is empty",
|
||||
"Did I set a code?",
|
||||
"I didn't set a password",
|
||||
];
|
||||
return getDictionaryAttackConfig(difficulty, [""], hintTemplates, ModelIds.NoPassword);
|
||||
};
|
||||
|
||||
export const getDefaultPasswordConfig = (difficulty: number): ServerConfig => {
|
||||
const hintTemplates = [
|
||||
"The password is the default password",
|
||||
"It's still the default",
|
||||
"The default password is set",
|
||||
"I never changed the password",
|
||||
"It's still the factory settings",
|
||||
];
|
||||
return getDictionaryAttackConfig(difficulty, defaultSettingsDictionary, hintTemplates, ModelIds.DefaultPassword);
|
||||
};
|
||||
|
||||
export const getCaptchaConfig = (difficulty: number): ServerConfig => {
|
||||
const password = getPassword(difficulty / 2 + 3);
|
||||
const filledPassword = password
|
||||
.split("")
|
||||
.map((char, i) => {
|
||||
if (i >= password.length - 1) {
|
||||
return char;
|
||||
}
|
||||
return char + getFillerChars();
|
||||
})
|
||||
.join("");
|
||||
|
||||
return {
|
||||
modelId: ModelIds.Captcha,
|
||||
password,
|
||||
staticPasswordHint: "Type the numbers to prove you are human",
|
||||
passwordHintData: filledPassword,
|
||||
};
|
||||
};
|
||||
|
||||
const getFillerChars = () => {
|
||||
let result = "";
|
||||
const num = Math.ceil(Math.random() * 3);
|
||||
for (let i = 0; i < num; i++) {
|
||||
result += filler[Math.floor(Math.random() * filler.length)];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export const getDogNameConfig = (difficulty: number): ServerConfig => {
|
||||
const hintTemplates = ["It's my dog's name", "It's the dog's name", "my first dog's name"];
|
||||
return getDictionaryAttackConfig(difficulty, dogNameDictionary, hintTemplates, ModelIds.DogNames);
|
||||
};
|
||||
|
||||
export const getMastermindHintConfig = (difficulty: number): ServerConfig => {
|
||||
const alphanumeric = difficulty > 16 && Math.random() < 0.3;
|
||||
const passwordLength = Math.min((alphanumeric ? -1 : 2) + difficulty / 5, 10);
|
||||
return {
|
||||
modelId: ModelIds.MastermindHint,
|
||||
password: getPassword(passwordLength, alphanumeric),
|
||||
staticPasswordHint: "Only a true master may pass",
|
||||
};
|
||||
};
|
||||
|
||||
export const getTimingAttackConfig = (difficulty: number): ServerConfig => {
|
||||
const hintTemplates = [
|
||||
"I thought about it for some time, but that is not the password.",
|
||||
"I spent a while on it, but that's not right",
|
||||
"I considered it for a bit, but that's not it",
|
||||
"I spent some time on it, but that's not the password",
|
||||
];
|
||||
const alphanumeric = difficulty > 16 && Math.random() < 0.3;
|
||||
const length = (alphanumeric ? 0 : 3) + difficulty / 4;
|
||||
return {
|
||||
modelId: ModelIds.TimingAttack,
|
||||
password: getPassword(length, alphanumeric),
|
||||
staticPasswordHint: hintTemplates[Math.floor(Math.random() * hintTemplates.length)],
|
||||
};
|
||||
};
|
||||
|
||||
export const getRomanNumeralConfig = (difficulty: number): ServerConfig => {
|
||||
const password = Math.floor(Math.random() * 10 * (10 * (difficulty + 1)));
|
||||
if (difficulty < 8) {
|
||||
const encodedPassword = romanNumeralEncoder(password);
|
||||
return {
|
||||
modelId: ModelIds.RomanNumeral,
|
||||
password: `${password}`,
|
||||
staticPasswordHint: `The password is the value of the number '${encodedPassword}'`,
|
||||
passwordHintData: encodedPassword,
|
||||
};
|
||||
} else {
|
||||
const passwordRangeMin = Math.random() < 0.3 ? 0 : Math.floor(password * (Math.random() * 0.2 + 0.6));
|
||||
const passwordRangeMax = password + Math.floor(Math.random() * difficulty * 10 + 10);
|
||||
const encodedMin = romanNumeralEncoder(passwordRangeMin);
|
||||
const encodedMax = romanNumeralEncoder(passwordRangeMax);
|
||||
const hint = `The password is between '${encodedMin}' and '${encodedMax}'`;
|
||||
const hintData = `${encodedMin},${encodedMax}`;
|
||||
return {
|
||||
modelId: ModelIds.RomanNumeral,
|
||||
password: `${password}`,
|
||||
staticPasswordHint: hint,
|
||||
passwordHintData: hintData,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const getLargestPrimeFactorConfig = (difficulty: number): ServerConfig => {
|
||||
const largestPrimePasswordDetails = getLargestPrimeFactorPassword(difficulty);
|
||||
return {
|
||||
modelId: ModelIds.LargestPrimeFactor,
|
||||
password: `${largestPrimePasswordDetails.largestPrime}`,
|
||||
staticPasswordHint: `The password is the largest prime factor of ${largestPrimePasswordDetails.targetNumber}`,
|
||||
passwordHintData: `${largestPrimePasswordDetails.targetNumber}`,
|
||||
};
|
||||
};
|
||||
|
||||
export const getGuessNumberConfig = (difficulty: number): ServerConfig => {
|
||||
const password = `${Math.floor((Math.random() * 10 * (difficulty + 3)) / 3)}`;
|
||||
const maxNumber = 10 ** password.length;
|
||||
return {
|
||||
modelId: ModelIds.GuessNumber,
|
||||
password,
|
||||
staticPasswordHint: `The password is a number between 0 and ${maxNumber}`,
|
||||
};
|
||||
};
|
||||
|
||||
export const getLargeDictionaryConfig = (difficulty: number): ServerConfig => {
|
||||
return getDictionaryAttackConfig(
|
||||
difficulty,
|
||||
commonPasswordDictionary,
|
||||
["It's a common password"],
|
||||
ModelIds.CommonPasswordDictionary,
|
||||
);
|
||||
};
|
||||
|
||||
export const getEuCountryDictionaryConfig = (difficulty: number): ServerConfig => {
|
||||
return getDictionaryAttackConfig(difficulty, EUCountries, ["My favorite EU country"], ModelIds.EUCountryDictionary);
|
||||
};
|
||||
|
||||
export const getYesn_tConfig = (difficulty: number): ServerConfig => {
|
||||
const password = getPassword(3 + difficulty / 2, difficulty > 8);
|
||||
return {
|
||||
modelId: ModelIds.Yesn_t,
|
||||
password,
|
||||
staticPasswordHint: "you are one who's'nt authorized",
|
||||
};
|
||||
};
|
||||
|
||||
export const getBufferOverflowConfig = (): ServerConfig => {
|
||||
const length = Math.floor(4 + Math.random() * 4);
|
||||
const password = getPassword(length, true);
|
||||
return {
|
||||
modelId: ModelIds.BufferOverflow,
|
||||
password,
|
||||
staticPasswordHint: `Warning: password buffer is ${length} bytes`,
|
||||
};
|
||||
};
|
||||
|
||||
export const getBinaryEncodedConfig = (difficulty: number): ServerConfig => {
|
||||
const password = getPassword(2 + difficulty / 5, difficulty > 8);
|
||||
const binaryEncodedPassword = password
|
||||
.split("")
|
||||
.map((char) => char.charCodeAt(0).toString(2).padStart(8, "0"))
|
||||
.join(" ");
|
||||
return {
|
||||
modelId: ModelIds.BinaryEncodedFeedback,
|
||||
password,
|
||||
staticPasswordHint: "beep boop",
|
||||
passwordHintData: binaryEncodedPassword,
|
||||
};
|
||||
};
|
||||
|
||||
export const getXorMaskEncryptedPasswordConfig = (): ServerConfig => {
|
||||
const password = getPassword(3 + Math.random() * 3, true);
|
||||
let passwordWithXorMaskApplied: string;
|
||||
let xorMaskStrings: string[];
|
||||
|
||||
do {
|
||||
passwordWithXorMaskApplied = "";
|
||||
xorMaskStrings = [];
|
||||
for (const c of password) {
|
||||
const charCode = c.charCodeAt(0);
|
||||
const xorMask = Math.floor(Math.random() * 32);
|
||||
xorMaskStrings.push(xorMask.toString(2).padStart(8, "0"));
|
||||
passwordWithXorMaskApplied += String.fromCharCode(charCode ^ xorMask);
|
||||
}
|
||||
// Prevent characters that would break parsing in encoded output
|
||||
} while (passwordWithXorMaskApplied.includes(";") || passwordWithXorMaskApplied.includes(" "));
|
||||
|
||||
return {
|
||||
modelId: ModelIds.encryptedPassword,
|
||||
password,
|
||||
staticPasswordHint: `XOR mask encrypted password: "${passwordWithXorMaskApplied}".`,
|
||||
passwordHintData: `${passwordWithXorMaskApplied};${xorMaskStrings.join(" ")}`,
|
||||
};
|
||||
};
|
||||
|
||||
export const getSpiceLevelConfig = (difficulty: number): ServerConfig => {
|
||||
const password = getPassword(3 + difficulty / 3, difficulty > 8);
|
||||
return {
|
||||
modelId: ModelIds.SpiceLevel,
|
||||
password,
|
||||
staticPasswordHint: "!!🌶️!!",
|
||||
};
|
||||
};
|
||||
|
||||
export const getConvertToBase10Config = (difficulty: number): ServerConfig => {
|
||||
const password = Math.ceil(Math.random() * 99 * (difficulty + 1));
|
||||
const bases = [2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16];
|
||||
let base = bases[Math.floor(Math.random() * bases.length)];
|
||||
if (difficulty > 12) {
|
||||
base += bases[Math.floor(Math.random() * bases.length)] / 10;
|
||||
}
|
||||
const encodedPassword = encodeNumberInBaseN(password, base);
|
||||
return {
|
||||
modelId: ModelIds.ConvertToBase10,
|
||||
password: `${password}`,
|
||||
staticPasswordHint: `the password is the base ${base} number ${encodedPassword} in base 10`,
|
||||
passwordHintData: `${base},${encodedPassword}`,
|
||||
};
|
||||
};
|
||||
|
||||
export const getParseArithmeticExpressionConfig = (difficulty: number): ServerConfig => {
|
||||
let expression = generateSimpleArithmeticExpression(difficulty);
|
||||
const result = parseSimpleArithmeticExpression(expression);
|
||||
if (difficulty > 12) {
|
||||
expression = expression.replaceAll("*", "ҳ").replaceAll("/", "÷").replaceAll("+", "➕").replaceAll("-", "➖");
|
||||
}
|
||||
if ((difficulty > 16 && Math.random() < 0.3) || Math.random() < 0.01) {
|
||||
expression += getCodeInjection();
|
||||
}
|
||||
const parenCount = expression.split("(").length - 1;
|
||||
if (difficulty > 20 && Math.random() < 0.3 && parenCount > 1) {
|
||||
expression = expression.replace("(", "(ns.exit(),");
|
||||
}
|
||||
return {
|
||||
modelId: ModelIds.parsedExpression,
|
||||
password: `${result}`,
|
||||
staticPasswordHint: `The password is the evaluation of this expression`,
|
||||
passwordHintData: expression,
|
||||
};
|
||||
};
|
||||
|
||||
export const getDivisibilityTestConfig = (difficulty: number): ServerConfig => {
|
||||
const password = getPasswordMadeUpOfPrimesProduct(difficulty);
|
||||
return {
|
||||
modelId: ModelIds.divisibilityTest,
|
||||
password: `${password}`,
|
||||
staticPasswordHint: `The password is divisible by 1 ;)`,
|
||||
};
|
||||
};
|
||||
|
||||
export const getTripleModuloConfig = (difficulty: number): ServerConfig => {
|
||||
const password = getPassword(3 + difficulty / 5);
|
||||
return {
|
||||
modelId: ModelIds.tripleModulo,
|
||||
password: `${password}`,
|
||||
staticPasswordHint: `(password % n) % (n % 32)`,
|
||||
};
|
||||
};
|
||||
|
||||
export const getKingOfTheHillConfig = (difficulty: number): ServerConfig => {
|
||||
const password = getPassword(Math.min(1 + difficulty / 6, 10));
|
||||
return {
|
||||
modelId: ModelIds.globalMaxima,
|
||||
password,
|
||||
staticPasswordHint: "Ascend the highest mountain!",
|
||||
};
|
||||
};
|
||||
|
||||
export const getPacketSnifferConfig = (difficulty: number): ServerConfig => {
|
||||
return {
|
||||
modelId: ModelIds.packetSniffer,
|
||||
password: getPassword(3 + difficulty / 3, difficulty > 8),
|
||||
staticPasswordHint: "(I'm busy browsing social media at the cafe)",
|
||||
};
|
||||
};
|
||||
|
||||
export const encodeNumberInBaseN = (decimalNumber: number, base: number) => {
|
||||
const characters = [...numbers.split(""), ...lettersUppercase.split("")];
|
||||
let digits = Math.floor(Math.log(decimalNumber) / Math.log(base));
|
||||
let remaining = decimalNumber;
|
||||
let result: string = "";
|
||||
|
||||
while (remaining >= 0.0001 || digits >= 0) {
|
||||
if (digits === -1) {
|
||||
result += ".";
|
||||
}
|
||||
const place = Math.floor(remaining / base ** digits);
|
||||
result += characters[place];
|
||||
remaining -= place * base ** digits;
|
||||
digits -= 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const parseBaseNNumberString = (numberString: string, base: number): number => {
|
||||
const characters = [...numbers.split(""), ...lettersUppercase.split("")];
|
||||
let result = 0;
|
||||
let index = 0;
|
||||
let digit = numberString.split(".")[0].length - 1;
|
||||
|
||||
while (index < numberString.length) {
|
||||
const currentDigit = numberString[index];
|
||||
if (currentDigit === ".") {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
result += characters.indexOf(currentDigit) * base ** digit;
|
||||
index += 1;
|
||||
digit -= 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// example: 4 + 5 * ( 6 + 7 ) / 2
|
||||
export const parseSimpleArithmeticExpression = (expression: string): number => {
|
||||
const tokens = cleanArithmeticExpression(expression).split("");
|
||||
|
||||
// Identify parentheses
|
||||
let currentDepth = 0;
|
||||
const depth = tokens.map((token) => {
|
||||
if (token === "(") {
|
||||
currentDepth += 1;
|
||||
} else if (token === ")") {
|
||||
currentDepth -= 1;
|
||||
return currentDepth + 1;
|
||||
}
|
||||
return currentDepth;
|
||||
});
|
||||
const depth1Start = depth.indexOf(1);
|
||||
// find the last 1 before the first 0 after depth1Start
|
||||
const firstZeroAfterDepth1Start = depth.indexOf(0, depth1Start);
|
||||
const depth1End = firstZeroAfterDepth1Start === -1 ? depth.length - 1 : firstZeroAfterDepth1Start - 1;
|
||||
if (depth1Start !== -1) {
|
||||
const subExpression = tokens.slice(depth1Start + 1, depth1End).join("");
|
||||
const result = parseSimpleArithmeticExpression(subExpression);
|
||||
tokens.splice(depth1Start, depth1End - depth1Start + 1, result.toString());
|
||||
return parseSimpleArithmeticExpression(tokens.join(""));
|
||||
}
|
||||
|
||||
// handle multiplication and division
|
||||
let remainingExpression = tokens.join("");
|
||||
|
||||
// breakdown and explanation for this regex: https://regex101.com/r/mZhiBn/1
|
||||
const multiplicationDivisionRegex = /(-?\d*\.?\d+) *([*/]) *(-?\d*\.?\d+)/;
|
||||
let match = remainingExpression.match(multiplicationDivisionRegex);
|
||||
|
||||
while (match) {
|
||||
const [__, left, operator, right] = match;
|
||||
const result = operator === "*" ? parseFloat(left) * parseFloat(right) : parseFloat(left) / parseFloat(right);
|
||||
const resultString = Math.abs(result) < 0.000001 ? result.toFixed(20) : result.toString();
|
||||
remainingExpression = remainingExpression.replace(match[0], resultString);
|
||||
match = remainingExpression.match(multiplicationDivisionRegex);
|
||||
}
|
||||
|
||||
// handle addition and subtraction
|
||||
const additionSubtractionRegex = /(-?\d*\.?\d+) *([+-]) *(-?\d*\.?\d+)/;
|
||||
match = remainingExpression.match(additionSubtractionRegex);
|
||||
|
||||
while (match) {
|
||||
const [__, left, operator, right] = match;
|
||||
const result = operator === "+" ? parseFloat(left) + parseFloat(right) : parseFloat(left) - parseFloat(right);
|
||||
remainingExpression = remainingExpression.replace(match[0], result.toString());
|
||||
match = remainingExpression.match(additionSubtractionRegex);
|
||||
}
|
||||
|
||||
const [__, leftover] = remainingExpression.match(/(-?\d*\.?\d+)/) ?? ["", ""];
|
||||
|
||||
return parseFloat(leftover);
|
||||
};
|
||||
|
||||
export const generateSimpleArithmeticExpression = (difficulty: number): string => {
|
||||
const operators = ["+", "-", "*", "/"];
|
||||
const operatorCount = Math.floor(difficulty / 4);
|
||||
const expression = [];
|
||||
for (let i = 0; i < operatorCount; i++) {
|
||||
expression.push(Math.ceil(Math.random() * 98));
|
||||
expression.push(operators[Math.floor(Math.random() * operators.length)]);
|
||||
|
||||
if (difficulty > 5 && Math.random() < difficulty / (difficulty + 50)) {
|
||||
expression.push("(");
|
||||
expression.push(generateSimpleArithmeticExpression(difficulty / 2));
|
||||
expression.push(")");
|
||||
expression.push(operators[Math.floor(Math.random() * operators.length)]);
|
||||
}
|
||||
}
|
||||
expression.push(Math.ceil(Math.random() * 98));
|
||||
|
||||
const result = expression.join(" ");
|
||||
|
||||
try {
|
||||
const calc = parseSimpleArithmeticExpression(cleanArithmeticExpression(result));
|
||||
if (Math.abs(calc) < 0.1) {
|
||||
return generateSimpleArithmeticExpression(difficulty);
|
||||
}
|
||||
} catch (__) {
|
||||
return generateSimpleArithmeticExpression(difficulty);
|
||||
}
|
||||
|
||||
if (difficulty > 18) {
|
||||
return result.replaceAll("*", "ҳ").replaceAll("/", "÷").replaceAll("+", "➕").replaceAll("-", "➖");
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const cleanArithmeticExpression = (expression: string): string => {
|
||||
const expressionWithFixedSymbols = expression
|
||||
.replaceAll("ҳ", "*")
|
||||
.replaceAll("÷", "/")
|
||||
.replaceAll("➕", "+")
|
||||
.replaceAll("➖", "-")
|
||||
.replaceAll("ns.exit(),", "");
|
||||
return expressionWithFixedSymbols.split(",")[0];
|
||||
};
|
||||
|
||||
const getCodeInjection = () => {
|
||||
return ` , !globalThis.pwn3d && (globalThis.pwn3d=true, alert("You've been hacked! You evaluated a string and let me inject code, didn't you? HAHAHAHA!") , globalThis.openDevMenu() ) , ns.exit()`;
|
||||
};
|
||||
|
||||
export const getPassword = (length: number, allowLetters = false): string => {
|
||||
const characters = numbers + (allowLetters ? letters : "");
|
||||
let password = "";
|
||||
const cappedLength = clampNumber(length, 1, MAX_PASSWORD_LENGTH);
|
||||
for (let i = 0; i < cappedLength; i++) {
|
||||
password += characters[Math.floor(Math.random() * characters.length)];
|
||||
}
|
||||
if (!allowLetters && Number(password) > Number.MAX_SAFE_INTEGER) {
|
||||
password = password.slice(0, 15);
|
||||
}
|
||||
// prevent leading zeros in multi-digit numeric passwords
|
||||
if (!allowLetters) {
|
||||
return Number(password).toString();
|
||||
}
|
||||
return password;
|
||||
};
|
||||
|
||||
export const getPasswordType = (password: string): "numeric" | "alphabetic" | "alphanumeric" | "ASCII" | "unicode" => {
|
||||
const passwordArr = password.split("");
|
||||
|
||||
if (passwordArr.every((char) => numbers.includes(char))) {
|
||||
return "numeric";
|
||||
}
|
||||
if (passwordArr.every((char) => letters.includes(char))) {
|
||||
return "alphabetic";
|
||||
}
|
||||
if (passwordArr.every((char) => numbers.includes(char) || letters.includes(char))) {
|
||||
return "alphanumeric";
|
||||
}
|
||||
if (passwordArr.every((char) => char.charCodeAt(0) < 128)) {
|
||||
return "ASCII";
|
||||
}
|
||||
return "unicode";
|
||||
};
|
||||
|
||||
export const romanNumeralEncoder = (input: number): string => {
|
||||
const romanNumerals: { [key: number]: string } = {
|
||||
1: "I",
|
||||
4: "IV",
|
||||
5: "V",
|
||||
9: "IX",
|
||||
10: "X",
|
||||
40: "XL",
|
||||
50: "L",
|
||||
90: "XC",
|
||||
100: "C",
|
||||
400: "CD",
|
||||
500: "D",
|
||||
900: "CM",
|
||||
1000: "M",
|
||||
};
|
||||
|
||||
const keys = Object.keys(romanNumerals).map((key) => Number(key));
|
||||
let result = "";
|
||||
for (let i = keys.length - 1; i >= 0; i--) {
|
||||
const key = keys[i];
|
||||
while (input >= key) {
|
||||
result += romanNumerals[key];
|
||||
input -= key;
|
||||
}
|
||||
}
|
||||
return result || "nulla";
|
||||
};
|
||||
|
||||
export const romanNumeralDecoder = (input: string): number => {
|
||||
if (input.toLowerCase() === "nulla") {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const romanToInt: { [key: string]: number } = {
|
||||
I: 1,
|
||||
V: 5,
|
||||
X: 10,
|
||||
L: 50,
|
||||
C: 100,
|
||||
D: 500,
|
||||
M: 1000,
|
||||
};
|
||||
let total = 0;
|
||||
let prevValue = 0;
|
||||
|
||||
for (let i = input.length - 1; i >= 0; i--) {
|
||||
const currentValue = romanToInt[input[i]];
|
||||
if (currentValue < prevValue) {
|
||||
total -= currentValue;
|
||||
} else {
|
||||
total += currentValue;
|
||||
}
|
||||
prevValue = currentValue;
|
||||
}
|
||||
|
||||
return total;
|
||||
};
|
||||
|
||||
export const smallPrimes = [
|
||||
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
|
||||
];
|
||||
export const largePrimes = [
|
||||
1069, 1409, 1471, 1567, 1597, 1601, 1697, 1747, 1801, 1889, 1979, 1999, 2063, 2207, 2371, 2503, 2539, 2693, 2741,
|
||||
2753, 2801, 2819, 2837, 2909, 2939, 3169, 3389, 3571, 3761, 3881, 4217, 4289, 4547, 4729, 4789, 4877, 4943, 4951,
|
||||
4957, 5393, 5417, 5419, 5441, 5519, 5527, 5647, 5779, 5881, 6007, 6089, 6133, 6389, 6451, 6469, 6547, 6661, 6719,
|
||||
6841, 7103, 7549, 7559, 7573, 7691, 7753, 7867, 8053, 8081, 8221, 8329, 8599, 8677, 8761, 8839, 8963, 9103, 9199,
|
||||
9343, 9467, 9551, 9601, 9739, 9749, 9859,
|
||||
];
|
||||
|
||||
const getLargestPrimeFactorPassword = (difficulty = 1) => {
|
||||
const factorCount = 1 + Math.min(5, Math.floor(difficulty / 3));
|
||||
|
||||
const largePrimeIndex = 2 + Math.floor(Math.random() * (largePrimes.length - 2));
|
||||
const largestPrime = largePrimes[largePrimeIndex];
|
||||
let number = largestPrime;
|
||||
for (let i = 1; i <= factorCount; i++) {
|
||||
number *= smallPrimes[Math.floor(Math.random() * smallPrimes.length)];
|
||||
}
|
||||
|
||||
return {
|
||||
largestPrime: largestPrime,
|
||||
targetNumber: number,
|
||||
};
|
||||
};
|
||||
|
||||
const getPasswordMadeUpOfPrimesProduct = (difficulty = 1) => {
|
||||
const scale = Math.min(difficulty / 2, 15);
|
||||
let password;
|
||||
|
||||
do {
|
||||
password = BigInt(Math.floor(Math.random() * 5 * (scale + 1)) + 1);
|
||||
for (let i = 0; i < scale / 3; i++) {
|
||||
if (Math.random() < 0.5) {
|
||||
password *= BigInt(Math.ceil(Math.random() * 5));
|
||||
} else {
|
||||
password *= BigInt(smallPrimes[Math.floor(Math.random() * smallPrimes.length)]);
|
||||
}
|
||||
}
|
||||
if (difficulty > 12) {
|
||||
password *= BigInt(largePrimes[Math.floor(Math.random() * largePrimes.length)]);
|
||||
}
|
||||
if (difficulty > 24) {
|
||||
password *= BigInt(largePrimes[Math.floor(Math.random() * largePrimes.length)]);
|
||||
}
|
||||
} while (BigInt(Number(password)) !== password); // ensure it fits in JS number precision
|
||||
return password.toString();
|
||||
};
|
||||
Reference in New Issue
Block a user