From 8cd9e8954df0918d2a069b24b361dfa57fc3b682 Mon Sep 17 00:00:00 2001 From: Daniel Xie Date: Thu, 25 May 2017 07:18:34 -0500 Subject: [PATCH] Refactored Netscript Hacknet Node library. Now an array called hacknetnodes[i] can be used to access hacknet nodes, and they can be upgraded with functions --- src/Constants.js | 7 +- src/HacknetNode.js | 21 +++-- src/NetscriptEvaluator.js | 173 +++++++++++++++++++++++++++----------- src/NetscriptParser.js | 47 ++++++++++- src/NetscriptTokenizer.js | 4 +- src/NetscriptWorker.js | 2 +- src/Script.js | 10 ++- src/engine.js | 1 + 8 files changed, 196 insertions(+), 69 deletions(-) diff --git a/src/Constants.js b/src/Constants.js index 975c337ca..cb13fb0db 100644 --- a/src/Constants.js +++ b/src/Constants.js @@ -52,8 +52,11 @@ CONSTANTS = { ScriptGetHackingLevelRamCost: 0.1, ScriptGetServerMoneyRamCost: 0.1, ScriptOperatorRamCost: 0.01, - ScriptPurchaseHacknetRamCost: 1.0, - ScriptUpgradeHacknetRamCost: 1.0, + ScriptPurchaseHacknetRamCost: 1.5, + ScriptHacknetNodesRamCost: 1.0, //Base cost for accessing hacknet nodes array + ScriptHNUpgLevelRamCost: 0.4, + ScriptHNUpgRamRamCost: 0.6, + ScriptHNUpgCoreRamCost: 0.8, //Server growth rate ServerGrowthRate: 1.00075, diff --git a/src/HacknetNode.js b/src/HacknetNode.js index 42f2fb507..18143696e 100644 --- a/src/HacknetNode.js +++ b/src/HacknetNode.js @@ -73,15 +73,16 @@ HacknetNode.prototype.calculateLevelUpgradeCost = function(levels=1) { HacknetNode.prototype.purchaseLevelUpgrade = function(levels=1) { var cost = this.calculateLevelUpgradeCost(levels); - if (isNaN(cost)) {throw new Error("Cost is NaN"); return;} - if (cost > Player.money) {return;} + if (isNaN(cost)) {return false;} + if (cost > Player.money) {return false;} Player.loseMoney(cost); if (this.level + levels >= CONSTANTS.HacknetNodeMaxLevel) { this.level = CONSTANTS.HacknetNodeMaxLevel; - return; + return false; } this.level += levels; this.updateMoneyGainRate(); + return true; } HacknetNode.prototype.calculateRamUpgradeCost = function() { @@ -96,12 +97,13 @@ HacknetNode.prototype.calculateRamUpgradeCost = function() { HacknetNode.prototype.purchaseRamUpgrade = function() { var cost = this.calculateRamUpgradeCost(); - if (isNaN(cost)) {throw new Error("Cost is NaN"); return;} - if (cost > Player.money) {return;} + if (isNaN(cost)) {return false;} + if (cost > Player.money) {return false;} Player.loseMoney(cost); - if (this.ram >= CONSTANTS.HacknetNodeMaxRam) {return;} + if (this.ram >= CONSTANTS.HacknetNodeMaxRam) {return false;} this.ram *= 2; //Ram is always doubled this.updateMoneyGainRate(); + return true; } HacknetNode.prototype.calculateCoreUpgradeCost = function() { @@ -112,12 +114,13 @@ HacknetNode.prototype.calculateCoreUpgradeCost = function() { HacknetNode.prototype.purchaseCoreUpgrade = function() { var cost = this.calculateCoreUpgradeCost(); - if (isNaN(cost)) {throw new Error("Cost is NaN"); return;} - if (cost > Player.money) {return;} + if (isNaN(cost)) {return false;} + if (cost > Player.money) {return false;} Player.loseMoney(cost); - if (this.numCores >= CONSTANTS.HacknetNodeMaxCores) {return;} + if (this.numCores >= CONSTANTS.HacknetNodeMaxCores) {return false;} ++this.numCores; this.updateMoneyGainRate(); + return true; } /* Saving and loading HackNets */ diff --git a/src/NetscriptEvaluator.js b/src/NetscriptEvaluator.js index bd881f0c6..b0ed43598 100644 --- a/src/NetscriptEvaluator.js +++ b/src/NetscriptEvaluator.js @@ -23,6 +23,17 @@ function evaluate(exp, workerScript) { case "var": return new Promise(function(resolve, reject) { if (env.stopFlag) {reject(workerScript);} + if (exp.value == "hacknetnodes") { + setTimeout(function() { + var pEvaluateHacknetNode = evaluateHacknetNode(exp, workerScript); + pEvaluateHacknetNode.then(function(res) { + resolve(res); + }, function(e) { + reject(e); + }); + }, CONSTANTS.CodeInstructionRunTime); + return; + } try { resolve(env.get(exp.value)); } catch (e) { @@ -747,7 +758,7 @@ function evaluate(exp, workerScript) { } if (cost > Player.money) { workerScript.scriptRef.log("Could not afford to purchase new Hacknet Node"); - resolve(""); + resolve(false); return; } @@ -761,62 +772,15 @@ function evaluate(exp, workerScript) { Player.hacknetNodes.push(node); displayHacknetNodesContent(); workerScript.scriptRef.log("Purchased new Hacknet Node with name: " + name); - resolve(name); + resolve(numOwned); }, CONSTANTS.CodeInstructionRunTime); - } else if (exp.func.value == "upgradeHacknetNode") { - if (exp.args.length != 1) { - reject("|"+workerScript.serverIp+"|"+workerScript.name+"|upgradeHacknetNode() call has incorrect number of arguments. Takes 1 argument"); - return; - } - var namePromise = evaluate(exp.args[0], workerScript); - namePromise.then(function(name) { - var node = getHacknetNode(name); - if (node == null) { - reject("|"+workerScript.serverIp+"|"+workerScript.name+"|Invalid Hacknet Node name passed into upgradeHacknetNode()"); - return; - } - var cost = node.calculateLevelUpgradeCost(1); - if (isNaN(cost)) { - reject("|"+workerScript.serverIp+"|"+workerScript.name+"|Could not calculate cost in upgradeHacknetNode(). This is a bug please report to game dev"); - return; - } - if (cost > Player.money) { - workerScript.scriptRef.log("Could not afford to upgrade Hacknet Node: " + name); - resolve(false); - return; - } - if (node.level >= CONSTANTS.HacknetNodeMaxLevel) { - workerScript.scriptRef.log("Hacknet Node " + name + " already at max level"); - node.level = CONSTANTS.HacknetNodeMaxLevel; - resolve(false); - return; - } - Player.loseMoney(cost); - node.level += 1; - node.updateMoneyGainRate(); - workerScript.scriptRef.log("Hacknet node " + name + " upgraded to level " + node.level + "!"); - resolve(true); - }, function(e) { - reject(e); - }); - } else if (exp.func.value == "getNumHacknetNodes") { - if (exp.args.length != 0) { - reject("|"+workerScript.serverIp+"|"+workerScript.name+"|getNumHacknetNodes() call has incorrect number of arguments. Takes 0 arguments"); - return; - } - setTimeout(function() { - if (env.stopFlag) {reject(workerScript);} - workerScript.scriptRef.log("getNumHacknetNodes() returned " + Player.hacknetNodes.length); - resolve(Player.hacknetNodes.length); - }, CONSTANTS.CodeInstructionRunTime); - } + } }, CONSTANTS.CodeInstructionRunTime); }); reject("|" + workerScript.serverIp + "|" + workerScript.name + "|Unrecognized function call"); break; - default: - reject("|" + workerScript.serverIp + "|" + workerScript.name + "|Can't evaluate type " + exp.type); + break; } } @@ -980,6 +944,109 @@ function evaluateWhile(exp, workerScript) { }); } +function evaluateHacknetNode(exp, workerScript) { + var env = workerScript.env; + return new Promise(function(resolve, reject) { + setTimeout(function() { + if (exp.index == null) { + if ((exp.op.type == "call" && exp.op.value == "length") || + (exp.op.type == "var" && exp.op.value == "length")) { + resolve(Player.hacknetNodes.length); + workerScript.scriptRef.log("hacknetnodes.length returned " + Player.hacknetNodes.length); + return; + } else { + workerScript.scriptRef.log("Invalid/null index for hacknetnodes"); + reject(makeRuntimeRejectMsg(workerScript, "Invalid/null index. hacknetnodes array must be accessed with an index")); + return; + } + + } + var indexPromise = evaluate(exp.index.value, workerScript); + indexPromise.then(function(index) { + if (isNaN(index) || index >= Player.hacknetNodes.length || index < 0) { + workerScript.scriptRef.log("Invalid index value for hacknetnodes[]"); + reject(makeRuntimeRejectMsg(workerScript, "Invalid index value for hacknetnodes[].")); + return; + } + var nodeObj = Player.hacknetNodes[index]; + if (exp.op == null) { + reject(makeRuntimeRejectMsg(workerScript, "No operator or property called for hacknetnodes. Usage: hacknetnodes[i].property/operator")); + return; + } else if (exp.op.type == "var") { + //Get properties: level, ram, cores + switch(exp.op.value) { + case "level": + resolve(nodeObj.level); + break; + case "ram": + resolve(nodeObj.ram); + break; + case "cores": + resolve(nodeObj.numCores); + break; + default: + reject(makeRuntimeRejectMsg(workerScript, "Unrecognized property for Hacknet Node. Valid properties: ram, cores, level")); + break; + } + + } else if (exp.op.type == "call") { + switch(exp.op.func.value) { + case "upgradeLevel": + if (exp.op.args.length == 1) { + var argPromise = evaluate(exp.op.args[0], workerScript); + argPromise.then(function(arg) { + if (isNaN(arg)) { + reject(makeRuntimeRejectMsg(workerScript, "Argument passed into upgradeLevel() is not numeric")); + return; + } + var res = nodeObj.purchaseLevelUpgrade(arg); + if (res) { + workerScript.scriptRef.log("Upgraded " + nodeObj.name + arg + " times to level " + nodeObj.level); + } + resolve(res); + }, function(e) { + reject(e); + }); + } else { + var res = nodeObj.purchaseLevelUpgrade(1); + if (res) { + workerScript.scriptRef.log("Upgraded " + nodeObj.name + " once to level " + nodeObj.level); + } + resolve(res); + } + break; + case "upgradeRam": + var res = nodeObj.purchaseRamUpgrade(); + if (res) { + workerScript.scriptRef.log("Upgraded " + nodeObj.name + "'s RAM to " + nodeObj.ram + "GB"); + } + resolve(res); + break; + case "upgradeCore": + var res = nodeObj.purchaseCoreUpgrade(); + if (res) { + workerScript.scriptRef.log("Upgraded " + nodeObj.name + "'s number of cores to " + nodeObj.numCores); + } + resolve(res); + break; + default: + reject(makeRuntimeRejectMsg(workerScript, "Unrecognized function/operator for hacknet node. Valid functions: upgradeLevel(n), upgradeRam(), upgradeCore()")); + break; + } + } else { + reject(makeRuntimeRejectMsg(workerScript, "Unrecognized operation for hacknet node")); + return; + } + }, function(e) { + reject(e); + }); + + }, CONSTANTS.CodeInstructionRunTime); + }, function(e) { + reject(e); + }); +} + function evaluateProg(exp, workerScript, index) { var env = workerScript.env; @@ -1016,6 +1083,10 @@ function evaluateProg(exp, workerScript, index) { }); } +function makeRuntimeRejectMsg(workerScript, msg) { + return "|"+workerScript.serverIp+"|"+workerScript.name+"|" + msg; +} + function apply_op(op, a, b) { function num(x) { if (typeof x != "number") diff --git a/src/NetscriptParser.js b/src/NetscriptParser.js index 95f4fad71..9f57b12fc 100644 --- a/src/NetscriptParser.js +++ b/src/NetscriptParser.js @@ -187,8 +187,48 @@ function Parser(input) { cond: cond, code: code } - } + + /* hacknetnodes[i].operator + */ + function parse_hacknetnodes() { + var index = null; + if (is_punc("[")) { + index = parse_expression(); + if (index.type != "index") { + console.log("Failed here"); + unexpected(); + } + } + + if (is_punc(".")) { + checkPuncAndSkip("."); + var op = maybe_call(function() { + var tok = input.next(); + return tok; + }); + return { + type: "var", + value: "hacknetnodes", + index: index, + op: op, + } + } + console.log("Failed here"); + unexpected(); + } + + function parse_arrayindex() { + var index = delimited("[", "]", ";", parse_expression); + var val = 0; + if (index.length == 1 && (index[0].type == "num" || index[0].type == "var")) { + val = index[0]; + } else { + console.log("WARNING: Extra indices passed in") + } + + return { type: "index", value: val }; + } function parse_bool() { return { @@ -211,13 +251,16 @@ function Parser(input) { return exp; } if (is_punc("{")) return parse_prog(); + if (is_punc("[")) return parse_arrayindex(); if (is_kw("if")) return parse_if(); if (is_kw("for")) return parse_for(); if (is_kw("while")) return parse_while(); + //if (is_kw("hacknetnodes")) return parse_hacknetnodes(); //Note, let for loops be function calls (call node types) if (is_kw("true") || is_kw("false")) return parse_bool(); - + var tok = input.next(); + if (tok.type == "var" && tok.value == "hacknetnodes") return parse_hacknetnodes(); if (tok.type == "var" || tok.type == "num" || tok.type == "str") return tok; unexpected(); diff --git a/src/NetscriptTokenizer.js b/src/NetscriptTokenizer.js index d7307e775..38d591ed0 100644 --- a/src/NetscriptTokenizer.js +++ b/src/NetscriptTokenizer.js @@ -6,7 +6,7 @@ * {type: "punc", value: "(" } // punctuation: parens, comma, semicolon etc. * {type: "num", value: 5 } // numbers (including floats) * {type: "str", value: "Hello World!" } // strings - * {type: "kw", value: "for/if/" } // keywords, see defs below + * {type: "kw", value: "for/if/..." } // keywords, see defs below * {type: "var", value: "a" } // identifiers/variables * {type: "op", value: "!=" } // operator characters * {type: "bool", value: "true" } // Booleans @@ -46,7 +46,7 @@ function Tokenizer(input) { } function is_punc(ch) { - return ",;(){}[]".indexOf(ch) >= 0; + return ",;(){}[].".indexOf(ch) >= 0; } function is_whitespace(ch) { diff --git a/src/NetscriptWorker.js b/src/NetscriptWorker.js index 1076ced86..4f57bb201 100644 --- a/src/NetscriptWorker.js +++ b/src/NetscriptWorker.js @@ -31,7 +31,7 @@ function runScriptsLoop() { if (workerScripts[i].running == false && workerScripts[i].env.stopFlag == false) { try { var ast = Parser(Tokenizer(InputStream(workerScripts[i].code))); - //console.log(ast); + console.log(ast); } catch (e) { dialogBoxCreate("Syntax ERROR in " + workerScripts[i].name + ":", e, "", ""); workerScripts[i].env.stopFlag = true; diff --git a/src/Script.js b/src/Script.js index 4619ea862..2624489bc 100644 --- a/src/Script.js +++ b/src/Script.js @@ -185,7 +185,10 @@ Script.prototype.updateRamUsage = function() { var getServerMoneyAvailableCount = numOccurrences(codeCopy, "getServerMoneyAvailable("); var numOperators = numNetscriptOperators(codeCopy); var purchaseHacknetCount = numOccurrences(codeCopy, "purchaseHacknetNode("); - var upgradeHacknetCount = numOccurrences(codeCopy, "upgradeHacknetNode("); + var hacknetnodesArrayCount = numOccurrences(codeCopy, "hacknetnodes["); + var hnUpgLevelCount = numOccurrences(codeCopy, ".upgradeLevel("); + var hnUpgRamCount = numOccurrences(codeCopy, ".upgradeRam()"); + var hnUpgCoreCount = numOccurrences(codeCopy, ".upgradeCore()"); this.ramUsage = baseRam + ((whileCount * CONSTANTS.ScriptWhileRamCost) + @@ -204,7 +207,10 @@ Script.prototype.updateRamUsage = function() { (getServerMoneyAvailableCount * CONSTANTS.ScriptGetServerMoneyRamCost) + (numOperators * CONSTANTS.ScriptOperatorRamCost) + (purchaseHacknetCount * CONSTANTS.ScriptPurchaseHacknetRamCost) + - (upgradeHacknetCount * CONSTANTS.ScriptUpgradeHacknetRamCost)); + (hacknetnodesArrayCount * CONSTANTS.ScriptHacknetNodesRamCost) + + (hnUpgLevelCount * CONSTANTS.ScriptHNUpgLevelRamCost) + + (hnUpgRamCount * CONSTANTS.ScriptHNUpgRamRamCost) + + (hnUpgCoreCount * CONSTANTS.ScriptHNUpgCoreRamCost)); console.log("ram usage: " + this.ramUsage); if (isNaN(this.ramUsage)) { dialogBoxCreate("ERROR in calculating ram usage. This is a bug, please report to game develoepr"); diff --git a/src/engine.js b/src/engine.js index bb9289b6b..8733b7f97 100644 --- a/src/engine.js +++ b/src/engine.js @@ -1080,6 +1080,7 @@ var Engine = { //DEBUG document.getElementById("debug-delete-scripts-link").addEventListener("click", function() { + console.log("Deleting running scripts on home computer"); Player.getHomeComputer().runningScripts = []; return false; });