148 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			148 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						|
 * Scan the entire network for the best device to hack.
 | 
						|
 * @param ns {NS} - BitBurner API
 | 
						|
 * @returns {string[]} - Sorted list of targets to hack based on financial return
 | 
						|
 */
 | 
						|
export function bestTarget(ns) {
 | 
						|
	const [devices, network] = scanNetwork(ns, 'home');
 | 
						|
	return devices.map(d => ns.getServer(d)).filter(d => d.hasAdminRights).map(d => ({
 | 
						|
		...d,
 | 
						|
		moneyAMinute: (ns.hackAnalyze(d.hostname) * ns.getServerMaxMoney(d.hostname)) * ((60 / (ns.getHackTime(d.hostname) / 1000)) * ns.hackAnalyzeChance(d.hostname))}
 | 
						|
	)).sort((a, b) => {
 | 
						|
		if(a.moneyAMinute < b.moneyAMinute) return 1;
 | 
						|
		if(a.moneyAMinute > b.moneyAMinute) return -1;
 | 
						|
		return 0;
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Copy a file & scan it for dependencies copying them as well.
 | 
						|
 * @param ns {NS} - BitBurner API
 | 
						|
 * @param src {string} - File to scan & copy
 | 
						|
 * @param device {string} - Device to copy files to
 | 
						|
 * @returns {string[]} - Array of coppied files
 | 
						|
 */
 | 
						|
export async function copyWithDependencies(ns, src, device) {
 | 
						|
	const queue = [src], found = [src];
 | 
						|
	while(queue.length) {
 | 
						|
		const file = queue.splice(0, 1)[0];
 | 
						|
		const imports = new RegExp(/from ["']\.?(\/.+)["']/g);
 | 
						|
		const script = await ns.read(file);
 | 
						|
		let match;
 | 
						|
		while((match = imports.exec(script)) != null) {
 | 
						|
			const path = `${match[1]}.js`;
 | 
						|
			queue.push(path);
 | 
						|
			found.push(path);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	await ns.scp(found, device);
 | 
						|
	return found.reverse();
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Display a progress bar in the terminal which updates in real time.
 | 
						|
 *
 | 
						|
 * **Example:**
 | 
						|
 *
 | 
						|
 * `/script/test.js          [||||||||||----------]  50% (24.2 MB/s)`
 | 
						|
 *
 | 
						|
 * @param ns {NS} - BitBurner API
 | 
						|
 * @param name {string} - Name to display at the begging of bar
 | 
						|
 * @param showSpeed {boolean} - Show the speed in the progress bar
 | 
						|
 * @param time {number} - Time it takes for bar to fill
 | 
						|
 */
 | 
						|
export async function progressBar(ns, name, showSpeed = true, time = Math.random() + 0.5) {
 | 
						|
	const text = (percentage, speed) => {
 | 
						|
		const p = percentage > 1 ? 1 : percentage < 0 ? 0 : percentage;
 | 
						|
		const spacer = Array(30 - name.length).fill(' ').join('');
 | 
						|
		const bar = `[${Array(Math.round(20 * p)).fill('|').join('')}${Array(Math.round(20 * (1 - p))).fill('-').join('')}]`;
 | 
						|
		const percent = `${Math.round(p * 100)}`;
 | 
						|
		const percentSpacer = Array(3 - percent.length).fill(' ').join('');
 | 
						|
		return `${name}${spacer}${bar} ${percentSpacer}${percent}%${speed != null ? ` (${speed} MB/s)` : ''}`;
 | 
						|
	}
 | 
						|
 | 
						|
	let speed = Math.round((20 + Math.random() * 10) * 10) / 10;
 | 
						|
	ns.tprint(text(1, speed)); // Display the complete bar (This is the one that will be shown on redraws)
 | 
						|
	await ns.sleep(25); // Wait for the new line to display
 | 
						|
	const terminalOutput = eval('document').querySelectorAll('#terminal li p');
 | 
						|
	const updateLine = terminalOutput[terminalOutput.length - 1];
 | 
						|
	if(!updateLine) return await ns.sleep(time * 1000);
 | 
						|
	const script = updateLine.innerText.split(': ')[0];
 | 
						|
	for(let p = 0; p <= 100; p++) {
 | 
						|
		await ns.sleep((time * 1000) / 100);
 | 
						|
		if(p % 5 == 0) speed = Math.round((speed + (Math.random() > 0.5 ? 1 : -1) * Math.random()) * 10) / 10;
 | 
						|
		updateLine.innerText = `${script}: ${text(p / 100, showSpeed ? p == 0 ? 0 : speed : null)}`;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * **Impure:** Prune tree down to keys which pass function
 | 
						|
 * @param tree {object} - Tree to search
 | 
						|
 * @param fn {(key: string) => boolean} - Function to test each key with
 | 
						|
 * @returns {boolean} - True if a match was found
 | 
						|
 */
 | 
						|
export function pruneTree(tree, fn) {
 | 
						|
	return !!Object.keys(tree).map(k => {
 | 
						|
		let matches = fn(k), children = Object.keys(k), childrenMatch = false;
 | 
						|
		if(children.length) childrenMatch = pruneTree(tree[k], fn);
 | 
						|
		if(!childrenMatch && !matches) delete tree[k];
 | 
						|
		return childrenMatch || matches;
 | 
						|
	}).find(el => el);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Scan the network of a given device.
 | 
						|
 * @param ns {NS} - BitBurner API
 | 
						|
 * @param device {string} - Device network that will be scanned
 | 
						|
 * @param maxDepth - Depth to scan to
 | 
						|
 * @returns {[string[], Object]} - A tuple including an array of discovered devices & a tree of the network
 | 
						|
 */
 | 
						|
export function scanNetwork(ns, device = ns.getHostname(), maxDepth = Infinity) {
 | 
						|
	let discovered = [device];
 | 
						|
	function scan (device, depth = 1) {
 | 
						|
		if(depth > maxDepth) return {};
 | 
						|
		const localTargets = ns.scan(device).filter(newDevice => !discovered.includes(newDevice));
 | 
						|
		discovered = [...discovered, ...localTargets];
 | 
						|
		return localTargets.reduce((acc, device) => ({...acc, [device]: scan(device, depth + 1)}), {});
 | 
						|
	}
 | 
						|
	const network = scan(device);
 | 
						|
	return [discovered.slice(1), network];
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Print text to the terminal & then delay for a random amount of time to emulate execution time.
 | 
						|
 * @param ns {NS} - BitBurner API
 | 
						|
 * @param message {string} - Text to display
 | 
						|
 * @param min {number} - minimum amount of time to wait after printing text
 | 
						|
 * @param max {number} - maximum amount of time to wait after printing text
 | 
						|
 */
 | 
						|
export async function slowPrint(ns, message, min = 0.5, max = 1.5) {
 | 
						|
	const time = ~~(Math.random() * (max * 1000 - min * 1000)) + min * 1000;
 | 
						|
	ns.tprint(message);
 | 
						|
	await ns.sleep(time);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Write a command to the terminal.
 | 
						|
 * @param command {string} - Command that will be run
 | 
						|
 * @returns {Promise<string>} - Command line response
 | 
						|
 */
 | 
						|
export function terminal(command) {
 | 
						|
	// Get the terminal
 | 
						|
	const terminalInput = document.getElementById("terminal-input");
 | 
						|
	const handler = Object.keys(terminalInput)[1];
 | 
						|
 | 
						|
	// Send command
 | 
						|
	terminalInput.value = command; // Enter the command
 | 
						|
	terminalInput[handler].onChange({target:terminalInput}); // React on change
 | 
						|
	terminalInput[handler].onKeyDown({key: 'Enter', preventDefault: () => null}); // Enter 'keystroke'
 | 
						|
 | 
						|
	// Return any new terminal output
 | 
						|
	return new Promise(res => setTimeout(() => {
 | 
						|
		const terminalOutput = Array.from(eval('document')
 | 
						|
			.querySelectorAll('#terminal li p')).map(out => out.innerText);
 | 
						|
		const i = terminalOutput.length - terminalOutput.reverse().findIndex(o => o.indexOf(command) != -1);
 | 
						|
		res(terminalOutput.reverse().slice(i));
 | 
						|
	}, 25));
 | 
						|
}
 |