add "cyclic_weaken.js" feature. hacker.js, main.js, libs: refactor. README.md: update.

[?]
May 14, 2020, 2:51 AM
JGNALSUQSZ42WSYKTRIDDZQ2P4HRICMEJUABH6PDA7RUXBZEGLQAC

Dependencies

  • [2] FBAPT2WW executables: dynamically load help messages. lshw.js, main.js: add options to change score correction method and score factor multipliers. README.md: update.
  • [3] ZVQK2652 executables: fix help message.
  • [4] DJDWBCCQ add missing "nop.js". killall.js: rename to kill.js. add `--script` and `--server` options.
  • [5] CJBGAILA add optional `--target` parameter for `main.js`. update `README.md`.
  • [6] 2WOLGB42 README.md: update. kill.js: fix argument parser. lshw.js: add `-p` option. increase information given. main.js: fix error message. set saner defaults. hacker.js: split some functions into "lib_time.js" and "lib_score.js". ram.js: fix.
  • [7] NQ22FUSW add more options to `main.js`. update `README.md`.
  • [8] ZRPOW4ER hacker.js, lib_time.js: supress error messages. hacker.js: use `getBitNodeMultipliers`. lib_time.js: use `getStats`. README.md: update.
  • [9] HSNSECD5 all: refactor. main.js: fix call to `void_kill_script_named_server_named`.
  • [10] ZNH2OJ3C hacker.js, main.js: added continuous scheduling mode. README.md: update. credits to Mei for helping me figure out how to time the continuous mode and to Kozd for making `sort_by` work with methods.
  • [11] 6MBUKAG6 remove unneeded functions and comments. update readme.
  • [12] SLSWBNYT added ram utilisation logic to `ram.js` and `servers.js`. split and refactored redundant code into separate library files.
  • [13] VMXI7PS4 added action cap. improved scoring system. fixed parts of security and cash predictors. made lshw loopable.
  • [14] 2BKHJI2S init
  • [15] FA3U4WUJ more `README.md` changes.
  • [16] RWMZ7DVL split and refactor various logics. update "README.md".
  • [17] AXGQ7FML split and refactor hacking logic to "hacker.js" and argument parsing and script execution logic to "main.js". update "README.md".
  • [18] Y5OWMCQR all: remove unecessary uses of `exec`. refactor. main.js: add RAM reservation logic. lib_ram.js: renamed to lib_ram_server.js. README.js: update.
  • [19] P6ZJC2SH hacker.js: normalise factors used in server scoring function.
  • [20] BKG7YVUV main.js, hacker.js, servers.js, ram.js: refactor.
  • [21] HLC2L3NJ add "tor.js" and "programs.js". use `minimist` for "main.js" argument parsing.
  • [22] SXEJJKPI executables: add `--help` flag. hacker.js: change scheduling logic. refactor. kill.js: allow handling more than one server or script at a time. cyclic_weaken.js: add. README.md: update.
  • [23] V4DMWF25 remove redundant preparation steps in "hacker.js".

Change contents

  • replacement in sbin/hacker.js at line 2
    [3.1179][3.1179:1308]()
    * add a function that grinds XP by running cyclic weaken on a server using the remaining ram
    * remove unecessary cloning/copying
    [3.42]
    [3.1308]
    * refactor unecessary cloning/copying
  • replacement in sbin/hacker.js at line 18
    [3.1208][3.1208:1253]()
    object_get_constants(ns).array_workers
    [3.1208]
    [3.1253]
    [
    string_get_script(ns, "weaken"),
    string_get_script(ns, "grow"),
    string_get_script(ns, "hack")
    ]
  • edit in sbin/hacker.js at line 34
    [3.1887][3.1887:1946]()
    float_get_server_ram_total,
    float_get_server_ram_used,
  • edit in sbin/hacker.js at line 36
    [3.2002]
    [3.2002]
    import { object_make_server_used } from "lib_server_used.js";
    import {
    object_get_server_ram_free_biggest,
    array_get_servers_used_updated,
    array_make_servers,
    object_get_clone,
    array_get_clone,
    float_clamp,
    } from "lib_no_ns.js";
  • edit in sbin/hacker.js at line 56
    [3.2285][3.2285:3124]()
    // https://stackoverflow.com/a/54157459
    const object_get_clone = function (object_original) {
    const object_serialised_deserialised = JSON.parse(
    JSON.stringify(object_original)
    ),
    object_merged = Object.assign({}, object_original);
    return (
    Object.assign(object_merged, object_serialised_deserialised), object_merged
    );
    };
    const array_get_clone = function (array_original) {
    const array = [];
    for (
    let integer_index_element = 0;
    integer_index_element < array_original.length;
    ++integer_index_element
    ) {
    const element = array_original[integer_index_element];
    switch (typeof element) {
    case "object":
    // fall-through
    case "function":
    array.push(object_get_clone(element));
    break;
    default:
    array.push(element);
    }
    }
    return array;
    };
  • replacement in sbin/hacker.js at line 138
    [3.21058][3.21058:21168]()
    // filenames and ram cost of helper object_scripts
    array_workers: ["weaken.js", "grow.js", "hack.js"]
    [3.20998]
    [3.21168]
    // filenames and ram cost of helper scripts
    array_workers: [
    {
    name: "weaken.js",
    ram: function () {
    return ns.getScriptRam(this.name);
    },
    },
    {
    name: "grow.js",
    ram: function () {
    return ns.getScriptRam(this.name);
    },
    },
    {
    name: "hack.js",
    ram: function () {
    return ns.getScriptRam(this.name);
    },
    },
    ],
  • replacement in sbin/cyclic_weaken.js at line 2
    [3.53430][3.53430:53489]()
    for (;;) await ns.weaken(ns.args[0]), await ns.sleep(1);
    [3.53430]
    [3.53489]
    const
    float_duration_sleep = ns.args[0] * 1e3,
    parent_window = parent["window"];
    for (
    parent_window.d = parent_window["document"],
    parent_window.w = parent_window;
    ;
    ) {
    try {
    await ns.weaken(d.string_server_target);
    } catch (error) {
    await ns.sleep(float_duration_sleep);
    }
    await ns.sleep(float_duration_sleep);
    }
  • file deletion: lib_ram_script.js (----------)
    [3.3711246][3.3722722:3722763](),[3.3722763][3.3722357:3722357]()
    // lib_ram_script.js - 1.7GB
    // return true if a server has enough RAM to run a script with a stated number of threads
    export const boolean_can_server_run_script_threads = function (
    ns,
    float_server_used_ram_free,
    string_script,
    integer_threads
    ) {
    return !(
    ns.getScriptRam(string_script) * integer_threads >
    float_server_used_ram_free
    );
    };
  • replacement in lib/lib_ram_server.js at line 41
    [3.3721554][3.3721554:3721678]()
    // returns the RAM utilisation of the botnet as a decimal
    export const float_get_network_ram_utilisation = function (ns) {
    [3.3721055]
    [3.3308]
    // returns total free ram of the botnet
    export const float_get_network_ram_free = function (ns) {
  • replacement in lib/lib_ram_server.js at line 44
    [3.3319][3.3319:3453](),[3.3453][3.3721753:3722081](),[3.3721753][3.3721753:3722081]()
    float_get_network_ram_trait(ns, float_get_server_ram_used) /
    float_get_network_ram_trait(ns, float_get_server_ram_total)
    );
    };
    // sort an array of servers by their amounts of RAM, from lowest to highest
    const void_sort_by_server_ram = function (ns, array_servers) {
    return array_servers.sort(
    (string_element_0, string_element_1) =>
    float_get_server_ram_total(ns, string_element_0) -
    float_get_server_ram_total(ns, string_element_1)
    [3.3319]
    [3.3722081]
    float_get_network_ram_trait(ns, float_get_server_ram_total) -
    float_get_network_ram_trait(ns, float_get_server_ram_used)
  • replacement in lib/lib_ram_server.js at line 49
    [3.3722090][3.3722090:3722219]()
    export const array_get_servers_rooted_sorted_by_ram = function (ns) {
    let array_servers_rooted = array_get_servers_rooted(ns);
    [3.3722090]
    [3.3722219]
    // returns the RAM utilisation of the botnet as a decimal
    export const float_get_network_ram_utilisation = function (ns) {
  • replacement in lib/lib_ram_server.js at line 52
    [3.3722230][3.3722230:3722306]()
    void_sort_by_server_ram(ns, array_servers_rooted), array_servers_rooted
    [3.3722230]
    [3.3722306]
    float_get_network_ram_trait(ns, float_get_server_ram_used) /
    float_get_network_ram_trait(ns, float_get_server_ram_total)
  • replacement in lib/lib_ps.js at line 4
    [3.3724687][3.3724687:3724756]()
    export const boolean_script_running = function (ns, string_script) {
    [3.3724687]
    [3.3724756]
    const boolean_script_running = function (ns, string_script) {
  • edit in lib/lib_no_ns.js at line 2
    [3.63]
    [3.63]
    // make an array of server objects
    export const array_make_servers = function (
    ns,
    array_method_get_servers,
    object_method_make_server
    ) {
    const
    array_servers = array_method_get_servers(ns),
    array_object_servers = [];
    for (
    let integer_index_server = 0;
    integer_index_server < array_servers.length;
    ++integer_index_server
    ) {
    const string_server = array_servers[integer_index_server];
    array_object_servers.push(object_method_make_server(ns, string_server));
    }
    return array_object_servers;
    };
    // return a new array of useable server state objects after simulating a change based on a schedule item on to an older array of useable server state objects
    export const array_get_servers_used_updated = function (
    array_servers_used,
    object_job
    ) {
    // clone array of servers used to preserve original
    const array_servers_used_updated = array_get_clone(array_servers_used);
    for (
    let integer_index_server = 0;
    integer_index_server < array_servers_used_updated.length;
    ++integer_index_server
    ) {
    const object_server = array_servers_used_updated[integer_index_server];
    object_server.name === object_job.string_server_used &&
    object_server.apply_job(object_job);
    }
    return array_servers_used_updated;
    };
  • edit in lib/lib_no_ns.js at line 43
    [3.64]
    [3.64]
    // https://stackoverflow.com/a/979325. Thanks Kozd for making this work with methods.
    const sort_by = (
    string_property,
    boolean_reverse,
    primer
    ) => {
    const key = function (object) {
    return primer
    ? primer(object, string_property)
    : object[string_property];
    };
    return function (element_0, element_1) {
    return (
    (element_0 = key(element_0)),
    (element_1 = key(element_1)),
    (boolean_reverse ? -1 : 1) *
    ((element_0 > element_1) - (element_1 > element_0))
    );
    };
    };
    // wrapper function to evaluate method property given
    const void_method_evaluate = function (
    object,
    string_property
    ) {
    return object[string_property]();
    };
    // returns server object with biggest ram free from array of server objects
    export const object_get_server_ram_free_biggest = function (array_servers) {
    const array_server_sorted_ram = array_get_clone(array_servers).sort(
    sort_by("get_ram_free", !1, void_method_evaluate)
    );
    return array_server_sorted_ram[array_server_sorted_ram.length - 1];
    };
    // https://github.com/30-seconds/30-seconds-of-code/commit/611729214af360d935b8a96ec9c5a45f2c7bfb37
    // Clamps float_number within the inclusive range specified by the boundary values float_limit_a and float_limit_b.
    // If float_number falls within the range, return float_number. Otherwise, return the nearest number in the range.
    export const float_clamp = function (
    float_number,
    float_limit_a,
    float_limit_b
    ) {
    return Math.max(
    Math.min(float_number, Math.max(float_limit_a, float_limit_b)),
    Math.min(float_limit_a, float_limit_b)
    );
    };
    // https://stackoverflow.com/a/54157459
    export const object_get_clone = function (object_original) {
    const object_serialised_deserialised = JSON.parse(
    JSON.stringify(object_original)
    ),
    object_merged = Object.assign({}, object_original);
    return (
    Object.assign(object_merged, object_serialised_deserialised), object_merged
    );
    };
    export const array_get_clone = function (array_original) {
    const array = [];
    for (
    let integer_index_element = 0;
    integer_index_element < array_original.length;
    ++integer_index_element
    ) {
    const element = array_original[integer_index_element];
    switch (typeof element) {
    case "object":
    // fall-through
    case "function":
    array.push(object_get_clone(element));
    break;
    default:
    array.push(element);
    }
    }
    return array;
    };
  • edit in bin/main.js at line 10
    [3.9720]
    [3.9720]
    object_get_server_ram_free_biggest,
    array_get_servers_used_updated,
    array_make_servers,
  • replacement in bin/main.js at line 14
    [3.9739][3.9739:9764]()
    object_parse_arguments
    [3.9739]
    [3.9764]
    object_parse_arguments,
    float_clamp
  • edit in bin/main.js at line 17
    [3.9787][3.9672:9777](),[3.1656][3.9672:9777]()
    import { array_get_servers } from "lib_servers.js";
    import { array_get_files_to_copy } from "lib_ls.js";
  • replacement in bin/main.js at line 19
    [3.1805][3.9778:9820]()
    array_get_servers_rooted_sorted_by_ram,
    [3.1805]
    [3.9820]
    float_get_network_ram_free,
    array_get_servers_useable
  • edit in bin/main.js at line 22
    [3.9848]
    import { object_make_server_used } from "lib_server_used.js";
    import { array_get_servers } from "lib_servers.js";
    import { array_get_files_to_copy } from "lib_ls.js";
    import { void_kill_script_named_server_named } from "lib_kill.js";
    const object_get_constants = function () {
    return {
    // default values
    object_defaults: {
    // time period used for checking the time in seconds
    float_period_check_seconds: 10,
    // duration between each job used to prevent collisions between them to keep them in sequence
    float_padding_seconds: 2,
    // maximum amount of jobs to execute per schedule, used to prevent using up too much IRL RAM
    integer_job_cap: 100,
    // name of purchased servers
    string_servers_bought_name: "server",
    // target
    string_server_target: "",
    // precision of the percentage to steal calculator
    float_precision: 0.01,
    // ram utilisiation threshold. upgrade ram or buy or replace servers when reached.
    float_ram_utilisation_threshold: 0.9,
    // the maximum percentage of cash that should be stolen from a server
    float_steal_cap: 0.9,
    // multiplier for skill factor used in server scoring system
    float_multiplier_factor_skill: 1,
    // multiplier for max cash factor used in server scoring system
    float_multiplier_factor_max_cash: 1,
    // multiplier for growth factor used in server scoring system
    float_multiplier_factor_growth: 1,
    // correction method for factors used in server scoring system. can be "standard" or "normal"
    string_method_score_correction: "standard",
    // fraction of botnet's ram to use for cyclic weaken
    float_ram_fraction_for_weaken_cyclic: 0.5,
    // whether to run discrete batches or continuously
    boolean_discrete: !1,
    },
    // helper scripts
    object_helpers: {
    string_nop: "nop.js",
    string_hacker: "hacker.js",
    string_ram: "ram.js",
    string_servers: "servers.js",
    string_tor: "tor.js",
    string_programs: "programs.js",
    string_botnet: "botnet.js",
    string_cyclic_weaken: "cyclic_weaken.js",
    },
    // argument names
    object_argument_names: {
    check_delay: {
    short: "c",
    long: "check-delay",
    },
    job_delay: {
    short: "d",
    long: "job-delay",
    },
    discrete: {
    short: "f",
    long: "discrete",
    },
    help: {
    short: "h",
    long: "help",
    },
    target: {
    short: "i",
    long: "target",
    },
    job_cap: {
    short: "j",
    long: "job-cap",
    },
    multiplier_skill: {
    short: "k",
    long: "multiplier-skill",
    },
    multiplier_cash: {
    short: "l",
    long: "multiplier-cash",
    },
    multiplier_growth: {
    short: "m",
    long: "multiplier-growth",
    },
    server_name: {
    short: "n",
    long: "server-name",
    },
    precision: {
    short: "p",
    long: "precision",
    },
    score_correction: {
    short: "q",
    long: "score-correction",
    },
    ram_utilisation: {
    short: "r",
    long: "ram-utilisation",
    },
    steal_cap: {
    short: "s",
    long: "steal-cap",
    },
    cyclic_weaken: {
    short: "u",
    long: "cyclic-weaken",
    },
    ram_cyclic_weaken: {
    short: "v",
    long: "ram-cyclic-weaken",
    },
    ram: {
    short: "a",
    long: "ram",
    },
    servers: {
    short: "e",
    long: "servers",
    },
    tor: {
    short: "o",
    long: "tor",
    },
    programs: {
    short: "g",
    long: "programs",
    },
    botnet: {
    short: "b",
    long: "botnet",
    },
    },
    };
    };
    // main
    export const main = async function (ns) {
    // variables
    const
    // defaults
    object_defaults = object_get_constants().object_defaults,
    // argument names
    object_argument_names = object_get_constants().object_argument_names,
    // helper scripts
    object_helpers = object_get_constants().object_helpers,
    // threads of "nop.js" required to reserve enough RAM to run "hacker.js"
    integer_threads_nop = integer_get_threads_nop(ns, object_helpers.string_hacker, ns.getScriptName());
    // if this server doesn't have enough RAM to run "hacker.js", eject
    if (
    !boolean_can_server_run_script_threads(
    ns,
    float_get_server_ram_free(ns, ns.getHostname()),
    object_helpers.string_nop,
    integer_threads_nop
    )
    ) {
    const string_message_error = `This server has insufficient RAM to run "${object_helpers.string_nop}" with "${integer_threads_nop}" thread(s).`;
    throw (ns.tprint(`ERROR: ${integer_threads_nop}`), new Error(string_message_error));
    }
    let
    string_servers_bought_name = object_defaults.string_servers_bought_name,
    integer_job_cap = object_defaults.integer_job_cap,
    float_padding_seconds = object_defaults.float_padding_seconds,
    float_precision = object_defaults.float_precision,
    float_steal_cap = object_defaults.float_steal_cap,
    float_period_check_seconds = object_defaults.float_period_check_seconds,
    string_server_target = object_defaults.string_server_target,
    float_ram_utilisation_threshold = object_defaults.float_ram_utilisation_threshold,
    float_multiplier_factor_skill = object_defaults.float_multiplier_factor_skill,
    float_multiplier_factor_max_cash = object_defaults.float_multiplier_factor_max_cash,
    float_multiplier_factor_growth = object_defaults.float_multiplier_factor_growth,
    string_method_score_correction = object_defaults.string_method_score_correction,
    float_ram_fraction_for_weaken_cyclic = object_defaults.float_ram_fraction_for_weaken_cyclic,
    boolean_discrete = object_defaults.boolean_discrete,
    // whether to display help and exit
    boolean_print_help = !1;
    // argument parsing
    const object_arguments = object_parse_arguments(ns.args);
    for (const string_argument in object_arguments)
    if (object_arguments.hasOwnProperty(string_argument)) {
    const argument_value = object_arguments[string_argument];
    switch (string_argument) {
    case object_argument_names.check_delay.short:
    // fall-through
    case object_argument_names.check_delay.long:
    float_period_check_seconds = argument_value;
    break;
    case object_argument_names.job_delay.short:
    // fall-through
    case object_argument_names.job_delay.long:
    float_padding_seconds = argument_value;
    break;
    case object_argument_names.discrete.short:
    // fall-through
    case object_argument_names.discrete.long:
    boolean_discrete = argument_value;
    break;
    case object_argument_names.help.short:
    // fall-through
    case object_argument_names.help.long:
    boolean_print_help = argument_value;
    break;
    case object_argument_names.target.short:
    // fall-through
    case object_argument_names.target.long:
    string_server_target = argument_value;
    break;
    case object_argument_names.job_cap.short:
    // fall-through
    case object_argument_names.job_cap.long:
    integer_job_cap = argument_value;
    break;
    case object_argument_names.multiplier_skill.short:
    // fall-through
    case object_argument_names.multiplier_skill.long:
    float_multiplier_factor_skill = argument_value;
    break;
    case object_argument_names.multiplier_cash.short:
    // fall-through
    case object_argument_names.multiplier_cash.long:
    float_multiplier_factor_max_cash = argument_value;
    break;
    case object_argument_names.multiplier_growth.short:
    // fall-through
    case object_argument_names.multiplier_growth.long:
    float_multiplier_factor_growth = argument_value;
    break;
    case object_argument_names.server_name.short:
    // fall-through
    case object_argument_names.server_name.long:
    string_servers_bought_name = argument_value;
    break;
    case object_argument_names.precision.short:
    // fall-through
    case object_argument_names.precision.long:
    float_precision = argument_value;
    break;
    case object_argument_names.score_correction.short:
    // fall-through
    case object_argument_names.score_correction.long:
    string_method_score_correction = argument_value;
    break;
    case object_argument_names.ram_utilisation.short:
    // fall-through
    case object_argument_names.ram_utilisation.long:
    float_ram_utilisation_threshold = argument_value;
    break;
    case object_argument_names.steal_cap.short:
    // fall-through
    case object_argument_names.steal_cap.long:
    float_steal_cap = argument_value;
    break;
    case object_argument_names.ram_cyclic_weaken.short:
    // fall-through
    case object_argument_names.ram_cyclic_weaken.long:
    float_ram_fraction_for_weaken_cyclic = argument_value;
    break;
    }
    }
    let array_helpers = [
    {
    file: object_helpers.string_ram,
    threads_or_ram_botnet: 1,
    args: [float_period_check_seconds, float_ram_utilisation_threshold],
    },
    {
    file: object_helpers.string_servers,
    threads_or_ram_botnet: 1,
    args: [
    float_period_check_seconds,
    string_servers_bought_name,
    float_ram_utilisation_threshold,
    ],
    },
    {
    file: object_helpers.string_tor,
    threads_or_ram_botnet: 1,
    args: [float_period_check_seconds],
    },
    {
    file: object_helpers.string_programs,
    threads_or_ram_botnet: 1,
    args: [
    float_period_check_seconds,
    [
    "BruteSSH.exe",
    "FTPCrack.exe",
    "relaySMTP.exe",
    "HTTPWorm.exe",
    "SQLInject.exe",
    "DeepscanV1.exe",
    "DeepscanV2.exe",
    "Autolink.exe",
    ],
    ],
    },
    {
    file: object_helpers.string_botnet,
    threads_or_ram_botnet: 1,
    args: [float_period_check_seconds],
    },
    {
    file: object_helpers.string_cyclic_weaken,
    threads_or_ram_botnet: float_ram_fraction_for_weaken_cyclic,
    args: [float_period_check_seconds],
    },
    ];
    for (const string_argument in object_arguments)
    if (object_arguments.hasOwnProperty(string_argument)) {
    const argument_value = object_arguments[string_argument];
    ((object_argument_names.ram.short === string_argument && argument_value) ||
    (object_argument_names.ram.long === string_argument && !argument_value)) &&
    array_helpers.splice(
    integer_get_index_of_file(array_helpers, object_helpers.string_ram),
    1
    ),
    ((object_argument_names.servers.short === string_argument && argument_value) ||
    (object_argument_names.servers.long === string_argument && !argument_value)) &&
    array_helpers.splice(
    integer_get_index_of_file(
    array_helpers,
    object_helpers.string_servers
    ),
    1
    ),
    ((object_argument_names.tor.short === string_argument && argument_value) ||
    (object_argument_names.tor.long === string_argument && !argument_value)) &&
    array_helpers.splice(
    integer_get_index_of_file(array_helpers, object_helpers.string_tor),
    1
    ),
    ((object_argument_names.programs.short === string_argument && argument_value) ||
    (object_argument_names.programs.long === string_argument && !argument_value)) &&
    array_helpers.splice(
    integer_get_index_of_file(
    array_helpers,
    object_helpers.string_programs
    ),
    1
    ),
    ((object_argument_names.botnet.short === string_argument && argument_value) ||
    (object_argument_names.botnet.long === string_argument && !argument_value)) &&
    array_helpers.splice(
    integer_get_index_of_file(
    array_helpers,
    object_helpers.string_botnet
    ),
    1
    ),
    ((object_argument_names.cyclic_weaken.short === string_argument && argument_value) ||
    (object_argument_names.cyclic_weaken.long === string_argument && !argument_value)) &&
    array_helpers.splice(
    integer_get_index_of_file(
    array_helpers,
    object_helpers.string_cyclic_weaken
    ),
    1
    );
    }
    // main
    if (boolean_print_help)
    return void_print_help(ns);
    // reserve enough RAM for "hacker.js"
    ns.exec(object_helpers.string_nop, ns.getHostname(), integer_threads_nop);
    void_copy_scripts(ns);
    void_schedule_script_runner(ns, array_make_schedule_script(ns, array_helpers));
    // kill "nop.js" scripts to free RAM
    void_kill_script_named_server_named(
    ns,
    ns.getHostname(),
    object_helpers.string_nop
    );
    ns.spawn(
    object_helpers.string_hacker,
    1,
    integer_job_cap,
    float_precision,
    float_steal_cap,
    float_padding_seconds,
    boolean_discrete,
    string_method_score_correction,
    float_multiplier_factor_skill,
    float_multiplier_factor_max_cash,
    float_multiplier_factor_growth,
    string_server_target
    );
    };
    // functions
    const void_print_help = function (ns) {
    const
    object_defaults = object_get_constants().object_defaults,
    object_argument_names = object_get_constants().object_argument_names,
    object_helpers = object_get_constants().object_helpers;
    ns.tprint(
    string_sanitise(`
    USAGE
    run ${ns.getScriptName()} [FLAGS ...] [OPTIONS ...]
    FLAGS
    -${object_argument_names.ram.short}, --no-${object_argument_names.ram.long}
    Prevents the "${object_helpers.string_nop}" script from being started which is responsible for upgrading the RAM of the "home" server.
    -${object_argument_names.botnet.short}, --no-${object_argument_names.botnet.long}
    Prevents the "${object_helpers.string_botnet}" script from being started which is responsible for rooting servers in the network.
    -${object_argument_names.servers.short}, --no-${object_argument_names.servers.long}
    Prevents the "${object_helpers.string_servers}" script from being started which is responsible for buying and replacing bought servers.
    -${object_argument_names.discrete.short}, --${object_argument_names.discrete.long}
    Run discrete batches instead of continuously. The former mode has downtime but may result in more correctly scheduled jobs compared to the latter.
    -${object_argument_names.programs.short}, --no-${object_argument_names.programs.long}
    Prevents the "${object_helpers.string_programs}" script from being started which is responsible for buying programs from the "darkweb" server.
    -${object_argument_names.help.short}, --${object_argument_names.help.long}
    Displays this message then exits.
    -${object_argument_names.tor.short}, --no-${object_argument_names.tor.long}
    Prevents the "${object_helpers.string_tor}" script from being started which is responsible for buying a TOR Router.
    -${object_argument_names.cyclic_weaken.short}, --no-${object_argument_names.cyclic_weaken.long}
    Prevents the "${object_helpers.string_cyclic_weaken}" script from being started which is responsible for running \`weaken\` continuously to gain hacking experience.
    OPTIONS
    -${object_argument_names.check_delay.short}, --${object_argument_names.check_delay.long} <SECONDS>
    SECONDS = The duration of delay between each main loop used in helper scripts, in seconds. Should be a floating-point number > 0. Defaults to ${object_defaults.float_period_check_seconds}.
    -${object_argument_names.job_delay.short}, --${object_argument_names.job_delay.long} <SECONDS>
    SECONDS = The duration of delay between each job, in seconds. Should be a floating-point number > 0. Defaults to ${object_defaults.float_padding_seconds}.
    -${object_argument_names.target.short}, --${object_argument_names.target.long} <SERVER>
    SERVER = The server that should be targetted by the \`weaken\`, \`grow\` and \`hack\` functions. Should be a string. Defaults to choosing an optimal target using a scoring system based on the server's maximum cash, growth, required hacking level, and the player's current hacking level.
    -${object_argument_names.job_cap.short}, --${object_argument_names.job_cap.long} <CAP>
    CAP = The maximum amount of jobs to execute per schedule. This is ignored when running in continuous mode. Should be an integer > 0. Defaults to ${object_defaults.integer_job_cap}.
    -${object_argument_names.server_name.short}, --${object_argument_names.server_name.long} <NAME>
    NAME = The name to be used for purchased servers. Should be a string. Defaults to "${object_defaults.string_servers_bought_name}".
    -${object_argument_names.precision.short}, --${object_argument_names.precision.long} <PRECISION>
    PRECISION = A value used in determining how many cycles of bisection the binary search algorithm used for the percentage to steal calculator should use. Should be a floating point number > 0 <= 1. Values closer to 0 will result in greater precision in the calculation, but potentially longer run-times and compared to values closer to 1. Defaults to ${object_defaults.float_precision}.
    -${object_argument_names.ram_utilisation.short}, --${object_argument_names.ram_utilisation.long} <THRESHOLD>
    THRESHOLD = The botnet's ram utilisation threshold after which upgrades/replacements should be bought for servers and the RAM of "home". Should be a floating point number >= 0 <= 1. Values closer to 0 will result in attempting more frequent upgrades/replacements at the cost of less efficient RAM utilisation to cash spenditure ratios. Defaults to ${object_defaults.float_ram_utilisation_threshold}.
    -${object_argument_names.steal_cap.short}, --${object_argument_names.steal_cap.long} <CAP>
    CAP = The maximum fraction of cash to steal from the target server per \`hack\` job. Should be an integer >= 0 <=1. Defaults to ${object_defaults.float_steal_cap}.
    -${object_argument_names.multiplier_skill.short}, --${object_argument_names.multiplier_skill.long} <FLOAT>
    FLOAT = The multiplier used to change the weight of the factor representing your skill against the target server used in the server scoring system. Should a floating point number. 1 = factor has normal importance, > 1 = factor has more importance, < 1 = factor has less importance, 0 = factor is not used, < 0 = factor has negative effect. Defaults to ${object_defaults.float_multiplier_factor_skill}.
    -${object_argument_names.multiplier_cash.short}, --${object_argument_names.multiplier_cash.long} <FLOAT>
    FLOAT = The multiplier used to change the weight of the factor representing the target server's maximum cash used in the server scoring system. Should a floating point number. 1 = factor has normal importance, > 1 = factor has more importance, < 1 = factor has less importance, 0 = factor is not used, < 0 = factor has negative effect. Defaults to ${object_defaults.float_multiplier_factor_max_cash}.
    -${object_argument_names.multiplier_growth.short}, --${object_argument_names.multiplier_growth.long} <FLOAT>
    FLOAT = The multiplier used to change the weight of the factor representing the target server's growth used in the server scoring system. Should a floating point number. 1 = factor has normal importance, > 1 = factor has more importance, < 1 = factor has less importance, 0 = factor is not used, < 0 = factor has negative effect. Defaults to ${object_defaults.float_multiplier_factor_growth}.
    -${object_argument_names.score_correction.short}, --${object_argument_names.score_correction.long} <METHOD>
    METHOD = The method used to correct the factors used in the server scoring system. Can be "standard" (uses standard scoring) or "normal" (uses mean normalised scoring). Defaults to "${object_defaults.string_method_score_correction}".
    -${object_argument_names.ram_cyclic_weaken.short}, --${object_argument_names.ram_cyclic_weaken.long} <FLOAT>
    FLOAT = The fraction of the botnet's current available RAM to be used to run ${object_helpers.string_cyclic_weaken}. Should be a floating point number > 0. Defaults to ${object_defaults.float_ram_fraction_for_weaken_cyclic}.`
    )
    );
    };
    // return true if a server has enough RAM to run a script with a stated number of threads
    const boolean_can_server_run_script_threads = function (
    ns,
    float_server_used_ram_free,
    string_script,
    integer_threads
    ) {
    return !(
    ns.getScriptRam(string_script) * integer_threads >
    float_server_used_ram_free
    );
    };
    // return the difference in RAM requirements between two scripts
    const float_get_ram_difference = function (
    ns,
    string_script_0,
    string_script_1
    ) {
    return ns.getScriptRam(string_script_0) - ns.getScriptRam(string_script_1);
    };
    // return the amount of threads of "nop.js" is required to make up RAM difference between two scripts
    const integer_get_threads_nop = function (
    ns,
    string_script_0,
    string_script_1
    ) {
    return Math.ceil(
    float_get_ram_difference(ns, string_script_0, string_script_1) /
    ns.getScriptRam(object_get_constants().object_helpers.string_nop)
    );
    };
    // copies files to all rooted servers
    const void_copy_files_to_servers = function (
    ns,
    array_files,
    string_source
    ) {
    const array_servers = array_get_servers(ns);
    for (
    let integer_index_server = 0;
    integer_index_server < array_servers.length;
    ++integer_index_server
    )
    for (
    let integer_index_file = 0;
    integer_index_file < array_files.length;
    ++integer_index_file
    )
    ns.scp(
    array_files[integer_index_file],
    string_source,
    array_servers[integer_index_server]
    );
    };
    // copy all scripts from the current server to all servers
    const void_copy_scripts = function (ns) {
    const string_host = ns.getHostname(),
    array_script_extensions = [".js", ".ns", ".script"];
    for (
    let integer_index_extension = 0;
    integer_index_extension < array_script_extensions.length;
    ++integer_index_extension
    ) {
    const string_extension = array_script_extensions[integer_index_extension];
    void_copy_files_to_servers(
    ns,
    array_get_files_to_copy(ns, string_host, string_extension),
    string_host
    );
    }
    };
    // returns the index of the scripts array which matches the filename input
    const integer_get_index_of_file = function (
    array_scripts,
    string_file
    ) {
    for (
    let integer_index_script = 0;
    integer_index_script < array_scripts.length;
    ++integer_index_script
    ) {
    const object_script = array_scripts[integer_index_script],
    string_script_file = object_script.file;
    if (string_script_file === string_file) return integer_index_script;
    }
    };
    // scheduling
    // makes a script schedule
    const array_make_schedule_script = function (ns, array_scripts) {
    const array_schedule = [];
    let array_servers_used = array_make_servers(
    ns,
    array_get_servers_useable,
    object_make_server_used
    );
    for (
    let integer_index_script = 0;
    integer_index_script < array_scripts.length;
    ++integer_index_script
    ) {
    const
    object_script = array_scripts[integer_index_script],
    string_script = object_script.file,
    threads_or_ram_botnet = object_script.threads_or_ram_botnet,
    array_arguments = object_script.args;
    if (
    object_get_server_ram_free_biggest(array_servers_used).can_run_job(
    string_script,
    1
    )
    ) {
    let integer_threads = 0;
    for (
    integer_threads =
    threads_or_ram_botnet >= 1
    ? threads_or_ram_botnet
    // assume we want to utilise fraction of botnet ram instead
    : Math.floor(
    (float_get_network_ram_free(ns) * threads_or_ram_botnet) /
    ns.getScriptRam(string_script)
    );
    integer_threads > 0 &&
    object_get_server_ram_free_biggest(array_servers_used).can_run_job(
    string_script,
    1
    );
    ) {
    const object_server_ram_free_biggest = object_get_server_ram_free_biggest(
    array_servers_used
    ),
    object_job = {
    string_script: string_script,
    string_server_used: object_server_ram_free_biggest.name,
    integer_threads: float_clamp(
    integer_threads,
    1,
    object_server_ram_free_biggest.get_threads_max(string_script)
    ),
    args: array_arguments,
    };
    (integer_threads -= object_job.integer_threads),
    array_schedule.push(object_job),
    (array_servers_used = array_get_servers_used_updated(
    array_servers_used,
    object_job
    ));
    }
    integer_threads > 0 &&
    ns.tprint(
    `WARNING: Failed to run the remaining ${integer_threads} threads of "${string_script}". Skipped.`
    );
    } else ns.tprint(`WARNING: Unable to find a server to run "${string_script}". Skipped.`);
    }
    debugger;
    return array_schedule;
    };
    // runs a script schedule
    const void_schedule_script_runner = function (
    ns,
    array_schedule
    ) {
    for (
    let integer_index_job = 0;
    integer_index_job < array_schedule.length;
    ++integer_index_job
    ) {
    const object_job = array_schedule[integer_index_job];
    ns.exec(
    object_job.string_script,
    object_job.string_server_used,
    object_job.integer_threads,
    ...object_job.args,
    integer_index_job
    );
    }
    };
  • replacement in README.md at line 13
    [3.2772][3.2772:2955]()
    * Run the helper scripts "ram.js"\* (6.6 GB), "servers.js" (8.85 GB), "tor.js"\* (3.8 GB), "programs.js"\* (3.7 GB) and "botnet.js" (2.2 GB) which should, respectively, attempt to:
    [3.2772]
    [3.5832]
    * Run the helper scripts "ram.js"\* (6.6 GB), "servers.js" (8.85 GB), "tor.js"\* (3.8 GB), "programs.js"\* (3.7 GB), "botnet.js" (2.2 GB) and variable threads of "cyclic_weaken.js" (1.75 GB) which should, respectively, attempt to:
  • edit in README.md at line 19
    [3.6016]
    [3.4327]
    * Gain hacking experience by continuously weakening the server that will be targeted by "hacker.js".
  • replacement in README.md at line 22
    [3.2396][3.3595:3643]()
    * Wait until the current schedule finishes.
    [3.2396]
    [3.3444]
    * Wait for a duration of time such that jobs in a new schedule will start finishing after the older schedule finishes.
  • replacement in README.md at line 60
    [3.3559][2.12058:12203]()
    * Prevents the "cyclic_weaken.js" script from being started which is responsible for running `weaken` continuously to gain hacking experience.
    [3.3559]
    [2.12203]
    * Prevents the "cyclic_weaken.js" script from being started which is responsible for running `weaken` continuously against the server targeted by "hacker.js" to gain hacking experience.
  • replacement in README.md at line 77
    [3.6192][3.16538:16643]()
    * CAP = The maximum amount of jobs to execute per schedule. Should be an integer > 0. Defaults to 300.
    [3.6192]
    [3.3873389]
    * CAP = The maximum amount of jobs to execute per schedule. This is ignored when running in continuous mode. Should be an integer > 0. Defaults to 100.
  • replacement in README.md at line 93
    [3.3873925][3.17589:17731]()
    * CAP = The maximum amount fraction of cash to steal from the target server per `hack` job. Should be an integer >= 0 <=1. Defaults to 0.9.
    [3.3873925]
    [2.12205]
    * CAP = The maximum fraction of cash to steal from the target server per `hack` job. Should be an integer >= 0 <=1. Defaults to 0.9.
  • replacement in README.md at line 96
    [2.12241][2.12241:12598]()
    FLOAT = The multiplier used to change the weight of the factor representing your skill against the target server used in the server scoring system. Should a floating point number. 1 = factor has normal importance, > 1 = factor has more importance, < 1 = factor has less importance, 0 = factor is not used, < 0 = factor has negative effect. Defaults to 1.
    [2.12241]
    [2.12598]
    * FLOAT = The multiplier used to change the weight of the factor representing your skill against the target server used in the server scoring system. Should a floating point number. 1 = factor has normal importance, > 1 = factor has more importance, < 1 = factor has less importance, 0 = factor is not used, < 0 = factor has negative effect. Defaults to 1.
  • replacement in README.md at line 100
    [2.12633][2.12633:12988]()
    FLOAT = The multiplier used to change the weight of the factor representing the target server's maximum cash used in the server scoring system. Should a floating point number. 1 = factor has normal importance, > 1 = factor has more importance, < 1 = factor has less importance, 0 = factor is not used, < 0 = factor has negative effect. Defaults to 1.
    [2.12633]
    [2.12988]
    * FLOAT = The multiplier used to change the weight of the factor representing the target server's maximum cash used in the server scoring system. Should a floating point number. 1 = factor has normal importance, > 1 = factor has more importance, < 1 = factor has less importance, 0 = factor is not used, < 0 = factor has negative effect. Defaults to 1.
  • replacement in README.md at line 104
    [2.13025][2.13025:13374]()
    FLOAT = The multiplier used to change the weight of the factor representing the target server's growth used in the server scoring system. Should a floating point number. 1 = factor has normal importance, > 1 = factor has more importance, < 1 = factor has less importance, 0 = factor is not used, < 0 = factor has negative effect. Defaults to 1.
    [2.13025]
    [2.13374]
    * FLOAT = The multiplier used to change the weight of the factor representing the target server's growth used in the server scoring system. Should a floating point number. 1 = factor has normal importance, > 1 = factor has more importance, < 1 = factor has less importance, 0 = factor is not used, < 0 = factor has negative effect. Defaults to 1.
  • replacement in README.md at line 108
    [2.13411][2.13411:13608]()
    METHOD = The method used to correct the factors used in the server scoring system. Can be "standard" (uses standard scoring) or "normal" (uses mean normalised scoring). Defaults to "standard".
    [2.13411]
    [2.13608]
    * METHOD = The method used to correct the factors used in the server scoring system. Can be "standard" (uses standard scoring) or "normal" (uses mean normalised scoring). Defaults to "standard".
  • replacement in README.md at line 112
    [2.13647][2.13647:13794]()
    FLOAT = The fraction of the botnet's available RAM to be used to run cyclic_weaken.js. Should be a floating point number > 0. Defaults to 0.5.
    [2.13647]
    [2.13794]
    * FLOAT = The fraction of the botnet's current available RAM to be used to run "cyclic_weaken.js". Should be a floating point number > 0. Defaults to 0.5.