From cdb5dfec624cbeb9f7127326268db5171a9742cc Mon Sep 17 00:00:00 2001 From: danielyxie Date: Mon, 6 May 2019 18:01:06 -0700 Subject: [PATCH] Resolved more circular dependencies. Installed acorn-walk and imported it in RamCalculations using ES6 modules. Fixed bugs in stock market rework --- package-lock.json | 11 +- package.json | 3 +- src/Hacknet/HacknetHelpers.jsx | 2 +- src/Hacknet/HacknetNode.ts | 1 - src/Hacknet/ui/Root.jsx | 21 +- src/InteractiveTutorial.js | 2 +- src/NetscriptFunctions.js | 7 +- src/NetscriptWorker.js | 2 + src/SaveObject.js | 2 +- src/Script/RamCalculations.d.ts | 4 +- src/Script/RamCalculations.js | 57 +- src/Script/Script.ts | 17 +- src/Script/ScriptHelpers.js | 12 +- src/Server/BaseServer.ts | 8 +- src/Server/ServerHelpers.ts | 2 +- src/StockMarket/BuyingAndSelling.ts | 26 +- src/StockMarket/Order.ts | 11 +- src/StockMarket/OrderProcessing.ts | 115 +- src/StockMarket/StockMarket.jsx | 44 +- src/StockMarket/StockMarketHelpers.ts | 18 +- src/StockMarket/data/InitStockMetadata.ts | 132 +- src/StockMarket/ui/StockTickerHeaderText.tsx | 4 +- test/Netscript/StaticRamCalculationTests.js | 17 + test/README.md | 5 + {tests => test}/StockMarketTests.js | 21 +- {tests => test}/index.html | 7 +- test/index.js | 2 + test/tests.bundle.js | 2035 ++++++++ test/tests.bundle.js.map | 1 + test/tests.css | 4889 ++++++++++++++++++ test/tests.css.map | 1 + tests/NetscriptJSTest.js | 52 - tests/index.js | 1 - webpack.config.js | 19 +- 34 files changed, 7246 insertions(+), 305 deletions(-) create mode 100644 test/Netscript/StaticRamCalculationTests.js create mode 100644 test/README.md rename {tests => test}/StockMarketTests.js (98%) rename {tests => test}/index.html (71%) create mode 100644 test/index.js create mode 100644 test/tests.bundle.js create mode 100644 test/tests.bundle.js.map create mode 100644 test/tests.css create mode 100644 test/tests.css.map delete mode 100644 tests/NetscriptJSTest.js delete mode 100644 tests/index.js diff --git a/package-lock.json b/package-lock.json index b17f2dbad..7185875bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -671,9 +671,9 @@ } }, "acorn": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.4.1.tgz", - "integrity": "sha512-XLmq3H/BVvW6/GbxKryGxWORz1ebilSsUDlyC27bXhWGWAZWkGwS6FLHjOlwFXNFoWFQEO/Df4u0YYd0K3BQgQ==" + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==" }, "acorn-dynamic-import": { "version": "2.0.2", @@ -707,6 +707,11 @@ } } }, + "acorn-walk": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", + "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==" + }, "ajv": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", diff --git a/package.json b/package.json index 1f2446d73..1860add86 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,9 @@ "@types/numeral": "0.0.25", "@types/react": "^16.8.6", "@types/react-dom": "^16.8.2", - "acorn": "^5.0.0", + "acorn": "^5.7.3", "acorn-dynamic-import": "^2.0.0", + "acorn-walk": "^6.1.1", "ajv": "^5.1.5", "ajv-keywords": "^2.0.0", "async": "^2.6.1", diff --git a/src/Hacknet/HacknetHelpers.jsx b/src/Hacknet/HacknetHelpers.jsx index fedc4361c..086b27111 100644 --- a/src/Hacknet/HacknetHelpers.jsx +++ b/src/Hacknet/HacknetHelpers.jsx @@ -61,7 +61,7 @@ export function purchaseHacknet() { } } /* END INTERACTIVE TUTORIAL */ - + const numOwned = Player.hacknetNodes.length; if (hasHacknetServers()) { const cost = getCostOfNextHacknetServer(); diff --git a/src/Hacknet/HacknetNode.ts b/src/Hacknet/HacknetNode.ts index fe1d94615..964acd064 100644 --- a/src/Hacknet/HacknetNode.ts +++ b/src/Hacknet/HacknetNode.ts @@ -9,7 +9,6 @@ import { IHacknetNode } from "./IHacknetNode"; import { CONSTANTS } from "../Constants"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; -import { IPlayer } from "../PersonObjects/IPlayer"; import { dialogBoxCreate } from "../../utils/DialogBox"; import { Generic_fromJSON, diff --git a/src/Hacknet/ui/Root.jsx b/src/Hacknet/ui/Root.jsx index 7c21d400b..886ddae19 100644 --- a/src/Hacknet/ui/Root.jsx +++ b/src/Hacknet/ui/Root.jsx @@ -39,6 +39,8 @@ export class HacknetRoot extends React.Component { } this.createHashUpgradesPopup = this.createHashUpgradesPopup.bind(this); + this.handlePurchaseButtonClick = this.handlePurchaseButtonClick.bind(this); + this.recalculateTotalProduction = this.recalculateTotalProduction.bind(this); } componentDidMount() { @@ -50,6 +52,12 @@ export class HacknetRoot extends React.Component { createPopup(id, HashUpgradePopup, { popupId: id, rerender: this.createHashUpgradesPopup }); } + handlePurchaseButtonClick() { + if (purchaseHacknet() >= 0) { + this.recalculateTotalProduction(); + } + } + recalculateTotalProduction() { let total = 0; for (let i = 0; i < Player.hacknetNodes.length; ++i) { @@ -85,13 +93,6 @@ export class HacknetRoot extends React.Component { purchaseCost = getCostOfNextHacknetNode(); } - // onClick event handler for purchase button - const purchaseOnClick = () => { - if (purchaseHacknet() >= 0) { - this.recalculateTotalProduction(); - } - } - // onClick event handlers for purchase multiplier buttons const purchaseMultiplierOnClicks = [ this.setPurchaseMultiplier.bind(this, PurchaseMultipliers.x1), @@ -112,7 +113,7 @@ export class HacknetRoot extends React.Component { key={hserver.hostname} node={hserver} purchaseMultiplier={this.state.purchaseMultiplier} - recalculate={this.recalculateTotalProduction.bind(this)} + recalculate={this.recalculateTotalProduction} /> ) } else { @@ -121,7 +122,7 @@ export class HacknetRoot extends React.Component { key={node.name} node={node} purchaseMultiplier={this.state.purchaseMultiplier} - recalculate={this.recalculateTotalProduction.bind(this)} + recalculate={this.recalculateTotalProduction} /> ) } @@ -132,7 +133,7 @@ export class HacknetRoot extends React.Component {

Hacknet Nodes

- +
diff --git a/src/InteractiveTutorial.js b/src/InteractiveTutorial.js index 3899d0683..da5543868 100644 --- a/src/InteractiveTutorial.js +++ b/src/InteractiveTutorial.js @@ -321,7 +321,7 @@ function iTutorialEvaluateStep() { Engine.loadActiveScriptsContent(); iTutorialSetText("This page displays stats/information about all of your scripts that are " + "running across every existing server. You can use this to gauge how well " + - "your scripts are doing. Let's go back to the Terminal now using the 'Terminal'" + + "your scripts are doing. Let's go back to the Terminal now using the 'Terminal' " + "link."); nextBtn.style.display = "none"; diff --git a/src/NetscriptFunctions.js b/src/NetscriptFunctions.js index b056394f1..f785c5e2e 100644 --- a/src/NetscriptFunctions.js +++ b/src/NetscriptFunctions.js @@ -1145,8 +1145,7 @@ function NetscriptFunctions(workerScript) { } // Create new script if it does not already exist - var newScript = new Script(); - newScript.filename = scriptname; + var newScript = new Script(scriptname); newScript.code = sourceScript.code; newScript.ramUsage = sourceScript.ramUsage; newScript.server = destServer.ip; @@ -1932,12 +1931,12 @@ function NetscriptFunctions(workerScript) { let script = workerScript.getScriptOnServer(fn); if (script == null) { // Create a new script - script = new Script(fn, data, server.ip); + script = new Script(fn, data, server.ip, server.scripts); server.scripts.push(script); return true; } mode === "w" ? script.code = data : script.code += data; - script.updateRamUsage(); + script.updateRamUsage(server.scripts); } else { // Write to text file let txtFile = getTextFile(fn, server); diff --git a/src/NetscriptWorker.js b/src/NetscriptWorker.js index 1f55d600f..9aa31f713 100644 --- a/src/NetscriptWorker.js +++ b/src/NetscriptWorker.js @@ -20,6 +20,8 @@ import { import { NetscriptFunctions } from "./NetscriptFunctions"; import { executeJSScript } from "./NetscriptJSEvaluator"; import { NetscriptPort } from "./NetscriptPort"; +import { Player } from "./Player"; +import { RunningScript } from "./Script/RunningScript"; import { getRamUsageFromRunningScript } from "./Script/RunningScriptHelpers"; import { findRunningScript, diff --git a/src/SaveObject.js b/src/SaveObject.js index 000b4a2ee..354fcf696 100755 --- a/src/SaveObject.js +++ b/src/SaveObject.js @@ -18,8 +18,8 @@ import { processHacknetEarnings } from "./Hacknet/HacknetHelpers"; import { loadMessages, initMessages, Messages } from "./Message/MessageHelpers"; +import { loadAllRunningScripts } from "./NetscriptWorker"; import { Player, loadPlayer } from "./Player"; -import { loadAllRunningScripts } from "./Script/ScriptHelpers"; import { AllServers, loadAllServers } from "./Server/AllServers"; import { Settings } from "./Settings/Settings"; import { diff --git a/src/Script/RamCalculations.d.ts b/src/Script/RamCalculations.d.ts index 0a886c842..7a7b889c1 100644 --- a/src/Script/RamCalculations.d.ts +++ b/src/Script/RamCalculations.d.ts @@ -1 +1,3 @@ -export declare function calculateRamUsage(codeCopy: string): number; +import { Script } from "./Script"; + +export declare function calculateRamUsage(codeCopy: string, otherScripts: Script[]): number; diff --git a/src/Script/RamCalculations.js b/src/Script/RamCalculations.js index 77d02797e..9a14beb5b 100644 --- a/src/Script/RamCalculations.js +++ b/src/Script/RamCalculations.js @@ -1,5 +1,5 @@ // Calculate a script's RAM usage -const walk = require("acorn/dist/walk"); // Importing this doesn't work for some reason. +import * as walk from "acorn-walk"; import { RamCosts, RamCostConstants } from "../Netscript/RamCostGenerator"; import { parse, Node } from "../../utils/acorn"; @@ -16,7 +16,7 @@ const memCheckGlobalKey = ".__GLOBAL__"; // Calcluates the amount of RAM a script uses. Uses parsing and AST walking only, // rather than NetscriptEvaluator. This is useful because NetscriptJS code does // not work under NetscriptEvaluator. -async function parseOnlyRamCalculate(server, code, workerScript) { +async function parseOnlyRamCalculate(otherScripts, code, workerScript) { try { // Maps dependent identifiers to their dependencies. // @@ -74,11 +74,25 @@ async function parseOnlyRamCalculate(server, code, workerScript) { return -1; } } else { - const script = server.getScript(nextModule.startsWith("./") ? nextModule.slice(2) : nextModule); - if (!script) { + if (!Array.isArray(otherScripts)) { + console.warn(`parseOnlyRamCalculate() not called with array of scripts`); + return -1; + } + + let script = null; + let fn = nextModule.startsWith("./") ? nextModule.slice(2) : nextModule; + for (const s of otherScripts) { + if (s.filename === fn) { + script = s; + break; + } + } + + if (script == null) { console.warn("Invalid script"); return -1; // No such script on the server. } + code = script.code; } @@ -251,36 +265,6 @@ function parseOnlyCalculateDeps(code, currentModule) { } } - //Spread syntax not supported in Edge yet, use Object.assign - /* - walk.recursive(ast, {key: globalKey}, { - ImportDeclaration: (node, st, walkDeeper) => { - const importModuleName = node.source.value; - additionalModules.push(importModuleName); - - // This module's global scope refers to that module's global scope, no matter how we - // import it. - dependencyMap[st.key].add(importModuleName + memCheckGlobalKey); - - for (let i = 0; i < node.specifiers.length; ++i) { - const spec = node.specifiers[i]; - if (spec.imported !== undefined && spec.local !== undefined) { - // We depend on specific things. - internalToExternal[spec.local.name] = importModuleName + "." + spec.imported.name; - } else { - // We depend on everything. - dependencyMap[st.key].add(importModuleName + ".*"); - } - } - }, - FunctionDeclaration: (node, st, walkDeeper) => { - // Don't use walkDeeper, because we are changing the visitor set. - const key = currentModule + "." + node.id.name; - walk.recursive(node, {key: key}, commonVisitors()); - }, - ...commonVisitors() - }); - */ walk.recursive(ast, {key: globalKey}, Object.assign({ ImportDeclaration: (node, st, walkDeeper) => { const importModuleName = node.source.value; @@ -311,19 +295,18 @@ function parseOnlyCalculateDeps(code, currentModule) { return {dependencyMap: dependencyMap, additionalModules: additionalModules}; } -export async function calculateRamUsage(codeCopy) { +export async function calculateRamUsage(codeCopy, otherScripts) { // We don't need a real WorkerScript for this. Just an object that keeps // track of whatever's needed for RAM calculations const workerScript = { loadedFns: {}, - serverIp: currServ.ip, env: { vars: RamCosts, } } try { - return await parseOnlyRamCalculate(currServ, codeCopy, workerScript); + return await parseOnlyRamCalculate(otherScripts, codeCopy, workerScript); } catch (e) { console.error(`Failed to parse script for RAM calculations:`); console.error(e); diff --git a/src/Script/Script.ts b/src/Script/Script.ts index bc1f3659b..0dcc4f022 100644 --- a/src/Script/Script.ts +++ b/src/Script/Script.ts @@ -35,13 +35,13 @@ export class Script { server: string = ""; - constructor(fn: string = "", code: string = "", server: string = "") { + constructor(fn: string="", code: string="", server: string="", otherScripts: Script[]=[]) { this.filename = fn; this.code = code; this.ramUsage = 0; this.server = server; // IP of server this script is on this.module = ""; - if (this.code !== "") {this.updateRamUsage();} + if (this.code !== "") { this.updateRamUsage(otherScripts); } }; download(): void { @@ -64,7 +64,7 @@ export class Script { } // Save a script FROM THE SCRIPT EDITOR - saveScript(code: string, currServ: string): void { + saveScript(code: string, serverIp: string, otherScripts: Script[]): void { if (routing.isOn(Page.ScriptEditor)) { //Update code and filename this.code = code.replace(/^\s+|\s+$/g, ''); @@ -77,21 +77,18 @@ export class Script { this.filename = filenameElem!.value; // Server - this.server = currServ; + this.server = serverIp; //Calculate/update ram usage, execution time, etc. - this.updateRamUsage(); + this.updateRamUsage(otherScripts); this.module = ""; } } // Updates the script's RAM usage based on its code - async updateRamUsage() { - // TODO Commented this out because I think its unnecessary - // DOuble check/Test - // var codeCopy = this.code.repeat(1); - var res = await calculateRamUsage(this.code); + async updateRamUsage(otherScripts: Script[]) { + var res = await calculateRamUsage(this.code, otherScripts); if (res !== -1) { this.ramUsage = roundToTwo(res); } diff --git a/src/Script/ScriptHelpers.js b/src/Script/ScriptHelpers.js index 916dd03f7..557ee5b37 100644 --- a/src/Script/ScriptHelpers.js +++ b/src/Script/ScriptHelpers.js @@ -189,7 +189,7 @@ export async function updateScriptEditorContent() { } var codeCopy = code.repeat(1); - var ramUsage = await calculateRamUsage(codeCopy); + var ramUsage = await calculateRamUsage(codeCopy, Player.getCurrentServer().scripts); if (ramUsage !== -1) { scriptEditorRamText.innerText = "RAM: " + numeralWrapper.format(ramUsage, '0.00') + " GB"; } else { @@ -236,15 +236,15 @@ function saveAndCloseScriptEditor() { let s = Player.getCurrentServer(); for (var i = 0; i < s.scripts.length; i++) { if (filename == s.scripts[i].filename) { - s.scripts[i].saveScript(getCurrentEditor().getCode(), Player.currentServer); + s.scripts[i].saveScript(getCurrentEditor().getCode(), Player.currentServer, Player.getCurrentServer().scripts); Engine.loadTerminalContent(); return iTutorialNextStep(); } } - //If the current script does NOT exist, create a new one + // If the current script does NOT exist, create a new one let script = new Script(); - script.saveScript(getCurrentEditor().getCode(), Player.currentServer); + script.saveScript(getCurrentEditor().getCode(), Player.currentServer, Player.getCurrentServer().scripts); s.scripts.push(script); return iTutorialNextStep(); @@ -272,7 +272,7 @@ function saveAndCloseScriptEditor() { //If the current script already exists on the server, overwrite it for (var i = 0; i < s.scripts.length; i++) { if (filename == s.scripts[i].filename) { - s.scripts[i].saveScript(getCurrentEditor().getCode(), Player.currentServer); + s.scripts[i].saveScript(getCurrentEditor().getCode(), Player.currentServer, Player.getCurrentServer().scripts); Engine.loadTerminalContent(); return; } @@ -280,7 +280,7 @@ function saveAndCloseScriptEditor() { //If the current script does NOT exist, create a new one const script = new Script(); - script.saveScript(getCurrentEditor().getCode(), Player.currentServer); + script.saveScript(getCurrentEditor().getCode(), Player.currentServer, Player.getCurrentServer().scripts); s.scripts.push(script); } else if (filename.endsWith(".txt")) { for (var i = 0; i < s.textFiles.length; ++i) { diff --git a/src/Server/BaseServer.ts b/src/Server/BaseServer.ts index 6fd0282ac..b787bd807 100644 --- a/src/Server/BaseServer.ts +++ b/src/Server/BaseServer.ts @@ -231,7 +231,7 @@ export class BaseServer { if (fn === this.scripts[i].filename) { let script = this.scripts[i]; script.code = code; - script.updateRamUsage(); + script.updateRamUsage(this.scripts); script.module = ""; ret.overwritten = true; ret.success = true; @@ -240,11 +240,7 @@ export class BaseServer { } //Otherwise, create a new script - const newScript = new Script(); - newScript.filename = fn; - newScript.code = code; - newScript.updateRamUsage(); - newScript.server = this.ip; + const newScript = new Script(fn, code, this.ip, this.scripts); this.scripts.push(newScript); ret.success = true; return ret; diff --git a/src/Server/ServerHelpers.ts b/src/Server/ServerHelpers.ts index f91a2f8fc..c4a2c15de 100644 --- a/src/Server/ServerHelpers.ts +++ b/src/Server/ServerHelpers.ts @@ -107,7 +107,7 @@ export function prestigeHomeComputer(homeComp: Server) { //Update RAM usage on all scripts homeComp.scripts.forEach(function(script) { - script.updateRamUsage(); + script.updateRamUsage(homeComp.scripts); }); homeComp.messages.length = 0; //Remove .lit and .msg files diff --git a/src/StockMarket/BuyingAndSelling.ts b/src/StockMarket/BuyingAndSelling.ts index 52e3f9d0f..21a32313b 100644 --- a/src/StockMarket/BuyingAndSelling.ts +++ b/src/StockMarket/BuyingAndSelling.ts @@ -41,11 +41,11 @@ export function buyStock(stock: Stock, shares: number, workerScript: WorkerScrip // Validate arguments shares = Math.round(shares); - if (shares == 0 || shares < 0) { return false; } + if (shares <= 0) { return false; } if (stock == null || isNaN(shares)) { if (tixApi) { workerScript!.log(`ERROR: buyStock() failed due to invalid arguments`); - } else if (opts.suppressDialog != null && !opts.suppressDialog) { + } else if (opts.suppressDialog !== true) { dialogBoxCreate("Failed to buy stock. This may be a bug, contact developer"); } @@ -58,7 +58,7 @@ export function buyStock(stock: Stock, shares: number, workerScript: WorkerScrip if (Player.money.lt(totalPrice)) { if (tixApi) { workerScript!.log(`ERROR: buyStock() failed because you do not have enough money to purchase this potiion. You need ${numeralWrapper.formatMoney(totalPrice)}`); - } else if (opts.suppressDialog != null && !opts.suppressDialog) { + } else if (opts.suppressDialog !== true) { dialogBoxCreate(`You do not have enough money to purchase this. You need ${numeralWrapper.formatMoney(totalPrice)}`); } @@ -69,7 +69,7 @@ export function buyStock(stock: Stock, shares: number, workerScript: WorkerScrip if (shares + stock.playerShares + stock.playerShortShares > stock.maxShares) { if (tixApi) { workerScript!.log(`ERROR: buyStock() failed because purchasing this many shares would exceed ${stock.symbol}'s maximum number of shares`); - } else if (opts.suppressDialog != null && !opts.suppressDialog) { + } else if (opts.suppressDialog !== true) { dialogBoxCreate(`You cannot purchase this many shares. ${stock.symbol} has a maximum of ${numeralWrapper.formatBigNumber(stock.maxShares)} shares.`); } @@ -90,7 +90,7 @@ export function buyStock(stock: Stock, shares: number, workerScript: WorkerScrip `Paid ${numeralWrapper.formatMoney(CONSTANTS.StockMarketCommission)} in commission fees.` if (tixApi) { if (workerScript!.shouldLog("buyStock")) { workerScript!.log(resultTxt); } - } else if (opts.suppressDialog != null && !opts.suppressDialog) { + } else if (opts.suppressDialog !== true) { dialogBoxCreate(resultTxt); } @@ -112,15 +112,15 @@ export function sellStock(stock: Stock, shares: number, workerScript: WorkerScri if (stock == null || shares < 0 || isNaN(shares)) { if (tixApi) { workerScript!.log(`ERROR: sellStock() failed due to invalid arguments`); - } else if (opts.suppressDialog != null && !opts.suppressDialog) { + } else if (opts.suppressDialog !== true) { dialogBoxCreate("Failed to sell stock. This is probably due to an invalid quantity. Otherwise, this may be a bug, contact developer"); } return false; } shares = Math.round(shares); - if (shares > stock.playerShares) {shares = stock.playerShares;} - if (shares === 0) {return false;} + if (shares > stock.playerShares) { shares = stock.playerShares; } + if (shares === 0) { return false; } const gains = getSellTransactionGain(stock, shares, PositionTypes.Long); if (gains == null) { return false; } @@ -148,7 +148,7 @@ export function sellStock(stock: Stock, shares: number, workerScript: WorkerScri `After commissions, you gained a total of ${numeralWrapper.formatMoney(gains)}.`; if (tixApi) { if (workerScript!.shouldLog("sellStock")) { workerScript!.log(resultTxt); } - } else if (opts.suppressDialog != null && !opts.suppressDialog) { + } else if (opts.suppressDialog !== true) { dialogBoxCreate(resultTxt); } @@ -168,11 +168,11 @@ export function shortStock(stock: Stock, shares: number, workerScript: WorkerScr // Validate arguments shares = Math.round(shares); - if (shares === 0 || shares < 0) { return false; } + if (shares <= 0) { return false; } if (stock == null || isNaN(shares)) { if (tixApi) { workerScript!.log("ERROR: shortStock() failed because of invalid arguments."); - } else if (opts.suppressDialog != null && !opts.suppressDialog) { + } else if (opts.suppressDialog !== true) { dialogBoxCreate("Failed to initiate a short position in a stock. This is probably " + "due to an invalid quantity. Otherwise, this may be a bug, so contact developer"); } @@ -187,7 +187,7 @@ export function shortStock(stock: Stock, shares: number, workerScript: WorkerScr workerScript!.log("ERROR: shortStock() failed because you do not have enough " + "money to purchase this short position. You need " + numeralWrapper.formatMoney(totalPrice)); - } else if (opts.suppressDialog != null && !opts.suppressDialog) { + } else if (opts.suppressDialog !== true) { dialogBoxCreate("You do not have enough money to purchase this short position. You need " + numeralWrapper.formatMoney(totalPrice)); } @@ -199,7 +199,7 @@ export function shortStock(stock: Stock, shares: number, workerScript: WorkerScr if (shares + stock.playerShares + stock.playerShortShares > stock.maxShares) { if (tixApi) { workerScript!.log(`ERROR: shortStock() failed because purchasing this many short shares would exceed ${stock.symbol}'s maximum number of shares.`); - } else if (opts.suppressDialog != null && !opts.suppressDialog) { + } else if (opts.suppressDialog !== true) { dialogBoxCreate(`You cannot purchase this many shares. ${stock.symbol} has a maximum of ${stock.maxShares} shares.`); } diff --git a/src/StockMarket/Order.ts b/src/StockMarket/Order.ts index 098cea14e..7fe880713 100644 --- a/src/StockMarket/Order.ts +++ b/src/StockMarket/Order.ts @@ -2,14 +2,13 @@ * Represents a Limit or Buy Order on the stock market. Does not represent * a Market Order since those are just executed immediately */ -import { Stock } from "./Stock"; import { OrderTypes } from "./data/OrderTypes"; import { PositionTypes } from "./data/PositionTypes"; import { Generic_fromJSON, Generic_toJSON, - Reviver + Reviver, } from "../../utils/JSONReviver"; export class Order { @@ -23,10 +22,10 @@ export class Order { readonly pos: PositionTypes; readonly price: number; shares: number; - readonly stock: Stock; + readonly stockSymbol: string; readonly type: OrderTypes; - constructor(stk: Stock = new Stock(), shares: number=0, price: number=0, typ: OrderTypes=OrderTypes.LimitBuy, pos: PositionTypes=PositionTypes.Long) { + constructor(stockSymbol: string="", shares: number=0, price: number=0, typ: OrderTypes=OrderTypes.LimitBuy, pos: PositionTypes=PositionTypes.Long) { // Validate arguments let invalidArgs: boolean = false; if (typeof shares !== "number" || typeof price !== "number") { @@ -35,14 +34,14 @@ export class Order { if (isNaN(shares) || isNaN(price)) { invalidArgs = true; } - if (!(stk instanceof Stock)) { + if (typeof stockSymbol !== "string") { invalidArgs = true; } if (invalidArgs) { throw new Error(`Invalid constructor paramters for Order`); } - this.stock = stk; + this.stockSymbol = stockSymbol; this.shares = shares; this.price = price; this.type = typ; diff --git a/src/StockMarket/OrderProcessing.ts b/src/StockMarket/OrderProcessing.ts index 13d65d2a0..71ebfbae2 100644 --- a/src/StockMarket/OrderProcessing.ts +++ b/src/StockMarket/OrderProcessing.ts @@ -16,6 +16,8 @@ import { Stock } from "./Stock"; import { OrderTypes } from "./data/OrderTypes"; import { PositionTypes } from "./data/PositionTypes"; +import { IMap } from "../types"; + import { numeralWrapper } from "../ui/numeralFormat"; import { dialogBoxCreate } from "../../utils/DialogBox"; @@ -23,6 +25,7 @@ import { dialogBoxCreate } from "../../utils/DialogBox"; interface IProcessOrderRefs { rerenderFn: () => void; stockMarket: IStockMarket; + symbolToStockMap: IMap; } /** @@ -56,30 +59,30 @@ export function processOrders(stock: Stock, orderType: OrderTypes, posType: Posi switch (order.type) { case OrderTypes.LimitBuy: if (order.pos === PositionTypes.Long && stock.price <= order.price) { - executeOrder/*66*/(order, refs.stockMarket, refs.rerenderFn); + executeOrder/*66*/(order, refs); } else if (order.pos === PositionTypes.Short && stock.price >= order.price) { - executeOrder/*66*/(order, refs.stockMarket, refs.rerenderFn); + executeOrder/*66*/(order, refs); } break; case OrderTypes.LimitSell: if (order.pos === PositionTypes.Long && stock.price >= order.price) { - executeOrder/*66*/(order, refs.stockMarket, refs.rerenderFn); + executeOrder/*66*/(order, refs); } else if (order.pos === PositionTypes.Short && stock.price <= order.price) { - executeOrder/*66*/(order, refs.stockMarket, refs.rerenderFn); + executeOrder/*66*/(order, refs); } break; case OrderTypes.StopBuy: if (order.pos === PositionTypes.Long && stock.price >= order.price) { - executeOrder/*66*/(order, refs.stockMarket, refs.rerenderFn); + executeOrder/*66*/(order, refs); } else if (order.pos === PositionTypes.Short && stock.price <= order.price) { - executeOrder/*66*/(order, refs.stockMarket, refs.rerenderFn); + executeOrder/*66*/(order, refs); } break; case OrderTypes.StopSell: if (order.pos === PositionTypes.Long && stock.price <= order.price) { - executeOrder/*66*/(order, refs.stockMarket, refs.rerenderFn); + executeOrder/*66*/(order, refs); } else if (order.pos === PositionTypes.Short && stock.price >= order.price) { - executeOrder/*66*/(order, refs.stockMarket, refs.rerenderFn); + executeOrder/*66*/(order, refs); } break; default: @@ -95,24 +98,27 @@ export function processOrders(stock: Stock, orderType: OrderTypes, posType: Posi * @param {Order} order - Order being executed * @param {IStockMarket} stockMarket - Reference to StockMarket object */ -function executeOrder(order: Order, stockMarket: IStockMarket, rerenderFn: () => void) { - const stock = order.stock; +function executeOrder(order: Order, refs: IProcessOrderRefs) { + const stock = refs.symbolToStockMap[order.stockSymbol]; + if (!(stock instanceof Stock)) { + console.error(`Could not find stock for this order: ${order.stockSymbol}`); + return; + } + const stockMarket = refs.stockMarket; const orderBook = stockMarket["Orders"]; const stockOrders = orderBook[stock.symbol]; const isLimit = (order.type === OrderTypes.LimitBuy || order.type === OrderTypes.LimitSell); - let limitShares = 0; + let sharesTransacted = 0; // When orders are executed, the buying and selling functions shouldn't // emit popup dialog boxes. This options object configures the functions for that const opts = { - rerenderFn: rerenderFn, + rerenderFn: refs.rerenderFn, suppressDialog: true } let res = true; let isBuy = false; - console.log("Executing the following order:"); - console.log(order); switch (order.type) { case OrderTypes.LimitBuy: { isBuy = true; @@ -122,8 +128,9 @@ function executeOrder(order: Order, stockMarket: IStockMarket, rerenderFn: () => const firstShares = Math.min(order.shares, stock.shareTxUntilMovement); // First transaction to trigger movement - if (isLong ? buyStock(stock, firstShares, null, opts) : shortStock(stock, firstShares, null, opts)) { - limitShares = firstShares; + let res = (isLong ? buyStock(stock, firstShares, null, opts) : shortStock(stock, firstShares, null, opts)); + if (res) { + sharesTransacted = firstShares; } else { break; } @@ -138,16 +145,19 @@ function executeOrder(order: Order, stockMarket: IStockMarket, rerenderFn: () => } const shares = Math.min(remainingShares, stock.shareTxForMovement); - if (isLong ? buyStock(stock, shares, null, opts) : shortStock(stock, shares, null, opts)) { - limitShares += shares; + let res = (isLong ? buyStock(stock, shares, null, opts) : shortStock(stock, shares, null, opts)); + if (res) { + sharesTransacted += shares; remainingShares -= shares; } else { break; } } + break; } case OrderTypes.StopBuy: { isBuy = true; + sharesTransacted = order.shares; if (order.pos === PositionTypes.Long) { res = buyStock(stock, order.shares, null, opts) && res; } else if (order.pos === PositionTypes.Short) { @@ -156,18 +166,23 @@ function executeOrder(order: Order, stockMarket: IStockMarket, rerenderFn: () => break; } case OrderTypes.LimitSell: { + // TODO need to account for player's shares here // We only execute limit orders until the price fails to match the order condition const isLong = (order.pos === PositionTypes.Long); - const firstShares = Math.min(order.shares, stock.shareTxUntilMovement); + const totalShares = Math.min((isLong ? stock.playerShares : stock.playerShortShares), order.shares); + if (totalShares === 0) { + return; // Player has no shares + } + const firstShares = Math.min(totalShares, stock.shareTxUntilMovement); // First transaction to trigger movement if (isLong ? sellStock(stock, firstShares, null, opts) : sellShort(stock, firstShares, null, opts)) { - limitShares = firstShares; + sharesTransacted = firstShares; } else { break; } - let remainingShares = order.shares - firstShares; + let remainingShares = totalShares - firstShares; let remainingIterations = Math.ceil(remainingShares / stock.shareTxForMovement); for (let i = 0; i < remainingIterations; ++i) { if (isLong && stock.price < order.price) { @@ -178,18 +193,23 @@ function executeOrder(order: Order, stockMarket: IStockMarket, rerenderFn: () => const shares = Math.min(remainingShares, stock.shareTxForMovement); if (isLong ? sellStock(stock, shares, null, opts) : sellShort(stock, shares, null, opts)) { - limitShares += shares; + sharesTransacted += shares; remainingShares -= shares; } else { break; } } + break; } case OrderTypes.StopSell: { if (order.pos === PositionTypes.Long) { - res = sellStock(stock, order.shares, null, opts) && res; + sharesTransacted = Math.min(stock.playerShares, order.shares); + if (sharesTransacted <= 0) { return; } + res = sellStock(stock, sharesTransacted, null, opts) && res; } else if (order.pos === PositionTypes.Short) { - res = sellShort(stock, order.shares, null, opts) && res; + sharesTransacted = Math.min(stock.playerShortShares, order.shares); + if (sharesTransacted <= 0) { return; } + res = sellShort(stock, sharesTransacted, null, opts) && res; } break; } @@ -198,42 +218,27 @@ function executeOrder(order: Order, stockMarket: IStockMarket, rerenderFn: () => return; } + if (isLimit) { + res = (sharesTransacted > 0); + } + // Position type, for logging/message purposes const pos = order.pos === PositionTypes.Long ? "Long" : "Short"; if (res) { - if (isLimit) { - - } else { - - for (let i = 0; i < stockOrders.length; ++i) { - if (order == stockOrders[i]) { - - } - } - } - for (let i = 0; i < stockOrders.length; ++i) { if (order == stockOrders[i]) { - if (isLimit) { - // Limit orders might only transact a certain # of shares, so we have the adjust the order qty. - stockOrders[i].shares -= limitShares; - if (stockOrders[i].shares <= 0) { - stockOrders.splice(i, 1); - dialogBoxCreate(`${order.type} for ${order.stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}) was filled ` + - `(${Math.round(limitShares)} shares`); - } else { - dialogBoxCreate(`${order.type} for ${order.stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}) was partially filled ` + - `(${Math.round(limitShares)} shares transacted, ${stockOrders[i].shares} shares remaining`); - } - } else { - // Stop orders will transact everything, so they can be removed completely + // Limit orders might only transact a certain # of shares, so we have the adjust the order qty. + stockOrders[i].shares -= sharesTransacted; + if (stockOrders[i].shares <= 0) { stockOrders.splice(i, 1); - dialogBoxCreate(`${order.type} for ${order.stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}) was filled ` + - `(${Math.round(order.shares)} shares transacted)`); + dialogBoxCreate(`${order.type} for ${stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}) was filled ` + + `(${numeralWrapper.formatBigNumber(Math.round(sharesTransacted))} share)`); + } else { + dialogBoxCreate(`${order.type} for ${stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}) was partially filled ` + + `(${numeralWrapper.formatBigNumber(Math.round(sharesTransacted))} shares transacted, ${stockOrders[i].shares} shares remaining`); } - - rerenderFn(); + refs.rerenderFn(); return; } } @@ -242,11 +247,11 @@ function executeOrder(order: Order, stockMarket: IStockMarket, rerenderFn: () => console.error(order); } else { if (isBuy) { - dialogBoxCreate(`Failed to execute ${order.type} for ${order.stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}). ` + + dialogBoxCreate(`Failed to execute ${order.type} for ${stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}). ` + `This is most likely because you do not have enough money or the order would exceed the stock's maximum number of shares`); } else { - dialogBoxCreate(`Failed to execute ${order.type} for ${order.stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}). ` + - `This is most likely a bug, please report to game developer with details.`); + dialogBoxCreate(`Failed to execute ${order.type} for ${stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}). ` + + `This may be a bug, please report to game developer with details.`); } } } diff --git a/src/StockMarket/StockMarket.jsx b/src/StockMarket/StockMarket.jsx index e50b9b20c..b41a0c61b 100644 --- a/src/StockMarket/StockMarket.jsx +++ b/src/StockMarket/StockMarket.jsx @@ -41,16 +41,24 @@ export let SymbolToStockMap = {}; // Maps symbol -> Stock object export function placeOrder(stock, shares, price, type, position, workerScript=null) { const tixApi = (workerScript instanceof WorkerScript); + if (!(stock instanceof Stock)) { + if (tixApi) { + workerScript.log(`ERROR: Invalid stock passed to placeOrder() function`); + } else { + dialogBoxCreate(`ERROR: Invalid stock passed to placeOrder() function`); + } + return false; + } if (typeof shares !== "number" || typeof price !== "number") { if (tixApi) { - workerScript.scriptRef.log("ERROR: Invalid numeric value provided for either 'shares' or 'price' argument"); + workerScript.log("ERROR: Invalid numeric value provided for either 'shares' or 'price' argument"); } else { dialogBoxCreate("ERROR: Invalid numeric value provided for either 'shares' or 'price' argument"); } return false; } - const order = new Order(stock, shares, price, type, position); + const order = new Order(stock.symbol, shares, price, type, position); if (StockMarket["Orders"] == null) { const orders = {}; for (const name in StockMarket) { @@ -66,8 +74,9 @@ export function placeOrder(stock, shares, price, type, position, workerScript=nu const processOrderRefs = { rerenderFn: displayStockMarketContent, stockMarket: StockMarket, + symbolToStockMap: SymbolToStockMap, } - processOrders(order.stock, order.type, order.pos, processOrderRefs); + processOrders(stock, order.type, order.pos, processOrderRefs); displayStockMarketContent(); return true; @@ -78,9 +87,9 @@ export function cancelOrder(params, workerScript=null) { var tixApi = (workerScript instanceof WorkerScript); if (StockMarket["Orders"] == null) {return false;} if (params.order && params.order instanceof Order) { - var order = params.order; + const order = params.order; // An 'Order' object is passed in - var stockOrders = StockMarket["Orders"][order.stock.symbol]; + var stockOrders = StockMarket["Orders"][order.stockSymbol]; for (var i = 0; i < stockOrders.length; ++i) { if (order == stockOrders[i]) { stockOrders.splice(i, 1); @@ -122,6 +131,24 @@ export function loadStockMarket(saveString) { StockMarket = {}; } else { StockMarket = JSON.parse(saveString, Reviver); + + // Backwards compatibility for v0.47.0 + const orderBook = StockMarket["Orders"]; + if (orderBook != null) { + // For each order, set its 'stockSymbol' property equal to the + // symbol of its 'stock' property + for (const stockSymbol in orderBook) { + const ordersForStock = orderBook[stockSymbol]; + if (Array.isArray(ordersForStock)) { + for (const order of ordersForStock) { + if (order instanceof Order && order.stock instanceof Stock) { + order.stockSymbol = order.stock.symbol; + } + } + } + } + console.log(`Converted Stock Market order book to v0.47.0 format`); + } } } @@ -175,7 +202,7 @@ export function stockMarketCycle() { if (stock.b) { thresh = 0.4; } if (Math.random() < thresh) { stock.b = !stock.b; - if (stock.otlkMag < 10) { stock.otlkMag += 0.4; } + if (stock.otlkMag < 10) { stock.otlkMag += 0.2; } } } } @@ -220,15 +247,16 @@ export function processStockPrices(numCycles=1) { const processOrderRefs = { rerenderFn: displayStockMarketContent, stockMarket: StockMarket, + symbolToStockMap: SymbolToStockMap, } if (c < chc) { - stock.price *= (1 + av); + stock.changePrice(stock.price * (1 + av)); processOrders(stock, OrderTypes.LimitBuy, PositionTypes.Short, processOrderRefs); processOrders(stock, OrderTypes.LimitSell, PositionTypes.Long, processOrderRefs); processOrders(stock, OrderTypes.StopBuy, PositionTypes.Long, processOrderRefs); processOrders(stock, OrderTypes.StopSell, PositionTypes.Short, processOrderRefs); } else { - stock.price /= (1 + av); + stock.changePrice(stock.price / (1 + av)); processOrders(stock, OrderTypes.LimitBuy, PositionTypes.Long, processOrderRefs); processOrders(stock, OrderTypes.LimitSell, PositionTypes.Short, processOrderRefs); processOrders(stock, OrderTypes.StopBuy, PositionTypes.Short, processOrderRefs); diff --git a/src/StockMarket/StockMarketHelpers.ts b/src/StockMarket/StockMarketHelpers.ts index 2d296e401..ca5e71b32 100644 --- a/src/StockMarket/StockMarketHelpers.ts +++ b/src/StockMarket/StockMarketHelpers.ts @@ -3,7 +3,7 @@ import { PositionTypes } from "./data/PositionTypes"; import { CONSTANTS } from "../Constants"; // Amount by which a stock's forecast changes during each price movement -export const forecastChangePerPriceMovement = 0.4; +export const forecastChangePerPriceMovement = 0.1; /** * Given a stock, calculates the amount by which the stock price is multiplied @@ -118,7 +118,7 @@ export function processBuyTransactionPriceMovement(stock: Stock, shares: number, if (stock.shareTxUntilMovement <= 0) { stock.shareTxUntilMovement = stock.shareTxForMovement; processPriceMovement(); - stock.price = currPrice; + stock.changePrice(currPrice); stock.otlkMag -= (forecastChangePerPriceMovement); } @@ -140,11 +140,15 @@ export function processBuyTransactionPriceMovement(stock: Stock, shares: number, stock.shareTxUntilMovement = stock.shareTxForMovement; processPriceMovement(); } - stock.price = currPrice; + stock.changePrice(currPrice); // Forecast always decreases in magnitude const forecastChange = Math.min(5, forecastChangePerPriceMovement * (numIterations - 1)); stock.otlkMag -= forecastChange; + if (stock.otlkMag < 0) { + stock.b = !stock.b; + stock.otlkMag = Math.abs(stock.otlkMag); + } } /** @@ -242,7 +246,7 @@ export function processSellTransactionPriceMovement(stock: Stock, shares: number if (stock.shareTxUntilMovement <= 0) { stock.shareTxUntilMovement = stock.shareTxForMovement; processPriceMovement(); - stock.price = currPrice; + stock.changePrice(currPrice); stock.otlkMag -= (forecastChangePerPriceMovement); } @@ -263,11 +267,15 @@ export function processSellTransactionPriceMovement(stock: Stock, shares: number stock.shareTxUntilMovement = stock.shareTxForMovement; processPriceMovement(); } - stock.price = currPrice; + stock.changePrice(currPrice); // Forecast always decreases in magnitude const forecastChange = Math.min(5, forecastChangePerPriceMovement * (numIterations - 1)); stock.otlkMag -= forecastChange; + if (stock.otlkMag < 0) { + stock.b = !stock.b; + stock.otlkMag = Math.abs(stock.otlkMag); + } } /** diff --git a/src/StockMarket/data/InitStockMetadata.ts b/src/StockMarket/data/InitStockMetadata.ts index 64b5ebe90..45331fb4f 100644 --- a/src/StockMarket/data/InitStockMetadata.ts +++ b/src/StockMarket/data/InitStockMetadata.ts @@ -33,8 +33,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 1, }, shareTxForMovement: { - max: 30e3, - min: 10e3, + max: 45e3, + min: 15e3, }, symbol: StockSymbols[LocationName.AevumECorp], }, @@ -59,8 +59,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 1, }, shareTxForMovement: { - max: 30e3, - min: 10e3, + max: 45e3, + min: 15e3, }, symbol: StockSymbols[LocationName.Sector12MegaCorp], }, @@ -85,8 +85,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 1, }, shareTxForMovement: { - max: 30e3, - min: 10e3, + max: 45e3, + min: 15e3, }, symbol: StockSymbols[LocationName.Sector12BladeIndustries], }, @@ -111,8 +111,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 1, }, shareTxForMovement: { - max: 30e3, - min: 10e3, + max: 45e3, + min: 15e3, }, symbol: StockSymbols[LocationName.AevumClarkeIncorporated], }, @@ -137,8 +137,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 1, }, shareTxForMovement: { - max: 30e3, - min: 10e3, + max: 45e3, + min: 15e3, }, symbol: StockSymbols[LocationName.VolhavenOmniTekIncorporated], }, @@ -163,8 +163,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 1, }, shareTxForMovement: { - max: 30e3, - min: 10e3, + max: 45e3, + min: 15e3, }, symbol: StockSymbols[LocationName.Sector12FourSigma], }, @@ -189,8 +189,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 1, }, shareTxForMovement: { - max: 30e3, - min: 10e3, + max: 45e3, + min: 15e3, }, symbol: StockSymbols[LocationName.ChongqingKuaiGongInternational], }, @@ -215,8 +215,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 1, }, shareTxForMovement: { - max: 30e3, - min: 10e3, + max: 45e3, + min: 15e3, }, symbol: StockSymbols[LocationName.AevumFulcrumTechnologies], }, @@ -241,8 +241,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 2, }, shareTxForMovement: { - max: 36e3, - min: 12e3, + max: 54e3, + min: 18e3, }, symbol: StockSymbols[LocationName.IshimaStormTechnologies], }, @@ -267,8 +267,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 2, }, shareTxForMovement: { - max: 36e3, - min: 12e3, + max: 54e3, + min: 18e3, }, symbol: StockSymbols[LocationName.NewTokyoDefComm], }, @@ -293,8 +293,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 2, }, shareTxForMovement: { - max: 36e3, - min: 12e3, + max: 54e3, + min: 18e3, }, symbol: StockSymbols[LocationName.VolhavenHeliosLabs], }, @@ -319,8 +319,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 2, }, shareTxForMovement: { - max: 36e3, - min: 12e3, + max: 54e3, + min: 18e3, }, symbol: StockSymbols[LocationName.NewTokyoVitaLife], }, @@ -345,8 +345,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 3, }, shareTxForMovement: { - max: 36e3, - min: 12e3, + max: 54e3, + min: 18e3, }, symbol: StockSymbols[LocationName.Sector12IcarusMicrosystems], }, @@ -371,8 +371,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 2, }, shareTxForMovement: { - max: 36e3, - min: 12e3, + max: 54e3, + min: 18e3, }, symbol: StockSymbols[LocationName.Sector12UniversalEnergy], }, @@ -397,8 +397,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 3, }, shareTxForMovement: { - max: 42e3, - min: 14e3, + max: 63e3, + min: 21e3, }, symbol: StockSymbols[LocationName.AevumAeroCorp], }, @@ -423,8 +423,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 4, }, shareTxForMovement: { - max: 42e3, - min: 14e3, + max: 63e3, + min: 21e3, }, symbol: StockSymbols[LocationName.VolhavenOmniaCybersystems], }, @@ -449,8 +449,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 4, }, shareTxForMovement: { - max: 42e3, - min: 14e3, + max: 63e3, + min: 21e3, }, symbol: StockSymbols[LocationName.ChongqingSolarisSpaceSystems], }, @@ -475,8 +475,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 4, }, shareTxForMovement: { - max: 42e3, - min: 14e3, + max: 63e3, + min: 21e3, }, symbol: StockSymbols[LocationName.NewTokyoGlobalPharmaceuticals], }, @@ -501,8 +501,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 4, }, shareTxForMovement: { - max: 42e3, - min: 14e3, + max: 63e3, + min: 21e3, }, symbol: StockSymbols[LocationName.IshimaNovaMedical], }, @@ -527,8 +527,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 5, }, shareTxForMovement: { - max: 18e3, - min: 4e3, + max: 27e3, + min: 6e3, }, symbol: StockSymbols[LocationName.AevumWatchdogSecurity], }, @@ -553,8 +553,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 5, }, shareTxForMovement: { - max: 36e3, - min: 12e3, + max: 54e3, + min: 18e3, }, symbol: StockSymbols[LocationName.VolhavenLexoCorp], }, @@ -579,8 +579,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 3, }, shareTxForMovement: { - max: 42e3, - min: 20e3, + max: 63e3, + min: 30e3, }, symbol: StockSymbols[LocationName.AevumRhoConstruction], }, @@ -605,8 +605,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 5, }, shareTxForMovement: { - max: 30e3, - min: 10e3, + max: 45e3, + min: 15e3, }, symbol: StockSymbols[LocationName.Sector12AlphaEnterprises], }, @@ -631,8 +631,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 5, }, shareTxForMovement: { - max: 30e3, - min: 10e3, + max: 45e3, + min: 15e3, }, symbol: StockSymbols[LocationName.VolhavenSysCoreSecurities], }, @@ -657,8 +657,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 4, }, shareTxForMovement: { - max: 42e3, - min: 20e3, + max: 63e3, + min: 30e3, }, symbol: StockSymbols[LocationName.VolhavenCompuTek], }, @@ -683,8 +683,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 5, }, shareTxForMovement: { - max: 18e3, - min: 6e3, + max: 27e3, + min: 9e3, }, symbol: StockSymbols[LocationName.AevumNetLinkTechnologies], }, @@ -709,8 +709,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 4, }, shareTxForMovement: { - max: 30e3, - min: 10e3, + max: 45e3, + min: 15e3, }, symbol: StockSymbols[LocationName.IshimaOmegaSoftware], }, @@ -735,8 +735,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 6, }, shareTxForMovement: { - max: 60e3, - min: 20e3, + max: 90e3, + min: 30e3, }, symbol: StockSymbols[LocationName.Sector12FoodNStuff], }, @@ -761,8 +761,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 6, }, shareTxForMovement: { - max: 28e3, - min: 8e3, + max: 42e3, + min: 12e3, }, symbol: StockSymbols["Sigma Cosmetics"], }, @@ -787,8 +787,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 6, }, shareTxForMovement: { - max: 21e3, - min: 6e3, + max: 32e3, + min: 9e3, }, symbol: StockSymbols["Joes Guns"], }, @@ -813,8 +813,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 5, }, shareTxForMovement: { - max: 24e3, - min: 8e3, + max: 36e3, + min: 12e3, }, symbol: StockSymbols["Catalyst Ventures"], }, @@ -839,8 +839,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 3, }, shareTxForMovement: { - max: 72e3, - min: 30e3, + max: 108e3, + min: 45e3, }, symbol: StockSymbols["Microdyne Technologies"], }, @@ -865,8 +865,8 @@ export const InitStockMetadata: IConstructorParams[] = [ min: 2, }, shareTxForMovement: { - max: 72e3, - min: 30e3, + max: 108e3, + min: 45e3, }, symbol: StockSymbols["Titan Laboratories"], }, diff --git a/src/StockMarket/ui/StockTickerHeaderText.tsx b/src/StockMarket/ui/StockTickerHeaderText.tsx index 1791d8be0..4ac3a8548 100644 --- a/src/StockMarket/ui/StockTickerHeaderText.tsx +++ b/src/StockMarket/ui/StockTickerHeaderText.tsx @@ -24,7 +24,9 @@ export function StockTickerHeaderText(props: IProps): React.ReactElement { let hdrText = `${stock.name}${" ".repeat(1 + TickerHeaderFormatData.longestName - stock.name.length + (TickerHeaderFormatData.longestSymbol - stock.symbol.length))}${stock.symbol} -${" ".repeat(10 - stockPriceFormat.length)}${stockPriceFormat}`; if (props.p.has4SData) { hdrText += ` - Volatility: ${numeralWrapper.format(stock.mv, '0,0.00')}% - Price Forecast: `; - hdrText += (stock.b ? "+" : "-").repeat(Math.floor(stock.otlkMag / 10) + 1); + let plusOrMinus = stock.b; // True for "+", false for "-" + if (stock.otlkMag < 0) { plusOrMinus = !plusOrMinus } + hdrText += (plusOrMinus ? "+" : "-").repeat(Math.floor(Math.abs(stock.otlkMag) / 10) + 1); } let styleMarkup = { diff --git a/test/Netscript/StaticRamCalculationTests.js b/test/Netscript/StaticRamCalculationTests.js new file mode 100644 index 000000000..51b5d3236 --- /dev/null +++ b/test/Netscript/StaticRamCalculationTests.js @@ -0,0 +1,17 @@ +/** + * TODO This should also test the calcualteRamUsage() function from + * /Script/RamCalculations but there's some issues with getting tests to run + * when any npm package is included in the build (/Script/RamCalculations includes + * walk from acorn). + */ +import { getRamCost } from "../../src/Netscript/RamCostGenerator"; +//import { calculateRamUsage } from "../../src/Script/RamCalculations" + +const assert = chai.assert; +const expect = chai.expect; + +console.log("Beginning Netscript Static RAM Calculation/Generation Tests"); + +describe("Netscript Static RAM Calculation/Generation Tests", function() { + +}); diff --git a/test/README.md b/test/README.md new file mode 100644 index 000000000..dc57f24cc --- /dev/null +++ b/test/README.md @@ -0,0 +1,5 @@ +# Unit Tests +This directory contains unit tests for Bitburner. + +Unit tests use Mocha/Chai and are run through the browser. The Mocha/Chai +packages are sourced directly in HTML. diff --git a/tests/StockMarketTests.js b/test/StockMarketTests.js similarity index 98% rename from tests/StockMarketTests.js rename to test/StockMarketTests.js index 59270fdd9..53283940f 100644 --- a/tests/StockMarketTests.js +++ b/test/StockMarketTests.js @@ -1,7 +1,8 @@ import { CONSTANTS } from "../src/Constants"; import { Order } from "../src/StockMarket/Order"; //import { processOrders } from "../src/StockMarket/OrderProcessing"; -// import { Stock } from "../src/StockMarket/Stock"; +import { Stock } from "../src/StockMarket/Stock"; +/* import { deleteStockMarket, initStockMarket, @@ -10,7 +11,7 @@ import { StockMarket, SymbolToStockMap, } from "../src/StockMarket/StockMarket"; -/* +*/ import { calculateIncreasingPriceMovement, calculateDecreasingPriceMovement, @@ -20,9 +21,8 @@ import { processBuyTransactionPriceMovement, processSellTransactionPriceMovement, } from "../src/StockMarket/StockMarketHelpers"; -*/ -// import { OrderTypes } from "../src/StockMarket/data/OrderTypes" -// import { PositionTypes } from "../src/StockMarket/data/PositionTypes"; +import { OrderTypes } from "../src/StockMarket/data/OrderTypes" +import { PositionTypes } from "../src/StockMarket/data/PositionTypes"; const assert = chai.assert; const expect = chai.expect; @@ -158,6 +158,8 @@ describe("Stock Market Tests", function() { }); }); + /* + // TODO These tests fail due to circular dependency errors describe("StockMarket object", function() { describe("Initialization", function() { // Keeps track of initialized stocks. Contains their symbols @@ -218,6 +220,7 @@ describe("Stock Market Tests", function() { }); }); + */ describe("Transaction Cost Calculator Functions", function() { describe("getBuyTransactionCost()", function() { @@ -644,16 +647,16 @@ describe("Stock Market Tests", function() { return new Order({}, 1, 1, OrderTypes.LimitBuy, PositionTypes.Long); } function invalid2() { - return new Order(new Stock(), "z", 0, OrderTypes.LimitBuy, PositionTypes.Short); + return new Order("FOO", "z", 0, OrderTypes.LimitBuy, PositionTypes.Short); } function invalid3() { - return new Order(new Stock(), 1, {}, OrderTypes.LimitBuy, PositionTypes.Short); + return new Order("FOO", 1, {}, OrderTypes.LimitBuy, PositionTypes.Short); } function invalid4() { - return new Order(new Stock(), 1, NaN, OrderTypes.LimitBuy, PositionTypes.Short); + return new Order("FOO", 1, NaN, OrderTypes.LimitBuy, PositionTypes.Short); } function invalid5() { - return new Order(new Stock(), NaN, 0, OrderTypes.LimitBuy, PositionTypes.Short); + return new Order("FOO", NaN, 0, OrderTypes.LimitBuy, PositionTypes.Short); } expect(invalid1).to.throw(); diff --git a/tests/index.html b/test/index.html similarity index 71% rename from tests/index.html rename to test/index.html index d0e4c0c80..8767e8875 100644 --- a/tests/index.html +++ b/test/index.html @@ -8,15 +8,16 @@
- - + + - diff --git a/test/index.js b/test/index.js new file mode 100644 index 000000000..4f65f6efd --- /dev/null +++ b/test/index.js @@ -0,0 +1,2 @@ +export * from "./Netscript/StaticRamCalculationTests"; +export * from "./StockMarketTests"; diff --git a/test/tests.bundle.js b/test/tests.bundle.js new file mode 100644 index 000000000..1e0f415e6 --- /dev/null +++ b/test/tests.bundle.js @@ -0,0 +1,2035 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 300); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ 10: +/*!*******************************************!*\ + !*** ./src/Netscript/RamCostGenerator.ts ***! + \*******************************************/ +/*! no static exports found */ +/*! all exports used */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +// TODO remember to update RamCalculations.js and WorkerScript.js +// RAM costs for Netscript functions +exports.RamCostConstants = { + ScriptBaseRamCost: 1.6, + ScriptDomRamCost: 25, + ScriptHackRamCost: 0.1, + ScriptHackAnalyzeRamCost: 1, + ScriptGrowRamCost: 0.15, + ScriptGrowthAnalyzeRamCost: 1, + ScriptWeakenRamCost: 0.15, + ScriptScanRamCost: 0.2, + ScriptPortProgramRamCost: 0.05, + ScriptRunRamCost: 1.0, + ScriptExecRamCost: 1.3, + ScriptSpawnRamCost: 2.0, + ScriptScpRamCost: 0.6, + ScriptKillRamCost: 0.5, + ScriptHasRootAccessRamCost: 0.05, + ScriptGetHostnameRamCost: 0.05, + ScriptGetHackingLevelRamCost: 0.05, + ScriptGetMultipliersRamCost: 4.0, + ScriptGetServerRamCost: 0.1, + ScriptFileExistsRamCost: 0.1, + ScriptIsRunningRamCost: 0.1, + ScriptHacknetNodesRamCost: 4.0, + ScriptHNUpgLevelRamCost: 0.4, + ScriptHNUpgRamRamCost: 0.6, + ScriptHNUpgCoreRamCost: 0.8, + ScriptGetStockRamCost: 2.0, + ScriptBuySellStockRamCost: 2.5, + ScriptGetPurchaseServerRamCost: 0.25, + ScriptPurchaseServerRamCost: 2.25, + ScriptGetPurchasedServerLimit: 0.05, + ScriptGetPurchasedServerMaxRam: 0.05, + ScriptRoundRamCost: 0.05, + ScriptReadWriteRamCost: 1.0, + ScriptArbScriptRamCost: 1.0, + ScriptGetScriptRamCost: 0.1, + ScriptGetHackTimeRamCost: 0.05, + ScriptGetFavorToDonate: 0.10, + ScriptCodingContractBaseRamCost: 10, + ScriptSleeveBaseRamCost: 4, + ScriptSingularityFn1RamCost: 2, + ScriptSingularityFn2RamCost: 3, + ScriptSingularityFn3RamCost: 5, + ScriptGangApiBaseRamCost: 4, + ScriptBladeburnerApiBaseRamCost: 4, +}; +exports.RamCosts = { + hacknet: { + numNodes: () => 0, + purchaseNode: () => 0, + getPurchaseNodeCost: () => 0, + getNodeStats: () => 0, + upgradeLevel: () => 0, + upgradeRam: () => 0, + upgradeCore: () => 0, + upgradeCache: () => 0, + getLevelUpgradeCost: () => 0, + getRamUpgradeCost: () => 0, + getCoreUpgradeCost: () => 0, + getCacheUpgradeCost: () => 0, + numHashes: () => 0, + hashCost: () => 0, + spendHashes: () => 0, + }, + sprintf: () => 0, + vsprintf: () => 0, + scan: () => exports.RamCostConstants.ScriptScanRamCost, + hack: () => exports.RamCostConstants.ScriptHackRamCost, + hackAnalyzeThreads: () => exports.RamCostConstants.ScriptHackAnalyzeRamCost, + hackAnalyzePercent: () => exports.RamCostConstants.ScriptHackAnalyzeRamCost, + hackChance: () => exports.RamCostConstants.ScriptHackAnalyzeRamCost, + sleep: () => 0, + grow: () => exports.RamCostConstants.ScriptGrowRamCost, + growthAnalyze: () => exports.RamCostConstants.ScriptGrowthAnalyzeRamCost, + weaken: () => exports.RamCostConstants.ScriptWeakenRamCost, + print: () => 0, + tprint: () => 0, + clearLog: () => 0, + disableLog: () => 0, + enableLog: () => 0, + isLogEnabled: () => 0, + getScriptLogs: () => 0, + nuke: () => exports.RamCostConstants.ScriptPortProgramRamCost, + brutessh: () => exports.RamCostConstants.ScriptPortProgramRamCost, + ftpcrack: () => exports.RamCostConstants.ScriptPortProgramRamCost, + relaysmtp: () => exports.RamCostConstants.ScriptPortProgramRamCost, + httpworm: () => exports.RamCostConstants.ScriptPortProgramRamCost, + sqlinject: () => exports.RamCostConstants.ScriptPortProgramRamCost, + run: () => exports.RamCostConstants.ScriptRunRamCost, + exec: () => exports.RamCostConstants.ScriptExecRamCost, + spawn: () => exports.RamCostConstants.ScriptSpawnRamCost, + kill: () => exports.RamCostConstants.ScriptKillRamCost, + killall: () => exports.RamCostConstants.ScriptKillRamCost, + exit: () => 0, + scp: () => exports.RamCostConstants.ScriptScpRamCost, + ls: () => exports.RamCostConstants.ScriptScanRamCost, + ps: () => exports.RamCostConstants.ScriptScanRamCost, + hasRootAccess: () => exports.RamCostConstants.ScriptHasRootAccessRamCost, + getIp: () => exports.RamCostConstants.ScriptGetHostnameRamCost, + getHostname: () => exports.RamCostConstants.ScriptGetHostnameRamCost, + getHackingLevel: () => exports.RamCostConstants.ScriptGetHackingLevelRamCost, + getHackingMultipliers: () => exports.RamCostConstants.ScriptGetMultipliersRamCost, + getHacknetMultipliers: () => exports.RamCostConstants.ScriptGetMultipliersRamCost, + getBitNodeMultipliers: () => exports.RamCostConstants.ScriptGetMultipliersRamCost, + getServerMoneyAvailable: () => exports.RamCostConstants.ScriptGetServerRamCost, + getServerSecurityLevel: () => exports.RamCostConstants.ScriptGetServerRamCost, + getServerBaseSecurityLevel: () => exports.RamCostConstants.ScriptGetServerRamCost, + getServerMinSecurityLevel: () => exports.RamCostConstants.ScriptGetServerRamCost, + getServerRequiredHackingLevel: () => exports.RamCostConstants.ScriptGetServerRamCost, + getServerMaxMoney: () => exports.RamCostConstants.ScriptGetServerRamCost, + getServerGrowth: () => exports.RamCostConstants.ScriptGetServerRamCost, + getServerNumPortsRequired: () => exports.RamCostConstants.ScriptGetServerRamCost, + getServerRam: () => exports.RamCostConstants.ScriptGetServerRamCost, + serverExists: () => exports.RamCostConstants.ScriptGetServerRamCost, + fileExists: () => exports.RamCostConstants.ScriptFileExistsRamCost, + isRunning: () => exports.RamCostConstants.ScriptIsRunningRamCost, + getStockSymbols: () => exports.RamCostConstants.ScriptGetStockRamCost, + getStockPrice: () => exports.RamCostConstants.ScriptGetStockRamCost, + getStockAskPrice: () => exports.RamCostConstants.ScriptGetStockRamCost, + getStockBidPrice: () => exports.RamCostConstants.ScriptGetStockRamCost, + getStockPosition: () => exports.RamCostConstants.ScriptGetStockRamCost, + getStockMaxShares: () => exports.RamCostConstants.ScriptGetStockRamCost, + getStockPurchaseCost: () => exports.RamCostConstants.ScriptGetStockRamCost, + getStockSaleGain: () => exports.RamCostConstants.ScriptGetStockRamCost, + buyStock: () => exports.RamCostConstants.ScriptBuySellStockRamCost, + sellStock: () => exports.RamCostConstants.ScriptBuySellStockRamCost, + shortStock: () => exports.RamCostConstants.ScriptBuySellStockRamCost, + sellShort: () => exports.RamCostConstants.ScriptBuySellStockRamCost, + placeOrder: () => exports.RamCostConstants.ScriptBuySellStockRamCost, + cancelOrder: () => exports.RamCostConstants.ScriptBuySellStockRamCost, + getOrders: () => exports.RamCostConstants.ScriptBuySellStockRamCost, + getStockVolatility: () => exports.RamCostConstants.ScriptBuySellStockRamCost, + getStockForecast: () => exports.RamCostConstants.ScriptBuySellStockRamCost, + purchase4SMarketData: () => exports.RamCostConstants.ScriptBuySellStockRamCost, + purchase4SMarketDataTixApi: () => exports.RamCostConstants.ScriptBuySellStockRamCost, + getPurchasedServerLimit: () => exports.RamCostConstants.ScriptGetPurchasedServerLimit, + getPurchasedServerMaxRam: () => exports.RamCostConstants.ScriptGetPurchasedServerMaxRam, + getPurchasedServerCost: () => exports.RamCostConstants.ScriptGetPurchaseServerRamCost, + purchaseServer: () => exports.RamCostConstants.ScriptPurchaseServerRamCost, + deleteServer: () => exports.RamCostConstants.ScriptPurchaseServerRamCost, + getPurchasedServers: () => exports.RamCostConstants.ScriptPurchaseServerRamCost, + write: () => exports.RamCostConstants.ScriptReadWriteRamCost, + tryWrite: () => exports.RamCostConstants.ScriptReadWriteRamCost, + read: () => exports.RamCostConstants.ScriptReadWriteRamCost, + peek: () => exports.RamCostConstants.ScriptReadWriteRamCost, + clear: () => exports.RamCostConstants.ScriptReadWriteRamCost, + getPortHandle: () => exports.RamCostConstants.ScriptReadWriteRamCost * 10, + rm: () => exports.RamCostConstants.ScriptReadWriteRamCost, + scriptRunning: () => exports.RamCostConstants.ScriptArbScriptRamCost, + scriptKill: () => exports.RamCostConstants.ScriptArbScriptRamCost, + getScriptName: () => 0, + getScriptRam: () => exports.RamCostConstants.ScriptGetScriptRamCost, + getHackTime: () => exports.RamCostConstants.ScriptGetHackTimeRamCost, + getGrowTime: () => exports.RamCostConstants.ScriptGetHackTimeRamCost, + getWeakenTime: () => exports.RamCostConstants.ScriptGetHackTimeRamCost, + getScriptIncome: () => exports.RamCostConstants.ScriptGetScriptRamCost, + getScriptExpGain: () => exports.RamCostConstants.ScriptGetScriptRamCost, + nFormat: () => 0, + getTimeSinceLastAug: () => exports.RamCostConstants.ScriptGetHackTimeRamCost, + prompt: () => 0, + wget: () => 0, + getFavorToDonate: () => exports.RamCostConstants.ScriptGetFavorToDonate, + // Singularity Functions + universityCourse: () => exports.RamCostConstants.ScriptSingularityFn1RamCost, + gymWorkout: () => exports.RamCostConstants.ScriptSingularityFn1RamCost, + travelToCity: () => exports.RamCostConstants.ScriptSingularityFn1RamCost, + purchaseTor: () => exports.RamCostConstants.ScriptSingularityFn1RamCost, + purchaseProgram: () => exports.RamCostConstants.ScriptSingularityFn1RamCost, + getStats: () => exports.RamCostConstants.ScriptSingularityFn1RamCost / 4, + getCharacterInformation: () => exports.RamCostConstants.ScriptSingularityFn1RamCost / 4, + isBusy: () => exports.RamCostConstants.ScriptSingularityFn1RamCost / 4, + stopAction: () => exports.RamCostConstants.ScriptSingularityFn1RamCost / 2, + upgradeHomeRam: () => exports.RamCostConstants.ScriptSingularityFn2RamCost, + getUpgradeHomeRamCost: () => exports.RamCostConstants.ScriptSingularityFn2RamCost / 2, + workForCompany: () => exports.RamCostConstants.ScriptSingularityFn2RamCost, + applyToCompany: () => exports.RamCostConstants.ScriptSingularityFn2RamCost, + getCompanyRep: () => exports.RamCostConstants.ScriptSingularityFn2RamCost / 3, + getCompanyFavor: () => exports.RamCostConstants.ScriptSingularityFn2RamCost / 3, + getCompanyFavorGain: () => exports.RamCostConstants.ScriptSingularityFn2RamCost / 4, + checkFactionInvitations: () => exports.RamCostConstants.ScriptSingularityFn2RamCost, + joinFaction: () => exports.RamCostConstants.ScriptSingularityFn2RamCost, + workForFaction: () => exports.RamCostConstants.ScriptSingularityFn2RamCost, + getFactionRep: () => exports.RamCostConstants.ScriptSingularityFn2RamCost / 3, + getFactionFavor: () => exports.RamCostConstants.ScriptSingularityFn2RamCost / 3, + getFactionFavorGain: () => exports.RamCostConstants.ScriptSingularityFn2RamCost / 4, + donateToFaction: () => exports.RamCostConstants.ScriptSingularityFn3RamCost, + createProgram: () => exports.RamCostConstants.ScriptSingularityFn3RamCost, + commitCrime: () => exports.RamCostConstants.ScriptSingularityFn3RamCost, + getCrimeChance: () => exports.RamCostConstants.ScriptSingularityFn3RamCost, + getOwnedAugmentations: () => exports.RamCostConstants.ScriptSingularityFn3RamCost, + getOwnedSourceFiles: () => exports.RamCostConstants.ScriptSingularityFn3RamCost, + getAugmentationsFromFaction: () => exports.RamCostConstants.ScriptSingularityFn3RamCost, + getAugmentationPrereq: () => exports.RamCostConstants.ScriptSingularityFn3RamCost, + getAugmentationCost: () => exports.RamCostConstants.ScriptSingularityFn3RamCost, + purchaseAugmentation: () => exports.RamCostConstants.ScriptSingularityFn3RamCost, + installAugmentations: () => exports.RamCostConstants.ScriptSingularityFn3RamCost, + // Gang API + gang: { + getMemberNames: () => exports.RamCostConstants.ScriptGangApiBaseRamCost / 4, + getGangInformation: () => exports.RamCostConstants.ScriptGangApiBaseRamCost / 2, + getOtherGangInformation: () => exports.RamCostConstants.ScriptGangApiBaseRamCost / 2, + getMemberInformation: () => exports.RamCostConstants.ScriptGangApiBaseRamCost / 2, + canRecruitMember: () => exports.RamCostConstants.ScriptGangApiBaseRamCost / 4, + recruitMember: () => exports.RamCostConstants.ScriptGangApiBaseRamCost / 2, + getTaskNames: () => exports.RamCostConstants.ScriptGangApiBaseRamCost / 4, + setMemberTask: () => exports.RamCostConstants.ScriptGangApiBaseRamCost / 2, + getEquipmentNames: () => exports.RamCostConstants.ScriptGangApiBaseRamCost / 4, + getEquipmentCost: () => exports.RamCostConstants.ScriptGangApiBaseRamCost / 2, + getEquipmentType: () => exports.RamCostConstants.ScriptGangApiBaseRamCost / 2, + purchaseEquipment: () => exports.RamCostConstants.ScriptGangApiBaseRamCost, + ascendMember: () => exports.RamCostConstants.ScriptGangApiBaseRamCost, + setTerritoryWarfare: () => exports.RamCostConstants.ScriptGangApiBaseRamCost / 2, + getChanceToWinClash: () => exports.RamCostConstants.ScriptGangApiBaseRamCost, + getBonusTime: () => 0, + }, + // Bladeburner API + bladeburner: { + getContractNames: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10, + getOperationNames: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10, + getBlackOpNames: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10, + getBlackOpRank: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost / 2, + getGeneralActionNames: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10, + getSkillNames: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10, + startAction: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + stopBladeburnerAction: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost / 2, + getCurrentAction: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost / 4, + getActionTime: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getActionEstimatedSuccessChance: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getActionRepGain: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getActionCountRemaining: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getActionMaxLevel: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getActionCurrentLevel: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getActionAutolevel: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + setActionAutolevel: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + setActionLevel: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getRank: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getSkillPoints: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getSkillLevel: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getSkillUpgradeCost: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + upgradeSkill: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getTeamSize: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + setTeamSize: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getCityEstimatedPopulation: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getCityEstimatedCommunities: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getCityChaos: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getCity: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + switchCity: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getStamina: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + joinBladeburnerFaction: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + joinBladeburnerDivision: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + getBonusTime: () => exports.RamCostConstants.ScriptBladeburnerApiBaseRamCost, + }, + // Coding Contract API + codingcontract: { + attempt: () => exports.RamCostConstants.ScriptCodingContractBaseRamCost, + getContractType: () => exports.RamCostConstants.ScriptCodingContractBaseRamCost / 2, + getData: () => exports.RamCostConstants.ScriptCodingContractBaseRamCost / 2, + getDescription: () => exports.RamCostConstants.ScriptCodingContractBaseRamCost / 2, + getNumTriesRemaining: () => exports.RamCostConstants.ScriptCodingContractBaseRamCost / 2, + }, + // Duplicate Sleeve API + sleeve: { + getNumSleeves: () => exports.RamCostConstants.ScriptSleeveBaseRamCost, + setToShockRecovery: () => exports.RamCostConstants.ScriptSleeveBaseRamCost, + setToSynchronize: () => exports.RamCostConstants.ScriptSleeveBaseRamCost, + setToCommitCrime: () => exports.RamCostConstants.ScriptSleeveBaseRamCost, + setToUniversityCourse: () => exports.RamCostConstants.ScriptSleeveBaseRamCost, + travel: () => exports.RamCostConstants.ScriptSleeveBaseRamCost, + setToCompanyWork: () => exports.RamCostConstants.ScriptSleeveBaseRamCost, + setToFactionWork: () => exports.RamCostConstants.ScriptSleeveBaseRamCost, + setToGymWorkout: () => exports.RamCostConstants.ScriptSleeveBaseRamCost, + getSleeveStats: () => exports.RamCostConstants.ScriptSleeveBaseRamCost, + getTask: () => exports.RamCostConstants.ScriptSleeveBaseRamCost, + getInformation: () => exports.RamCostConstants.ScriptSleeveBaseRamCost, + getSleeveAugmentations: () => exports.RamCostConstants.ScriptSleeveBaseRamCost, + getSleevePurchasableAugs: () => exports.RamCostConstants.ScriptSleeveBaseRamCost, + purchaseSleeveAug: () => exports.RamCostConstants.ScriptSleeveBaseRamCost, + }, + heart: { + // Easter egg function + break: () => 0, + } +}; +function getRamCost(...args) { + if (args.length === 0) { + console.warn(`No arguments passed to getRamCost()`); + return 0; + } + let curr = exports.RamCosts[args[0]]; + for (let i = 1; i < args.length; ++i) { + if (curr == null) { + console.warn(`Invalid function passed to getRamCost: ${args}`); + return 0; + } + const currType = typeof curr; + if (currType === "function" || currType === "number") { + break; + } + curr = curr[args[i]]; + } + const currType = typeof curr; + if (currType === "function") { + return curr(); + } + if (currType === "number") { + return curr; + } + console.warn(`Expected type: ${currType}`); + return 0; +} +exports.getRamCost = getRamCost; + + +/***/ }), + +/***/ 15: +/*!***************************************!*\ + !*** ./utils/helpers/getRandomInt.ts ***! + \***************************************/ +/*! no static exports found */ +/*! all exports used */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Gets a random integer bounded by the values passed in. + * @param min The minimum value in the range. + * @param max The maximum value in the range. + */ +function getRandomInt(min, max) { + const lower = Math.min(min, max); + const upper = Math.max(min, max); + return Math.floor(Math.random() * (upper - lower + 1)) + lower; +} +exports.getRandomInt = getRandomInt; + + +/***/ }), + +/***/ 16: +/*!******************************!*\ + !*** ./utils/JSONReviver.js ***! + \******************************/ +/*! exports provided: Reviver, Generic_toJSON, Generic_fromJSON */ +/*! all exports used */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Reviver", function() { return Reviver; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Generic_toJSON", function() { return Generic_toJSON; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Generic_fromJSON", function() { return Generic_fromJSON; }); +/* Generic Reviver, toJSON, and fromJSON functions used for saving and loading objects */ + +// A generic "smart reviver" function. +// Looks for object values with a `ctor` property and +// a `data` property. If it finds them, and finds a matching +// constructor that has a `fromJSON` property on it, it hands +// off to that `fromJSON` fuunction, passing in the value. +function Reviver(key, value) { + var ctor; + if (value == null) { + console.log("Reviver WRONGLY called with key: " + key + ", and value: " + value); + return 0; + } + + if (typeof value === "object" && + typeof value.ctor === "string" && + typeof value.data !== "undefined") { + // Compatibility for version v0.43.1 + // TODO Remove this eventually + if (value.ctor === "AllServersMap") { + console.log('Converting AllServersMap for v0.43.1'); + return value.data; + } + + ctor = Reviver.constructors[value.ctor] || window[value.ctor]; + + if (typeof ctor === "function" && + typeof ctor.fromJSON === "function") { + + return ctor.fromJSON(value); + } + } + return value; +} +Reviver.constructors = {}; // A list of constructors the smart reviver should know about + +// A generic "toJSON" function that creates the data expected +// by Reviver. +// `ctorName` The name of the constructor to use to revive it +// `obj` The object being serialized +// `keys` (Optional) Array of the properties to serialize, +// if not given then all of the objects "own" properties +// that don't have function values will be serialized. +// (Note: If you list a property in `keys`, it will be serialized +// regardless of whether it's an "own" property.) +// Returns: The structure (which will then be turned into a string +// as part of the JSON.stringify algorithm) +function Generic_toJSON(ctorName, obj, keys) { + var data, index, key; + + if (!keys) { + keys = Object.keys(obj); // Only "own" properties are included + } + + data = {}; + for (let index = 0; index < keys.length; ++index) { + key = keys[index]; + data[key] = obj[key]; + } + return {ctor: ctorName, data: data}; +} + +// A generic "fromJSON" function for use with Reviver: Just calls the +// constructor function with no arguments, then applies all of the +// key/value pairs from the raw data to the instance. Only useful for +// constructors that can be reasonably called without arguments! +// `ctor` The constructor to call +// `data` The data to apply +// Returns: The object +function Generic_fromJSON(ctor, data) { + var obj, name; + + obj = new ctor(); + for (name in data) { + obj[name] = data[name]; + } + return obj; +} + + + + +/***/ }), + +/***/ 205: +/*!**********************************!*\ + !*** ./test/StockMarketTests.js ***! + \**********************************/ +/*! no exports provided */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony import */ var _src_Constants__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../src/Constants */ 8); +/* harmony import */ var _src_Constants__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_src_Constants__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _src_StockMarket_Order__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../src/StockMarket/Order */ 98); +/* harmony import */ var _src_StockMarket_Order__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_src_StockMarket_Order__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _src_StockMarket_Stock__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../src/StockMarket/Stock */ 66); +/* harmony import */ var _src_StockMarket_Stock__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_src_StockMarket_Stock__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../src/StockMarket/StockMarketHelpers */ 27); +/* harmony import */ var _src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__); +/* harmony import */ var _src_StockMarket_data_OrderTypes__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../src/StockMarket/data/OrderTypes */ 54); +/* harmony import */ var _src_StockMarket_data_OrderTypes__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_src_StockMarket_data_OrderTypes__WEBPACK_IMPORTED_MODULE_4__); +/* harmony import */ var _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../src/StockMarket/data/PositionTypes */ 23); +/* harmony import */ var _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__); + + +//import { processOrders } from "../src/StockMarket/OrderProcessing"; + +/* +import { + deleteStockMarket, + initStockMarket, + initSymbolToStockMap, + loadStockMarket, + StockMarket, + SymbolToStockMap, +} from "../src/StockMarket/StockMarket"; +*/ + + + + +const assert = chai.assert; +const expect = chai.expect; + +console.log("Beginning Stock Market Tests"); + +describe("Stock Market Tests", function() { + const commission = _src_Constants__WEBPACK_IMPORTED_MODULE_0__["CONSTANTS"].StockMarketCommission; + + // Generic Stock object that can be used by each test + let stock; + const ctorParams = { + b: true, + initPrice: 10e3, + marketCap: 5e9, + mv: 1, + name: "MockStock", + otlkMag: 10, + spreadPerc: 1, + shareTxForMovement: 5e3, + symbol: "mock", + }; + + beforeEach(function() { + function construct() { + stock = new _src_StockMarket_Stock__WEBPACK_IMPORTED_MODULE_2__["Stock"](ctorParams); + } + + expect(construct).to.not.throw(); + }); + + describe("Stock Class", function() { + describe("constructor", function() { + it("should have default parameters", function() { + let defaultStock; + function construct() { + defaultStock = new _src_StockMarket_Stock__WEBPACK_IMPORTED_MODULE_2__["Stock"](); + } + + expect(construct).to.not.throw(); + expect(defaultStock.name).to.equal(""); + }); + + it("should properly initialize props from parameters", function() { + expect(stock.name).to.equal(ctorParams.name); + expect(stock.symbol).to.equal(ctorParams.symbol); + expect(stock.price).to.equal(ctorParams.initPrice); + expect(stock.lastPrice).to.equal(ctorParams.initPrice); + expect(stock.b).to.equal(ctorParams.b); + expect(stock.mv).to.equal(ctorParams.mv); + expect(stock.shareTxForMovement).to.equal(ctorParams.shareTxForMovement); + expect(stock.shareTxUntilMovement).to.equal(ctorParams.shareTxForMovement); + expect(stock.maxShares).to.be.below(stock.totalShares); + expect(stock.spreadPerc).to.equal(ctorParams.spreadPerc); + expect(stock.priceMovementPerc).to.be.a("number"); + expect(stock.priceMovementPerc).to.be.at.most(stock.spreadPerc); + expect(stock.priceMovementPerc).to.be.at.least(0); + }); + + it ("should properly initialize props from range-values", function() { + let stock; + const params = { + b: true, + initPrice: { + max: 10e3, + min: 1e3, + }, + marketCap: 5e9, + mv: { + divisor: 100, + max: 150, + min: 50, + }, + name: "MockStock", + otlkMag: 10, + spreadPerc: { + divisor: 10, + max: 10, + min: 1, + }, + shareTxForMovement: { + max: 10e3, + min: 5e3, + }, + symbol: "mock", + }; + + function construct() { + stock = new _src_StockMarket_Stock__WEBPACK_IMPORTED_MODULE_2__["Stock"](params); + } + + expect(construct).to.not.throw(); + expect(stock.price).to.be.within(params.initPrice.min, params.initPrice.max); + expect(stock.mv).to.be.within(params.mv.min / params.mv.divisor, params.mv.max / params.mv.divisor); + expect(stock.spreadPerc).to.be.within(params.spreadPerc.min / params.spreadPerc.divisor, params.spreadPerc.max / params.spreadPerc.divisor); + expect(stock.shareTxForMovement).to.be.within(params.shareTxForMovement.min, params.shareTxForMovement.max); + }); + + it("should round the 'totalShare' prop to the nearest 100k", function() { + expect(stock.totalShares % 100e3).to.equal(0); + }); + }); + + describe("#changePrice()", function() { + it("should set both the last price and current price properties", function() { + const newPrice = 20e3; + stock.changePrice(newPrice); + expect(stock.lastPrice).to.equal(ctorParams.initPrice); + expect(stock.price).to.equal(newPrice); + }); + }); + + describe("#getAskPrice()", function() { + it("should return the price increased by spread percentage", function() { + const perc = stock.spreadPerc / 100; + expect(perc).to.be.at.most(1); + expect(perc).to.be.at.least(0); + + const expected = stock.price * (1 + perc); + expect(stock.getAskPrice()).to.equal(expected); + }); + }); + + describe("#getBidPrice()", function() { + it("should return the price decreased by spread percentage", function() { + const perc = stock.spreadPerc / 100; + expect(perc).to.be.at.most(1); + expect(perc).to.be.at.least(0); + + const expected = stock.price * (1 - perc); + expect(stock.getBidPrice()).to.equal(expected); + }); + }); + }); + + /* + // TODO These tests fail due to circular dependency errors + describe("StockMarket object", function() { + describe("Initialization", function() { + // Keeps track of initialized stocks. Contains their symbols + const stocks = []; + + before(function() { + expect(initStockMarket).to.not.throw(); + expect(initSymbolToStockMap).to.not.throw(); + }); + + it("should have Stock objects", function() { + for (const prop in StockMarket) { + const stock = StockMarket[prop]; + if (stock instanceof Stock) { + stocks.push(stock.symbol); + } + } + + // We'll just check that there are some stocks + expect(stocks.length).to.be.at.least(1); + }); + + it("should have an order book in the 'Orders' property", function() { + expect(StockMarket).to.have.property("Orders"); + + const orderbook = StockMarket["Orders"]; + for (const symbol of stocks) { + const ordersForStock = orderbook[symbol]; + expect(ordersForStock).to.be.an("array"); + expect(ordersForStock.length).to.equal(0); + } + }); + + it("should have properties for managing game cycles", function() { + expect(StockMarket).to.have.property("storedCycles"); + expect(StockMarket).to.have.property("lastUpdate"); + }); + }); + + // Because 'StockMarket' is a global object, the effects of initialization from + // the block above should still stand + describe("Deletion", function() { + it("should set StockMarket to be an empty object", function() { + expect(StockMarket).to.be.an("object").that.is.not.empty; + deleteStockMarket(); + expect(StockMarket).to.be.an("object").that.is.empty; + }); + }); + + // Reset stock market for each test + beforeEach(function() { + deleteStockMarket(); + initStockMarket(); + initSymbolToStockMap(); + }); + + it("should properly initialize", function() { + + }); + }); + */ + + describe("Transaction Cost Calculator Functions", function() { + describe("getBuyTransactionCost()", function() { + it("should fail on invalid 'stock' argument", function() { + const res = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getBuyTransactionCost"])({}, 10, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(res).to.equal(null); + }); + + it("should fail on invalid 'shares' arg", function() { + let res = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getBuyTransactionCost"])(stock, NaN, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(res).to.equal(null); + + res = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getBuyTransactionCost"])(stock, -1, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(res).to.equal(null); + }); + + it("should properly evaluate LONG transactions that doesn't trigger a price movement", function() { + const shares = ctorParams.shareTxForMovement / 2; + const res = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getBuyTransactionCost"])(stock, shares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(res).to.equal(shares * stock.getAskPrice() + commission); + }); + + it("should properly evaluate SHORT transactions that doesn't trigger a price movement", function() { + const shares = ctorParams.shareTxForMovement / 2; + const res = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getBuyTransactionCost"])(stock, shares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + expect(res).to.equal(shares * stock.getBidPrice() + commission); + }); + + it("should properly evaluate LONG transactions that trigger price movements", function() { + const sharesPerMvmt = ctorParams.shareTxForMovement; + const shares = sharesPerMvmt * 3; + const res = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getBuyTransactionCost"])(stock, shares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + + // Calculate expected cost + const secondPrice = stock.getAskPrice() * Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["calculateIncreasingPriceMovement"])(stock); + const thirdPrice = secondPrice * Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["calculateIncreasingPriceMovement"])(stock); + let expected = (sharesPerMvmt * stock.getAskPrice()) + (sharesPerMvmt * secondPrice) + (sharesPerMvmt * thirdPrice); + + expect(res).to.equal(expected + commission); + }); + + it("should properly evaluate SHORT transactions that trigger price movements", function() { + const sharesPerMvmt = ctorParams.shareTxForMovement; + const shares = sharesPerMvmt * 3; + const res = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getBuyTransactionCost"])(stock, shares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + + // Calculate expected cost + const secondPrice = stock.getBidPrice() * Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["calculateDecreasingPriceMovement"])(stock); + const thirdPrice = secondPrice * Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["calculateDecreasingPriceMovement"])(stock); + let expected = (sharesPerMvmt * stock.getBidPrice()) + (sharesPerMvmt * secondPrice) + (sharesPerMvmt * thirdPrice); + + expect(res).to.equal(expected + commission); + }); + + it("should cap the 'shares' argument at the stock's maximum number of shares", function() { + const maxRes = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getBuyTransactionCost"])(stock, stock.maxShares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + const exceedRes = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getBuyTransactionCost"])(stock, stock.maxShares * 10, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(maxRes).to.equal(exceedRes); + }); + }); + + describe("getSellTransactionGain()", function() { + it("should fail on invalid 'stock' argument", function() { + const res = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getSellTransactionGain"])({}, 10, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(res).to.equal(null); + }); + + it("should fail on invalid 'shares' arg", function() { + let res = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getSellTransactionGain"])(stock, NaN, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(res).to.equal(null); + + res = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getSellTransactionGain"])(stock, -1, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(res).to.equal(null); + }); + + it("should properly evaluate LONG transactions that doesn't trigger a price movement", function() { + const shares = ctorParams.shareTxForMovement / 2; + const res = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getSellTransactionGain"])(stock, shares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + const expected = shares * stock.getBidPrice() - commission; + expect(res).to.equal(expected); + }); + + it("should properly evaluate SHORT transactions that doesn't trigger a price movement", function() { + // We need to set this property in order to calculate gains from short position + stock.playerAvgShortPx = stock.price * 2; + + const shares = ctorParams.shareTxForMovement / 2; + const res = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getSellTransactionGain"])(stock, shares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + const expected = (shares * stock.playerAvgShortPx) + (shares * (stock.playerAvgShortPx - stock.getAskPrice())) - commission; + expect(res).to.equal(expected); + }); + + it("should properly evaluate LONG transactions that trigger price movements", function() { + const sharesPerMvmt = ctorParams.shareTxForMovement; + const shares = sharesPerMvmt * 3; + const res = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getSellTransactionGain"])(stock, shares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + + // Calculated expected gain + const mvmt = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["calculateDecreasingPriceMovement"])(stock); + const secondPrice = stock.getBidPrice() * mvmt; + const thirdPrice = secondPrice * mvmt; + const expected = (sharesPerMvmt * stock.getBidPrice()) + (sharesPerMvmt * secondPrice) + (sharesPerMvmt * thirdPrice); + + expect(res).to.equal(expected - commission); + }); + + it("should properly evaluate SHORT transactions that trigger price movements", function() { + // We need to set this property in order to calculate gains from short position + stock.playerAvgShortPx = stock.price * 2; + + const sharesPerMvmt = ctorParams.shareTxForMovement; + const shares = sharesPerMvmt * 3; + const res = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getSellTransactionGain"])(stock, shares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + + // Calculate expected gain + const mvmt = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["calculateIncreasingPriceMovement"])(stock); + const secondPrice = stock.getAskPrice() * mvmt; + const thirdPrice = secondPrice * mvmt; + function getGainForPrice(thisPrice) { + const origCost = sharesPerMvmt * stock.playerAvgShortPx; + return origCost + ((stock.playerAvgShortPx - thisPrice) * sharesPerMvmt); + } + const expected = getGainForPrice(stock.getAskPrice()) + getGainForPrice(secondPrice) + getGainForPrice(thirdPrice); + + expect(res).to.equal(expected - commission); + }); + + it("should cap the 'shares' argument at the stock's maximum number of shares", function() { + const maxRes = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getSellTransactionGain"])(stock, stock.maxShares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + const exceedRes = Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["getSellTransactionGain"])(stock, stock.maxShares * 10, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(maxRes).to.equal(exceedRes); + }); + }); + }); + + describe("Price Movement Processor Functions", function() { + // N = 1 is the original price + function getNthPriceIncreasing(origPrice, n) { + let price = origPrice; + for (let i = 1; i < n; ++i) { + price *= Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["calculateIncreasingPriceMovement"])(stock); + } + + return price; + } + + // N = 1 is the original price + function getNthPriceDecreasing(origPrice, n) { + let price = origPrice; + for (let i = 1; i < n; ++i) { + price *= Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["calculateDecreasingPriceMovement"])(stock); + } + + return price; + } + + // N = 1 is the original forecast + function getNthForecast(origForecast, n) { + return origForecast - _src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["forecastChangePerPriceMovement"] * (n - 1); + } + + describe("processBuyTransactionPriceMovement()", function() { + const noMvmtShares = Math.round(ctorParams.shareTxForMovement / 2.2); + const mvmtShares = ctorParams.shareTxForMovement * 3 + noMvmtShares; + + it("should do nothing on invalid 'stock' argument", function() { + const oldPrice = stock.price; + const oldTracker = stock.shareTxUntilMovement; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processBuyTransactionPriceMovement"])({}, mvmtShares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(stock.price).to.equal(oldPrice); + expect(stock.shareTxUntilMovement).to.equal(oldTracker); + }); + + it("should do nothing on invalid 'shares' arg", function() { + const oldPrice = stock.price; + const oldTracker = stock.shareTxUntilMovement; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processBuyTransactionPriceMovement"])(stock, NaN, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(stock.price).to.equal(oldPrice); + expect(stock.shareTxUntilMovement).to.equal(oldTracker); + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processBuyTransactionPriceMovement"])(stock, -1, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(stock.price).to.equal(oldPrice); + expect(stock.shareTxUntilMovement).to.equal(oldTracker); + }); + + it("should properly evaluate a LONG transaction that doesn't trigger a price movement", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processBuyTransactionPriceMovement"])(stock, noMvmtShares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(stock.price).to.equal(oldPrice); + expect(stock.otlkMag).to.equal(oldForecast); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares); + }); + + it("should properly evaluate a SHORT transaction that doesn't trigger a price movement", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processBuyTransactionPriceMovement"])(stock, noMvmtShares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + expect(stock.price).to.equal(oldPrice); + expect(stock.otlkMag).to.equal(oldForecast); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares); + }); + + it("should properly evaluate LONG transactions that trigger price movements", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processBuyTransactionPriceMovement"])(stock, mvmtShares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 4)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares); + }); + + it("should properly evaluate SHORT transactions that trigger price movements", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processBuyTransactionPriceMovement"])(stock, mvmtShares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 4)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares); + }); + + it("should properly evaluate LONG transactions of exactly 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processBuyTransactionPriceMovement"])(stock, stock.shareTxForMovement, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 2)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate LONG transactions that total to 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processBuyTransactionPriceMovement"])(stock, Math.round(stock.shareTxForMovement / 2), _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processBuyTransactionPriceMovement"])(stock, stock.shareTxUntilMovement, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 2)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate LONG transactions that are a multiple of 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processBuyTransactionPriceMovement"])(stock, 3 * stock.shareTxForMovement, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 4)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate SHORT transactions of exactly 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processBuyTransactionPriceMovement"])(stock, stock.shareTxForMovement, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 2)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate SHORT transactions that total to 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processBuyTransactionPriceMovement"])(stock, Math.round(stock.shareTxForMovement / 2), _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processBuyTransactionPriceMovement"])(stock, stock.shareTxUntilMovement, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 2)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate SHORT transactions that are a multiple of 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processBuyTransactionPriceMovement"])(stock, 3 * stock.shareTxForMovement, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 4)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + }); + + describe("processSellTransactionPriceMovement()", function() { + const noMvmtShares = Math.round(ctorParams.shareTxForMovement / 2.2); + const mvmtShares = ctorParams.shareTxForMovement * 3 + noMvmtShares; + + it("should do nothing on invalid 'stock' argument", function() { + const oldPrice = stock.price; + const oldTracker = stock.shareTxUntilMovement; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processSellTransactionPriceMovement"])({}, mvmtShares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(stock.price).to.equal(oldPrice); + expect(stock.shareTxUntilMovement).to.equal(oldTracker); + }); + + it("should do nothing on invalid 'shares' arg", function() { + const oldPrice = stock.price; + const oldTracker = stock.shareTxUntilMovement; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processSellTransactionPriceMovement"])(stock, NaN, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(stock.price).to.equal(oldPrice); + expect(stock.shareTxUntilMovement).to.equal(oldTracker); + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processSellTransactionPriceMovement"])(stock, -1, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(stock.price).to.equal(oldPrice); + expect(stock.shareTxUntilMovement).to.equal(oldTracker); + }); + + it("should properly evaluate a LONG transaction that doesn't trigger a price movement", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processSellTransactionPriceMovement"])(stock, noMvmtShares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(stock.price).to.equal(oldPrice); + expect(stock.otlkMag).to.equal(oldForecast); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares); + }); + + it("should properly evaluate a SHORT transaction that doesn't trigger a price movement", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processSellTransactionPriceMovement"])(stock, noMvmtShares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + expect(stock.price).to.equal(oldPrice); + expect(stock.otlkMag).to.equal(oldForecast); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares); + }); + + it("should properly evaluate LONG transactions that trigger price movements", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processSellTransactionPriceMovement"])(stock, mvmtShares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 4)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares); + }); + + it("should properly evaluate SHORT transactions that trigger price movements", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processSellTransactionPriceMovement"])(stock, mvmtShares, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 4)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares); + }); + + it("should properly evaluate LONG transactions of exactly 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processSellTransactionPriceMovement"])(stock, stock.shareTxForMovement, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 2)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate LONG transactions that total to 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processSellTransactionPriceMovement"])(stock, Math.round(stock.shareTxForMovement / 2), _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processSellTransactionPriceMovement"])(stock, stock.shareTxUntilMovement, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 2)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate LONG transactions that are a multiple of 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processSellTransactionPriceMovement"])(stock, 3 * stock.shareTxForMovement, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 4)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate SHORT transactions of exactly 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processSellTransactionPriceMovement"])(stock, stock.shareTxForMovement, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 2)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate SHORT transactions that total to 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processSellTransactionPriceMovement"])(stock, Math.round(stock.shareTxForMovement / 2), _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processSellTransactionPriceMovement"])(stock, stock.shareTxUntilMovement, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 2)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + + it("should properly evaluate SHORT transactions that are a multiple of 'shareTxForMovement' shares", function() { + const oldPrice = stock.price; + const oldForecast = stock.otlkMag; + + Object(_src_StockMarket_StockMarketHelpers__WEBPACK_IMPORTED_MODULE_3__["processSellTransactionPriceMovement"])(stock, 3 * stock.shareTxForMovement, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 4)); + expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4)); + expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement); + }); + }); + }); + + describe("Order Class", function() { + it("should throw on invalid arguments", function() { + function invalid1() { + return new _src_StockMarket_Order__WEBPACK_IMPORTED_MODULE_1__["Order"]({}, 1, 1, _src_StockMarket_data_OrderTypes__WEBPACK_IMPORTED_MODULE_4__["OrderTypes"].LimitBuy, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Long); + } + function invalid2() { + return new _src_StockMarket_Order__WEBPACK_IMPORTED_MODULE_1__["Order"]("FOO", "z", 0, _src_StockMarket_data_OrderTypes__WEBPACK_IMPORTED_MODULE_4__["OrderTypes"].LimitBuy, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + } + function invalid3() { + return new _src_StockMarket_Order__WEBPACK_IMPORTED_MODULE_1__["Order"]("FOO", 1, {}, _src_StockMarket_data_OrderTypes__WEBPACK_IMPORTED_MODULE_4__["OrderTypes"].LimitBuy, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + } + function invalid4() { + return new _src_StockMarket_Order__WEBPACK_IMPORTED_MODULE_1__["Order"]("FOO", 1, NaN, _src_StockMarket_data_OrderTypes__WEBPACK_IMPORTED_MODULE_4__["OrderTypes"].LimitBuy, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + } + function invalid5() { + return new _src_StockMarket_Order__WEBPACK_IMPORTED_MODULE_1__["Order"]("FOO", NaN, 0, _src_StockMarket_data_OrderTypes__WEBPACK_IMPORTED_MODULE_4__["OrderTypes"].LimitBuy, _src_StockMarket_data_PositionTypes__WEBPACK_IMPORTED_MODULE_5__["PositionTypes"].Short); + } + + expect(invalid1).to.throw(); + expect(invalid2).to.throw(); + expect(invalid3).to.throw(); + expect(invalid4).to.throw(); + expect(invalid5).to.throw(); + }); + }); + + describe("Order Processing", function() { + + }); +}); + + +/***/ }), + +/***/ 206: +/*!*****************************************************!*\ + !*** ./test/Netscript/StaticRamCalculationTests.js ***! + \*****************************************************/ +/*! no exports provided */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony import */ var _src_Netscript_RamCostGenerator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../src/Netscript/RamCostGenerator */ 10); +/* harmony import */ var _src_Netscript_RamCostGenerator__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_src_Netscript_RamCostGenerator__WEBPACK_IMPORTED_MODULE_0__); +/** + * TODO This should also test the calcualteRamUsage() function from + * /Script/RamCalculations but there's some issues with getting tests to run + * when any npm package is included in the build (/Script/RamCalculations includes + * walk from acorn). + */ + +//import { calculateRamUsage } from "../../src/Script/RamCalculations" + +const assert = chai.assert; +const expect = chai.expect; + +console.log("Beginning Netscript Static RAM Calculation/Generation Tests"); + +describe("Netscript Static RAM Calculation/Generation Tests", function() { + it("should run", function() { + expect(1).to.equal(1); + }); +}); + + +/***/ }), + +/***/ 23: +/*!***********************************************!*\ + !*** ./src/StockMarket/data/PositionTypes.ts ***! + \***********************************************/ +/*! no static exports found */ +/*! all exports used */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var PositionTypes; +(function (PositionTypes) { + PositionTypes["Long"] = "L"; + PositionTypes["Short"] = "S"; +})(PositionTypes = exports.PositionTypes || (exports.PositionTypes = {})); + + +/***/ }), + +/***/ 27: +/*!***********************************************!*\ + !*** ./src/StockMarket/StockMarketHelpers.ts ***! + \***********************************************/ +/*! no static exports found */ +/*! all exports used */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const Stock_1 = __webpack_require__(/*! ./Stock */ 66); +const PositionTypes_1 = __webpack_require__(/*! ./data/PositionTypes */ 23); +const Constants_1 = __webpack_require__(/*! ../Constants */ 8); +// Amount by which a stock's forecast changes during each price movement +exports.forecastChangePerPriceMovement = 0.1; +/** + * Given a stock, calculates the amount by which the stock price is multiplied + * for an 'upward' price movement. This does not actually increase the stock's price, + * just calculates the multiplier + * @param {Stock} stock - Stock for price movement + * @returns {number | null} Number by which stock's price should be multiplied. Null for invalid args + */ +function calculateIncreasingPriceMovement(stock) { + if (!(stock instanceof Stock_1.Stock)) { + return null; + } + return (1 + (stock.priceMovementPerc / 100)); +} +exports.calculateIncreasingPriceMovement = calculateIncreasingPriceMovement; +/** + * Given a stock, calculates the amount by which the stock price is multiplied + * for a "downward" price movement. This does not actually increase the stock's price, + * just calculates the multiplier + * @param {Stock} stock - Stock for price movement + * @returns {number | null} Number by which stock's price should be multiplied. Null for invalid args + */ +function calculateDecreasingPriceMovement(stock) { + if (!(stock instanceof Stock_1.Stock)) { + return null; + } + return (1 - (stock.priceMovementPerc / 100)); +} +exports.calculateDecreasingPriceMovement = calculateDecreasingPriceMovement; +/** + * Calculate the total cost of a "buy" transaction. This accounts for spread, + * price movements, and commission. + * @param {Stock} stock - Stock being purchased + * @param {number} shares - Number of shares being transacted + * @param {PositionTypes} posType - Long or short position + * @returns {number | null} Total transaction cost. Returns null for an invalid transaction + */ +function getBuyTransactionCost(stock, shares, posType) { + if (isNaN(shares) || shares <= 0 || !(stock instanceof Stock_1.Stock)) { + return null; + } + // Cap the 'shares' arg at the stock's maximum shares. This'll prevent + // hanging in the case when a really big number is passed in + shares = Math.min(shares, stock.maxShares); + const isLong = (posType === PositionTypes_1.PositionTypes.Long); + // If the number of shares doesn't trigger a price movement, its a simple calculation + if (shares <= stock.shareTxUntilMovement) { + if (isLong) { + return (shares * stock.getAskPrice()) + Constants_1.CONSTANTS.StockMarketCommission; + } + else { + return (shares * stock.getBidPrice()) + Constants_1.CONSTANTS.StockMarketCommission; + } + } + // Calculate how many iterations of price changes we need to account for + let remainingShares = shares - stock.shareTxUntilMovement; + let numIterations = 1 + Math.ceil(remainingShares / stock.shareTxForMovement); + // The initial cost calculation takes care of the first "iteration" + let currPrice = isLong ? stock.getAskPrice() : stock.getBidPrice(); + let totalCost = (stock.shareTxUntilMovement * currPrice); + const increasingMvmt = calculateIncreasingPriceMovement(stock); + const decreasingMvmt = calculateDecreasingPriceMovement(stock); + function processPriceMovement() { + if (isLong) { + currPrice *= increasingMvmt; + } + else { + currPrice *= decreasingMvmt; + } + } + for (let i = 1; i < numIterations; ++i) { + processPriceMovement(); + const amt = Math.min(stock.shareTxForMovement, remainingShares); + totalCost += (amt * currPrice); + remainingShares -= amt; + } + return totalCost + Constants_1.CONSTANTS.StockMarketCommission; +} +exports.getBuyTransactionCost = getBuyTransactionCost; +/** + * Processes a buy transaction's resulting price AND forecast movement. + * @param {Stock} stock - Stock being purchased + * @param {number} shares - Number of shares being transacted + * @param {PositionTypes} posType - Long or short position + */ +function processBuyTransactionPriceMovement(stock, shares, posType) { + if (isNaN(shares) || shares <= 0 || !(stock instanceof Stock_1.Stock)) { + return; + } + // Cap the 'shares' arg at the stock's maximum shares. This'll prevent + // hanging in the case when a really big number is passed in + shares = Math.min(shares, stock.maxShares); + const isLong = (posType === PositionTypes_1.PositionTypes.Long); + let currPrice = stock.price; + function processPriceMovement() { + if (isLong) { + currPrice *= calculateIncreasingPriceMovement(stock); + } + else { + currPrice *= calculateDecreasingPriceMovement(stock); + } + } + // No price/forecast movement + if (shares <= stock.shareTxUntilMovement) { + stock.shareTxUntilMovement -= shares; + if (stock.shareTxUntilMovement <= 0) { + stock.shareTxUntilMovement = stock.shareTxForMovement; + processPriceMovement(); + stock.changePrice(currPrice); + stock.otlkMag -= (exports.forecastChangePerPriceMovement); + } + return; + } + // Calculate how many iterations of price changes we need to account for + let remainingShares = shares - stock.shareTxUntilMovement; + let numIterations = 1 + Math.ceil(remainingShares / stock.shareTxForMovement); + for (let i = 1; i < numIterations; ++i) { + processPriceMovement(); + } + stock.shareTxUntilMovement = stock.shareTxForMovement - ((shares - stock.shareTxUntilMovement) % stock.shareTxForMovement); + if (stock.shareTxUntilMovement === stock.shareTxForMovement || stock.shareTxUntilMovement <= 0) { + // The shareTxUntilMovement ended up at 0 at the end of the "processing" + ++numIterations; + stock.shareTxUntilMovement = stock.shareTxForMovement; + processPriceMovement(); + } + stock.changePrice(currPrice); + // Forecast always decreases in magnitude + const forecastChange = Math.min(5, exports.forecastChangePerPriceMovement * (numIterations - 1)); + stock.otlkMag -= forecastChange; + if (stock.otlkMag < 0) { + stock.b = !stock.b; + stock.otlkMag = Math.abs(stock.otlkMag); + } +} +exports.processBuyTransactionPriceMovement = processBuyTransactionPriceMovement; +/** + * Calculate the TOTAL amount of money gained from a sale (NOT net profit). This accounts + * for spread, price movements, and commission. + * @param {Stock} stock - Stock being sold + * @param {number} shares - Number of sharse being transacted + * @param {PositionTypes} posType - Long or short position + * @returns {number | null} Amount of money gained from transaction. Returns null for an invalid transaction + */ +function getSellTransactionGain(stock, shares, posType) { + if (isNaN(shares) || shares <= 0 || !(stock instanceof Stock_1.Stock)) { + return null; + } + // Cap the 'shares' arg at the stock's maximum shares. This'll prevent + // hanging in the case when a really big number is passed in + shares = Math.min(shares, stock.maxShares); + const isLong = (posType === PositionTypes_1.PositionTypes.Long); + // If the number of shares doesn't trigger a price mvoement, its a simple calculation + if (shares <= stock.shareTxUntilMovement) { + if (isLong) { + return (shares * stock.getBidPrice()) - Constants_1.CONSTANTS.StockMarketCommission; + } + else { + // Calculating gains for a short position requires calculating the profit made + const origCost = shares * stock.playerAvgShortPx; + const profit = ((stock.playerAvgShortPx - stock.getAskPrice()) * shares) - Constants_1.CONSTANTS.StockMarketCommission; + return origCost + profit; + } + } + // Calculate how many iterations of price changes we need to account for + let remainingShares = shares - stock.shareTxUntilMovement; + let numIterations = 1 + Math.ceil(remainingShares / stock.shareTxForMovement); + // Helper function to calculate gain for a single iteration + function calculateGain(thisPrice, thisShares) { + if (isLong) { + return thisShares * thisPrice; + } + else { + const origCost = thisShares * stock.playerAvgShortPx; + const profit = ((stock.playerAvgShortPx - thisPrice) * thisShares); + return origCost + profit; + } + } + // The initial cost calculation takes care of the first "iteration" + let currPrice = isLong ? stock.getBidPrice() : stock.getAskPrice(); + let totalGain = calculateGain(currPrice, stock.shareTxUntilMovement); + for (let i = 1; i < numIterations; ++i) { + // Price movement + if (isLong) { + currPrice *= calculateDecreasingPriceMovement(stock); + } + else { + currPrice *= calculateIncreasingPriceMovement(stock); + } + const amt = Math.min(stock.shareTxForMovement, remainingShares); + totalGain += calculateGain(currPrice, amt); + remainingShares -= amt; + } + return totalGain - Constants_1.CONSTANTS.StockMarketCommission; +} +exports.getSellTransactionGain = getSellTransactionGain; +/** + * Processes a sell transaction's resulting price movement + * @param {Stock} stock - Stock being sold + * @param {number} shares - Number of sharse being transacted + * @param {PositionTypes} posType - Long or short position + */ +function processSellTransactionPriceMovement(stock, shares, posType) { + if (isNaN(shares) || shares <= 0 || !(stock instanceof Stock_1.Stock)) { + return; + } + // Cap the 'shares' arg at the stock's maximum shares. This'll prevent + // hanging in the case when a really big number is passed in + shares = Math.min(shares, stock.maxShares); + const isLong = (posType === PositionTypes_1.PositionTypes.Long); + let currPrice = stock.price; + function processPriceMovement() { + if (isLong) { + currPrice *= calculateDecreasingPriceMovement(stock); + } + else { + currPrice *= calculateIncreasingPriceMovement(stock); + } + } + // No price/forecast movement + if (shares <= stock.shareTxUntilMovement) { + stock.shareTxUntilMovement -= shares; + if (stock.shareTxUntilMovement <= 0) { + stock.shareTxUntilMovement = stock.shareTxForMovement; + processPriceMovement(); + stock.changePrice(currPrice); + stock.otlkMag -= (exports.forecastChangePerPriceMovement); + } + return; + } + // Calculate how many iterations of price changes we need to account for + let remainingShares = shares - stock.shareTxUntilMovement; + let numIterations = 1 + Math.ceil(remainingShares / stock.shareTxForMovement); + for (let i = 1; i < numIterations; ++i) { + processPriceMovement(); + } + stock.shareTxUntilMovement = stock.shareTxForMovement - ((shares - stock.shareTxUntilMovement) % stock.shareTxForMovement); + if (stock.shareTxUntilMovement === stock.shareTxForMovement || stock.shareTxUntilMovement <= 0) { + ++numIterations; + stock.shareTxUntilMovement = stock.shareTxForMovement; + processPriceMovement(); + } + stock.changePrice(currPrice); + // Forecast always decreases in magnitude + const forecastChange = Math.min(5, exports.forecastChangePerPriceMovement * (numIterations - 1)); + stock.otlkMag -= forecastChange; + if (stock.otlkMag < 0) { + stock.b = !stock.b; + stock.otlkMag = Math.abs(stock.otlkMag); + } +} +exports.processSellTransactionPriceMovement = processSellTransactionPriceMovement; +/** + * Calculate the maximum number of shares of a stock that can be purchased. + * Handles mid-transaction price movements, both L and S positions, etc. + * Used for the "Buy Max" button in the UI + * @param {Stock} stock - Stock being purchased + * @param {PositionTypes} posType - Long or short position + * @param {number} money - Amount of money player has + * @returns maximum number of shares that the player can purchase + */ +function calculateBuyMaxAmount(stock, posType, money) { + if (!(stock instanceof Stock_1.Stock)) { + return 0; + } + const isLong = (posType === PositionTypes_1.PositionTypes.Long); + const increasingMvmt = calculateIncreasingPriceMovement(stock); + const decreasingMvmt = calculateDecreasingPriceMovement(stock); + if (increasingMvmt == null || decreasingMvmt == null) { + return 0; + } + let remainingMoney = money - Constants_1.CONSTANTS.StockMarketCommission; + let currPrice = isLong ? stock.getAskPrice() : stock.getBidPrice(); + // No price movement + const firstIterationCost = stock.shareTxUntilMovement * currPrice; + if (remainingMoney < firstIterationCost) { + return Math.floor(remainingMoney / currPrice); + } + // We'll avoid any accidental infinite loops by having a hardcoded maximum number of + // iterations + let numShares = stock.shareTxUntilMovement; + remainingMoney -= firstIterationCost; + for (let i = 0; i < 10e3; ++i) { + if (isLong) { + currPrice *= increasingMvmt; + } + else { + currPrice *= decreasingMvmt; + } + const affordableShares = Math.floor(remainingMoney / currPrice); + const actualShares = Math.min(stock.shareTxForMovement, affordableShares); + // Can't afford any more, so we're done + if (actualShares <= 0) { + break; + } + numShares += actualShares; + let cost = actualShares * currPrice; + remainingMoney -= cost; + if (remainingMoney <= 0) { + break; + } + } + return Math.floor(numShares); +} +exports.calculateBuyMaxAmount = calculateBuyMaxAmount; + + +/***/ }), + +/***/ 300: +/*!***********************!*\ + !*** ./test/index.js ***! + \***********************/ +/*! no exports provided */ +/*! all exports used */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _Netscript_StaticRamCalculationTests__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Netscript/StaticRamCalculationTests */ 206); +/* harmony import */ var _StockMarketTests__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./StockMarketTests */ 205); + + + + +/***/ }), + +/***/ 54: +/*!********************************************!*\ + !*** ./src/StockMarket/data/OrderTypes.ts ***! + \********************************************/ +/*! no static exports found */ +/*! all exports used */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var OrderTypes; +(function (OrderTypes) { + OrderTypes["LimitBuy"] = "Limit Buy Order"; + OrderTypes["LimitSell"] = "Limit Sell Order"; + OrderTypes["StopBuy"] = "Stop Buy Order"; + OrderTypes["StopSell"] = "Stop Sell Order"; +})(OrderTypes = exports.OrderTypes || (exports.OrderTypes = {})); + + +/***/ }), + +/***/ 66: +/*!**********************************!*\ + !*** ./src/StockMarket/Stock.ts ***! + \**********************************/ +/*! no static exports found */ +/*! all exports used */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const JSONReviver_1 = __webpack_require__(/*! ../../utils/JSONReviver */ 16); +const getRandomInt_1 = __webpack_require__(/*! ../../utils/helpers/getRandomInt */ 15); +const defaultConstructorParams = { + b: true, + initPrice: 10e3, + marketCap: 1e12, + mv: 1, + name: "", + otlkMag: 0, + spreadPerc: 0, + shareTxForMovement: 1e6, + symbol: "", +}; +// Helper function that convert a IMinMaxRange to a number +function toNumber(n) { + let value; + switch (typeof n) { + case "number": { + return n; + } + case "object": { + const range = n; + value = getRandomInt_1.getRandomInt(range.min, range.max); + break; + } + default: + throw Error(`Do not know how to convert the type '${typeof n}' to a number`); + } + if (typeof n === "object" && typeof n.divisor === "number") { + return value / n.divisor; + } + return value; +} +/** + * Represents the valuation of a company in the World Stock Exchange. + */ +class Stock { + /** + * Initializes a Stock from a JSON save state + */ + static fromJSON(value) { + return JSONReviver_1.Generic_fromJSON(Stock, value.data); + } + constructor(p = defaultConstructorParams) { + this.name = p.name; + this.symbol = p.symbol; + this.price = toNumber(p.initPrice); + this.lastPrice = this.price; + this.playerShares = 0; + this.playerAvgPx = 0; + this.playerShortShares = 0; + this.playerAvgShortPx = 0; + this.mv = toNumber(p.mv); + this.b = p.b; + this.otlkMag = p.otlkMag; + this.cap = getRandomInt_1.getRandomInt(this.price * 1e3, this.price * 25e3); + this.spreadPerc = toNumber(p.spreadPerc); + this.priceMovementPerc = this.spreadPerc / (getRandomInt_1.getRandomInt(10, 30) / 10); + this.shareTxForMovement = toNumber(p.shareTxForMovement); + this.shareTxUntilMovement = this.shareTxForMovement; + // Total shares is determined by market cap, and is rounded to nearest 100k + let totalSharesUnrounded = (p.marketCap / this.price); + this.totalShares = Math.round(totalSharesUnrounded / 1e5) * 1e5; + // Max Shares (Outstanding shares) is a percentage of total shares + const outstandingSharePercentage = 0.2; + this.maxShares = Math.round((this.totalShares * outstandingSharePercentage) / 1e5) * 1e5; + } + changePrice(newPrice) { + this.lastPrice = this.price; + this.price = newPrice; + } + /** + * Return the price at which YOUR stock is bought (market ask price). Accounts for spread + */ + getAskPrice() { + return this.price * (1 + (this.spreadPerc / 100)); + } + /** + * Return the price at which YOUR stock is sold (market bid price). Accounts for spread + */ + getBidPrice() { + return this.price * (1 - (this.spreadPerc / 100)); + } + /** + * Serialize the Stock to a JSON save state. + */ + toJSON() { + return JSONReviver_1.Generic_toJSON("Stock", this); + } +} +exports.Stock = Stock; +JSONReviver_1.Reviver.constructors.Stock = Stock; + + +/***/ }), + +/***/ 8: +/*!**************************!*\ + !*** ./src/Constants.ts ***! + \**************************/ +/*! no static exports found */ +/*! all exports used */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CONSTANTS = { + Version: "0.46.3", + /** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience + * and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then + * the player will have this level assuming no multipliers. Multipliers can cause skills to go above this. + */ + MaxSkillLevel: 975, + // Milliseconds per game cycle + MilliPerCycle: 200, + // How much reputation is needed to join a megacorporation's faction + CorpFactionRepRequirement: 200e3, + // Base RAM costs + BaseCostFor1GBOfRamHome: 32000, + BaseCostFor1GBOfRamServer: 55000, + // Cost to travel to another city + TravelCost: 200e3, + // Faction and Company favor-related things + BaseFavorToDonate: 150, + DonateMoneyToRepDivisor: 1e6, + FactionReputationToFavorBase: 500, + FactionReputationToFavorMult: 1.02, + CompanyReputationToFavorBase: 500, + CompanyReputationToFavorMult: 1.02, + // NeuroFlux Governor Augmentation cost multiplier + NeuroFluxGovernorLevelMult: 1.14, + NumNetscriptPorts: 20, + // Server-related constants + HomeComputerMaxRam: 1073741824, + ServerBaseGrowthRate: 1.03, + ServerMaxGrowthRate: 1.0035, + ServerFortifyAmount: 0.002, + ServerWeakenAmount: 0.05, + PurchasedServerLimit: 25, + PurchasedServerMaxRam: 1048576, + // Augmentation Constants + AugmentationCostMultiplier: 5, + AugmentationRepMultiplier: 2.5, + MultipleAugMultiplier: 1.9, + // TOR Router + TorRouterCost: 200e3, + // Infiltration + InfiltrationBribeBaseAmount: 100e3, + InfiltrationMoneyValue: 5e3, + InfiltrationRepValue: 1.4, + InfiltrationExpPow: 0.8, + // Stock market + WSEAccountCost: 200e6, + TIXAPICost: 5e9, + MarketData4SCost: 1e9, + MarketDataTixApi4SCost: 25e9, + StockMarketCommission: 100e3, + // Hospital/Health + HospitalCostPerHp: 100e3, + // Intelligence-related constants + IntelligenceCrimeWeight: 0.05, + IntelligenceInfiltrationWeight: 0.1, + IntelligenceCrimeBaseExpGain: 0.001, + IntelligenceProgramBaseExpGain: 500, + IntelligenceTerminalHackBaseExpGain: 200, + IntelligenceSingFnBaseExpGain: 0.002, + IntelligenceClassBaseExpGain: 0.000001, + IntelligenceHackingMissionBaseExpGain: 0.03, + // Hacking Missions + // TODO Move this into Hacking Mission implementation + HackingMissionRepToDiffConversion: 10000, + HackingMissionRepToRewardConversion: 7, + HackingMissionSpamTimeIncrease: 25000, + HackingMissionTransferAttackIncrease: 1.05, + HackingMissionMiscDefenseIncrease: 1.05, + HackingMissionDifficultyToHacking: 135, + HackingMissionHowToPlay: "Hacking missions are a minigame that, if won, will reward you with faction reputation.

" + + "In this game you control a set of Nodes and use them to try and defeat an enemy. Your Nodes " + + "are colored blue, while the enemy's are red. There are also other nodes on the map colored gray " + + "that initially belong to neither you nor the enemy. The goal of the game is " + + "to capture all of the enemy's Database nodes within the time limit. " + + "If you fail to do this, you will lose.

" + + "Each Node has three stats: Attack, Defense, and HP. There are five different actions that " + + "a Node can take:

" + + "Attack - Targets an enemy Node and lowers its HP. The effectiveness is determined by the owner's Attack, the Player's " + + "hacking level, and the enemy's defense.

" + + "Scan - Targets an enemy Node and lowers its Defense. The effectiveness is determined by the owner's Attack, the Player's hacking level, and the " + + "enemy's defense.

" + + "Weaken - Targets an enemy Node and lowers its Attack. The effectiveness is determined by the owner's Attack, the Player's hacking level, and the enemy's " + + "defense.

" + + "Fortify - Raises the Node's Defense. The effectiveness is determined by your hacking level.

" + + "Overflow - Raises the Node's Attack but lowers its Defense. The effectiveness is determined by your hacking level.

" + + "Note that when determining the effectiveness of the above actions, the TOTAL Attack or Defense of the team is used, not just the " + + "Attack/Defense of the individual Node that is performing the action.

" + + "To capture a Node, you must lower its HP down to 0.

" + + "There are six different types of Nodes:

" + + "CPU Core - These are your main Nodes that are used to perform actions. Capable of performing every action

" + + "Firewall - Nodes with high defense. These Nodes can 'Fortify'

" + + "Database - A special type of Node. The player's objective is to conquer all of the enemy's Database Nodes within " + + "the time limit. These Nodes cannot perform any actions

" + + "Spam - Conquering one of these Nodes will slow the enemy's trace, giving the player additional time to complete " + + "the mission. These Nodes cannot perform any actions

" + + "Transfer - Conquering one of these nodes will increase the Attack of all of your CPU Cores by a small fixed percentage. " + + "These Nodes are capable of performing every action except the 'Attack' action

" + + "Shield - Nodes with high defense. These Nodes can 'Fortify'

" + + "To assign an action to a Node, you must first select one of your Nodes. This can be done by simply clicking on it. Double-clicking " + + "a node will select all of your Nodes of the same type (e.g. select all CPU Core Nodes or all Transfer Nodes). Note that only Nodes " + + "that can perform actions (CPU Core, Transfer, Shield, Firewall) can be selected. Selected Nodes will be denoted with a white highlight. After selecting a Node or multiple Nodes, " + + "select its action using the Action Buttons near the top of the screen. Every action also has a corresponding keyboard " + + "shortcut.

" + + "For certain actions such as attacking, scanning, and weakening, the Node performing the action must have a target. To target " + + "another node, simply click-and-drag from the 'source' Node to a target. A Node can only have one target, and you can target " + + "any Node that is adjacent to one of your Nodes (immediately above, below, or to the side. NOT diagonal). Furthermore, only CPU Cores and Transfer Nodes " + + "can target, since they are the only ones that can perform the related actions. To remove a target, you can simply click on the line that represents " + + "the connection between one of your Nodes and its target. Alternatively, you can select the 'source' Node and click the 'Drop Connection' button, " + + "or press 'd'.

" + + "Other Notes:

" + + "-Whenever a miscellenaous Node (not owned by the player or enemy) is conquered, the defense of all remaining miscellaneous Nodes that " + + "are not actively being targeted will increase by a fixed percentage.

" + + "-Whenever a Node is conquered, its stats are significantly reduced

" + + "-Miscellaneous Nodes slowly raise their defense over time

" + + "-Nodes slowly regenerate health over time.", + // Time-related constants + MillisecondsPer20Hours: 72000000, + GameCyclesPer20Hours: 72000000 / 200, + MillisecondsPer10Hours: 36000000, + GameCyclesPer10Hours: 36000000 / 200, + MillisecondsPer8Hours: 28800000, + GameCyclesPer8Hours: 28800000 / 200, + MillisecondsPer4Hours: 14400000, + GameCyclesPer4Hours: 14400000 / 200, + MillisecondsPer2Hours: 7200000, + GameCyclesPer2Hours: 7200000 / 200, + MillisecondsPerHour: 3600000, + GameCyclesPerHour: 3600000 / 200, + MillisecondsPerHalfHour: 1800000, + GameCyclesPerHalfHour: 1800000 / 200, + MillisecondsPerQuarterHour: 900000, + GameCyclesPerQuarterHour: 900000 / 200, + MillisecondsPerFiveMinutes: 300000, + GameCyclesPerFiveMinutes: 300000 / 200, + // Player Work & Action + FactionWorkHacking: "Faction Hacking Work", + FactionWorkField: "Faction Field Work", + FactionWorkSecurity: "Faction Security Work", + WorkTypeCompany: "Working for Company", + WorkTypeCompanyPartTime: "Working for Company part-time", + WorkTypeFaction: "Working for Faction", + WorkTypeCreateProgram: "Working on Create a Program", + WorkTypeStudyClass: "Studying or Taking a class at university", + WorkTypeCrime: "Committing a crime", + ClassStudyComputerScience: "studying Computer Science", + ClassDataStructures: "taking a Data Structures course", + ClassNetworks: "taking a Networks course", + ClassAlgorithms: "taking an Algorithms course", + ClassManagement: "taking a Management course", + ClassLeadership: "taking a Leadership course", + ClassGymStrength: "training your strength at a gym", + ClassGymDefense: "training your defense at a gym", + ClassGymDexterity: "training your dexterity at a gym", + ClassGymAgility: "training your agility at a gym", + ClassDataStructuresBaseCost: 40, + ClassNetworksBaseCost: 80, + ClassAlgorithmsBaseCost: 320, + ClassManagementBaseCost: 160, + ClassLeadershipBaseCost: 320, + ClassGymBaseCost: 120, + CrimeShoplift: "shoplift", + CrimeRobStore: "rob a store", + CrimeMug: "mug someone", + CrimeLarceny: "commit larceny", + CrimeDrugs: "deal drugs", + CrimeBondForgery: "forge corporate bonds", + CrimeTraffickArms: "traffick illegal arms", + CrimeHomicide: "commit homicide", + CrimeGrandTheftAuto: "commit grand theft auto", + CrimeKidnap: "kidnap someone for ransom", + CrimeAssassination: "assassinate a high-profile target", + CrimeHeist: "pull off the ultimate heist", + // Coding Contract + // TODO Move this into Coding contract impelmentation? + CodingContractBaseFactionRepGain: 2500, + CodingContractBaseCompanyRepGain: 4000, + CodingContractBaseMoneyGain: 75e6, + // BitNode/Source-File related stuff + TotalNumBitNodes: 24, + LatestUpdate: ` + v0.47.0 + * Stock Market changes: + ** Implemented spread. Stock's now have bid and ask prices at which transactions occur + ** Large transactions will now influence a stock's price and forecast + ** This "influencing" can take effect in the middle of a transaction + ** See documentation for more details on these changes + ** Added getStockAskPrice(), getStockBidPrice() Netscript functions to the TIX API + ** Added getStockPurchaseCost(), getStockSaleGain() Netscript functions to the TIX API + + * Re-sleeves can no longer have the NeuroFlux Governor augmentation + ** This is just a temporary patch until the mechanic gets re-worked + + * Adjusted RAM costs of Netscript Singularity functions (mostly increased) + * Netscript Singularity functions no longer cost extra RAM outside of BitNode-4 + * Corporation employees no longer have an "age" stat + * Bug Fix: Corporation employees stats should no longer become negative + * Bug Fix: Fixed sleeve.getInformation() throwing error in certain scenarios + * Bug Fix: Coding contracts should no longer generate on the w0r1d_d43m0n server + * Bug Fix: Duplicate Sleeves now properly have access to all Augmentations if you have a gang + * Bug Fix: getAugmentationsFromFaction() & purchaseAugmentation() functions should now work properly if you have a gang + * Bug Fix: Fixed issue that caused messages (.msg) to be sent when refreshing/reloading the game + * Bug Fix: Purchasing hash upgrades for Bladeburner/Corporation when you don't actually have access to those mechanics no longer gives hashes + * Bug Fix: run(), exec(), and spawn() Netscript functions now throw if called with 0 threads + * Bug Fix: Faction UI should now automatically update reputation + ` +}; + + +/***/ }), + +/***/ 98: +/*!**********************************!*\ + !*** ./src/StockMarket/Order.ts ***! + \**********************************/ +/*! no static exports found */ +/*! exports used: Order */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Represents a Limit or Buy Order on the stock market. Does not represent + * a Market Order since those are just executed immediately + */ +const OrderTypes_1 = __webpack_require__(/*! ./data/OrderTypes */ 54); +const PositionTypes_1 = __webpack_require__(/*! ./data/PositionTypes */ 23); +const JSONReviver_1 = __webpack_require__(/*! ../../utils/JSONReviver */ 16); +class Order { + /** + * Initializes a Order from a JSON save state + */ + static fromJSON(value) { + return JSONReviver_1.Generic_fromJSON(Order, value.data); + } + constructor(stockSymbol = "", shares = 0, price = 0, typ = OrderTypes_1.OrderTypes.LimitBuy, pos = PositionTypes_1.PositionTypes.Long) { + // Validate arguments + let invalidArgs = false; + if (typeof shares !== "number" || typeof price !== "number") { + invalidArgs = true; + } + if (isNaN(shares) || isNaN(price)) { + invalidArgs = true; + } + if (typeof stockSymbol !== "string") { + invalidArgs = true; + } + if (invalidArgs) { + throw new Error(`Invalid constructor paramters for Order`); + } + this.stockSymbol = stockSymbol; + this.shares = shares; + this.price = price; + this.type = typ; + this.pos = pos; + } + /** + * Serialize the Order to a JSON save state. + */ + toJSON() { + return JSONReviver_1.Generic_toJSON("Order", this); + } +} +exports.Order = Order; +JSONReviver_1.Reviver.constructors.Order = Order; + + +/***/ }) + +/******/ }); +//# sourceMappingURL=tests.bundle.js.map \ No newline at end of file diff --git a/test/tests.bundle.js.map b/test/tests.bundle.js.map new file mode 100644 index 000000000..565ad9a66 --- /dev/null +++ b/test/tests.bundle.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/Netscript/RamCostGenerator.ts","webpack:///./utils/helpers/getRandomInt.ts","webpack:///./utils/JSONReviver.js","webpack:///./test/StockMarketTests.js","webpack:///./test/Netscript/StaticRamCalculationTests.js","webpack:///./src/StockMarket/data/PositionTypes.ts","webpack:///./src/StockMarket/StockMarketHelpers.ts","webpack:///./test/index.js","webpack:///./src/StockMarket/data/OrderTypes.ts","webpack:///./src/StockMarket/Stock.ts","webpack:///./src/Constants.ts","webpack:///./src/StockMarket/Order.ts"],"names":[],"mappings":";AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,kDAA0C,gCAAgC;AAC1E;AACA;;AAEA;AACA;AACA;AACA,gEAAwD,kBAAkB;AAC1E;AACA,yDAAiD,cAAc;AAC/D;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iDAAyC,iCAAiC;AAC1E,wHAAgH,mBAAmB,EAAE;AACrI;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;;AAGA;AACA;;;;;;;;;;;;;;;;AChFA,iEAAiE;AAEjE,oCAAoC;AACvB,wBAAgB,GAAiB;IAC1C,iBAAiB,EAAE,GAAG;IACtB,gBAAgB,EAAE,EAAE;IACpB,iBAAiB,EAAE,GAAG;IACtB,wBAAwB,EAAE,CAAC;IAC3B,iBAAiB,EAAE,IAAI;IACvB,0BAA0B,EAAE,CAAC;IAC7B,mBAAmB,EAAE,IAAI;IACzB,iBAAiB,EAAE,GAAG;IACtB,wBAAwB,EAAE,IAAI;IAC9B,gBAAgB,EAAE,GAAG;IACrB,iBAAiB,EAAE,GAAG;IACtB,kBAAkB,EAAE,GAAG;IACvB,gBAAgB,EAAE,GAAG;IACrB,iBAAiB,EAAE,GAAG;IACtB,0BAA0B,EAAE,IAAI;IAChC,wBAAwB,EAAE,IAAI;IAC9B,4BAA4B,EAAE,IAAI;IAClC,2BAA2B,EAAE,GAAG;IAChC,sBAAsB,EAAE,GAAG;IAC3B,uBAAuB,EAAE,GAAG;IAC5B,sBAAsB,EAAE,GAAG;IAC3B,yBAAyB,EAAE,GAAG;IAC9B,uBAAuB,EAAE,GAAG;IAC5B,qBAAqB,EAAE,GAAG;IAC1B,sBAAsB,EAAE,GAAG;IAC3B,qBAAqB,EAAE,GAAG;IAC1B,yBAAyB,EAAE,GAAG;IAC9B,8BAA8B,EAAE,IAAI;IACpC,2BAA2B,EAAE,IAAI;IACjC,6BAA6B,EAAE,IAAI;IACnC,8BAA8B,EAAE,IAAI;IACpC,kBAAkB,EAAE,IAAI;IACxB,sBAAsB,EAAE,GAAG;IAC3B,sBAAsB,EAAE,GAAG;IAC3B,sBAAsB,EAAE,GAAG;IAC3B,wBAAwB,EAAE,IAAI;IAC9B,sBAAsB,EAAE,IAAI;IAC5B,+BAA+B,EAAE,EAAE;IACnC,uBAAuB,EAAE,CAAC;IAE1B,2BAA2B,EAAE,CAAC;IAC9B,2BAA2B,EAAE,CAAC;IAC9B,2BAA2B,EAAE,CAAC;IAE9B,wBAAwB,EAAE,CAAC;IAE3B,+BAA+B,EAAE,CAAC;CACrC;AAEY,gBAAQ,GAAc;IAC/B,OAAO,EAAE;QACL,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;QACjB,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;QACrB,mBAAmB,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5B,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;QACrB,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;QACrB,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;QACnB,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC;QACpB,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;QACrB,mBAAmB,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5B,iBAAiB,EAAE,GAAG,EAAE,CAAC,CAAC;QAC1B,kBAAkB,EAAE,GAAG,EAAE,CAAC,CAAC;QAC3B,mBAAmB,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5B,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;QAClB,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;QACjB,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC;KACvB;IACD,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;IAChB,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;IACjB,IAAI,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,iBAAiB;IAC9C,IAAI,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,iBAAiB;IAC9C,kBAAkB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;IACnE,kBAAkB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;IACnE,UAAU,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;IAC3D,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IACd,IAAI,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,iBAAiB;IAC9C,aAAa,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,0BAA0B;IAChE,MAAM,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,mBAAmB;IAClD,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IACd,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACf,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;IACjB,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;IACnB,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IAClB,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;IACrB,aAAa,EAAE,GAAG,EAAE,CAAC,CAAC;IACtB,IAAI,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;IACrD,QAAQ,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;IACzD,QAAQ,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;IACzD,SAAS,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;IAC1D,QAAQ,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;IACzD,SAAS,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;IAC1D,GAAG,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,gBAAgB;IAC5C,IAAI,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,iBAAiB;IAC9C,KAAK,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,kBAAkB;IAChD,IAAI,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,iBAAiB;IAC9C,OAAO,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,iBAAiB;IACjD,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;IACb,GAAG,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,gBAAgB;IAC5C,EAAE,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,iBAAiB;IAC5C,EAAE,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,iBAAiB;IAC5C,aAAa,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,0BAA0B;IAChE,KAAK,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;IACtD,WAAW,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;IAC5D,eAAe,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,4BAA4B;IACpE,qBAAqB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IACzE,qBAAqB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IACzE,qBAAqB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IACzE,uBAAuB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IACtE,sBAAsB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IACrE,0BAA0B,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IACzE,yBAAyB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IACxE,6BAA6B,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IAC5E,iBAAiB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IAChE,eAAe,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IAC9D,yBAAyB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IACxE,YAAY,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IAC3D,YAAY,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IAC3D,UAAU,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,uBAAuB;IAC1D,SAAS,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IACxD,eAAe,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,qBAAqB;IAC7D,aAAa,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,qBAAqB;IAC3D,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,qBAAqB;IAC9D,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,qBAAqB;IAC9D,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,qBAAqB;IAC9D,iBAAiB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,qBAAqB;IAC/D,oBAAoB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,qBAAqB;IAClE,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,qBAAqB;IAC9D,QAAQ,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,yBAAyB;IAC1D,SAAS,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,yBAAyB;IAC3D,UAAU,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,yBAAyB;IAC5D,SAAS,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,yBAAyB;IAC3D,UAAU,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,yBAAyB;IAC5D,WAAW,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,yBAAyB;IAC7D,SAAS,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,yBAAyB;IAC3D,kBAAkB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,yBAAyB;IACpE,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,yBAAyB;IAClE,oBAAoB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,yBAAyB;IACtE,0BAA0B,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,yBAAyB;IAC5E,uBAAuB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,6BAA6B;IAC7E,wBAAwB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,8BAA8B;IAC/E,sBAAsB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,8BAA8B;IAC7E,cAAc,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IAClE,YAAY,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IAChE,mBAAmB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IACvE,KAAK,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IACpD,QAAQ,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IACvD,IAAI,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IACnD,IAAI,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IACnD,KAAK,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IACpD,aAAa,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB,GAAG,EAAE;IACjE,EAAE,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IACjD,aAAa,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IAC5D,UAAU,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IACzD,aAAa,EAAE,GAAG,EAAE,CAAC,CAAC;IACtB,YAAY,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IAC3D,WAAW,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;IAC5D,WAAW,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;IAC5D,aAAa,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;IAC9D,eAAe,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IAC9D,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IAC/D,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;IAChB,mBAAmB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;IACpE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACf,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;IACb,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,sBAAsB;IAE/D,wBAAwB;IACxB,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IACpE,UAAU,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IAC9D,YAAY,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IAChE,WAAW,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IAC/D,eAAe,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IACnE,QAAQ,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B,GAAG,CAAC;IAChE,uBAAuB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B,GAAG,CAAC;IAC/E,MAAM,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B,GAAG,CAAC;IAC9D,UAAU,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B,GAAG,CAAC;IAClE,cAAc,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IAClE,qBAAqB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B,GAAG,CAAC;IAC7E,cAAc,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IAClE,cAAc,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IAClE,aAAa,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B,GAAG,CAAC;IACrE,eAAe,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B,GAAG,CAAC;IACvE,mBAAmB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B,GAAG,CAAC;IAC3E,uBAAuB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IAC3E,WAAW,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IAC/D,cAAc,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IAClE,aAAa,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B,GAAG,CAAC;IACrE,eAAe,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B,GAAG,CAAC;IACvE,mBAAmB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B,GAAG,CAAC;IAC3E,eAAe,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IACnE,aAAa,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IACjE,WAAW,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IAC/D,cAAc,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IAClE,qBAAqB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IACzE,mBAAmB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IACvE,2BAA2B,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IAC/E,qBAAqB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IACzE,mBAAmB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IACvE,oBAAoB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IACxE,oBAAoB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,2BAA2B;IAExE,WAAW;IACX,IAAI,EAAG;QACH,cAAc,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB,GAAG,CAAC;QACnE,kBAAkB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB,GAAG,CAAC;QACvE,uBAAuB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB,GAAG,CAAC;QAC5E,oBAAoB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB,GAAG,CAAC;QACzE,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB,GAAG,CAAC;QACrE,aAAa,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB,GAAG,CAAC;QAClE,YAAY,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB,GAAG,CAAC;QACjE,aAAa,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB,GAAG,CAAC;QAClE,iBAAiB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB,GAAG,CAAC;QACtE,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB,GAAG,CAAC;QACrE,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB,GAAG,CAAC;QACrE,iBAAiB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;QAClE,YAAY,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;QAC7D,mBAAmB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB,GAAG,CAAC;QACxE,mBAAmB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,wBAAwB;QACpE,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;KACxB;IAED,kBAAkB;IAClB,WAAW,EAAG;QACV,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B,GAAG,EAAE;QAC7E,iBAAiB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B,GAAG,EAAE;QAC9E,eAAe,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B,GAAG,EAAE;QAC5E,cAAc,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B,GAAG,CAAC;QAC1E,qBAAqB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B,GAAG,EAAE;QAClF,aAAa,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B,GAAG,EAAE;QAC1E,WAAW,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QACnE,qBAAqB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B,GAAG,CAAC;QACjF,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B,GAAG,CAAC;QAC5E,aAAa,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QACrE,+BAA+B,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QACvF,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QACxE,uBAAuB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QAC/E,iBAAiB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QACzE,qBAAqB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QAC7E,kBAAkB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QAC1E,kBAAkB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QAC1E,cAAc,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QACtE,OAAO,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QAC/D,cAAc,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QACtE,aAAa,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QACrE,mBAAmB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QAC3E,YAAY,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QACpE,WAAW,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QACnE,WAAW,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QACnE,0BAA0B,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QAClF,2BAA2B,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QACnF,YAAY,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QACpE,OAAO,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QAC/D,UAAU,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QAClE,UAAU,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QAClE,sBAAsB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QAC9E,uBAAuB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QAC/E,YAAY,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;KACvE;IAED,sBAAsB;IACtB,cAAc,EAAG;QACb,OAAO,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B;QAC/D,eAAe,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B,GAAG,CAAC;QAC3E,OAAO,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B,GAAG,CAAC;QACnE,cAAc,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B,GAAG,CAAC;QAC1E,oBAAoB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,+BAA+B,GAAG,CAAC;KACnF;IAED,uBAAuB;IACvB,MAAM,EAAG;QACL,aAAa,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,uBAAuB;QAC7D,kBAAkB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,uBAAuB;QAClE,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,uBAAuB;QAChE,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,uBAAuB;QAChE,qBAAqB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,uBAAuB;QACrE,MAAM,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,uBAAuB;QACtD,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,uBAAuB;QAChE,gBAAgB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,uBAAuB;QAChE,eAAe,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,uBAAuB;QAC/D,cAAc,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,uBAAuB;QAC9D,OAAO,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,uBAAuB;QACvD,cAAc,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,uBAAuB;QAC9D,sBAAsB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,uBAAuB;QACtE,wBAAwB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,uBAAuB;QACxE,iBAAiB,EAAE,GAAG,EAAE,CAAC,wBAAgB,CAAC,uBAAuB;KACpE;IAED,KAAK,EAAE;QACH,sBAAsB;QACtB,KAAK,EAAG,GAAG,EAAE,CAAC,CAAC;KAClB;CACJ;AAED,oBAA2B,GAAG,IAAc;IACxC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE;QACnB,OAAO,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;QACpD,OAAO,CAAC,CAAC;KACZ;IAED,IAAI,IAAI,GAAG,gBAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE;QAClC,IAAI,IAAI,IAAI,IAAI,EAAE;YACd,OAAO,CAAC,IAAI,CAAC,0CAA0C,IAAI,EAAE,CAAC,CAAC;YAC/D,OAAO,CAAC,CAAC;SACZ;QAED,MAAM,QAAQ,GAAG,OAAO,IAAI,CAAC;QAC7B,IAAI,QAAQ,KAAK,UAAU,IAAI,QAAQ,KAAK,QAAQ,EAAE;YAClD,MAAM;SACT;QAED,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;KACxB;IAED,MAAM,QAAQ,GAAG,OAAO,IAAI,CAAC;IAC7B,IAAI,QAAQ,KAAK,UAAU,EAAE;QACzB,OAAO,IAAI,EAAE,CAAC;KACjB;IAED,IAAI,QAAQ,KAAK,QAAQ,EAAE;QACvB,OAAO,IAAI,CAAC;KACf;IAED,OAAO,CAAC,IAAI,CAAC,kBAAkB,QAAQ,EAAE,CAAC,CAAC;IAC3C,OAAO,CAAC,CAAC;AACb,CAAC;AAhCD,gCAgCC;;;;;;;;;;;;;;;;AC3UD;;;;GAIG;AACH,sBAA6B,GAAW,EAAE,GAAW;IACjD,MAAM,KAAK,GAAW,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACzC,MAAM,KAAK,GAAW,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAEzC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;AACnE,CAAC;AALD,oCAKC;;;;;;;;;;;;;;;;;ACVD;AAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,0BAA0B;;AAE1B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,4BAA4B;AAC5B;;AAEA;AACA,qBAAqB,qBAAqB;AAC1C;AACA;AACA;AACA,UAAU;AACV;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEQ;;;;;;;;;;;;;;;;;;;;;;;;;AC/EY;AACJ;AAChB,UAAU,gBAAgB;AACV;AAChB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AACD;AASC;AACoB;AACG;;AAExB;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA,KAAK;;AAEL;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,aAAa;;AAEb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA;AACA,qBAAqB;AACrB;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA,aAAa;AACb,SAAS;;AAET;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb,SAAS;;AAET;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,aAAa;AACb,SAAS;;AAET;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,aAAa;AACb,SAAS;AACT,KAAK;;AAEL;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,aAAa;;AAEb;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;AACA,aAAa;AACb,SAAS;;AAET;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa;AACb,SAAS;;AAET;AACA;AACA;AACA;AACA;AACA,SAAS;;AAET;;AAEA,SAAS;AACT,KAAK;AACL;;AAEA;AACA;AACA;AACA,gIAAoD;AACpD;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA,aAAa;;AAEb;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA,aAAa;;AAEb;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA,aAAa;;AAEb;AACA;AACA;AACA;AACA,aAAa;AACb,SAAS;;AAET;AACA;AACA,iIAAqD;AACrD;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA,aAAa;;AAEb;AACA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,aAAa;;AAEb;AACA;AACA;AACA;AACA,aAAa;AACb,SAAS;AACT,KAAK;;AAEL;AACA;AACA;AACA;AACA,2BAA2B,OAAO;AAClC;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,2BAA2B,OAAO;AAClC;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA,iIAAqD;AACrD;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;AACb,SAAS;;AAET;AACA;AACA;;AAEA;AACA;AACA;;AAEA,kIAAsD;AACtD;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,aAAa;;AAEb;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,aAAa;AACb,SAAS;AACT,KAAK;;AAEL;AACA;AACA;AACA,0FAAmC;AACnC;AACA;AACA;AACA;AACA;AACA,oGAA6C;AAC7C;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT,KAAK;;AAEL;;AAEA,KAAK;AACL,CAAC;;;;;;;;;;;;;AChqBD;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACqB;AACrB,UAAU,oBAAoB;;AAE9B;AACA;;AAEA;;AAEA;AACA;AACA;AACA,KAAK;AACL,CAAC;;;;;;;;;;;;;;;;AClBD,IAAY,aAGX;AAHD,WAAY,aAAa;IACrB,2BAAU;IACV,4BAAW;AACf,CAAC,EAHW,aAAa,GAAb,qBAAa,KAAb,qBAAa,QAGxB;;;;;;;;;;;;;;;;ACHD,uDAAgC;AAChC,4EAAqD;AACrD,+DAAyC;AAEzC,wEAAwE;AAC3D,sCAA8B,GAAG,GAAG,CAAC;AAElD;;;;;;GAMG;AACH,0CAAiD,KAAY;IACzD,IAAI,CAAC,CAAC,KAAK,YAA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","file":"test/tests.bundle.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 300);\n","import { IMap } from \"../types\";\r\n\r\n// TODO remember to update RamCalculations.js and WorkerScript.js\r\n\r\n// RAM costs for Netscript functions\r\nexport const RamCostConstants: IMap = {\r\n ScriptBaseRamCost: 1.6,\r\n ScriptDomRamCost: 25,\r\n ScriptHackRamCost: 0.1,\r\n ScriptHackAnalyzeRamCost: 1,\r\n ScriptGrowRamCost: 0.15,\r\n ScriptGrowthAnalyzeRamCost: 1,\r\n ScriptWeakenRamCost: 0.15,\r\n ScriptScanRamCost: 0.2,\r\n ScriptPortProgramRamCost: 0.05,\r\n ScriptRunRamCost: 1.0,\r\n ScriptExecRamCost: 1.3,\r\n ScriptSpawnRamCost: 2.0,\r\n ScriptScpRamCost: 0.6,\r\n ScriptKillRamCost: 0.5,\r\n ScriptHasRootAccessRamCost: 0.05,\r\n ScriptGetHostnameRamCost: 0.05,\r\n ScriptGetHackingLevelRamCost: 0.05,\r\n ScriptGetMultipliersRamCost: 4.0,\r\n ScriptGetServerRamCost: 0.1,\r\n ScriptFileExistsRamCost: 0.1,\r\n ScriptIsRunningRamCost: 0.1,\r\n ScriptHacknetNodesRamCost: 4.0,\r\n ScriptHNUpgLevelRamCost: 0.4,\r\n ScriptHNUpgRamRamCost: 0.6,\r\n ScriptHNUpgCoreRamCost: 0.8,\r\n ScriptGetStockRamCost: 2.0,\r\n ScriptBuySellStockRamCost: 2.5,\r\n ScriptGetPurchaseServerRamCost: 0.25,\r\n ScriptPurchaseServerRamCost: 2.25,\r\n ScriptGetPurchasedServerLimit: 0.05,\r\n ScriptGetPurchasedServerMaxRam: 0.05,\r\n ScriptRoundRamCost: 0.05,\r\n ScriptReadWriteRamCost: 1.0,\r\n ScriptArbScriptRamCost: 1.0,\r\n ScriptGetScriptRamCost: 0.1,\r\n ScriptGetHackTimeRamCost: 0.05,\r\n ScriptGetFavorToDonate: 0.10,\r\n ScriptCodingContractBaseRamCost: 10,\r\n ScriptSleeveBaseRamCost: 4,\r\n\r\n ScriptSingularityFn1RamCost: 2,\r\n ScriptSingularityFn2RamCost: 3,\r\n ScriptSingularityFn3RamCost: 5,\r\n\r\n ScriptGangApiBaseRamCost: 4,\r\n\r\n ScriptBladeburnerApiBaseRamCost: 4,\r\n}\r\n\r\nexport const RamCosts: IMap = {\r\n hacknet: {\r\n numNodes: () => 0,\r\n purchaseNode: () => 0,\r\n getPurchaseNodeCost: () => 0,\r\n getNodeStats: () => 0,\r\n upgradeLevel: () => 0,\r\n upgradeRam: () => 0,\r\n upgradeCore: () => 0,\r\n upgradeCache: () => 0,\r\n getLevelUpgradeCost: () => 0,\r\n getRamUpgradeCost: () => 0,\r\n getCoreUpgradeCost: () => 0,\r\n getCacheUpgradeCost: () => 0,\r\n numHashes: () => 0,\r\n hashCost: () => 0,\r\n spendHashes: () => 0,\r\n },\r\n sprintf: () => 0,\r\n vsprintf: () => 0,\r\n scan: () => RamCostConstants.ScriptScanRamCost,\r\n hack: () => RamCostConstants.ScriptHackRamCost,\r\n hackAnalyzeThreads: () => RamCostConstants.ScriptHackAnalyzeRamCost,\r\n hackAnalyzePercent: () => RamCostConstants.ScriptHackAnalyzeRamCost,\r\n hackChance: () => RamCostConstants.ScriptHackAnalyzeRamCost,\r\n sleep: () => 0,\r\n grow: () => RamCostConstants.ScriptGrowRamCost,\r\n growthAnalyze: () => RamCostConstants.ScriptGrowthAnalyzeRamCost,\r\n weaken: () => RamCostConstants.ScriptWeakenRamCost,\r\n print: () => 0,\r\n tprint: () => 0,\r\n clearLog: () => 0,\r\n disableLog: () => 0,\r\n enableLog: () => 0,\r\n isLogEnabled: () => 0,\r\n getScriptLogs: () => 0,\r\n nuke: () => RamCostConstants.ScriptPortProgramRamCost,\r\n brutessh: () => RamCostConstants.ScriptPortProgramRamCost,\r\n ftpcrack: () => RamCostConstants.ScriptPortProgramRamCost,\r\n relaysmtp: () => RamCostConstants.ScriptPortProgramRamCost,\r\n httpworm: () => RamCostConstants.ScriptPortProgramRamCost,\r\n sqlinject: () => RamCostConstants.ScriptPortProgramRamCost,\r\n run: () => RamCostConstants.ScriptRunRamCost,\r\n exec: () => RamCostConstants.ScriptExecRamCost,\r\n spawn: () => RamCostConstants.ScriptSpawnRamCost,\r\n kill: () => RamCostConstants.ScriptKillRamCost,\r\n killall: () => RamCostConstants.ScriptKillRamCost,\r\n exit: () => 0,\r\n scp: () => RamCostConstants.ScriptScpRamCost,\r\n ls: () => RamCostConstants.ScriptScanRamCost,\r\n ps: () => RamCostConstants.ScriptScanRamCost,\r\n hasRootAccess: () => RamCostConstants.ScriptHasRootAccessRamCost,\r\n getIp: () => RamCostConstants.ScriptGetHostnameRamCost,\r\n getHostname: () => RamCostConstants.ScriptGetHostnameRamCost,\r\n getHackingLevel: () => RamCostConstants.ScriptGetHackingLevelRamCost,\r\n getHackingMultipliers: () => RamCostConstants.ScriptGetMultipliersRamCost,\r\n getHacknetMultipliers: () => RamCostConstants.ScriptGetMultipliersRamCost,\r\n getBitNodeMultipliers: () => RamCostConstants.ScriptGetMultipliersRamCost,\r\n getServerMoneyAvailable: () => RamCostConstants.ScriptGetServerRamCost,\r\n getServerSecurityLevel: () => RamCostConstants.ScriptGetServerRamCost,\r\n getServerBaseSecurityLevel: () => RamCostConstants.ScriptGetServerRamCost,\r\n getServerMinSecurityLevel: () => RamCostConstants.ScriptGetServerRamCost,\r\n getServerRequiredHackingLevel: () => RamCostConstants.ScriptGetServerRamCost,\r\n getServerMaxMoney: () => RamCostConstants.ScriptGetServerRamCost,\r\n getServerGrowth: () => RamCostConstants.ScriptGetServerRamCost,\r\n getServerNumPortsRequired: () => RamCostConstants.ScriptGetServerRamCost,\r\n getServerRam: () => RamCostConstants.ScriptGetServerRamCost,\r\n serverExists: () => RamCostConstants.ScriptGetServerRamCost,\r\n fileExists: () => RamCostConstants.ScriptFileExistsRamCost,\r\n isRunning: () => RamCostConstants.ScriptIsRunningRamCost,\r\n getStockSymbols: () => RamCostConstants.ScriptGetStockRamCost,\r\n getStockPrice: () => RamCostConstants.ScriptGetStockRamCost,\r\n getStockAskPrice: () => RamCostConstants.ScriptGetStockRamCost,\r\n getStockBidPrice: () => RamCostConstants.ScriptGetStockRamCost,\r\n getStockPosition: () => RamCostConstants.ScriptGetStockRamCost,\r\n getStockMaxShares: () => RamCostConstants.ScriptGetStockRamCost,\r\n getStockPurchaseCost: () => RamCostConstants.ScriptGetStockRamCost,\r\n getStockSaleGain: () => RamCostConstants.ScriptGetStockRamCost,\r\n buyStock: () => RamCostConstants.ScriptBuySellStockRamCost,\r\n sellStock: () => RamCostConstants.ScriptBuySellStockRamCost,\r\n shortStock: () => RamCostConstants.ScriptBuySellStockRamCost,\r\n sellShort: () => RamCostConstants.ScriptBuySellStockRamCost,\r\n placeOrder: () => RamCostConstants.ScriptBuySellStockRamCost,\r\n cancelOrder: () => RamCostConstants.ScriptBuySellStockRamCost,\r\n getOrders: () => RamCostConstants.ScriptBuySellStockRamCost,\r\n getStockVolatility: () => RamCostConstants.ScriptBuySellStockRamCost,\r\n getStockForecast: () => RamCostConstants.ScriptBuySellStockRamCost,\r\n purchase4SMarketData: () => RamCostConstants.ScriptBuySellStockRamCost,\r\n purchase4SMarketDataTixApi: () => RamCostConstants.ScriptBuySellStockRamCost,\r\n getPurchasedServerLimit: () => RamCostConstants.ScriptGetPurchasedServerLimit,\r\n getPurchasedServerMaxRam: () => RamCostConstants.ScriptGetPurchasedServerMaxRam,\r\n getPurchasedServerCost: () => RamCostConstants.ScriptGetPurchaseServerRamCost,\r\n purchaseServer: () => RamCostConstants.ScriptPurchaseServerRamCost,\r\n deleteServer: () => RamCostConstants.ScriptPurchaseServerRamCost,\r\n getPurchasedServers: () => RamCostConstants.ScriptPurchaseServerRamCost,\r\n write: () => RamCostConstants.ScriptReadWriteRamCost,\r\n tryWrite: () => RamCostConstants.ScriptReadWriteRamCost,\r\n read: () => RamCostConstants.ScriptReadWriteRamCost,\r\n peek: () => RamCostConstants.ScriptReadWriteRamCost,\r\n clear: () => RamCostConstants.ScriptReadWriteRamCost,\r\n getPortHandle: () => RamCostConstants.ScriptReadWriteRamCost * 10,\r\n rm: () => RamCostConstants.ScriptReadWriteRamCost,\r\n scriptRunning: () => RamCostConstants.ScriptArbScriptRamCost,\r\n scriptKill: () => RamCostConstants.ScriptArbScriptRamCost,\r\n getScriptName: () => 0,\r\n getScriptRam: () => RamCostConstants.ScriptGetScriptRamCost,\r\n getHackTime: () => RamCostConstants.ScriptGetHackTimeRamCost,\r\n getGrowTime: () => RamCostConstants.ScriptGetHackTimeRamCost,\r\n getWeakenTime: () => RamCostConstants.ScriptGetHackTimeRamCost,\r\n getScriptIncome: () => RamCostConstants.ScriptGetScriptRamCost,\r\n getScriptExpGain: () => RamCostConstants.ScriptGetScriptRamCost,\r\n nFormat: () => 0,\r\n getTimeSinceLastAug: () => RamCostConstants.ScriptGetHackTimeRamCost,\r\n prompt: () => 0,\r\n wget: () => 0,\r\n getFavorToDonate: () => RamCostConstants.ScriptGetFavorToDonate,\r\n\r\n // Singularity Functions\r\n universityCourse: () => RamCostConstants.ScriptSingularityFn1RamCost,\r\n gymWorkout: () => RamCostConstants.ScriptSingularityFn1RamCost,\r\n travelToCity: () => RamCostConstants.ScriptSingularityFn1RamCost,\r\n purchaseTor: () => RamCostConstants.ScriptSingularityFn1RamCost,\r\n purchaseProgram: () => RamCostConstants.ScriptSingularityFn1RamCost,\r\n getStats: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,\r\n getCharacterInformation: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,\r\n isBusy: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,\r\n stopAction: () => RamCostConstants.ScriptSingularityFn1RamCost / 2,\r\n upgradeHomeRam: () => RamCostConstants.ScriptSingularityFn2RamCost,\r\n getUpgradeHomeRamCost: () => RamCostConstants.ScriptSingularityFn2RamCost / 2,\r\n workForCompany: () => RamCostConstants.ScriptSingularityFn2RamCost,\r\n applyToCompany: () => RamCostConstants.ScriptSingularityFn2RamCost,\r\n getCompanyRep: () => RamCostConstants.ScriptSingularityFn2RamCost / 3,\r\n getCompanyFavor: () => RamCostConstants.ScriptSingularityFn2RamCost / 3,\r\n getCompanyFavorGain: () => RamCostConstants.ScriptSingularityFn2RamCost / 4,\r\n checkFactionInvitations: () => RamCostConstants.ScriptSingularityFn2RamCost,\r\n joinFaction: () => RamCostConstants.ScriptSingularityFn2RamCost,\r\n workForFaction: () => RamCostConstants.ScriptSingularityFn2RamCost,\r\n getFactionRep: () => RamCostConstants.ScriptSingularityFn2RamCost / 3,\r\n getFactionFavor: () => RamCostConstants.ScriptSingularityFn2RamCost / 3,\r\n getFactionFavorGain: () => RamCostConstants.ScriptSingularityFn2RamCost / 4,\r\n donateToFaction: () => RamCostConstants.ScriptSingularityFn3RamCost,\r\n createProgram: () => RamCostConstants.ScriptSingularityFn3RamCost,\r\n commitCrime: () => RamCostConstants.ScriptSingularityFn3RamCost,\r\n getCrimeChance: () => RamCostConstants.ScriptSingularityFn3RamCost,\r\n getOwnedAugmentations: () => RamCostConstants.ScriptSingularityFn3RamCost,\r\n getOwnedSourceFiles: () => RamCostConstants.ScriptSingularityFn3RamCost,\r\n getAugmentationsFromFaction: () => RamCostConstants.ScriptSingularityFn3RamCost,\r\n getAugmentationPrereq: () => RamCostConstants.ScriptSingularityFn3RamCost,\r\n getAugmentationCost: () => RamCostConstants.ScriptSingularityFn3RamCost,\r\n purchaseAugmentation: () => RamCostConstants.ScriptSingularityFn3RamCost,\r\n installAugmentations: () => RamCostConstants.ScriptSingularityFn3RamCost,\r\n\r\n // Gang API\r\n gang : {\r\n getMemberNames: () => RamCostConstants.ScriptGangApiBaseRamCost / 4,\r\n getGangInformation: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,\r\n getOtherGangInformation: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,\r\n getMemberInformation: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,\r\n canRecruitMember: () => RamCostConstants.ScriptGangApiBaseRamCost / 4,\r\n recruitMember: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,\r\n getTaskNames: () => RamCostConstants.ScriptGangApiBaseRamCost / 4,\r\n setMemberTask: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,\r\n getEquipmentNames: () => RamCostConstants.ScriptGangApiBaseRamCost / 4,\r\n getEquipmentCost: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,\r\n getEquipmentType: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,\r\n purchaseEquipment: () => RamCostConstants.ScriptGangApiBaseRamCost,\r\n ascendMember: () => RamCostConstants.ScriptGangApiBaseRamCost,\r\n setTerritoryWarfare: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,\r\n getChanceToWinClash: () => RamCostConstants.ScriptGangApiBaseRamCost,\r\n getBonusTime: () => 0,\r\n },\r\n\r\n // Bladeburner API\r\n bladeburner : {\r\n getContractNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,\r\n getOperationNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,\r\n getBlackOpNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,\r\n getBlackOpRank: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 2,\r\n getGeneralActionNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,\r\n getSkillNames: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,\r\n startAction: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n stopBladeburnerAction: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 2,\r\n getCurrentAction: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost / 4,\r\n getActionTime: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n getActionEstimatedSuccessChance: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n getActionRepGain: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n getActionCountRemaining: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n getActionMaxLevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n getActionCurrentLevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n getActionAutolevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n setActionAutolevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n setActionLevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n getRank: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n getSkillPoints: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n getSkillLevel: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n getSkillUpgradeCost: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n upgradeSkill: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n getTeamSize: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n setTeamSize: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n getCityEstimatedPopulation: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n getCityEstimatedCommunities: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n getCityChaos: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n getCity: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n switchCity: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n getStamina: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n joinBladeburnerFaction: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n joinBladeburnerDivision: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n getBonusTime: () => RamCostConstants.ScriptBladeburnerApiBaseRamCost,\r\n },\r\n\r\n // Coding Contract API\r\n codingcontract : {\r\n attempt: () => RamCostConstants.ScriptCodingContractBaseRamCost,\r\n getContractType: () => RamCostConstants.ScriptCodingContractBaseRamCost / 2,\r\n getData: () => RamCostConstants.ScriptCodingContractBaseRamCost / 2,\r\n getDescription: () => RamCostConstants.ScriptCodingContractBaseRamCost / 2,\r\n getNumTriesRemaining: () => RamCostConstants.ScriptCodingContractBaseRamCost / 2,\r\n },\r\n\r\n // Duplicate Sleeve API\r\n sleeve : {\r\n getNumSleeves: () => RamCostConstants.ScriptSleeveBaseRamCost,\r\n setToShockRecovery: () => RamCostConstants.ScriptSleeveBaseRamCost,\r\n setToSynchronize: () => RamCostConstants.ScriptSleeveBaseRamCost,\r\n setToCommitCrime: () => RamCostConstants.ScriptSleeveBaseRamCost,\r\n setToUniversityCourse: () => RamCostConstants.ScriptSleeveBaseRamCost,\r\n travel: () => RamCostConstants.ScriptSleeveBaseRamCost,\r\n setToCompanyWork: () => RamCostConstants.ScriptSleeveBaseRamCost,\r\n setToFactionWork: () => RamCostConstants.ScriptSleeveBaseRamCost,\r\n setToGymWorkout: () => RamCostConstants.ScriptSleeveBaseRamCost,\r\n getSleeveStats: () => RamCostConstants.ScriptSleeveBaseRamCost,\r\n getTask: () => RamCostConstants.ScriptSleeveBaseRamCost,\r\n getInformation: () => RamCostConstants.ScriptSleeveBaseRamCost,\r\n getSleeveAugmentations: () => RamCostConstants.ScriptSleeveBaseRamCost,\r\n getSleevePurchasableAugs: () => RamCostConstants.ScriptSleeveBaseRamCost,\r\n purchaseSleeveAug: () => RamCostConstants.ScriptSleeveBaseRamCost,\r\n },\r\n\r\n heart: {\r\n // Easter egg function\r\n break : () => 0,\r\n }\r\n}\r\n\r\nexport function getRamCost(...args: string[]): number {\r\n if (args.length === 0) {\r\n console.warn(`No arguments passed to getRamCost()`);\r\n return 0;\r\n }\r\n\r\n let curr = RamCosts[args[0]];\r\n for (let i = 1; i < args.length; ++i) {\r\n if (curr == null) {\r\n console.warn(`Invalid function passed to getRamCost: ${args}`);\r\n return 0;\r\n }\r\n\r\n const currType = typeof curr;\r\n if (currType === \"function\" || currType === \"number\") {\r\n break;\r\n }\r\n\r\n curr = curr[args[i]];\r\n }\r\n\r\n const currType = typeof curr;\r\n if (currType === \"function\") {\r\n return curr();\r\n }\r\n\r\n if (currType === \"number\") {\r\n return curr;\r\n }\r\n\r\n console.warn(`Expected type: ${currType}`);\r\n return 0;\r\n}\r\n","/**\r\n * Gets a random integer bounded by the values passed in.\r\n * @param min The minimum value in the range.\r\n * @param max The maximum value in the range.\r\n */\r\nexport function getRandomInt(min: number, max: number) {\r\n const lower: number = Math.min(min, max);\r\n const upper: number = Math.max(min, max);\r\n\r\n return Math.floor(Math.random() * (upper - lower + 1)) + lower;\r\n}\r\n","/* Generic Reviver, toJSON, and fromJSON functions used for saving and loading objects */\r\n\r\n// A generic \"smart reviver\" function.\r\n// Looks for object values with a `ctor` property and\r\n// a `data` property. If it finds them, and finds a matching\r\n// constructor that has a `fromJSON` property on it, it hands\r\n// off to that `fromJSON` fuunction, passing in the value.\r\nfunction Reviver(key, value) {\r\n\tvar ctor;\r\n if (value == null) {\r\n console.log(\"Reviver WRONGLY called with key: \" + key + \", and value: \" + value);\r\n return 0;\r\n }\r\n\r\n\tif (typeof value === \"object\" &&\r\n\t\ttypeof value.ctor === \"string\" &&\r\n\t\ttypeof value.data !== \"undefined\") {\r\n\t\t\t// Compatibility for version v0.43.1\r\n\t\t\t// TODO Remove this eventually\r\n\t\t\tif (value.ctor === \"AllServersMap\") {\r\n\t\t\t\tconsole.log('Converting AllServersMap for v0.43.1');\r\n\t\t\t\treturn value.data;\r\n\t\t\t}\r\n\r\n\t\t\tctor = Reviver.constructors[value.ctor] || window[value.ctor];\r\n\r\n\t\t\tif (typeof ctor === \"function\" &&\r\n\t\t\t\ttypeof ctor.fromJSON === \"function\") {\r\n\r\n\t\t\t\t\treturn ctor.fromJSON(value);\r\n\t\t\t}\r\n\t}\r\n\treturn value;\r\n}\r\nReviver.constructors = {}; // A list of constructors the smart reviver should know about\r\n\r\n// A generic \"toJSON\" function that creates the data expected\r\n// by Reviver.\r\n// `ctorName` The name of the constructor to use to revive it\r\n// `obj` The object being serialized\r\n// `keys` (Optional) Array of the properties to serialize,\r\n// if not given then all of the objects \"own\" properties\r\n// that don't have function values will be serialized.\r\n// (Note: If you list a property in `keys`, it will be serialized\r\n// regardless of whether it's an \"own\" property.)\r\n// Returns: The structure (which will then be turned into a string\r\n// as part of the JSON.stringify algorithm)\r\nfunction Generic_toJSON(ctorName, obj, keys) {\r\n var data, index, key;\r\n\r\n if (!keys) {\r\n keys = Object.keys(obj); // Only \"own\" properties are included\r\n }\r\n\r\n data = {};\r\n for (let index = 0; index < keys.length; ++index) {\r\n key = keys[index];\r\n data[key] = obj[key];\r\n }\r\n return {ctor: ctorName, data: data};\r\n}\r\n\r\n// A generic \"fromJSON\" function for use with Reviver: Just calls the\r\n// constructor function with no arguments, then applies all of the\r\n// key/value pairs from the raw data to the instance. Only useful for\r\n// constructors that can be reasonably called without arguments!\r\n// `ctor` The constructor to call\r\n// `data` The data to apply\r\n// Returns: The object\r\nfunction Generic_fromJSON(ctor, data) {\r\n var obj, name;\r\n\r\n obj = new ctor();\r\n for (name in data) {\r\n obj[name] = data[name];\r\n }\r\n return obj;\r\n}\r\n\r\nexport {Reviver, Generic_toJSON, Generic_fromJSON};\r\n","import { CONSTANTS } from \"../src/Constants\";\r\nimport { Order } from \"../src/StockMarket/Order\";\r\n//import { processOrders } from \"../src/StockMarket/OrderProcessing\";\r\nimport { Stock } from \"../src/StockMarket/Stock\";\r\n/*\r\nimport {\r\n deleteStockMarket,\r\n initStockMarket,\r\n initSymbolToStockMap,\r\n loadStockMarket,\r\n StockMarket,\r\n SymbolToStockMap,\r\n} from \"../src/StockMarket/StockMarket\";\r\n*/\r\nimport {\r\n calculateIncreasingPriceMovement,\r\n calculateDecreasingPriceMovement,\r\n forecastChangePerPriceMovement,\r\n getBuyTransactionCost,\r\n getSellTransactionGain,\r\n processBuyTransactionPriceMovement,\r\n processSellTransactionPriceMovement,\r\n} from \"../src/StockMarket/StockMarketHelpers\";\r\nimport { OrderTypes } from \"../src/StockMarket/data/OrderTypes\"\r\nimport { PositionTypes } from \"../src/StockMarket/data/PositionTypes\";\r\n\r\nconst assert = chai.assert;\r\nconst expect = chai.expect;\r\n\r\nconsole.log(\"Beginning Stock Market Tests\");\r\n\r\ndescribe(\"Stock Market Tests\", function() {\r\n const commission = CONSTANTS.StockMarketCommission;\r\n\r\n // Generic Stock object that can be used by each test\r\n let stock;\r\n const ctorParams = {\r\n b: true,\r\n initPrice: 10e3,\r\n marketCap: 5e9,\r\n mv: 1,\r\n name: \"MockStock\",\r\n otlkMag: 10,\r\n spreadPerc: 1,\r\n shareTxForMovement: 5e3,\r\n symbol: \"mock\",\r\n };\r\n\r\n beforeEach(function() {\r\n function construct() {\r\n stock = new Stock(ctorParams);\r\n }\r\n\r\n expect(construct).to.not.throw();\r\n });\r\n\r\n describe(\"Stock Class\", function() {\r\n describe(\"constructor\", function() {\r\n it(\"should have default parameters\", function() {\r\n let defaultStock;\r\n function construct() {\r\n defaultStock = new Stock();\r\n }\r\n\r\n expect(construct).to.not.throw();\r\n expect(defaultStock.name).to.equal(\"\");\r\n });\r\n\r\n it(\"should properly initialize props from parameters\", function() {\r\n expect(stock.name).to.equal(ctorParams.name);\r\n expect(stock.symbol).to.equal(ctorParams.symbol);\r\n expect(stock.price).to.equal(ctorParams.initPrice);\r\n expect(stock.lastPrice).to.equal(ctorParams.initPrice);\r\n expect(stock.b).to.equal(ctorParams.b);\r\n expect(stock.mv).to.equal(ctorParams.mv);\r\n expect(stock.shareTxForMovement).to.equal(ctorParams.shareTxForMovement);\r\n expect(stock.shareTxUntilMovement).to.equal(ctorParams.shareTxForMovement);\r\n expect(stock.maxShares).to.be.below(stock.totalShares);\r\n expect(stock.spreadPerc).to.equal(ctorParams.spreadPerc);\r\n expect(stock.priceMovementPerc).to.be.a(\"number\");\r\n expect(stock.priceMovementPerc).to.be.at.most(stock.spreadPerc);\r\n expect(stock.priceMovementPerc).to.be.at.least(0);\r\n });\r\n\r\n it (\"should properly initialize props from range-values\", function() {\r\n let stock;\r\n const params = {\r\n b: true,\r\n initPrice: {\r\n max: 10e3,\r\n min: 1e3,\r\n },\r\n marketCap: 5e9,\r\n mv: {\r\n divisor: 100,\r\n max: 150,\r\n min: 50,\r\n },\r\n name: \"MockStock\",\r\n otlkMag: 10,\r\n spreadPerc: {\r\n divisor: 10,\r\n max: 10,\r\n min: 1,\r\n },\r\n shareTxForMovement: {\r\n max: 10e3,\r\n min: 5e3,\r\n },\r\n symbol: \"mock\",\r\n };\r\n\r\n function construct() {\r\n stock = new Stock(params);\r\n }\r\n\r\n expect(construct).to.not.throw();\r\n expect(stock.price).to.be.within(params.initPrice.min, params.initPrice.max);\r\n expect(stock.mv).to.be.within(params.mv.min / params.mv.divisor, params.mv.max / params.mv.divisor);\r\n expect(stock.spreadPerc).to.be.within(params.spreadPerc.min / params.spreadPerc.divisor, params.spreadPerc.max / params.spreadPerc.divisor);\r\n expect(stock.shareTxForMovement).to.be.within(params.shareTxForMovement.min, params.shareTxForMovement.max);\r\n });\r\n\r\n it(\"should round the 'totalShare' prop to the nearest 100k\", function() {\r\n expect(stock.totalShares % 100e3).to.equal(0);\r\n });\r\n });\r\n\r\n describe(\"#changePrice()\", function() {\r\n it(\"should set both the last price and current price properties\", function() {\r\n const newPrice = 20e3;\r\n stock.changePrice(newPrice);\r\n expect(stock.lastPrice).to.equal(ctorParams.initPrice);\r\n expect(stock.price).to.equal(newPrice);\r\n });\r\n });\r\n\r\n describe(\"#getAskPrice()\", function() {\r\n it(\"should return the price increased by spread percentage\", function() {\r\n const perc = stock.spreadPerc / 100;\r\n expect(perc).to.be.at.most(1);\r\n expect(perc).to.be.at.least(0);\r\n\r\n const expected = stock.price * (1 + perc);\r\n expect(stock.getAskPrice()).to.equal(expected);\r\n });\r\n });\r\n\r\n describe(\"#getBidPrice()\", function() {\r\n it(\"should return the price decreased by spread percentage\", function() {\r\n const perc = stock.spreadPerc / 100;\r\n expect(perc).to.be.at.most(1);\r\n expect(perc).to.be.at.least(0);\r\n\r\n const expected = stock.price * (1 - perc);\r\n expect(stock.getBidPrice()).to.equal(expected);\r\n });\r\n });\r\n });\r\n\r\n /*\r\n // TODO These tests fail due to circular dependency errors\r\n describe(\"StockMarket object\", function() {\r\n describe(\"Initialization\", function() {\r\n // Keeps track of initialized stocks. Contains their symbols\r\n const stocks = [];\r\n\r\n before(function() {\r\n expect(initStockMarket).to.not.throw();\r\n expect(initSymbolToStockMap).to.not.throw();\r\n });\r\n\r\n it(\"should have Stock objects\", function() {\r\n for (const prop in StockMarket) {\r\n const stock = StockMarket[prop];\r\n if (stock instanceof Stock) {\r\n stocks.push(stock.symbol);\r\n }\r\n }\r\n\r\n // We'll just check that there are some stocks\r\n expect(stocks.length).to.be.at.least(1);\r\n });\r\n\r\n it(\"should have an order book in the 'Orders' property\", function() {\r\n expect(StockMarket).to.have.property(\"Orders\");\r\n\r\n const orderbook = StockMarket[\"Orders\"];\r\n for (const symbol of stocks) {\r\n const ordersForStock = orderbook[symbol];\r\n expect(ordersForStock).to.be.an(\"array\");\r\n expect(ordersForStock.length).to.equal(0);\r\n }\r\n });\r\n\r\n it(\"should have properties for managing game cycles\", function() {\r\n expect(StockMarket).to.have.property(\"storedCycles\");\r\n expect(StockMarket).to.have.property(\"lastUpdate\");\r\n });\r\n });\r\n\r\n // Because 'StockMarket' is a global object, the effects of initialization from\r\n // the block above should still stand\r\n describe(\"Deletion\", function() {\r\n it(\"should set StockMarket to be an empty object\", function() {\r\n expect(StockMarket).to.be.an(\"object\").that.is.not.empty;\r\n deleteStockMarket();\r\n expect(StockMarket).to.be.an(\"object\").that.is.empty;\r\n });\r\n });\r\n\r\n // Reset stock market for each test\r\n beforeEach(function() {\r\n deleteStockMarket();\r\n initStockMarket();\r\n initSymbolToStockMap();\r\n });\r\n\r\n it(\"should properly initialize\", function() {\r\n\r\n });\r\n });\r\n */\r\n\r\n describe(\"Transaction Cost Calculator Functions\", function() {\r\n describe(\"getBuyTransactionCost()\", function() {\r\n it(\"should fail on invalid 'stock' argument\", function() {\r\n const res = getBuyTransactionCost({}, 10, PositionTypes.Long);\r\n expect(res).to.equal(null);\r\n });\r\n\r\n it(\"should fail on invalid 'shares' arg\", function() {\r\n let res = getBuyTransactionCost(stock, NaN, PositionTypes.Long);\r\n expect(res).to.equal(null);\r\n\r\n res = getBuyTransactionCost(stock, -1, PositionTypes.Long);\r\n expect(res).to.equal(null);\r\n });\r\n\r\n it(\"should properly evaluate LONG transactions that doesn't trigger a price movement\", function() {\r\n const shares = ctorParams.shareTxForMovement / 2;\r\n const res = getBuyTransactionCost(stock, shares, PositionTypes.Long);\r\n expect(res).to.equal(shares * stock.getAskPrice() + commission);\r\n });\r\n\r\n it(\"should properly evaluate SHORT transactions that doesn't trigger a price movement\", function() {\r\n const shares = ctorParams.shareTxForMovement / 2;\r\n const res = getBuyTransactionCost(stock, shares, PositionTypes.Short);\r\n expect(res).to.equal(shares * stock.getBidPrice() + commission);\r\n });\r\n\r\n it(\"should properly evaluate LONG transactions that trigger price movements\", function() {\r\n const sharesPerMvmt = ctorParams.shareTxForMovement;\r\n const shares = sharesPerMvmt * 3;\r\n const res = getBuyTransactionCost(stock, shares, PositionTypes.Long);\r\n\r\n // Calculate expected cost\r\n const secondPrice = stock.getAskPrice() * calculateIncreasingPriceMovement(stock);\r\n const thirdPrice = secondPrice * calculateIncreasingPriceMovement(stock);\r\n let expected = (sharesPerMvmt * stock.getAskPrice()) + (sharesPerMvmt * secondPrice) + (sharesPerMvmt * thirdPrice);\r\n\r\n expect(res).to.equal(expected + commission);\r\n });\r\n\r\n it(\"should properly evaluate SHORT transactions that trigger price movements\", function() {\r\n const sharesPerMvmt = ctorParams.shareTxForMovement;\r\n const shares = sharesPerMvmt * 3;\r\n const res = getBuyTransactionCost(stock, shares, PositionTypes.Short);\r\n\r\n // Calculate expected cost\r\n const secondPrice = stock.getBidPrice() * calculateDecreasingPriceMovement(stock);\r\n const thirdPrice = secondPrice * calculateDecreasingPriceMovement(stock);\r\n let expected = (sharesPerMvmt * stock.getBidPrice()) + (sharesPerMvmt * secondPrice) + (sharesPerMvmt * thirdPrice);\r\n\r\n expect(res).to.equal(expected + commission);\r\n });\r\n\r\n it(\"should cap the 'shares' argument at the stock's maximum number of shares\", function() {\r\n const maxRes = getBuyTransactionCost(stock, stock.maxShares, PositionTypes.Long);\r\n const exceedRes = getBuyTransactionCost(stock, stock.maxShares * 10, PositionTypes.Long);\r\n expect(maxRes).to.equal(exceedRes);\r\n });\r\n });\r\n\r\n describe(\"getSellTransactionGain()\", function() {\r\n it(\"should fail on invalid 'stock' argument\", function() {\r\n const res = getSellTransactionGain({}, 10, PositionTypes.Long);\r\n expect(res).to.equal(null);\r\n });\r\n\r\n it(\"should fail on invalid 'shares' arg\", function() {\r\n let res = getSellTransactionGain(stock, NaN, PositionTypes.Long);\r\n expect(res).to.equal(null);\r\n\r\n res = getSellTransactionGain(stock, -1, PositionTypes.Long);\r\n expect(res).to.equal(null);\r\n });\r\n\r\n it(\"should properly evaluate LONG transactions that doesn't trigger a price movement\", function() {\r\n const shares = ctorParams.shareTxForMovement / 2;\r\n const res = getSellTransactionGain(stock, shares, PositionTypes.Long);\r\n const expected = shares * stock.getBidPrice() - commission;\r\n expect(res).to.equal(expected);\r\n });\r\n\r\n it(\"should properly evaluate SHORT transactions that doesn't trigger a price movement\", function() {\r\n // We need to set this property in order to calculate gains from short position\r\n stock.playerAvgShortPx = stock.price * 2;\r\n\r\n const shares = ctorParams.shareTxForMovement / 2;\r\n const res = getSellTransactionGain(stock, shares, PositionTypes.Short);\r\n const expected = (shares * stock.playerAvgShortPx) + (shares * (stock.playerAvgShortPx - stock.getAskPrice())) - commission;\r\n expect(res).to.equal(expected);\r\n });\r\n\r\n it(\"should properly evaluate LONG transactions that trigger price movements\", function() {\r\n const sharesPerMvmt = ctorParams.shareTxForMovement;\r\n const shares = sharesPerMvmt * 3;\r\n const res = getSellTransactionGain(stock, shares, PositionTypes.Long);\r\n\r\n // Calculated expected gain\r\n const mvmt = calculateDecreasingPriceMovement(stock);\r\n const secondPrice = stock.getBidPrice() * mvmt;\r\n const thirdPrice = secondPrice * mvmt;\r\n const expected = (sharesPerMvmt * stock.getBidPrice()) + (sharesPerMvmt * secondPrice) + (sharesPerMvmt * thirdPrice);\r\n\r\n expect(res).to.equal(expected - commission);\r\n });\r\n\r\n it(\"should properly evaluate SHORT transactions that trigger price movements\", function() {\r\n // We need to set this property in order to calculate gains from short position\r\n stock.playerAvgShortPx = stock.price * 2;\r\n\r\n const sharesPerMvmt = ctorParams.shareTxForMovement;\r\n const shares = sharesPerMvmt * 3;\r\n const res = getSellTransactionGain(stock, shares, PositionTypes.Short);\r\n\r\n // Calculate expected gain\r\n const mvmt = calculateIncreasingPriceMovement(stock);\r\n const secondPrice = stock.getAskPrice() * mvmt;\r\n const thirdPrice = secondPrice * mvmt;\r\n function getGainForPrice(thisPrice) {\r\n const origCost = sharesPerMvmt * stock.playerAvgShortPx;\r\n return origCost + ((stock.playerAvgShortPx - thisPrice) * sharesPerMvmt);\r\n }\r\n const expected = getGainForPrice(stock.getAskPrice()) + getGainForPrice(secondPrice) + getGainForPrice(thirdPrice);\r\n\r\n expect(res).to.equal(expected - commission);\r\n });\r\n\r\n it(\"should cap the 'shares' argument at the stock's maximum number of shares\", function() {\r\n const maxRes = getSellTransactionGain(stock, stock.maxShares, PositionTypes.Long);\r\n const exceedRes = getSellTransactionGain(stock, stock.maxShares * 10, PositionTypes.Long);\r\n expect(maxRes).to.equal(exceedRes);\r\n });\r\n });\r\n });\r\n\r\n describe(\"Price Movement Processor Functions\", function() {\r\n // N = 1 is the original price\r\n function getNthPriceIncreasing(origPrice, n) {\r\n let price = origPrice;\r\n for (let i = 1; i < n; ++i) {\r\n price *= calculateIncreasingPriceMovement(stock);\r\n }\r\n\r\n return price;\r\n }\r\n\r\n // N = 1 is the original price\r\n function getNthPriceDecreasing(origPrice, n) {\r\n let price = origPrice;\r\n for (let i = 1; i < n; ++i) {\r\n price *= calculateDecreasingPriceMovement(stock);\r\n }\r\n\r\n return price;\r\n }\r\n\r\n // N = 1 is the original forecast\r\n function getNthForecast(origForecast, n) {\r\n return origForecast - forecastChangePerPriceMovement * (n - 1);\r\n }\r\n\r\n describe(\"processBuyTransactionPriceMovement()\", function() {\r\n const noMvmtShares = Math.round(ctorParams.shareTxForMovement / 2.2);\r\n const mvmtShares = ctorParams.shareTxForMovement * 3 + noMvmtShares;\r\n\r\n it(\"should do nothing on invalid 'stock' argument\", function() {\r\n const oldPrice = stock.price;\r\n const oldTracker = stock.shareTxUntilMovement;\r\n\r\n processBuyTransactionPriceMovement({}, mvmtShares, PositionTypes.Long);\r\n expect(stock.price).to.equal(oldPrice);\r\n expect(stock.shareTxUntilMovement).to.equal(oldTracker);\r\n });\r\n\r\n it(\"should do nothing on invalid 'shares' arg\", function() {\r\n const oldPrice = stock.price;\r\n const oldTracker = stock.shareTxUntilMovement;\r\n\r\n processBuyTransactionPriceMovement(stock, NaN, PositionTypes.Long);\r\n expect(stock.price).to.equal(oldPrice);\r\n expect(stock.shareTxUntilMovement).to.equal(oldTracker);\r\n\r\n processBuyTransactionPriceMovement(stock, -1, PositionTypes.Long);\r\n expect(stock.price).to.equal(oldPrice);\r\n expect(stock.shareTxUntilMovement).to.equal(oldTracker);\r\n });\r\n\r\n it(\"should properly evaluate a LONG transaction that doesn't trigger a price movement\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processBuyTransactionPriceMovement(stock, noMvmtShares, PositionTypes.Long);\r\n expect(stock.price).to.equal(oldPrice);\r\n expect(stock.otlkMag).to.equal(oldForecast);\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares);\r\n });\r\n\r\n it(\"should properly evaluate a SHORT transaction that doesn't trigger a price movement\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processBuyTransactionPriceMovement(stock, noMvmtShares, PositionTypes.Short);\r\n expect(stock.price).to.equal(oldPrice);\r\n expect(stock.otlkMag).to.equal(oldForecast);\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares);\r\n });\r\n\r\n it(\"should properly evaluate LONG transactions that trigger price movements\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processBuyTransactionPriceMovement(stock, mvmtShares, PositionTypes.Long);\r\n expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 4));\r\n expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares);\r\n });\r\n\r\n it(\"should properly evaluate SHORT transactions that trigger price movements\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processBuyTransactionPriceMovement(stock, mvmtShares, PositionTypes.Short);\r\n expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 4));\r\n expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares);\r\n });\r\n\r\n it(\"should properly evaluate LONG transactions of exactly 'shareTxForMovement' shares\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processBuyTransactionPriceMovement(stock, stock.shareTxForMovement, PositionTypes.Long);\r\n expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 2));\r\n expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);\r\n });\r\n\r\n it(\"should properly evaluate LONG transactions that total to 'shareTxForMovement' shares\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processBuyTransactionPriceMovement(stock, Math.round(stock.shareTxForMovement / 2), PositionTypes.Long);\r\n processBuyTransactionPriceMovement(stock, stock.shareTxUntilMovement, PositionTypes.Long);\r\n expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 2));\r\n expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);\r\n });\r\n\r\n it(\"should properly evaluate LONG transactions that are a multiple of 'shareTxForMovement' shares\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processBuyTransactionPriceMovement(stock, 3 * stock.shareTxForMovement, PositionTypes.Long);\r\n expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 4));\r\n expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);\r\n });\r\n\r\n it(\"should properly evaluate SHORT transactions of exactly 'shareTxForMovement' shares\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processBuyTransactionPriceMovement(stock, stock.shareTxForMovement, PositionTypes.Short);\r\n expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 2));\r\n expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);\r\n });\r\n\r\n it(\"should properly evaluate SHORT transactions that total to 'shareTxForMovement' shares\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processBuyTransactionPriceMovement(stock, Math.round(stock.shareTxForMovement / 2), PositionTypes.Short);\r\n processBuyTransactionPriceMovement(stock, stock.shareTxUntilMovement, PositionTypes.Short);\r\n expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 2));\r\n expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);\r\n });\r\n\r\n it(\"should properly evaluate SHORT transactions that are a multiple of 'shareTxForMovement' shares\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processBuyTransactionPriceMovement(stock, 3 * stock.shareTxForMovement, PositionTypes.Short);\r\n expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 4));\r\n expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);\r\n });\r\n });\r\n\r\n describe(\"processSellTransactionPriceMovement()\", function() {\r\n const noMvmtShares = Math.round(ctorParams.shareTxForMovement / 2.2);\r\n const mvmtShares = ctorParams.shareTxForMovement * 3 + noMvmtShares;\r\n\r\n it(\"should do nothing on invalid 'stock' argument\", function() {\r\n const oldPrice = stock.price;\r\n const oldTracker = stock.shareTxUntilMovement;\r\n\r\n processSellTransactionPriceMovement({}, mvmtShares, PositionTypes.Long);\r\n expect(stock.price).to.equal(oldPrice);\r\n expect(stock.shareTxUntilMovement).to.equal(oldTracker);\r\n });\r\n\r\n it(\"should do nothing on invalid 'shares' arg\", function() {\r\n const oldPrice = stock.price;\r\n const oldTracker = stock.shareTxUntilMovement;\r\n\r\n processSellTransactionPriceMovement(stock, NaN, PositionTypes.Long);\r\n expect(stock.price).to.equal(oldPrice);\r\n expect(stock.shareTxUntilMovement).to.equal(oldTracker);\r\n\r\n processSellTransactionPriceMovement(stock, -1, PositionTypes.Long);\r\n expect(stock.price).to.equal(oldPrice);\r\n expect(stock.shareTxUntilMovement).to.equal(oldTracker);\r\n });\r\n\r\n it(\"should properly evaluate a LONG transaction that doesn't trigger a price movement\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processSellTransactionPriceMovement(stock, noMvmtShares, PositionTypes.Long);\r\n expect(stock.price).to.equal(oldPrice);\r\n expect(stock.otlkMag).to.equal(oldForecast);\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares);\r\n });\r\n\r\n it(\"should properly evaluate a SHORT transaction that doesn't trigger a price movement\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processSellTransactionPriceMovement(stock, noMvmtShares, PositionTypes.Short);\r\n expect(stock.price).to.equal(oldPrice);\r\n expect(stock.otlkMag).to.equal(oldForecast);\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares);\r\n });\r\n\r\n it(\"should properly evaluate LONG transactions that trigger price movements\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processSellTransactionPriceMovement(stock, mvmtShares, PositionTypes.Long);\r\n expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 4));\r\n expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares);\r\n });\r\n\r\n it(\"should properly evaluate SHORT transactions that trigger price movements\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processSellTransactionPriceMovement(stock, mvmtShares, PositionTypes.Short);\r\n expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 4));\r\n expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares);\r\n });\r\n\r\n it(\"should properly evaluate LONG transactions of exactly 'shareTxForMovement' shares\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processSellTransactionPriceMovement(stock, stock.shareTxForMovement, PositionTypes.Long);\r\n expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 2));\r\n expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);\r\n });\r\n\r\n it(\"should properly evaluate LONG transactions that total to 'shareTxForMovement' shares\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processSellTransactionPriceMovement(stock, Math.round(stock.shareTxForMovement / 2), PositionTypes.Long);\r\n processSellTransactionPriceMovement(stock, stock.shareTxUntilMovement, PositionTypes.Long);\r\n expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 2));\r\n expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);\r\n });\r\n\r\n it(\"should properly evaluate LONG transactions that are a multiple of 'shareTxForMovement' shares\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processSellTransactionPriceMovement(stock, 3 * stock.shareTxForMovement, PositionTypes.Long);\r\n expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 4));\r\n expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);\r\n });\r\n\r\n it(\"should properly evaluate SHORT transactions of exactly 'shareTxForMovement' shares\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processSellTransactionPriceMovement(stock, stock.shareTxForMovement, PositionTypes.Short);\r\n expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 2));\r\n expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);\r\n });\r\n\r\n it(\"should properly evaluate SHORT transactions that total to 'shareTxForMovement' shares\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processSellTransactionPriceMovement(stock, Math.round(stock.shareTxForMovement / 2), PositionTypes.Short);\r\n processSellTransactionPriceMovement(stock, stock.shareTxUntilMovement, PositionTypes.Short);\r\n expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 2));\r\n expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);\r\n });\r\n\r\n it(\"should properly evaluate SHORT transactions that are a multiple of 'shareTxForMovement' shares\", function() {\r\n const oldPrice = stock.price;\r\n const oldForecast = stock.otlkMag;\r\n\r\n processSellTransactionPriceMovement(stock, 3 * stock.shareTxForMovement, PositionTypes.Short);\r\n expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 4));\r\n expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));\r\n expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);\r\n });\r\n });\r\n });\r\n\r\n describe(\"Order Class\", function() {\r\n it(\"should throw on invalid arguments\", function() {\r\n function invalid1() {\r\n return new Order({}, 1, 1, OrderTypes.LimitBuy, PositionTypes.Long);\r\n }\r\n function invalid2() {\r\n return new Order(\"FOO\", \"z\", 0, OrderTypes.LimitBuy, PositionTypes.Short);\r\n }\r\n function invalid3() {\r\n return new Order(\"FOO\", 1, {}, OrderTypes.LimitBuy, PositionTypes.Short);\r\n }\r\n function invalid4() {\r\n return new Order(\"FOO\", 1, NaN, OrderTypes.LimitBuy, PositionTypes.Short);\r\n }\r\n function invalid5() {\r\n return new Order(\"FOO\", NaN, 0, OrderTypes.LimitBuy, PositionTypes.Short);\r\n }\r\n\r\n expect(invalid1).to.throw();\r\n expect(invalid2).to.throw();\r\n expect(invalid3).to.throw();\r\n expect(invalid4).to.throw();\r\n expect(invalid5).to.throw();\r\n });\r\n });\r\n\r\n describe(\"Order Processing\", function() {\r\n\r\n });\r\n});\r\n","/**\r\n * TODO This should also test the calcualteRamUsage() function from\r\n * /Script/RamCalculations but there's some issues with getting tests to run\r\n * when any npm package is included in the build (/Script/RamCalculations includes\r\n * walk from acorn).\r\n */\r\nimport { getRamCost } from \"../../src/Netscript/RamCostGenerator\";\r\n//import { calculateRamUsage } from \"../../src/Script/RamCalculations\"\r\n\r\nconst assert = chai.assert;\r\nconst expect = chai.expect;\r\n\r\nconsole.log(\"Beginning Netscript Static RAM Calculation/Generation Tests\");\r\n\r\ndescribe(\"Netscript Static RAM Calculation/Generation Tests\", function() {\r\n it(\"should run\", function() {\r\n expect(1).to.equal(1);\r\n });\r\n});\r\n","export enum PositionTypes {\r\n Long = \"L\",\r\n Short = \"S\"\r\n}\r\n","import { Stock } from \"./Stock\";\r\nimport { PositionTypes } from \"./data/PositionTypes\";\r\nimport { CONSTANTS } from \"../Constants\";\r\n\r\n// Amount by which a stock's forecast changes during each price movement\r\nexport const forecastChangePerPriceMovement = 0.1;\r\n\r\n/**\r\n * Given a stock, calculates the amount by which the stock price is multiplied\r\n * for an 'upward' price movement. This does not actually increase the stock's price,\r\n * just calculates the multiplier\r\n * @param {Stock} stock - Stock for price movement\r\n * @returns {number | null} Number by which stock's price should be multiplied. Null for invalid args\r\n */\r\nexport function calculateIncreasingPriceMovement(stock: Stock): number | null {\r\n if (!(stock instanceof Stock)) { return null; }\r\n\r\n return (1 + (stock.priceMovementPerc / 100));\r\n}\r\n\r\n/**\r\n * Given a stock, calculates the amount by which the stock price is multiplied\r\n * for a \"downward\" price movement. This does not actually increase the stock's price,\r\n * just calculates the multiplier\r\n * @param {Stock} stock - Stock for price movement\r\n * @returns {number | null} Number by which stock's price should be multiplied. Null for invalid args\r\n */\r\nexport function calculateDecreasingPriceMovement(stock: Stock): number | null {\r\n if (!(stock instanceof Stock)) { return null; }\r\n\r\n return (1 - (stock.priceMovementPerc / 100));\r\n}\r\n\r\n\r\n/**\r\n * Calculate the total cost of a \"buy\" transaction. This accounts for spread,\r\n * price movements, and commission.\r\n * @param {Stock} stock - Stock being purchased\r\n * @param {number} shares - Number of shares being transacted\r\n * @param {PositionTypes} posType - Long or short position\r\n * @returns {number | null} Total transaction cost. Returns null for an invalid transaction\r\n */\r\nexport function getBuyTransactionCost(stock: Stock, shares: number, posType: PositionTypes): number | null {\r\n if (isNaN(shares) || shares <= 0 || !(stock instanceof Stock)) { return null; }\r\n\r\n // Cap the 'shares' arg at the stock's maximum shares. This'll prevent\r\n // hanging in the case when a really big number is passed in\r\n shares = Math.min(shares, stock.maxShares);\r\n\r\n const isLong = (posType === PositionTypes.Long);\r\n\r\n // If the number of shares doesn't trigger a price movement, its a simple calculation\r\n if (shares <= stock.shareTxUntilMovement) {\r\n if (isLong) {\r\n return (shares * stock.getAskPrice()) + CONSTANTS.StockMarketCommission;\r\n } else {\r\n return (shares * stock.getBidPrice()) + CONSTANTS.StockMarketCommission;\r\n }\r\n }\r\n\r\n // Calculate how many iterations of price changes we need to account for\r\n let remainingShares = shares - stock.shareTxUntilMovement;\r\n let numIterations = 1 + Math.ceil(remainingShares / stock.shareTxForMovement);\r\n\r\n // The initial cost calculation takes care of the first \"iteration\"\r\n let currPrice = isLong ? stock.getAskPrice() : stock.getBidPrice();\r\n let totalCost = (stock.shareTxUntilMovement * currPrice);\r\n\r\n const increasingMvmt = calculateIncreasingPriceMovement(stock)!;\r\n const decreasingMvmt = calculateDecreasingPriceMovement(stock)!;\r\n\r\n function processPriceMovement() {\r\n if (isLong) {\r\n currPrice *= increasingMvmt;\r\n } else {\r\n currPrice *= decreasingMvmt;\r\n }\r\n }\r\n\r\n for (let i = 1; i < numIterations; ++i) {\r\n processPriceMovement();\r\n\r\n const amt = Math.min(stock.shareTxForMovement, remainingShares);\r\n totalCost += (amt * currPrice);\r\n remainingShares -= amt;\r\n }\r\n\r\n return totalCost + CONSTANTS.StockMarketCommission;\r\n}\r\n\r\n/**\r\n * Processes a buy transaction's resulting price AND forecast movement.\r\n * @param {Stock} stock - Stock being purchased\r\n * @param {number} shares - Number of shares being transacted\r\n * @param {PositionTypes} posType - Long or short position\r\n */\r\nexport function processBuyTransactionPriceMovement(stock: Stock, shares: number, posType: PositionTypes): void {\r\n if (isNaN(shares) || shares <= 0 || !(stock instanceof Stock)) { return; }\r\n\r\n // Cap the 'shares' arg at the stock's maximum shares. This'll prevent\r\n // hanging in the case when a really big number is passed in\r\n shares = Math.min(shares, stock.maxShares);\r\n\r\n const isLong = (posType === PositionTypes.Long);\r\n\r\n let currPrice = stock.price;\r\n function processPriceMovement() {\r\n if (isLong) {\r\n currPrice *= calculateIncreasingPriceMovement(stock)!;\r\n } else {\r\n currPrice *= calculateDecreasingPriceMovement(stock)!;\r\n }\r\n }\r\n\r\n // No price/forecast movement\r\n if (shares <= stock.shareTxUntilMovement) {\r\n stock.shareTxUntilMovement -= shares;\r\n if (stock.shareTxUntilMovement <= 0) {\r\n stock.shareTxUntilMovement = stock.shareTxForMovement;\r\n processPriceMovement();\r\n stock.changePrice(currPrice);\r\n stock.otlkMag -= (forecastChangePerPriceMovement);\r\n }\r\n\r\n return;\r\n }\r\n\r\n // Calculate how many iterations of price changes we need to account for\r\n let remainingShares = shares - stock.shareTxUntilMovement;\r\n let numIterations = 1 + Math.ceil(remainingShares / stock.shareTxForMovement);\r\n\r\n for (let i = 1; i < numIterations; ++i) {\r\n processPriceMovement();\r\n }\r\n\r\n stock.shareTxUntilMovement = stock.shareTxForMovement - ((shares - stock.shareTxUntilMovement) % stock.shareTxForMovement);\r\n if (stock.shareTxUntilMovement === stock.shareTxForMovement || stock.shareTxUntilMovement <= 0) {\r\n // The shareTxUntilMovement ended up at 0 at the end of the \"processing\"\r\n ++numIterations;\r\n stock.shareTxUntilMovement = stock.shareTxForMovement;\r\n processPriceMovement();\r\n }\r\n stock.changePrice(currPrice);\r\n\r\n // Forecast always decreases in magnitude\r\n const forecastChange = Math.min(5, forecastChangePerPriceMovement * (numIterations - 1));\r\n stock.otlkMag -= forecastChange;\r\n if (stock.otlkMag < 0) {\r\n stock.b = !stock.b;\r\n stock.otlkMag = Math.abs(stock.otlkMag);\r\n }\r\n}\r\n\r\n/**\r\n * Calculate the TOTAL amount of money gained from a sale (NOT net profit). This accounts\r\n * for spread, price movements, and commission.\r\n * @param {Stock} stock - Stock being sold\r\n * @param {number} shares - Number of sharse being transacted\r\n * @param {PositionTypes} posType - Long or short position\r\n * @returns {number | null} Amount of money gained from transaction. Returns null for an invalid transaction\r\n */\r\nexport function getSellTransactionGain(stock: Stock, shares: number, posType: PositionTypes): number | null {\r\n if (isNaN(shares) || shares <= 0 || !(stock instanceof Stock)) { return null; }\r\n\r\n // Cap the 'shares' arg at the stock's maximum shares. This'll prevent\r\n // hanging in the case when a really big number is passed in\r\n shares = Math.min(shares, stock.maxShares);\r\n\r\n const isLong = (posType === PositionTypes.Long);\r\n\r\n // If the number of shares doesn't trigger a price mvoement, its a simple calculation\r\n if (shares <= stock.shareTxUntilMovement) {\r\n if (isLong) {\r\n return (shares * stock.getBidPrice()) - CONSTANTS.StockMarketCommission;\r\n } else {\r\n // Calculating gains for a short position requires calculating the profit made\r\n const origCost = shares * stock.playerAvgShortPx;\r\n const profit = ((stock.playerAvgShortPx - stock.getAskPrice()) * shares) - CONSTANTS.StockMarketCommission;\r\n\r\n return origCost + profit;\r\n }\r\n }\r\n\r\n // Calculate how many iterations of price changes we need to account for\r\n let remainingShares = shares - stock.shareTxUntilMovement;\r\n let numIterations = 1 + Math.ceil(remainingShares / stock.shareTxForMovement);\r\n\r\n // Helper function to calculate gain for a single iteration\r\n function calculateGain(thisPrice: number, thisShares: number) {\r\n if (isLong) {\r\n return thisShares * thisPrice;\r\n } else {\r\n const origCost = thisShares * stock.playerAvgShortPx;\r\n const profit = ((stock.playerAvgShortPx - thisPrice) * thisShares);\r\n\r\n return origCost + profit;\r\n }\r\n }\r\n\r\n // The initial cost calculation takes care of the first \"iteration\"\r\n let currPrice = isLong ? stock.getBidPrice() : stock.getAskPrice();\r\n let totalGain = calculateGain(currPrice, stock.shareTxUntilMovement);\r\n for (let i = 1; i < numIterations; ++i) {\r\n // Price movement\r\n if (isLong) {\r\n currPrice *= calculateDecreasingPriceMovement(stock)!;\r\n } else {\r\n currPrice *= calculateIncreasingPriceMovement(stock)!;\r\n }\r\n\r\n const amt = Math.min(stock.shareTxForMovement, remainingShares);\r\n totalGain += calculateGain(currPrice, amt);\r\n remainingShares -= amt;\r\n }\r\n\r\n return totalGain - CONSTANTS.StockMarketCommission;\r\n}\r\n\r\n/**\r\n * Processes a sell transaction's resulting price movement\r\n * @param {Stock} stock - Stock being sold\r\n * @param {number} shares - Number of sharse being transacted\r\n * @param {PositionTypes} posType - Long or short position\r\n */\r\nexport function processSellTransactionPriceMovement(stock: Stock, shares: number, posType: PositionTypes): void {\r\n if (isNaN(shares) || shares <= 0 || !(stock instanceof Stock)) { return; }\r\n\r\n // Cap the 'shares' arg at the stock's maximum shares. This'll prevent\r\n // hanging in the case when a really big number is passed in\r\n shares = Math.min(shares, stock.maxShares);\r\n\r\n const isLong = (posType === PositionTypes.Long);\r\n\r\n let currPrice = stock.price;\r\n function processPriceMovement() {\r\n if (isLong) {\r\n currPrice *= calculateDecreasingPriceMovement(stock)!;\r\n } else {\r\n currPrice *= calculateIncreasingPriceMovement(stock)!;\r\n }\r\n }\r\n\r\n // No price/forecast movement\r\n if (shares <= stock.shareTxUntilMovement) {\r\n stock.shareTxUntilMovement -= shares;\r\n if (stock.shareTxUntilMovement <= 0) {\r\n stock.shareTxUntilMovement = stock.shareTxForMovement;\r\n processPriceMovement();\r\n stock.changePrice(currPrice);\r\n stock.otlkMag -= (forecastChangePerPriceMovement);\r\n }\r\n\r\n return;\r\n }\r\n\r\n // Calculate how many iterations of price changes we need to account for\r\n let remainingShares = shares - stock.shareTxUntilMovement;\r\n let numIterations = 1 + Math.ceil(remainingShares / stock.shareTxForMovement);\r\n\r\n for (let i = 1; i < numIterations; ++i) {\r\n processPriceMovement();\r\n }\r\n\r\n stock.shareTxUntilMovement = stock.shareTxForMovement - ((shares - stock.shareTxUntilMovement) % stock.shareTxForMovement);\r\n if (stock.shareTxUntilMovement === stock.shareTxForMovement || stock.shareTxUntilMovement <= 0) {\r\n ++numIterations;\r\n stock.shareTxUntilMovement = stock.shareTxForMovement;\r\n processPriceMovement();\r\n }\r\n stock.changePrice(currPrice);\r\n\r\n // Forecast always decreases in magnitude\r\n const forecastChange = Math.min(5, forecastChangePerPriceMovement * (numIterations - 1));\r\n stock.otlkMag -= forecastChange;\r\n if (stock.otlkMag < 0) {\r\n stock.b = !stock.b;\r\n stock.otlkMag = Math.abs(stock.otlkMag);\r\n }\r\n}\r\n\r\n/**\r\n * Calculate the maximum number of shares of a stock that can be purchased.\r\n * Handles mid-transaction price movements, both L and S positions, etc.\r\n * Used for the \"Buy Max\" button in the UI\r\n * @param {Stock} stock - Stock being purchased\r\n * @param {PositionTypes} posType - Long or short position\r\n * @param {number} money - Amount of money player has\r\n * @returns maximum number of shares that the player can purchase\r\n */\r\nexport function calculateBuyMaxAmount(stock: Stock, posType: PositionTypes, money: number): number {\r\n if (!(stock instanceof Stock)) { return 0; }\r\n\r\n const isLong = (posType === PositionTypes.Long);\r\n\r\n const increasingMvmt = calculateIncreasingPriceMovement(stock);\r\n const decreasingMvmt = calculateDecreasingPriceMovement(stock);\r\n if (increasingMvmt == null || decreasingMvmt == null) { return 0; }\r\n\r\n let remainingMoney = money - CONSTANTS.StockMarketCommission;\r\n let currPrice = isLong ? stock.getAskPrice() : stock.getBidPrice();\r\n\r\n // No price movement\r\n const firstIterationCost = stock.shareTxUntilMovement * currPrice;\r\n if (remainingMoney < firstIterationCost) {\r\n return Math.floor(remainingMoney / currPrice);\r\n }\r\n\r\n // We'll avoid any accidental infinite loops by having a hardcoded maximum number of\r\n // iterations\r\n let numShares = stock.shareTxUntilMovement;\r\n remainingMoney -= firstIterationCost;\r\n for (let i = 0; i < 10e3; ++i) {\r\n if (isLong) {\r\n currPrice *= increasingMvmt;\r\n } else {\r\n currPrice *= decreasingMvmt;\r\n }\r\n\r\n const affordableShares = Math.floor(remainingMoney / currPrice);\r\n const actualShares = Math.min(stock.shareTxForMovement, affordableShares);\r\n\r\n // Can't afford any more, so we're done\r\n if (actualShares <= 0) { break; }\r\n\r\n numShares += actualShares;\r\n\r\n let cost = actualShares * currPrice;\r\n remainingMoney -= cost;\r\n\r\n if (remainingMoney <= 0) { break; }\r\n }\r\n\r\n return Math.floor(numShares);\r\n}\r\n","export * from \"./Netscript/StaticRamCalculationTests\";\r\nexport * from \"./StockMarketTests\";\r\n","export enum OrderTypes {\r\n LimitBuy = \"Limit Buy Order\",\r\n LimitSell = \"Limit Sell Order\",\r\n StopBuy = \"Stop Buy Order\",\r\n StopSell = \"Stop Sell Order\"\r\n}\r\n","import { IMinMaxRange } from \"../types\";\r\nimport {\r\n Generic_fromJSON,\r\n Generic_toJSON,\r\n Reviver\r\n} from \"../../utils/JSONReviver\";\r\nimport { getRandomInt } from \"../../utils/helpers/getRandomInt\";\r\n\r\nexport interface IConstructorParams {\r\n b: boolean;\r\n initPrice: number | IMinMaxRange;\r\n marketCap: number;\r\n mv: number | IMinMaxRange;\r\n name: string;\r\n otlkMag: number;\r\n spreadPerc: number | IMinMaxRange;\r\n shareTxForMovement: number | IMinMaxRange;\r\n symbol: string;\r\n}\r\n\r\nconst defaultConstructorParams: IConstructorParams = {\r\n b: true,\r\n initPrice: 10e3,\r\n marketCap: 1e12,\r\n mv: 1,\r\n name: \"\",\r\n otlkMag: 0,\r\n spreadPerc: 0,\r\n shareTxForMovement: 1e6,\r\n symbol: \"\",\r\n}\r\n\r\n// Helper function that convert a IMinMaxRange to a number\r\nfunction toNumber(n: number | IMinMaxRange): number {\r\n let value: number;\r\n switch (typeof n) {\r\n case \"number\": {\r\n return n;\r\n }\r\n case \"object\": {\r\n const range = n;\r\n value = getRandomInt(range.min, range.max);\r\n break;\r\n }\r\n default:\r\n throw Error(`Do not know how to convert the type '${typeof n}' to a number`);\r\n }\r\n\r\n if (typeof n === \"object\" && typeof n.divisor === \"number\") {\r\n return value / n.divisor;\r\n }\r\n\r\n return value;\r\n}\r\n\r\n/**\r\n * Represents the valuation of a company in the World Stock Exchange.\r\n */\r\nexport class Stock {\r\n /**\r\n * Initializes a Stock from a JSON save state\r\n */\r\n static fromJSON(value: any): Stock {\r\n return Generic_fromJSON(Stock, value.data);\r\n }\r\n\r\n /**\r\n * Bear or bull (more likely to go up or down, based on otlkMag)\r\n */\r\n b: boolean;\r\n\r\n /**\r\n * Maximum price of a stock (per share)\r\n */\r\n readonly cap: number;\r\n\r\n /**\r\n * Stocks previous share price\r\n */\r\n lastPrice: number;\r\n\r\n /**\r\n * Maximum number of shares that player can own (both long and short combined)\r\n */\r\n readonly maxShares: number;\r\n\r\n /**\r\n * Maximum volatility\r\n */\r\n readonly mv: number;\r\n\r\n /**\r\n * Name of the company that the stock is for\r\n */\r\n readonly name: string;\r\n\r\n /**\r\n * Outlook magnitude. Represents the stock's forecast and likelihood\r\n * of increasing/decreasing (based on whether its in bear or bull mode)\r\n */\r\n otlkMag: number;\r\n\r\n /**\r\n * Average price of stocks that the player owns in the LONG position\r\n */\r\n playerAvgPx: number;\r\n\r\n /**\r\n * Average price of stocks that the player owns in the SHORT position\r\n */\r\n playerAvgShortPx: number;\r\n\r\n /**\r\n * Number of shares the player owns in the LONG position\r\n */\r\n playerShares: number;\r\n\r\n /**\r\n * Number of shares the player owns in the SHORT position\r\n */\r\n playerShortShares: number;\r\n\r\n /**\r\n * Stock's share price\r\n */\r\n price: number;\r\n\r\n /**\r\n * Percentage by which the stock's price changes for a transaction-induced\r\n * price movement.\r\n */\r\n readonly priceMovementPerc: number;\r\n\r\n /**\r\n * How many shares need to be transacted in order to trigger a price movement\r\n */\r\n readonly shareTxForMovement: number;\r\n\r\n /**\r\n * How many share transactions remaining until a price movement occurs\r\n */\r\n shareTxUntilMovement: number;\r\n\r\n /**\r\n * Spread percentage. The bid/ask prices for this stock are N% above or below\r\n * the \"real price\" to emulate spread.\r\n */\r\n readonly spreadPerc: number;\r\n\r\n /**\r\n * The stock's ticker symbol\r\n */\r\n readonly symbol: string;\r\n\r\n /**\r\n * Total number of shares of this stock\r\n * This is different than maxShares, as this is like authorized stock while\r\n * maxShares is outstanding stock.\r\n */\r\n readonly totalShares: number;\r\n\r\n constructor(p: IConstructorParams = defaultConstructorParams) {\r\n this.name = p.name;\r\n this.symbol = p.symbol;\r\n this.price = toNumber(p.initPrice);\r\n this.lastPrice = this.price;\r\n this.playerShares = 0;\r\n this.playerAvgPx = 0;\r\n this.playerShortShares = 0;\r\n this.playerAvgShortPx = 0;\r\n this.mv = toNumber(p.mv);\r\n this.b = p.b;\r\n this.otlkMag = p.otlkMag;\r\n this.cap = getRandomInt(this.price * 1e3, this.price * 25e3);\r\n this.spreadPerc = toNumber(p.spreadPerc);\r\n this.priceMovementPerc = this.spreadPerc / (getRandomInt(10, 30) / 10);\r\n this.shareTxForMovement = toNumber(p.shareTxForMovement);\r\n this.shareTxUntilMovement = this.shareTxForMovement;\r\n\r\n // Total shares is determined by market cap, and is rounded to nearest 100k\r\n let totalSharesUnrounded: number = (p.marketCap / this.price);\r\n this.totalShares = Math.round(totalSharesUnrounded / 1e5) * 1e5;\r\n\r\n // Max Shares (Outstanding shares) is a percentage of total shares\r\n const outstandingSharePercentage: number = 0.2;\r\n this.maxShares = Math.round((this.totalShares * outstandingSharePercentage) / 1e5) * 1e5;\r\n }\r\n\r\n changePrice(newPrice: number): void {\r\n this.lastPrice = this.price;\r\n this.price = newPrice;\r\n }\r\n\r\n /**\r\n * Return the price at which YOUR stock is bought (market ask price). Accounts for spread\r\n */\r\n getAskPrice(): number {\r\n return this.price * (1 + (this.spreadPerc / 100));\r\n }\r\n\r\n /**\r\n * Return the price at which YOUR stock is sold (market bid price). Accounts for spread\r\n */\r\n getBidPrice(): number {\r\n return this.price * (1 - (this.spreadPerc / 100));\r\n }\r\n\r\n /**\r\n * Serialize the Stock to a JSON save state.\r\n */\r\n toJSON(): any {\r\n return Generic_toJSON(\"Stock\", this);\r\n }\r\n}\r\n\r\nReviver.constructors.Stock = Stock;\r\n","/**\r\n * Generic Game Constants\r\n *\r\n * Constants for specific mechanics or features will NOT be here.\r\n */\r\nimport { IMap } from \"./types\";\r\n\r\nexport let CONSTANTS: IMap = {\r\n Version: \"0.46.3\",\r\n\r\n\t/** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience\r\n * and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then\r\n * the player will have this level assuming no multipliers. Multipliers can cause skills to go above this.\r\n */\r\n\tMaxSkillLevel: \t\t\t975,\r\n\r\n // Milliseconds per game cycle\r\n MilliPerCycle: 200,\r\n\r\n // How much reputation is needed to join a megacorporation's faction\r\n CorpFactionRepRequirement: 200e3,\r\n\r\n // Base RAM costs\r\n BaseCostFor1GBOfRamHome: 32000,\r\n BaseCostFor1GBOfRamServer: 55000, //1 GB of RAM\r\n\r\n // Cost to travel to another city\r\n TravelCost: 200e3,\r\n\r\n // Faction and Company favor-related things\r\n BaseFavorToDonate: 150,\r\n DonateMoneyToRepDivisor: 1e6,\r\n FactionReputationToFavorBase: 500,\r\n FactionReputationToFavorMult: 1.02,\r\n CompanyReputationToFavorBase: 500,\r\n CompanyReputationToFavorMult: 1.02,\r\n\r\n // NeuroFlux Governor Augmentation cost multiplier\r\n NeuroFluxGovernorLevelMult: 1.14,\r\n\r\n NumNetscriptPorts: 20,\r\n\r\n // Server-related constants\r\n HomeComputerMaxRam: 1073741824, // 2 ^ 30\r\n ServerBaseGrowthRate: 1.03, // Unadjusted Growth rate\r\n ServerMaxGrowthRate: 1.0035, // Maximum possible growth rate (max rate accounting for server security)\r\n ServerFortifyAmount: 0.002, // Amount by which server's security increases when its hacked/grown\r\n ServerWeakenAmount: 0.05, // Amount by which server's security decreases when weakened\r\n\r\n PurchasedServerLimit: 25,\r\n PurchasedServerMaxRam: 1048576, // 2^20\r\n\r\n // Augmentation Constants\r\n AugmentationCostMultiplier: 5, // Used for balancing costs without having to readjust every Augmentation cost\r\n AugmentationRepMultiplier: 2.5, // Used for balancing rep cost without having to readjust every value\r\n MultipleAugMultiplier: 1.9,\r\n\r\n // TOR Router\r\n TorRouterCost: 200e3,\r\n\r\n // Infiltration\r\n InfiltrationBribeBaseAmount: 100e3, //Amount per clearance level\r\n InfiltrationMoneyValue: 5e3, //Convert \"secret\" value to money\r\n InfiltrationRepValue: 1.4, //Convert \"secret\" value to faction reputation\r\n InfiltrationExpPow: 0.8,\r\n\r\n // Stock market\r\n WSEAccountCost: 200e6,\r\n TIXAPICost: 5e9,\r\n MarketData4SCost: 1e9,\r\n MarketDataTixApi4SCost: 25e9,\r\n StockMarketCommission: 100e3,\r\n\r\n // Hospital/Health\r\n HospitalCostPerHp: 100e3,\r\n\r\n // Intelligence-related constants\r\n IntelligenceCrimeWeight: 0.05, // Weight for how much int affects crime success rates\r\n IntelligenceInfiltrationWeight: 0.1, // Weight for how much int affects infiltration success rates\r\n IntelligenceCrimeBaseExpGain: 0.001,\r\n IntelligenceProgramBaseExpGain: 500, // Program required hack level divided by this to determine int exp gain\r\n IntelligenceTerminalHackBaseExpGain: 200, // Hacking exp divided by this to determine int exp gain\r\n IntelligenceSingFnBaseExpGain: 0.002,\r\n IntelligenceClassBaseExpGain: 0.000001,\r\n IntelligenceHackingMissionBaseExpGain: 0.03, // Hacking Mission difficulty multiplied by this to get exp gain\r\n\r\n // Hacking Missions\r\n // TODO Move this into Hacking Mission implementation\r\n HackingMissionRepToDiffConversion: 10000, // Faction rep is divided by this to get mission difficulty\r\n HackingMissionRepToRewardConversion: 7, // Faction rep divided byt his to get mission rep reward\r\n HackingMissionSpamTimeIncrease: 25000, // How much time limit increase is gained when conquering a Spam Node (ms)\r\n HackingMissionTransferAttackIncrease: 1.05, // Multiplier by which the attack for all Core Nodes is increased when conquering a Transfer Node\r\n HackingMissionMiscDefenseIncrease: 1.05, // The amount by which every misc node's defense is multiplied when one is conquered\r\n HackingMissionDifficultyToHacking: 135, // Difficulty is multiplied by this to determine enemy's \"hacking\" level (to determine effects of scan/attack, etc)\r\n HackingMissionHowToPlay: \"Hacking missions are a minigame that, if won, will reward you with faction reputation.

\" +\r\n \"In this game you control a set of Nodes and use them to try and defeat an enemy. Your Nodes \" +\r\n \"are colored blue, while the enemy's are red. There are also other nodes on the map colored gray \" +\r\n \"that initially belong to neither you nor the enemy. The goal of the game is \" +\r\n \"to capture all of the enemy's Database nodes within the time limit. \" +\r\n \"If you fail to do this, you will lose.

\" +\r\n \"Each Node has three stats: Attack, Defense, and HP. There are five different actions that \" +\r\n \"a Node can take:

\" +\r\n \"Attack - Targets an enemy Node and lowers its HP. The effectiveness is determined by the owner's Attack, the Player's \" +\r\n \"hacking level, and the enemy's defense.

\" +\r\n \"Scan - Targets an enemy Node and lowers its Defense. The effectiveness is determined by the owner's Attack, the Player's hacking level, and the \" +\r\n \"enemy's defense.

\" +\r\n \"Weaken - Targets an enemy Node and lowers its Attack. The effectiveness is determined by the owner's Attack, the Player's hacking level, and the enemy's \" +\r\n \"defense.

\" +\r\n \"Fortify - Raises the Node's Defense. The effectiveness is determined by your hacking level.

\" +\r\n \"Overflow - Raises the Node's Attack but lowers its Defense. The effectiveness is determined by your hacking level.

\" +\r\n \"Note that when determining the effectiveness of the above actions, the TOTAL Attack or Defense of the team is used, not just the \" +\r\n \"Attack/Defense of the individual Node that is performing the action.

\" +\r\n \"To capture a Node, you must lower its HP down to 0.

\" +\r\n \"There are six different types of Nodes:

\" +\r\n \"CPU Core - These are your main Nodes that are used to perform actions. Capable of performing every action

\" +\r\n \"Firewall - Nodes with high defense. These Nodes can 'Fortify'

\" +\r\n \"Database - A special type of Node. The player's objective is to conquer all of the enemy's Database Nodes within \" +\r\n \"the time limit. These Nodes cannot perform any actions

\" +\r\n \"Spam - Conquering one of these Nodes will slow the enemy's trace, giving the player additional time to complete \" +\r\n \"the mission. These Nodes cannot perform any actions

\" +\r\n \"Transfer - Conquering one of these nodes will increase the Attack of all of your CPU Cores by a small fixed percentage. \" +\r\n \"These Nodes are capable of performing every action except the 'Attack' action

\" +\r\n \"Shield - Nodes with high defense. These Nodes can 'Fortify'

\" +\r\n \"To assign an action to a Node, you must first select one of your Nodes. This can be done by simply clicking on it. Double-clicking \" +\r\n \"a node will select all of your Nodes of the same type (e.g. select all CPU Core Nodes or all Transfer Nodes). Note that only Nodes \" +\r\n \"that can perform actions (CPU Core, Transfer, Shield, Firewall) can be selected. Selected Nodes will be denoted with a white highlight. After selecting a Node or multiple Nodes, \" +\r\n \"select its action using the Action Buttons near the top of the screen. Every action also has a corresponding keyboard \" +\r\n \"shortcut.

\" +\r\n \"For certain actions such as attacking, scanning, and weakening, the Node performing the action must have a target. To target \" +\r\n \"another node, simply click-and-drag from the 'source' Node to a target. A Node can only have one target, and you can target \" +\r\n \"any Node that is adjacent to one of your Nodes (immediately above, below, or to the side. NOT diagonal). Furthermore, only CPU Cores and Transfer Nodes \" +\r\n \"can target, since they are the only ones that can perform the related actions. To remove a target, you can simply click on the line that represents \" +\r\n \"the connection between one of your Nodes and its target. Alternatively, you can select the 'source' Node and click the 'Drop Connection' button, \" +\r\n \"or press 'd'.

\" +\r\n \"Other Notes:

\" +\r\n \"-Whenever a miscellenaous Node (not owned by the player or enemy) is conquered, the defense of all remaining miscellaneous Nodes that \" +\r\n \"are not actively being targeted will increase by a fixed percentage.

\" +\r\n \"-Whenever a Node is conquered, its stats are significantly reduced

\" +\r\n \"-Miscellaneous Nodes slowly raise their defense over time

\" +\r\n \"-Nodes slowly regenerate health over time.\",\r\n\r\n // Time-related constants\r\n MillisecondsPer20Hours: 72000000,\r\n GameCyclesPer20Hours: 72000000 / 200,\r\n\r\n MillisecondsPer10Hours: 36000000,\r\n GameCyclesPer10Hours: 36000000 / 200,\r\n\r\n MillisecondsPer8Hours: 28800000,\r\n GameCyclesPer8Hours: 28800000 / 200,\r\n\r\n MillisecondsPer4Hours: 14400000,\r\n GameCyclesPer4Hours: 14400000 / 200,\r\n\r\n MillisecondsPer2Hours: 7200000,\r\n GameCyclesPer2Hours: 7200000 / 200,\r\n\r\n MillisecondsPerHour: 3600000,\r\n GameCyclesPerHour: 3600000 / 200,\r\n\r\n MillisecondsPerHalfHour: 1800000,\r\n GameCyclesPerHalfHour: 1800000 / 200,\r\n\r\n MillisecondsPerQuarterHour: 900000,\r\n GameCyclesPerQuarterHour: 900000 / 200,\r\n\r\n MillisecondsPerFiveMinutes: 300000,\r\n GameCyclesPerFiveMinutes: 300000 / 200,\r\n\r\n // Player Work & Action\r\n FactionWorkHacking: \"Faction Hacking Work\",\r\n FactionWorkField: \"Faction Field Work\",\r\n FactionWorkSecurity: \"Faction Security Work\",\r\n\r\n WorkTypeCompany: \"Working for Company\",\r\n WorkTypeCompanyPartTime: \"Working for Company part-time\",\r\n WorkTypeFaction: \"Working for Faction\",\r\n WorkTypeCreateProgram: \"Working on Create a Program\",\r\n WorkTypeStudyClass: \"Studying or Taking a class at university\",\r\n WorkTypeCrime: \"Committing a crime\",\r\n\r\n ClassStudyComputerScience: \"studying Computer Science\",\r\n ClassDataStructures: \"taking a Data Structures course\",\r\n ClassNetworks: \"taking a Networks course\",\r\n ClassAlgorithms: \"taking an Algorithms course\",\r\n ClassManagement: \"taking a Management course\",\r\n ClassLeadership: \"taking a Leadership course\",\r\n ClassGymStrength: \"training your strength at a gym\",\r\n ClassGymDefense: \"training your defense at a gym\",\r\n ClassGymDexterity: \"training your dexterity at a gym\",\r\n ClassGymAgility: \"training your agility at a gym\",\r\n\r\n ClassDataStructuresBaseCost: 40,\r\n ClassNetworksBaseCost: 80,\r\n ClassAlgorithmsBaseCost: 320,\r\n ClassManagementBaseCost: 160,\r\n ClassLeadershipBaseCost: 320,\r\n ClassGymBaseCost: 120,\r\n\r\n CrimeShoplift: \"shoplift\",\r\n CrimeRobStore: \"rob a store\",\r\n CrimeMug: \"mug someone\",\r\n CrimeLarceny: \"commit larceny\",\r\n CrimeDrugs: \"deal drugs\",\r\n CrimeBondForgery: \"forge corporate bonds\",\r\n CrimeTraffickArms: \"traffick illegal arms\",\r\n CrimeHomicide: \"commit homicide\",\r\n CrimeGrandTheftAuto: \"commit grand theft auto\",\r\n CrimeKidnap: \"kidnap someone for ransom\",\r\n CrimeAssassination: \"assassinate a high-profile target\",\r\n CrimeHeist: \"pull off the ultimate heist\",\r\n\r\n // Coding Contract\r\n // TODO Move this into Coding contract impelmentation?\r\n CodingContractBaseFactionRepGain: 2500,\r\n CodingContractBaseCompanyRepGain: 4000,\r\n CodingContractBaseMoneyGain: 75e6,\r\n\r\n // BitNode/Source-File related stuff\r\n TotalNumBitNodes: 24,\r\n\r\n LatestUpdate:\r\n `\r\n v0.47.0\r\n * Stock Market changes:\r\n ** Implemented spread. Stock's now have bid and ask prices at which transactions occur\r\n ** Large transactions will now influence a stock's price and forecast\r\n ** This \"influencing\" can take effect in the middle of a transaction\r\n ** See documentation for more details on these changes\r\n ** Added getStockAskPrice(), getStockBidPrice() Netscript functions to the TIX API\r\n ** Added getStockPurchaseCost(), getStockSaleGain() Netscript functions to the TIX API\r\n\r\n * Re-sleeves can no longer have the NeuroFlux Governor augmentation\r\n ** This is just a temporary patch until the mechanic gets re-worked\r\n\r\n * Adjusted RAM costs of Netscript Singularity functions (mostly increased)\r\n * Netscript Singularity functions no longer cost extra RAM outside of BitNode-4\r\n * Corporation employees no longer have an \"age\" stat\r\n * Bug Fix: Corporation employees stats should no longer become negative\r\n * Bug Fix: Fixed sleeve.getInformation() throwing error in certain scenarios\r\n * Bug Fix: Coding contracts should no longer generate on the w0r1d_d43m0n server\r\n * Bug Fix: Duplicate Sleeves now properly have access to all Augmentations if you have a gang\r\n * Bug Fix: getAugmentationsFromFaction() & purchaseAugmentation() functions should now work properly if you have a gang\r\n * Bug Fix: Fixed issue that caused messages (.msg) to be sent when refreshing/reloading the game\r\n * Bug Fix: Purchasing hash upgrades for Bladeburner/Corporation when you don't actually have access to those mechanics no longer gives hashes\r\n * Bug Fix: run(), exec(), and spawn() Netscript functions now throw if called with 0 threads\r\n * Bug Fix: Faction UI should now automatically update reputation\r\n `\r\n}\r\n","/**\r\n * Represents a Limit or Buy Order on the stock market. Does not represent\r\n * a Market Order since those are just executed immediately\r\n */\r\nimport { OrderTypes } from \"./data/OrderTypes\";\r\nimport { PositionTypes } from \"./data/PositionTypes\";\r\n\r\nimport {\r\n Generic_fromJSON,\r\n Generic_toJSON,\r\n Reviver,\r\n} from \"../../utils/JSONReviver\";\r\n\r\nexport class Order {\r\n /**\r\n * Initializes a Order from a JSON save state\r\n */\r\n static fromJSON(value: any): Order {\r\n return Generic_fromJSON(Order, value.data);\r\n }\r\n\r\n readonly pos: PositionTypes;\r\n readonly price: number;\r\n shares: number;\r\n readonly stockSymbol: string;\r\n readonly type: OrderTypes;\r\n\r\n constructor(stockSymbol: string=\"\", shares: number=0, price: number=0, typ: OrderTypes=OrderTypes.LimitBuy, pos: PositionTypes=PositionTypes.Long) {\r\n // Validate arguments\r\n let invalidArgs: boolean = false;\r\n if (typeof shares !== \"number\" || typeof price !== \"number\") {\r\n invalidArgs = true;\r\n }\r\n if (isNaN(shares) || isNaN(price)) {\r\n invalidArgs = true;\r\n }\r\n if (typeof stockSymbol !== \"string\") {\r\n invalidArgs = true;\r\n }\r\n if (invalidArgs) {\r\n throw new Error(`Invalid constructor paramters for Order`);\r\n }\r\n\r\n this.stockSymbol = stockSymbol;\r\n this.shares = shares;\r\n this.price = price;\r\n this.type = typ;\r\n this.pos = pos;\r\n }\r\n\r\n /**\r\n * Serialize the Order to a JSON save state.\r\n */\r\n toJSON(): any {\r\n return Generic_toJSON(\"Order\", this);\r\n }\r\n}\r\n\r\nReviver.constructors.Order = Order;\r\n"],"sourceRoot":""} \ No newline at end of file diff --git a/test/tests.css b/test/tests.css new file mode 100644 index 000000000..c5f22a196 --- /dev/null +++ b/test/tests.css @@ -0,0 +1,4889 @@ +/* COLORS */ +/* Attributes */ +/** + * Customized styling for the Code Mirror editor + */ +#codemirror-form-wrapper { + height: 80%; + margin: 10px 0px 0px 6px; } + +.CodeMirror { + height: 100%; + width: 100%; + border: 2px solid var(--my-highlight-color); + z-index: 1; + font-family: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", "Consolas", "Courier New", Courier, monospace, "Times New Roman"; + font-size: 16px; } + +/** + * Highlight matches + */ +.cm-matchhighlight { + background-color: #8F908A; } + +.CodeMirror-selection-highlight-scrollbar { + background-color: #8F908A; } + +/** + * Show Invisibles + */ +.cm-whitespace::before { + position: absolute; + pointer-events: none; + color: #404F7D; } + +/** + * Vim command display + */ +#codemirror-vim-command-display-wrapper { + background-color: white; + font-size: 13px; + height: 30px; + margin-left: 6px; } + +/* COLORS */ +/* Attributes */ +/* COLORS */ +/* Attributes */ +* { + font-size: 16px; + font-family: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", "Consolas", "Courier New", Courier, monospace, "Times New Roman"; } + +*, +*:before, +*:after { + margin: 0; + padding: 0; + box-sizing: border-box; + vertical-align: top; } + +:root { + --my-font-color: #6f3; + --my-background-color: #000; + --my-highlight-color: #fff; + --my-prompt-color: #f92672; } + +body { + background-color: var(--my-background-color); } + +p, +pre, +h2, +.text { + color: var(--my-font-color); } + +h1 { + font-size: 22px; + color: var(--my-font-color); } + +ul { + padding: 2px; + list-style-type: none; } + +li { + list-style-type: none; } + +#entire-game-container { + background-color: transparent; } + +/* Disable border highlight on elements */ +input:focus, +textarea:focus, +button:focus, +td:focus, +tr:focus { + outline: none; } + +/* Make html links ("a" elements) nice looking buttons with this class */ +a:link, +a:visited { + color: #fff; } + +.dropdown { + color: #fff; + background-color: #000; } + +.text-input { + color: #fff; + background-color: #000; + border-style: solid; + border-width: 1px; + border-color: white; } + +/* Notification icon (for create program right now only) */ +#create-program-tab { + position: relative; } + +#create-program-notification { + font-size: 10px; + position: absolute; + /* Position the badge within the relatively positioned button */ + top: 0; + right: 0; } + +.notification-on { + background-color: #fa3e3e; + color: #fff; + border-radius: 2px; + padding: 1px 3px; } + +.notification-off { + background-color: #333; + color: #333; + border-radius: 0; + padding: 0; } + +/* help tip. Question mark that opens popup with info/details */ +.help-tip { + background-color: black; + border: 1px solid #fff; + border-radius: 5px; + color: #fff; + content: '?'; + display: inline-block; + margin-left: 3px; + padding: 1px; } + +.help-tip-big { + content: '?'; + padding: 3px; + margin-left: 3px; + color: #fff; + border: 1px solid #fff; + border-radius: 8px; + display: inline-block; } + +.help-tip:hover, +.help-tip-big:hover { + background-color: #888; } + +.help-tip:active, +.help-tip-big:active { + -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); + -moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); + box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6); } + +/* Flashing button (Red) */ +@-webkit-keyframes glowing { + 0% { + background-color: #b20000; + -webkit-box-shadow: 0 0 3px #b20000; } + 50% { + background-color: #f00; + -webkit-box-shadow: 0 0 40px #f00; } + 100% { + background-color: #b20000; + -webkit-box-shadow: 0 0 3px #b20000; } } + +@-moz-keyframes glowing { + 0% { + background-color: #b20000; + -moz-box-shadow: 0 0 3px #b20000; } + 50% { + background-color: #f00; + -moz-box-shadow: 0 0 40px #f00; } + 100% { + background-color: #b20000; + -moz-box-shadow: 0 0 3px #b20000; } } + +@-o-keyframes glowing { + 0% { + background-color: #b20000; + box-shadow: 0 0 3px #b20000; } + 50% { + background-color: #f00; + box-shadow: 0 0 40px #f00; } + 100% { + background-color: #b20000; + box-shadow: 0 0 3px #b20000; } } + +@keyframes glowing { + 0% { + background-color: #b20000; + box-shadow: 0 0 3px #b20000; } + 50% { + background-color: #f00; + box-shadow: 0 0 40px #f00; } + 100% { + background-color: #b20000; + box-shadow: 0 0 3px #b20000; } } + +.flashing-button { + -webkit-animation: glowing 1500ms infinite; + -moz-animation: glowing 1500ms infinite; + -o-animation: glowing 1500ms infinite; + animation: glowing 1500ms infinite; } + +/* Blinking Cursor */ +/* ----- blinking cursor animation ----- */ +.typed-cursor { + opacity: 1; + -webkit-animation: blink 0.95s infinite; + -moz-animation: blink 0.95s infinite; + -ms-animation: blink 0.95s infinite; + -o-animation: blink 0.95s infinite; + animation: blink 0.95s infinite; } + +@-keyframes blink { + 0% { + opacity: 1; } + 50% { + opacity: 0; } + 100% { + opacity: 1; } } + +@-webkit-keyframes blink { + 0% { + opacity: 1; } + 50% { + opacity: 0; } + 100% { + opacity: 1; } } + +@-moz-keyframes blink { + 0% { + opacity: 1; } + 50% { + opacity: 0; } + 100% { + opacity: 1; } } + +@-ms-keyframes blink { + 0% { + opacity: 1; } + 50% { + opacity: 0; } + 100% { + opacity: 1; } } + +@-o-keyframes blink { + 0% { + opacity: 1; } + 50% { + opacity: 0; } + 100% { + opacity: 1; } } + +/* Status text */ +@-webkit-keyframes status-text { + from { + opacity: 1; } + to { + opacity: 0; } } + +.status-text { + display: inline-block; + position: fixed; + z-index: 2; + -webkit-animation: status-text 3s 1; } + +#status-text-container { + background-color: transparent; } + +#status-text { + background-color: transparent; + bottom: 0; + color: #fff; + display: none; + font-size: 20px; + margin-right: 14px; + opacity: 0; + padding: 4px; + right: 0; + top: 0; + width: auto; } + +/* Scan analyze links from AutoLink */ +.scan-analyze-link { + cursor: pointer; + color: #fff; + text-decoration: underline; } + .scan-analyze-link:hover { + text-decoration: none; } + +/* Accordion menus (Header with collapsible panel) */ +.accordion-header { + background-color: #444; + font-size: 20px; + color: #fff; + margin: 6px 6px 0 6px; + padding: 4px 6px; + cursor: pointer; + width: 80%; + text-align: left; + border: none; + outline: none; + position: relative; } + .accordion-header.active, .accordion-header:hover { + background-color: #555; } + .accordion-header.active:hover { + background-color: #666; } + .accordion-header:after { + content: '\2795'; + /* "plus" sign (+) */ + font-size: 14px; + float: right; + color: transparent; + text-shadow: 0 0 0 #fff; + position: absolute; + bottom: 5px; + right: 6px; } + .accordion-header.active:after { + content: "\2796"; + /* "minus" sign (-) */ } + +.accordion-panel { + margin: 0 6px 6px 6px; + padding: 0 6px 6px 6px; + width: 75%; + margin-left: 5%; + display: none; + background-color: #555; + overflow-y: auto; + overflow-x: none; } + .accordion-panel div, .accordion-panel ul, .accordion-panel p, .accordion-panel ul > li { + background-color: #555; } + +/* override the global styling */ +#active-scripts-total-production-active, +#active-scripts-total-prod-aug-total, +#active-scripts-total-prod-aug-avg { + margin: 0; + padding: 0; } + +/* Helper Classes */ +.hacker-green { + color: #adff2f; } + +.money-gold { + color: #ffd700; } + +.light-yellow { + color: #faffdf; } + +.failure { + color: #ff2929; + text-shadow: 0 0 0 #ff2929; } + +.success { + color: #3adb76; + text-shadow: 0 0 0 #3adb76; } + +.physical-yellow { + color: #faffdf; } + +.charisma-purple { + color: #a671d1; } + +.smallfont { + font-size: 13px; } + +/* COLORS */ +/* Attributes */ +/* Styling for tooltip-style elements */ +/* Tool tips (when hovering over an element */ +.tooltip { + display: inline-block; + position: relative; + /* Positioned to left of element rather than right */ + /* Tooltip goes below cursor instead of above */ } + .tooltip .tooltiptext { + visibility: hidden; + width: 300px; + background-color: var(--my-background-color); + border: 2px solid var(--my-highlight-color); + color: #fff; + text-align: center; + padding: 4px; + left: 101%; + pointer-events: none; + position: absolute; + z-index: 99; } + .tooltip .tooltiptextleft { + visibility: hidden; + width: 300px; + background-color: var(--my-background-color); + border: 2px solid var(--my-highlight-color); + color: #fff; + text-align: center; + padding: 4px; + top: 50%; + left: 50%; + transform: translate(-100%, -100%); + /* Backwards compatibility */ + -webkit-transform: translate(-100%, -100%); + -moz-transform: translate(-100%, -100%); + -o-transform: translate(-100%, -100%); + -ms-transform: translate(-100%, -100%); + position: absolute; + z-index: 99; } + .tooltip .tooltiptextlow { + visibility: hidden; + width: 300px; + background-color: var(--my-background-color); + border: 2px solid var(--my-highlight-color); + color: #fff; + text-align: center; + padding: 4px; + left: 101%; + pointer-events: none; + position: absolute; + z-index: 99; + bottom: 25%; } + +/* Same thing as a normal tooltip except its a bit higher */ +.tooltip .tooltiptexthigh { + visibility: hidden; + width: 300px; + background-color: var(--my-background-color); + border: 2px solid var(--my-highlight-color); + color: #fff; + text-align: center; + padding: 4px; + left: 101%; + bottom: -25%; + position: absolute; + z-index: 99; } + +.tooltip:hover .tooltiptext, +.tooltip:hover .tooltiptexthigh, +.tooltip:hover .tooltiptextleft, +.tooltip:hover .tooltiptextlow { + visibility: visible; } + +/* COLORS */ +/* Attributes */ +/** + * Styling for all buttons + * + * Includes