From 73269156fd4fe27e882c8b05d262c9ceda2a0a75 Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Sat, 22 Feb 2025 03:37:47 -0700 Subject: [PATCH] init commit --- buyServer.js | 61 ++++++ contracts/CaesarEncryption.js | 27 +++ contracts/GenerateIPAddresses.js | 119 ++++++++++++ contracts/LZ-Compress.js | 45 +++++ contracts/LZ-Decompress.js | 65 +++++++ contracts/ShortestPathInAGrid.js | 54 ++++++ contracts/contracts/CaesarEncryption.js | 27 +++ contracts/contracts/GenerateIPAddresses.js | 119 ++++++++++++ contracts/contracts/LZ-Compress.js | 45 +++++ contracts/contracts/LZ-Decompress.js | 65 +++++++ contracts/contracts/ShortestPathInAGrid.js | 54 ++++++ crack.js | 107 +++++++++++ findContracts.js | 68 +++++++ findPath.js | 76 ++++++++ formatMoney.js | 11 ++ getMaxMoney.js | 7 + getNodes.js | 204 +++++++++++++++++++++ grow.js | 6 + hack.js | 6 + manager.js | 137 ++++++++++++++ msg/index.js | 4 + nodes.txt | 34 ++++ weaken.js | 6 + 23 files changed, 1347 insertions(+) create mode 100644 buyServer.js create mode 100644 contracts/CaesarEncryption.js create mode 100644 contracts/GenerateIPAddresses.js create mode 100644 contracts/LZ-Compress.js create mode 100644 contracts/LZ-Decompress.js create mode 100644 contracts/ShortestPathInAGrid.js create mode 100644 contracts/contracts/CaesarEncryption.js create mode 100644 contracts/contracts/GenerateIPAddresses.js create mode 100644 contracts/contracts/LZ-Compress.js create mode 100644 contracts/contracts/LZ-Decompress.js create mode 100644 contracts/contracts/ShortestPathInAGrid.js create mode 100644 crack.js create mode 100644 findContracts.js create mode 100644 findPath.js create mode 100644 formatMoney.js create mode 100644 getMaxMoney.js create mode 100644 getNodes.js create mode 100644 grow.js create mode 100644 hack.js create mode 100644 manager.js create mode 100644 msg/index.js create mode 100644 nodes.txt create mode 100644 weaken.js diff --git a/buyServer.js b/buyServer.js new file mode 100644 index 0000000..5a522cc --- /dev/null +++ b/buyServer.js @@ -0,0 +1,61 @@ +import { formatMoney } from "formatMoney.js"; + +var servers; +var money; + +/** @param {NS} ns */ +function buyServer(ns, ram) { + ns.purchaseServer("server-" + servers.length, ram); +} + +/** @param {NS} ns */ +function buyServerWithMaxRAM(ns) { + var ram = calculateServerWithMaxRAM(ns); + buyServer(ns, ram); +} + +/** @param {NS} ns */ +function calculateServerCost(ns, ram) { + ram = Math.pow(2, ram); + var cost = ns.getPurchasedServerCost(ram); + + if(cost > money) { + ns.tprint("Cannot afford a server with " + ram + " GB of RAM!"); + return false; + } else return cost; +} + +/** @param {NS} ns */ +function calculateServerWithMaxRAM(ns) { + var ram = 1; + var cost = 0; + for(var i = 20; i >= 0; i--) { + ram = Math.pow(2, i); + if(cost = calculateServerCost(ns, i)) { + if(cost <= money) { + break; + } + } + } + + return ram; +} + +/** @param {NS} ns */ +export async function main(ns) { + servers = ns.getPurchasedServers(); + money = ns.getServerMoneyAvailable("home"); + + if(ns.args.length == 0) { + buyServerWithMaxRAM(ns); + } else if(ns.args[0] == "--max" || ns.args[0] == "-m") { + var ram = calculateServerWithMaxRAM(ns); + ns.tprint("cost: " + formatMoney(ns.getPurchasedServerCost(ram)) + " | ram: " + ram + " GB"); + } else if(ns.args.length == 2 && (ns.args[0] == "--ram" || ns.args[0] == "-r")) { + if(ns.args[1] == "max") { + buyServerWithMaxRAM(ns); + } else if(calculateServerCost(ns, ns.args[1])) { + buyServer(ns, ns.args[1]); + } + } +} \ No newline at end of file diff --git a/contracts/CaesarEncryption.js b/contracts/CaesarEncryption.js new file mode 100644 index 0000000..501e9ba --- /dev/null +++ b/contracts/CaesarEncryption.js @@ -0,0 +1,27 @@ +export function caesarEncrypt(text, shift) { + text = text.split(""); + var alphabet = [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ]; + var output = ""; + + text.forEach(function(c) { + if(c == " ") { + output += " "; + } else { + var letter = alphabet.indexOf(c) - shift; + if(letter < 0) { + letter = alphabet.length + letter; + } + output += alphabet[letter]; + } + }); + + return output; +} + +/** @param {NS} ns */ +export async function main(ns) { + var text = ns.args[0]; + var shift = ns.args[1]; + + ns.writePort(1, encrypt(text, shift)); +} \ No newline at end of file diff --git a/contracts/GenerateIPAddresses.js b/contracts/GenerateIPAddresses.js new file mode 100644 index 0000000..4282643 --- /dev/null +++ b/contracts/GenerateIPAddresses.js @@ -0,0 +1,119 @@ +/* +You are attempting to solve a Coding Contract. You have 10 tries remaining, after which the contract will self-destruct. + +Given the following string containing only digits, return an array with all possible valid IP address combinations that can be created from the string: + +Note that an octet cannot begin with a '0' unless the number itself is exactly '0'. For example, '192.168.010.1' is not a valid IP. + +Examples: + +25525511135 -> ["255.255.11.135", "255.255.111.35"] +1938718066 -> ["193.87.180.66"] +*/ + +/** @param {NS} ns */ +function isValidIP(ip) { + var octets = ip.split("."); + if(octets.length != 4) return false; + + for(var i = 0; i < octets.length; i++) { + var length = octets[i].length; + var value = parseInt(octets[i]); + if(length == 0 || length > 3 || octets[i].charAt(0) == '0' || value > 255 || value < 0) { + return false; + } + } + + return true; +} + +/** @param {NS} ns */ +function isValidOctet(octet) { + if(octet.length <= 0 || octet.length > 3) return false; + + var octetValue = parseInt(octet); + if(octetValue > 255 || octetValue < 0) return false; + + return true; +} + +function generateOctet(ips, octets, input, sliceRange) { + var octets1 = [...octets, input.slice(0, sliceRange)]; + if(isValidOctet(octets1[octets1.length - 1])) { + generateIPAddress(ips, octets1, input.slice(sliceRange, input.length)); + } +} + +function generateIPAddress(ips, octets, input) { + if(input.length == 0) { + var ip = octets.join("."); + if(isValidIP(ip)) { + if(ips.indexOf(ip) == -1) { + ips.push(ip); + return ips; + } + } + } else { + generateOctet(ips, octets, input, 3); + generateOctet(ips, octets, input, 2); + generateOctet(ips, octets, input, 1); + } + + return ips; +} + +/** @param {NS} ns */ +function test(ns, ips, expected) { + if(ips.length != expected.length) { + ns.tprint("test: generated and expected lengths differ!"); + return false; + } + + for(var i = 0; i < ips.length; i++) { + if(ips[i] != expected[i]) { + ns.tprint(ips[i] + " != " + expected[i]); + ns.tprint("test: values differ!"); + return false; + } + } + + return true; +} + +/** @param {NS} ns */ +function testClient(ns) { + var testInput1 = "25525511135"; + var testExpected1 = ["255.255.11.135", "255.255.111.35"]; + var testInput2 = "1938718066"; + var testExpected2 = ["193.87.180.66"]; + var testResult1 = generateIPAddress(ns, [], [], testInput1); + var testResult2 = generateIPAddress(ns, [], [], testInput2); + + ns.tprint("Result 1: " + testResult1); + ns.tprint("Result 2: " + testResult2); + + ns.tprint(test(ns, testResult1.reverse(), testExpected1)); + ns.tprint(test(ns, testResult2.reverse(), testExpected2)); +} + +export function generateIPAddresses(input) { + return generateIPAddress([], [], input.toString()) +} + +/** @param {NS} ns */ +export async function main(ns) { + if(ns.args.length > 0) { + var arg = ns.args[0]; + if(arg == "--test" || arg == "-t") { + testClient(ns); + } else if(ns.args.length == 2 && (arg == "--print" || arg == "-p")) { + var input = ns.args[1].toString(); + ns.tprint(generateIPAddress([], [], input)); + } else { + var input = arg.toString(); + return generateIPAddress([], [], input) + } + } else { + ns.tprint("Usage error: Require an input to generate IP addresses or the --test|-t flag."); + } +} \ No newline at end of file diff --git a/contracts/LZ-Compress.js b/contracts/LZ-Compress.js new file mode 100644 index 0000000..2c85a21 --- /dev/null +++ b/contracts/LZ-Compress.js @@ -0,0 +1,45 @@ +/** +Lempel-Ziv (LZ) compression is a data compression technique which encodes data using references to earlier parts of the data. In this variant of LZ, data is encoded in two types of chunk. Each chunk begins with a length L, encoded as a single ASCII digit from 1 to 9, followed by the chunk data, which is either: + +1. Exactly L characters, which are to be copied directly into the uncompressed data. +2. A reference to an earlier part of the uncompressed data. To do this, the length is followed by a second ASCII digit X: each of the L output characters is a copy of the character X places before it in the uncompressed data. + +For both chunk types, a length of 0 instead means the chunk ends immediately, and the next character is the start of a new chunk. The two chunk types alternate, starting with type 1, and the final chunk may be of either type. + +You are given the following input string: + gC1IS2Jj1IS2J2d6yS2JJJJyS2JJ2JJ2JJ2kfMDKgmikfMDKgkfMD0roQqsCroQQQQQhmMi6Gi +Encode it using Lempel-Ziv encoding with the minimum possible output length. + +Examples (some have other possible encodings of minimal length): + abracadabra -> 7abracad47 + mississippi -> 4miss433ppi + aAAaAAaAaAA -> 3aAA53035 + 2718281828 -> 627182844 + abcdefghijk -> 9abcdefghi02jk + aaaaaaaaaaaa -> 3aaa91 + aaaaaaaaaaaaa -> 1a91031 + aaaaaaaaaaaaaa -> 1a91041 +*/ + +export function compress(input) { +} + +/** @param {NS} ns */ +export async function main(ns) { + /* + var input = ns.args[0]; + if(!input) { + ns.tprint("Enter a string to compress!"); + return; + } + */ + var input = "abracadabra"; + + var compressed_string = compress(input); + ns.tprint("Compressed: " + compressed_string); + if(compressed_string == "7abracad4") { + ns.tprint("Compression Successful!"); + } else { + ns.tprint("Compression Failed!"); + } +} \ No newline at end of file diff --git a/contracts/LZ-Decompress.js b/contracts/LZ-Decompress.js new file mode 100644 index 0000000..1c20692 --- /dev/null +++ b/contracts/LZ-Decompress.js @@ -0,0 +1,65 @@ +/* +Lempel-Ziv (LZ) compression is a data compression technique which encodes data using references to earlier parts of the data. In this variant of LZ, data is encoded in two types of chunk. Each chunk begins with a length L, encoded as a single ASCII digit from 1 to 9, followed by the chunk data, which is either: + +1. Exactly L characters, which are to be copied directly into the uncompressed data. +2. A reference to an earlier part of the uncompressed data. To do this, the length is followed by a second ASCII digit X: each of the L output characters is a copy of the character X places before it in the uncompressed data. + +For both chunk types, a length of 0 instead means the chunk ends immediately, and the next character is the start of a new chunk. The two chunk types alternate, starting with type 1, and the final chunk may be of either type. + +You are given the following LZ-encoded string: + 2wg127MSbUJRP473Ak5681h74844rfOrky995VcHUV511Q854FDL4 +Decode it and output the original string. + +Example: decoding '5aaabb450723abb' chunk-by-chunk + + 5aaabb -> aaabb + 5aaabb45 -> aaabbaaab + 5aaabb450 -> aaabbaaab + 5aaabb45072 -> aaabbaaababababa + 5aaabb450723abb -> aaabbaaababababaabb +*/ + +/** @param {NS} ns */ +export function lzDecompress(ns, lz) { + var output = ""; + var type = 0; + for(var i = 0; i < lz.length;) { + var length = Number.parseInt(lz[i]); + + if(length == 0) { + i++; + continue; + } + + if(length == 0) { + ns.tprint("didn't continue!"); + } + + i++; + if(!isNaN(parseInt(lz[i]) && i != 1) && type == 1) { // Type 2 Chunk + var v = Number.parseInt(lz[i]); + var j = output.length - v; + + for(var counter = 0; counter < length; counter++) { + output += output[j]; + j = output.length - v; + } + i++; + type = 0; + } else { // Type 1 Chunk + var j = i; + for(; j < i + length; j++) { + output += lz[j]; + } + i = j; + type = 1; + } + } + + return output; +} + +export async function main(ns) { + var lz = ns.args[0]; + ns.tprint(lzDecompress(ns, lz)); +} \ No newline at end of file diff --git a/contracts/ShortestPathInAGrid.js b/contracts/ShortestPathInAGrid.js new file mode 100644 index 0000000..b7e8dbb --- /dev/null +++ b/contracts/ShortestPathInAGrid.js @@ -0,0 +1,54 @@ +/* +Shortest Path in a Grid +You are located in the top-left corner of the following grid: + + [[0,0,0,0,0,0,1,0,0], + [0,0,0,0,1,0,1,0,0], + [0,0,1,0,0,0,0,0,1], + [0,0,0,1,1,0,0,1,0], + [0,0,0,0,0,0,0,0,1], + [0,0,0,1,0,0,0,0,1], + [1,1,0,0,0,1,0,0,0]] + +You are trying to find the shortest path to the bottom-right corner of the grid, but there are obstacles on the grid that you cannot move onto. These obstacles are denoted by '1', while empty spaces are denoted by 0. + +Determine the shortest path from start to finish, if one exists. The answer should be given as a string of UDLR characters, indicating the moves along the path + +NOTE: If there are multiple equally short paths, any of them is accepted as answer. If there is no path, the answer should be an empty string. +NOTE: The data returned for this contract is an 2D array of numbers representing the grid. + +Examples: + + [[0,1,0,0,0], + [0,0,0,1,0]] + +Answer: 'DRRURRD' + + [[0,1], + [1,0]] +*/ +let grid; +let dest; +let solutions; + +function isValidPosition(x, y) { + return grid[x][y] == 0; +} + +function solve(ns, solution, x, y) { + if([x, y] == dest) { + solutions.push(solution); + return; + } +} + +/** @param {NS} ns */ +export async function main(ns) { + grid = [ + [0, 1, 0, 0, 0], + [0, 0, 0, 1, 0] + ]; + dest = [grid.length - 1, grid[grid.length - 1].length - 1]; + + solve(ns, "", 0, 0); +} \ No newline at end of file diff --git a/contracts/contracts/CaesarEncryption.js b/contracts/contracts/CaesarEncryption.js new file mode 100644 index 0000000..501e9ba --- /dev/null +++ b/contracts/contracts/CaesarEncryption.js @@ -0,0 +1,27 @@ +export function caesarEncrypt(text, shift) { + text = text.split(""); + var alphabet = [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ]; + var output = ""; + + text.forEach(function(c) { + if(c == " ") { + output += " "; + } else { + var letter = alphabet.indexOf(c) - shift; + if(letter < 0) { + letter = alphabet.length + letter; + } + output += alphabet[letter]; + } + }); + + return output; +} + +/** @param {NS} ns */ +export async function main(ns) { + var text = ns.args[0]; + var shift = ns.args[1]; + + ns.writePort(1, encrypt(text, shift)); +} \ No newline at end of file diff --git a/contracts/contracts/GenerateIPAddresses.js b/contracts/contracts/GenerateIPAddresses.js new file mode 100644 index 0000000..4282643 --- /dev/null +++ b/contracts/contracts/GenerateIPAddresses.js @@ -0,0 +1,119 @@ +/* +You are attempting to solve a Coding Contract. You have 10 tries remaining, after which the contract will self-destruct. + +Given the following string containing only digits, return an array with all possible valid IP address combinations that can be created from the string: + +Note that an octet cannot begin with a '0' unless the number itself is exactly '0'. For example, '192.168.010.1' is not a valid IP. + +Examples: + +25525511135 -> ["255.255.11.135", "255.255.111.35"] +1938718066 -> ["193.87.180.66"] +*/ + +/** @param {NS} ns */ +function isValidIP(ip) { + var octets = ip.split("."); + if(octets.length != 4) return false; + + for(var i = 0; i < octets.length; i++) { + var length = octets[i].length; + var value = parseInt(octets[i]); + if(length == 0 || length > 3 || octets[i].charAt(0) == '0' || value > 255 || value < 0) { + return false; + } + } + + return true; +} + +/** @param {NS} ns */ +function isValidOctet(octet) { + if(octet.length <= 0 || octet.length > 3) return false; + + var octetValue = parseInt(octet); + if(octetValue > 255 || octetValue < 0) return false; + + return true; +} + +function generateOctet(ips, octets, input, sliceRange) { + var octets1 = [...octets, input.slice(0, sliceRange)]; + if(isValidOctet(octets1[octets1.length - 1])) { + generateIPAddress(ips, octets1, input.slice(sliceRange, input.length)); + } +} + +function generateIPAddress(ips, octets, input) { + if(input.length == 0) { + var ip = octets.join("."); + if(isValidIP(ip)) { + if(ips.indexOf(ip) == -1) { + ips.push(ip); + return ips; + } + } + } else { + generateOctet(ips, octets, input, 3); + generateOctet(ips, octets, input, 2); + generateOctet(ips, octets, input, 1); + } + + return ips; +} + +/** @param {NS} ns */ +function test(ns, ips, expected) { + if(ips.length != expected.length) { + ns.tprint("test: generated and expected lengths differ!"); + return false; + } + + for(var i = 0; i < ips.length; i++) { + if(ips[i] != expected[i]) { + ns.tprint(ips[i] + " != " + expected[i]); + ns.tprint("test: values differ!"); + return false; + } + } + + return true; +} + +/** @param {NS} ns */ +function testClient(ns) { + var testInput1 = "25525511135"; + var testExpected1 = ["255.255.11.135", "255.255.111.35"]; + var testInput2 = "1938718066"; + var testExpected2 = ["193.87.180.66"]; + var testResult1 = generateIPAddress(ns, [], [], testInput1); + var testResult2 = generateIPAddress(ns, [], [], testInput2); + + ns.tprint("Result 1: " + testResult1); + ns.tprint("Result 2: " + testResult2); + + ns.tprint(test(ns, testResult1.reverse(), testExpected1)); + ns.tprint(test(ns, testResult2.reverse(), testExpected2)); +} + +export function generateIPAddresses(input) { + return generateIPAddress([], [], input.toString()) +} + +/** @param {NS} ns */ +export async function main(ns) { + if(ns.args.length > 0) { + var arg = ns.args[0]; + if(arg == "--test" || arg == "-t") { + testClient(ns); + } else if(ns.args.length == 2 && (arg == "--print" || arg == "-p")) { + var input = ns.args[1].toString(); + ns.tprint(generateIPAddress([], [], input)); + } else { + var input = arg.toString(); + return generateIPAddress([], [], input) + } + } else { + ns.tprint("Usage error: Require an input to generate IP addresses or the --test|-t flag."); + } +} \ No newline at end of file diff --git a/contracts/contracts/LZ-Compress.js b/contracts/contracts/LZ-Compress.js new file mode 100644 index 0000000..2c85a21 --- /dev/null +++ b/contracts/contracts/LZ-Compress.js @@ -0,0 +1,45 @@ +/** +Lempel-Ziv (LZ) compression is a data compression technique which encodes data using references to earlier parts of the data. In this variant of LZ, data is encoded in two types of chunk. Each chunk begins with a length L, encoded as a single ASCII digit from 1 to 9, followed by the chunk data, which is either: + +1. Exactly L characters, which are to be copied directly into the uncompressed data. +2. A reference to an earlier part of the uncompressed data. To do this, the length is followed by a second ASCII digit X: each of the L output characters is a copy of the character X places before it in the uncompressed data. + +For both chunk types, a length of 0 instead means the chunk ends immediately, and the next character is the start of a new chunk. The two chunk types alternate, starting with type 1, and the final chunk may be of either type. + +You are given the following input string: + gC1IS2Jj1IS2J2d6yS2JJJJyS2JJ2JJ2JJ2kfMDKgmikfMDKgkfMD0roQqsCroQQQQQhmMi6Gi +Encode it using Lempel-Ziv encoding with the minimum possible output length. + +Examples (some have other possible encodings of minimal length): + abracadabra -> 7abracad47 + mississippi -> 4miss433ppi + aAAaAAaAaAA -> 3aAA53035 + 2718281828 -> 627182844 + abcdefghijk -> 9abcdefghi02jk + aaaaaaaaaaaa -> 3aaa91 + aaaaaaaaaaaaa -> 1a91031 + aaaaaaaaaaaaaa -> 1a91041 +*/ + +export function compress(input) { +} + +/** @param {NS} ns */ +export async function main(ns) { + /* + var input = ns.args[0]; + if(!input) { + ns.tprint("Enter a string to compress!"); + return; + } + */ + var input = "abracadabra"; + + var compressed_string = compress(input); + ns.tprint("Compressed: " + compressed_string); + if(compressed_string == "7abracad4") { + ns.tprint("Compression Successful!"); + } else { + ns.tprint("Compression Failed!"); + } +} \ No newline at end of file diff --git a/contracts/contracts/LZ-Decompress.js b/contracts/contracts/LZ-Decompress.js new file mode 100644 index 0000000..1c20692 --- /dev/null +++ b/contracts/contracts/LZ-Decompress.js @@ -0,0 +1,65 @@ +/* +Lempel-Ziv (LZ) compression is a data compression technique which encodes data using references to earlier parts of the data. In this variant of LZ, data is encoded in two types of chunk. Each chunk begins with a length L, encoded as a single ASCII digit from 1 to 9, followed by the chunk data, which is either: + +1. Exactly L characters, which are to be copied directly into the uncompressed data. +2. A reference to an earlier part of the uncompressed data. To do this, the length is followed by a second ASCII digit X: each of the L output characters is a copy of the character X places before it in the uncompressed data. + +For both chunk types, a length of 0 instead means the chunk ends immediately, and the next character is the start of a new chunk. The two chunk types alternate, starting with type 1, and the final chunk may be of either type. + +You are given the following LZ-encoded string: + 2wg127MSbUJRP473Ak5681h74844rfOrky995VcHUV511Q854FDL4 +Decode it and output the original string. + +Example: decoding '5aaabb450723abb' chunk-by-chunk + + 5aaabb -> aaabb + 5aaabb45 -> aaabbaaab + 5aaabb450 -> aaabbaaab + 5aaabb45072 -> aaabbaaababababa + 5aaabb450723abb -> aaabbaaababababaabb +*/ + +/** @param {NS} ns */ +export function lzDecompress(ns, lz) { + var output = ""; + var type = 0; + for(var i = 0; i < lz.length;) { + var length = Number.parseInt(lz[i]); + + if(length == 0) { + i++; + continue; + } + + if(length == 0) { + ns.tprint("didn't continue!"); + } + + i++; + if(!isNaN(parseInt(lz[i]) && i != 1) && type == 1) { // Type 2 Chunk + var v = Number.parseInt(lz[i]); + var j = output.length - v; + + for(var counter = 0; counter < length; counter++) { + output += output[j]; + j = output.length - v; + } + i++; + type = 0; + } else { // Type 1 Chunk + var j = i; + for(; j < i + length; j++) { + output += lz[j]; + } + i = j; + type = 1; + } + } + + return output; +} + +export async function main(ns) { + var lz = ns.args[0]; + ns.tprint(lzDecompress(ns, lz)); +} \ No newline at end of file diff --git a/contracts/contracts/ShortestPathInAGrid.js b/contracts/contracts/ShortestPathInAGrid.js new file mode 100644 index 0000000..b7e8dbb --- /dev/null +++ b/contracts/contracts/ShortestPathInAGrid.js @@ -0,0 +1,54 @@ +/* +Shortest Path in a Grid +You are located in the top-left corner of the following grid: + + [[0,0,0,0,0,0,1,0,0], + [0,0,0,0,1,0,1,0,0], + [0,0,1,0,0,0,0,0,1], + [0,0,0,1,1,0,0,1,0], + [0,0,0,0,0,0,0,0,1], + [0,0,0,1,0,0,0,0,1], + [1,1,0,0,0,1,0,0,0]] + +You are trying to find the shortest path to the bottom-right corner of the grid, but there are obstacles on the grid that you cannot move onto. These obstacles are denoted by '1', while empty spaces are denoted by 0. + +Determine the shortest path from start to finish, if one exists. The answer should be given as a string of UDLR characters, indicating the moves along the path + +NOTE: If there are multiple equally short paths, any of them is accepted as answer. If there is no path, the answer should be an empty string. +NOTE: The data returned for this contract is an 2D array of numbers representing the grid. + +Examples: + + [[0,1,0,0,0], + [0,0,0,1,0]] + +Answer: 'DRRURRD' + + [[0,1], + [1,0]] +*/ +let grid; +let dest; +let solutions; + +function isValidPosition(x, y) { + return grid[x][y] == 0; +} + +function solve(ns, solution, x, y) { + if([x, y] == dest) { + solutions.push(solution); + return; + } +} + +/** @param {NS} ns */ +export async function main(ns) { + grid = [ + [0, 1, 0, 0, 0], + [0, 0, 0, 1, 0] + ]; + dest = [grid.length - 1, grid[grid.length - 1].length - 1]; + + solve(ns, "", 0, 0); +} \ No newline at end of file diff --git a/crack.js b/crack.js new file mode 100644 index 0000000..78e90ec --- /dev/null +++ b/crack.js @@ -0,0 +1,107 @@ +/** @param {NS} ns */ +function brutessh(ns, node) { + if(ns.fileExists("brutessh.exe", "home")) { + ns.brutessh(node); + return true; + } else { return false; } +} + +/** @param {NS} ns */ +function ftpcrack(ns, node) { + if(ns.fileExists("ftpcrack.exe", "home")) { + ns.ftpcrack(node); + return true; + } else { return false; } +} + +/** @param {NS} ns */ +function relaysmtp(ns, node) { + if(ns.fileExists("relaysmtp.exe", "home")) { + ns.relaysmtp(node); + return true; + } else { return false; } +} + +/** @param {NS} ns */ +function httpworm(ns, node) { + if(ns.fileExists("httpworm.exe", "home")) { + ns.httpworm(node); + return true; + } else { return false; } +} + +/** @param {NS} ns */ +function sqlinject(ns, node) { + if(ns.fileExists("sqlinject.exe", "home")) { + ns.sqlinject(node); + return true; + } else { return false; } +} + +/** @param {NS} ns */ +export async function crack(ns, node) { + let numPorts = ns.getServerNumPortsRequired(node); + + if(numPorts != 0) { + switch(numPorts) { + case 1: + if(brutessh(ns, node)) { + ns.nuke(node); + return true; + } else return false; + case 2: + if( + brutessh(ns, node) && + ftpcrack(ns, node) + ) { + ns.nuke(node); + return true; + } else return false; + case 3: + if( + brutessh(ns, node) && + ftpcrack(ns, node) && + relaysmtp(ns, node) + ) { + ns.nuke(node); + return true; + } else return false; + case 4: + if( + brutessh(ns, node) && + ftpcrack(ns, node) && + relaysmtp(ns, node) && + httpworm(ns, node) + ) { + ns.nuke(node); + return true; + } else return false; + case 5: + if( + brutessh(ns, node) && + ftpcrack(ns, node) && + relaysmtp(ns, node) && + httpworm(ns, node) && + sqlinject(ns, node) + ) { + ns.nuke(node); + return true; + } else return false; + } + } else { + ns.nuke(node); + return true; + } + + return false; +} + +/** @param {NS} ns */ +export async function main(ns) { + if(ns.args.length != 1) { + ns.tprint("invalid usage!"); + } + + var node = ns.args[0]; + ns.tprint(crack(ns, node) ? "Cracked " + node : "Failed to Crack " + node); +} \ No newline at end of file diff --git a/findContracts.js b/findContracts.js new file mode 100644 index 0000000..a6be7b7 --- /dev/null +++ b/findContracts.js @@ -0,0 +1,68 @@ +import { getNodes } from "getNodes.js"; + +// Contract Solution Imports +import { caesarEncrypt } from "contracts/CaesarEncryption.js"; +import { lzDecompress } from "contracts/LZ-Decompress.js"; +import { generateIPAddresses } from "contracts/GenerateIPAddresses.js"; + +/** @param {NS} ns */ +export async function main(ns) { + const nodes = await getNodes(ns); + var listAllContracts = false; + + if(nodes == null || nodes.length == 0) { + ns.tprint("Error retrieving nodes!"); + return; + } + + if(ns.args.length != 0) { + if((ns.args[0] == "--all" || ns.args[0] == "-a") && ns.args.length == 1) { + listAllContracts = true; + } else { + ns.tprint("Invalid usage!"); + return; + } + } + // scan(host) + nodes.forEach(function(n) { + if(n != "" || n != "\n") { + var contracts = []; + var files = ns.ls(n); + files.forEach(function(f) { + if(f.endsWith(".cct")) { + if(listAllContracts) { + ns.tprint(n + ":" + ns.codingcontract.getContractType(f, n)); + } + contracts.push(f); + } + }); + contracts.forEach(function(c) { + var contractType = ns.codingcontract.getContractType(c, n); + var data = ns.codingcontract.getData(c, n); + + if(contractType == "Encryption I: Caesar Cipher") { + var answer = caesarEncrypt(data[0], data[1]) + var attempt = ns.codingcontract.attempt(answer, c, n); + if(attempt) + ns.tprint(attempt); + else + ns.tprint("Attempt failed! Manual intervention is required."); + } else if(contractType == "Compression II: LZ Decompression") { + var answer = lzDecompress(ns, data); + var attempt = ns.codingcontract.attempt(answer, c, n); + if(attempt) + ns.tprint(attempt); + else + ns.tprint("Attempt failed! Manual intervention is required."); + } else if(contractType == "Generate IP Addresses") { + var answer = generateIPAddresses(data[0]); + var attempt = ns.codingcontract.attempt(answer, c, n); + if(attempt) + ns.tprint(attempt); + else + ns.tprint("Attempt failed! Manual intervention is required."); + } + }); + } + }); +} \ No newline at end of file diff --git a/findPath.js b/findPath.js new file mode 100644 index 0000000..01d7853 --- /dev/null +++ b/findPath.js @@ -0,0 +1,76 @@ +export function spider(ns) { + var serversSeen = ['home'] + for (var i = 0; i < serversSeen.length; i++) { + var thisScan = ns.scan(serversSeen[i]); + for (var j = 0; j < thisScan.length; j++) { + if (serversSeen.indexOf(thisScan[j]) === -1) { + serversSeen.push(thisScan[j]); + } + } + } + return serversSeen; +} + +/** @param {NS} ns */ +export function find(root, target, path, ns) { + // Add the current node to the path *before* exploring connections. + path.push(root); + + if (root === target) { + return path; // Return the complete path + } + + var connections = ns.scan(root); + if (!connections || connections.length == 0) { + // IMPORTANT: Remove the current node from the path if it's a dead end. + path.pop(); + return null; + } + + for (const c of connections) { + if (c !== "home") { // Assuming you want to exclude "home" + // *** CYCLE DETECTION *** + // Check if the connected node is already in the current path. + if (path.includes(c)) { + continue; // Skip this connection to avoid the cycle. + } + + // Recursively search from the connected node. + // Pass a *copy* of the path to avoid side effects. Crucially important! + const result = find(c, target, [...path], ns); // ...path creates a copy + + if (result) { // Check for a successful path (no need for result.length > 0) + // We don't need to add root here, it was already added at the beginning. + return result; // Return the successful path found by the recursive call. + } + } + } + + // If no path was found from any connection, remove the current node from the path. + path.pop(); // Backtrack: Remove the current node + return null; +} + +// Example usage (assuming this is in a .js file in Bitburner) +export async function main(ns) { + const target = ns.args[0]; // Get the target server from arguments + if (!target) { + ns.tprint("Usage: run find_path.js "); + return; + } + + const path = []; + const result = find("home", target, path, ns); // Start the search from "home" + + if (result) { + ns.tprint("Path found:"); + ns.tprint(result.join(" -> ")); // Display the path + var command = ""; + result.forEach(function(p) { + command += "connect " + p + ";"; + }); + navigator.clipboard.writeText(command); + } else { + ns.tprint("No path found to " + target); + } +} \ No newline at end of file diff --git a/formatMoney.js b/formatMoney.js new file mode 100644 index 0000000..43128ff --- /dev/null +++ b/formatMoney.js @@ -0,0 +1,11 @@ +export function formatMoney(number) { + return new Intl.NumberFormat("en-US", { + style: 'currency', + currency: 'USD' + }).format(number); +} + +/** @param {NS} ns */ +export async function main(ns) { + ns.tprint(formatMoney(ns.args[0])); +} \ No newline at end of file diff --git a/getMaxMoney.js b/getMaxMoney.js new file mode 100644 index 0000000..4b3da8d --- /dev/null +++ b/getMaxMoney.js @@ -0,0 +1,7 @@ +import { formatMoney } from "formatMoney.js"; + +/** @param {NS} ns */ +export async function main(ns) { + ns.tprint("Current money: " + formatMoney(ns.getServerMoneyAvailable(ns.args[0]))); + ns.tprint("Max money: " + formatMoney(ns.getServerMaxMoney(ns.args[0]))); +} \ No newline at end of file diff --git a/getNodes.js b/getNodes.js new file mode 100644 index 0000000..a6a70aa --- /dev/null +++ b/getNodes.js @@ -0,0 +1,204 @@ +import { crack } from "crack.js"; +/** @param {NS} ns */ +function printHelp(ns) { + ns.tprint( + "This is a core script which handles basic node (or server/host) operations.\n" + + "Some functions are available via an API, some are only available through\n" + + "the command line interface.\n\n" + + "API REFERENCE:\n" + + "isHackable(ns, node): boolean\n" + ); +} + +/** @param {NS} ns */ +export async function totalAvailableThreads(ns, script) { + var nodes = await getNodes(ns); + var threads = 0; + + nodes.forEach(function(node) { + threads += Math.floor(ns.getServerMaxRam(node) / ns.getScriptRam(script)); + }); + + return threads; +} + +/** @param {NS} ns */ +export function isHackable(ns, node) { + if(node == "" || node == "\n" || node == "home" || node == "darkweb") { + return false; + } + + if(node.slice(0, 6) == "server-") { + return true; + } + + // Skip servers I cannot hack. + if(ns.getHackingLevel() < ns.getServerRequiredHackingLevel(node)) { + return false; + } + + // Crack if I don't have root access. + if(!ns.hasRootAccess(node)) { + if(!crack(ns, node)) { + return false; + } + } + + return true; +} + +/** @param {NS} ns */ +export async function getNodesByRam(ns) { + let nodes = (await ns.read("nodes.txt")).split("\n"); + const n = nodes.length; + + for (let i = 0; i < n - 1; i++) { + let swapped = false; // Flag to optimize: if no swaps occur, the array is sorted + + for (let j = 0; j < n - i - 1; j++) { + if (ns.getServerMaxRam(nodes[j]) > ns.getServerMaxRam(nodes[j + 1])) { + // Swap arr[j] and arr[j+1] + [nodes[j], nodes[j + 1]] = [nodes[j + 1], nodes[j]]; // Array destructuring for swap + swapped = true; + } + } + + // If no two elements were swapped in inner loop, the array is sorted + if (swapped === false) { + break; + } + } + nodes = nodes.reverse(); + while(ns.getServerMaxRam(nodes[nodes.length - 1]) == 0) { + nodes.pop(); + } + + return nodes; +} + +/** @param {NS} ns */ +export async function getNodesByPriority(ns) { + const nodes = (await ns.read("nodes.txt")).split("\n"); + const n = nodes.length; + + for (let i = 0; i < n - 1; i++) { + let swapped = false; // Flag to optimize: if no swaps occur, the array is sorted + + for (let j = 0; j < n - i - 1; j++) { + if (ns.getServerMaxMoney(nodes[j]) > ns.getServerMaxMoney(nodes[j + 1])) { + // Swap arr[j] and arr[j+1] + [nodes[j], nodes[j + 1]] = [nodes[j + 1], nodes[j]]; // Array destructuring for swap + swapped = true; + } + } + + // If no two elements were swapped in inner loop, the array is sorted + if (swapped === false) { + break; + } + } + return nodes.reverse(); +} + +/** @param {NS} ns */ +async function getNodesByHackable(ns) { + const nodes = (await ns.read("nodes.txt")).split("\n"); + var hackableNodes = []; + + for (let i = 0; i < nodes.length; i++) { + if(isHackable(ns, nodes[i])) { + hackableNodes.push(nodes[i]); + } + } + + return hackableNodes; +} + +/** @param {NS} ns */ +export async function getNodes(ns) { + const nodes = await ns.read("nodes.txt"); + return nodes.split("\n"); +} + +/** @param {NS} ns */ +export function getIdealNode(ns) { + var nodes = ['home'] + for (var i = 0; i < nodes.length; i++) { + var node = ns.scan(nodes[i]); + for (var j = 0; j < node.length; j++) { + if (nodes.indexOf(node[j]) === -1 && isHackable(ns, node[j])) { + nodes.push(node[j]); + } + } + } + + var idealNode = "n00dles"; + nodes.forEach(function(n) { + if(ns.getServerMaxMoney(n) > ns.getServerMaxMoney(idealNode)) { + idealNode = n; + } + }); + + return idealNode; +} + +/** @param {NS} ns */ +async function write(ns, nodes) { + await ns.write("nodes.txt", "", "w"); + + var hackableNodes = []; + for(var i = 0; i < nodes.length; i++) { + if(isHackable(ns, nodes[i])) { + hackableNodes.push(nodes[i]); + } + } + + for(var i = 0; i < hackableNodes.length; i++) { + let text = hackableNodes[i]; + if(i != hackableNodes.length - 1) { + text += "\n"; + } + + await ns.write("nodes.txt", text, "a"); + } +} + +/** @param {NS} ns */ +export async function main(ns) { + var nodes = ['home'] + for (var i = 0; i < nodes.length; i++) { + var node = ns.scan(nodes[i]); + for (var j = 0; j < node.length; j++) { + if (nodes.indexOf(node[j]) === -1) { + nodes.push(node[j]); + } + } + } + + if(ns.args.length == 0) { + ns.tprint(nodes); + } else if(ns.args[0] == "--update" || ns.args[0] == "-u") { + await write(ns, nodes); + + // Get the ideal node to target. + } else if(ns.args[0] == "--priority" || ns.args[0] == "-p") { + ns.tprint("nodes by priority: " + await getNodesByPriority(ns)); + + } else if(ns.args[0] == "--hackable" || ns.args[0] == "-ha") { + var hackableNodes = await getNodesByHackable(ns); + ns.tprint(hackableNodes.length + " hackable nodes: " + hackableNodes); + + } else if(ns.args[0] == "--help" || ns.args[0] == "-h") { + printHelp(ns); + exit(); + + // Kill all scripts except on home. + } else if(ns.args[0] == "--killall" || ns.args[0] == "-k") { + var nodes = await getNodes(ns); + nodes.forEach(function(n) { + ns.killall(n); + }); + } + + return nodes; +} \ No newline at end of file diff --git a/grow.js b/grow.js new file mode 100644 index 0000000..469092e --- /dev/null +++ b/grow.js @@ -0,0 +1,6 @@ +/** @param {NS} ns */ +export async function main(ns) { + ns.print("growing " + ns.args[0]); + await ns.grow(ns.args[0]); + ns.print(ns.args[0] + " growed."); +} \ No newline at end of file diff --git a/hack.js b/hack.js new file mode 100644 index 0000000..c37f13a --- /dev/null +++ b/hack.js @@ -0,0 +1,6 @@ +/** @param {NS} ns */ +export async function main(ns) { + ns.print("hacking " + ns.args[0]); + await ns.hack(ns.args[0]); + ns.print(ns.args[0] + " hacked."); +} diff --git a/manager.js b/manager.js new file mode 100644 index 0000000..051e6e6 --- /dev/null +++ b/manager.js @@ -0,0 +1,137 @@ +import { isHackable, getNodesByPriority, getNodesByRam } from "getNodes.js"; +import { formatMoney } from "formatMoney.js"; + +var nodes = []; +var workerNodes = []; +var targetIndex = 0; + +/** + * @param {NS} ns + * @param {string[]} workerNodes + * @param {string} targetNode + * @param {string} script +*/ +async function distribute(ns, script) { + /* + if(script == "hack.js") { + var hackAmount = ns.hackAnalyze(nodes[targetIndex]); + var totalHackAmount = 0; + for(var i = 0; i < workerNodes.length; i++) { + var availableThreads = Math.floor(ns.getServerMaxRam(workerNodes[i]) / ns.getScriptRam(script)); + totalHackAmount += hackAmount * availableThreads; + + // This is to ensure that I do not over-hack the target. + if(totalHackAmount < .5) { + ns.killall(workerNodes[i]); + + ns.scp(script, workerNodes[i]); + ns.exec(script, workerNodes[i], availableThreads, nodes[targetIndex]); + } else { + if(targetIndex == nodes.length) { + ns.tprint("Something really bad has happened...ran out of nodes to hack!"); + exit(); + } + } + } + } else { + */ + // Use maximum RAM on all worker nodes. + for(var i = 0; i < workerNodes.length; i++) { + ns.killall(workerNodes[i]); + + var availableThreads = Math.floor(ns.getServerMaxRam(workerNodes[i]) / ns.getScriptRam(script)); + + ns.scp(script, workerNodes[i]); + ns.exec(script, workerNodes[i], availableThreads, nodes[targetIndex]); + } + + // Use 80% of the home server's RAM to execute the script. + var availableThreads = Math.floor((ns.getServerMaxRam("home") / ns.getScriptRam(script)) * 0.8); + ns.scriptKill("weaken.js", "home"); + ns.scriptKill("grow.js", "home"); + ns.scriptKill("hack.js", "home"); + ns.run(script, availableThreads, nodes[targetIndex]); + //} +} + +/** + * @param {NS} ns +*/ +async function grow(ns) { + ns.tprint("distributing grow.js..."); + distribute(ns, "grow.js"); + + var growTime = ns.getGrowTime(nodes[targetIndex]); + ns.alert("growing for " + Math.ceil(growTime / 60000) + " minutes"); + await ns.sleep(growTime + 1000); + ns.alert("finished growing " + nodes[targetIndex] + "\nnow at " + formatMoney(ns.getServerMoneyAvailable(nodes[targetIndex]))); +} + +/** + * @param {NS} ns +*/ +async function weaken(ns) { + ns.tprint("distributing weaken.js..."); + distribute(ns, "weaken.js"); + + var weakenTime = ns.getWeakenTime(nodes[targetIndex]); + ns.alert("weakening for " + Math.ceil(weakenTime / 60000) + " minutes"); + await ns.sleep(weakenTime + 1000); + ns.alert("finished weakening " + nodes[targetIndex]); +} + +/** + * @param {NS} ns + * @param {string[]} workerNodes + * @param {string} targetNode +*/ +async function hack(ns) { + ns.tprint("distributing hack.js..."); + distribute(ns, "hack.js"); + + var hackTime = ns.getHackTime(nodes[targetIndex]); + ns.alert("hacking for " + Math.ceil(hackTime / 60000) + " minutes"); + await ns.sleep(hackTime + 1000); + ns.alert("finished hacking " + nodes[targetIndex]); +} + +/** @param {NS} ns */ +export async function main(ns) { + nodes = await getNodesByPriority(ns); + workerNodes = await getNodesByRam(ns); + + // Override the default target node to use a user provided one instead. + if(ns.args.length == 2) { + if(ns.args[0] == "--target-node" || ns.args[0] == "-tn") { + if(nodes.indexOf(ns.args[1]) != -1 && isHackable(ns, nodes[ns.args[1]])) { + targetIndex = nodes.indexOf(ns.args[1]); + } + } + } + + /* + while(!isHackable(ns, nodes[targetIndex])) { + targetIndex++; + + if(targetIndex == nodes.length) { + ns.tprint("No hackable nodes!"); + exit(); + } + } + */ + + // @ignore-infinite + while(true) { + nodes = await getNodesByPriority(ns); + workerNodes = await getNodesByRam(ns); + let targetNode = nodes[0]; + + if(ns.getServerMoneyAvailable(targetNode) != ns.getServerMaxMoney(targetNode)) { + await grow(ns); + } else if(ns.getServerSecurityLevel(targetNode) > ns.getServerMinSecurityLevel(targetNode)) { + await weaken(ns); + } else { + await hack(ns); + } + } +} \ No newline at end of file diff --git a/msg/index.js b/msg/index.js new file mode 100644 index 0000000..07ae436 --- /dev/null +++ b/msg/index.js @@ -0,0 +1,4 @@ +/** @param {NS} ns */ +export async function main(ns) { + +} \ No newline at end of file diff --git a/nodes.txt b/nodes.txt new file mode 100644 index 0000000..54ebd2b --- /dev/null +++ b/nodes.txt @@ -0,0 +1,34 @@ +n00dles +foodnstuff +sigma-cosmetics +joesguns +hong-fang-tea +harakiri-sushi +iron-gym +server-0 +zer0 +max-hardware +CSEC +nectar-net +silver-helix +omega-net +phantasy +neo-net +computek +avmnite-02h +johnson-ortho +crush-fitness +the-hub +netlink +summit-uni +rothman-uni +syscore +I.I.I.I +catalyst +rho-construction +alpha-ent +millenium-fitness +lexo-corp +aevum-police +run4theh111z +. \ No newline at end of file diff --git a/weaken.js b/weaken.js new file mode 100644 index 0000000..16e846e --- /dev/null +++ b/weaken.js @@ -0,0 +1,6 @@ +/** @param {NS} ns */ +export async function main(ns) { + ns.print("weakening " + ns.args[0]); + await ns.weaken(ns.args[0]); + ns.print(ns.args[0] + " weakened."); +} \ No newline at end of file