From 4568d2ebca7c6e52f91598f83b4965ebaeeb757e Mon Sep 17 00:00:00 2001 From: Seth Nicholson Date: Wed, 13 Aug 2025 12:55:48 -0700 Subject: [PATCH 1/4] Shotgun batcher, gang, and server auto-purchase are working. Contract solving, stock trading, Singularity, and Bladeburner are partially working. --- autoexec.ts | 8 + bladeburner/chooseCity.ts | 57 ++++ bladeburner/forge.ts | 19 ++ bladeburner/genInfo/actions.ts | 100 ++++++ bladeburner/genInfo/cities.ts | 44 +++ bladeburner/genInfo/player.ts | 100 ++++++ bladeburner/get/action/autoLevel.ts | 11 + bladeburner/get/action/countRemaining.ts | 11 + bladeburner/get/action/currentLevel.ts | 11 + bladeburner/get/action/currentTime.ts | 5 + bladeburner/get/action/estSuccessChance.ts | 15 + bladeburner/get/action/maxLevel.ts | 11 + bladeburner/get/action/repGain.ts | 11 + bladeburner/get/action/successes.ts | 11 + bladeburner/get/action/time.ts | 11 + bladeburner/get/blackOp/next.ts | 5 + bladeburner/get/blackOp/rank.ts | 5 + bladeburner/get/city/chaos.ts | 10 + bladeburner/get/city/communities.ts | 10 + bladeburner/get/city/current.ts | 5 + bladeburner/get/city/estPop.ts | 10 + bladeburner/get/rank.ts | 5 + bladeburner/get/skill/level.ts | 10 + bladeburner/get/skill/points.ts | 5 + bladeburner/get/skill/upgradeCost.ts | 10 + bladeburner/get/stamina.ts | 5 + bladeburner/get/teamSize.ts | 5 + bladeburner/join/division.js | 4 + bladeburner/join/faction.js | 4 + bladeburner/launch.ts | 13 + bladeburner/manageSkills.ts | 312 +++++++++++++++++++ bladeburner/travel.ts | 3 + bladeburner/upgradeSkill.ts | 3 + bladeburner/util.ts | 43 +++ contract/2color.ts | 57 ++++ contract/arrayJumpI.ts | 22 ++ contract/arrayJumpII.ts | 25 ++ contract/attempt.js | 9 + contract/caesar.ts | 29 ++ contract/get/data.ts | 12 + contract/get/type.ts | 12 + contract/gridPathsI.js | 29 ++ contract/gridPathsII.ts | 35 +++ contract/intPartition.ts | 35 +++ contract/intPartitionSet.js | 30 ++ contract/intToHamming.js | 24 ++ contract/ipAddr.ts | 46 +++ contract/lpf.js | 53 ++++ contract/lzCompression.js | 24 ++ contract/lzDecompression.ts | 42 +++ contract/mathExprs.js | 68 +++++ contract/mergeIntervals.ts | 31 ++ contract/rleCompression.ts | 39 +++ contract/shortestGridPath.ts | 134 ++++++++ contract/solveLoop.tsx | 187 ++++++++++++ contract/spiralize.ts | 41 +++ contract/sqrt.ts | 37 +++ contract/stockTraderI.ts | 22 ++ contract/stockTraderII.ts | 19 ++ contract/stockTraderIII.ts | 19 ++ contract/stockTraderIV.ts | 73 +++++ contract/subarrayMaxSum.ts | 35 +++ contract/triMinSum.ts | 23 ++ contract/vigenere.ts | 34 +++ crack/crack.ts | 41 +++ crack/deployScripts.ts | 44 +++ crack/getTargets.ts | 13 + crack/tryNuke.ts | 17 ++ crack/worm.js | 36 +++ daemons/deployDynamo.ts | 9 + daemons/deployExecutor.ts | 9 + daemons/dynamo.ts | 105 +++++++ daemons/executor.ts | 76 +++++ debug/monitorPort.ts | 8 + deployShare.ts | 14 + exploit/bypass.ts | 4 + exploit/devMenu.js | 5 + exploit/rainbow.ts | 3 + exploit/reality.js | 8 + exploit/tamper.ts | 4 + exploit/time.js | 19 ++ gang/ascend.ts | 54 ++++ gang/ascendMember.ts | 5 + gang/assign.js | 169 +++++++++++ gang/canRecruitMember.ts | 5 + gang/equip.js | 93 ++++++ gang/genEquipmentInfo.js | 40 +++ gang/genInfo/gang.ts | 15 + gang/genInfo/members.ts | 24 ++ gang/genInfo/otherGangs.ts | 24 ++ gang/genTaskInfo.ts | 9 + gang/get/gangInfo.ts | 5 + gang/get/member/ascensionResult.ts | 5 + gang/get/member/info.ts | 5 + gang/get/member/names.ts | 5 + gang/get/otherGangInfo.ts | 5 + gang/info/equipment.json | 1 + gang/info/tasks.json | 1 + gang/kingpin.ts | 44 +++ gang/launch.ts | 7 + gang/launch2.ts | 7 + gang/mode.ts | 82 +++++ gang/recruit.ts | 44 +++ gang/recruitMember.ts | 5 + gang/rename.ts | 3 + gang/set/territoryWarfare.ts | 3 + go/getBoardState.js | 6 + go/getLiberties.js | 7 + go/move.js | 7 + go/play.js | 6 + hack/calcHackParams.js | 101 ++++++ hack/deployShotgunner.ts | 15 + hack/grow.ts | 10 + hack/hack.ts | 10 + hack/shotgunner.js | 338 +++++++++++++++++++++ hack/weaken.ts | 7 + hacknet/deploy.ts | 10 + hacknet/launch.ts | 6 + hacknet/naiveLoop.js | 60 ++++ hacknet/nodeFormulasLoop.ts | 151 +++++++++ info/BDTargets.js | 6 + info/contractList.js | 14 + info/contractTypes.js | 4 + info/crimeEV.tsx | 74 +++++ info/infilList.js | 12 + info/karma.js | 5 + info/pathToServer.js | 28 ++ init/cleanup.ts | 19 ++ init/collectLit.ts | 10 + init/init.ts | 19 ++ init/init2.ts | 18 ++ lib/deepScan.js | 12 + lib/genInfo/gameState.ts | 37 +++ lib/genInfo/reset.ts | 108 +++++++ lib/get/bitNodeMultipliers.ts | 6 + lib/get/moneySources.ts | 4 + lib/get/player.ts | 4 + lib/nsEnums.ts | 46 +++ lib/rm.ts | 4 + lib/tick.js | 6 + lib/util.ts | 109 +++++++ queenAdministrator.ts | 27 ++ readme.txt | 17 ++ rngesus.ts | 12 + share.ts | 6 + singularity/acceptFactionInvites.ts | 3 + singularity/applyForJobs.ts | 107 +++++++ singularity/applyToCompany.ts | 3 + singularity/buy/allPrograms.ts | 5 + singularity/buy/aug.ts | 4 + singularity/buy/core.ts | 3 + singularity/buy/maxNFG.ts | 4 + singularity/buy/program.ts | 7 + singularity/buy/ram.ts | 3 + singularity/buy/tor.ts | 3 + singularity/chooseWork.ts | 83 +++++ singularity/connect.ts | 24 ++ singularity/doWork/company.ts | 4 + singularity/doWork/createProgram.ts | 4 + singularity/doWork/faction.ts | 6 + singularity/farmInt.ts | 14 + singularity/genInfo/companyPositions.ts | 20 ++ singularity/genInfo/companyRep.ts | 20 ++ singularity/genInfo/darkweb.ts | 34 +++ singularity/genInfo/factionEnemies.ts | 21 ++ singularity/get/aug/basePrice.ts | 4 + singularity/get/aug/factions.ts | 4 + singularity/get/aug/prereq.ts | 4 + singularity/get/aug/price.ts | 4 + singularity/get/aug/repReq.ts | 4 + singularity/get/aug/stats.ts | 4 + singularity/get/company/favor.ts | 9 + singularity/get/company/favorGain.ts | 4 + singularity/get/company/positionInfo.ts | 6 + singularity/get/company/positions.ts | 9 + singularity/get/company/rep.ts | 8 + singularity/get/currentServer.ts | 6 + singularity/get/currentWork.ts | 5 + singularity/get/darkweb/programCost.ts | 4 + singularity/get/darkweb/programs.ts | 4 + singularity/get/faction/enemies.ts | 9 + singularity/get/faction/favor.ts | 6 + singularity/get/faction/favorGain.ts | 4 + singularity/get/faction/inviteReqs.ts | 4 + singularity/get/faction/invites.ts | 4 + singularity/get/faction/rep.ts | 4 + singularity/get/faction/workTypes.ts | 4 + singularity/installAugs.ts | 3 + singularity/installBackdoor.ts | 10 + singularity/is/busy.ts | 5 + singularity/is/focused.ts | 5 + singularity/stem.ts | 23 ++ singularity/travel.ts | 3 + singularity/tryBackdoor.ts | 44 +++ singularity/upgradeHome/cores.ts | 6 + singularity/upgradeHome/ram.ts | 6 + sleeve/genAugInfo.js | 18 ++ sleeve/get/augPrice.js | 6 + sleeve/get/augRepReq.js | 6 + sleeve/get/augs.js | 7 + sleeve/get/numSleeves.js | 6 + sleeve/get/purchasableAugs.js | 6 + sleeve/get/sleeve.js | 7 + sleeve/setTo/commitCrime.js | 5 + sleeve/setTo/companyWork.js | 5 + sleeve/setTo/factionWork.js | 5 + sleeve/setTo/gymWorkout.js | 5 + sleeve/setTo/idle.js | 5 + sleeve/setTo/shockRecovery.js | 5 + sleeve/setTo/synchronize.js | 5 + sleeve/setTo/universityCourse.js | 5 + sleeve/travel.js | 5 + srv/deploy.ts | 6 + srv/get.js | 11 + srv/purchase.ts | 6 + srv/purchaseHackers.ts | 44 +++ srv/rename.js | 4 + srv/upgrade.ts | 4 + stock/buy.ts | 4 + stock/buyLoop.ts | 73 +++++ stock/deploy.ts | 16 + stock/genSymbolInfo.ts | 66 ++++ stock/get/askPrice.ts | 4 + stock/get/forecast.ts | 6 + stock/get/maxShares.ts | 4 + stock/get/organization.ts | 4 + stock/get/poorManForecast.ts | 26 ++ stock/get/position.ts | 4 + stock/get/symbols.ts | 4 + stock/get/volatility.ts | 6 + stock/init.ts | 8 + stock/init2.ts | 9 + stock/launch.ts | 16 + stock/purchase/4s.ts | 3 + stock/purchase/4sAPI.ts | 3 + stock/purchase/tixAPI.ts | 3 + stock/purchase/wseAccount.ts | 3 + stock/sell.ts | 6 + stock/sellLoop.ts | 47 +++ stock/sellShort.ts | 6 + stock/updateSymbolInfo.ts | 57 ++++ 241 files changed, 5699 insertions(+) create mode 100644 autoexec.ts create mode 100644 bladeburner/chooseCity.ts create mode 100644 bladeburner/forge.ts create mode 100644 bladeburner/genInfo/actions.ts create mode 100644 bladeburner/genInfo/cities.ts create mode 100644 bladeburner/genInfo/player.ts create mode 100644 bladeburner/get/action/autoLevel.ts create mode 100644 bladeburner/get/action/countRemaining.ts create mode 100644 bladeburner/get/action/currentLevel.ts create mode 100644 bladeburner/get/action/currentTime.ts create mode 100644 bladeburner/get/action/estSuccessChance.ts create mode 100644 bladeburner/get/action/maxLevel.ts create mode 100644 bladeburner/get/action/repGain.ts create mode 100644 bladeburner/get/action/successes.ts create mode 100644 bladeburner/get/action/time.ts create mode 100644 bladeburner/get/blackOp/next.ts create mode 100644 bladeburner/get/blackOp/rank.ts create mode 100644 bladeburner/get/city/chaos.ts create mode 100644 bladeburner/get/city/communities.ts create mode 100644 bladeburner/get/city/current.ts create mode 100644 bladeburner/get/city/estPop.ts create mode 100644 bladeburner/get/rank.ts create mode 100644 bladeburner/get/skill/level.ts create mode 100644 bladeburner/get/skill/points.ts create mode 100644 bladeburner/get/skill/upgradeCost.ts create mode 100644 bladeburner/get/stamina.ts create mode 100644 bladeburner/get/teamSize.ts create mode 100644 bladeburner/join/division.js create mode 100644 bladeburner/join/faction.js create mode 100644 bladeburner/launch.ts create mode 100644 bladeburner/manageSkills.ts create mode 100644 bladeburner/travel.ts create mode 100644 bladeburner/upgradeSkill.ts create mode 100644 bladeburner/util.ts create mode 100644 contract/2color.ts create mode 100644 contract/arrayJumpI.ts create mode 100644 contract/arrayJumpII.ts create mode 100644 contract/attempt.js create mode 100644 contract/caesar.ts create mode 100644 contract/get/data.ts create mode 100644 contract/get/type.ts create mode 100644 contract/gridPathsI.js create mode 100644 contract/gridPathsII.ts create mode 100644 contract/intPartition.ts create mode 100644 contract/intPartitionSet.js create mode 100644 contract/intToHamming.js create mode 100644 contract/ipAddr.ts create mode 100644 contract/lpf.js create mode 100644 contract/lzCompression.js create mode 100644 contract/lzDecompression.ts create mode 100644 contract/mathExprs.js create mode 100644 contract/mergeIntervals.ts create mode 100644 contract/rleCompression.ts create mode 100644 contract/shortestGridPath.ts create mode 100644 contract/solveLoop.tsx create mode 100644 contract/spiralize.ts create mode 100644 contract/sqrt.ts create mode 100644 contract/stockTraderI.ts create mode 100644 contract/stockTraderII.ts create mode 100644 contract/stockTraderIII.ts create mode 100644 contract/stockTraderIV.ts create mode 100644 contract/subarrayMaxSum.ts create mode 100644 contract/triMinSum.ts create mode 100644 contract/vigenere.ts create mode 100644 crack/crack.ts create mode 100644 crack/deployScripts.ts create mode 100644 crack/getTargets.ts create mode 100644 crack/tryNuke.ts create mode 100644 crack/worm.js create mode 100644 daemons/deployDynamo.ts create mode 100644 daemons/deployExecutor.ts create mode 100644 daemons/dynamo.ts create mode 100644 daemons/executor.ts create mode 100644 debug/monitorPort.ts create mode 100644 deployShare.ts create mode 100644 exploit/bypass.ts create mode 100644 exploit/devMenu.js create mode 100644 exploit/rainbow.ts create mode 100644 exploit/reality.js create mode 100644 exploit/tamper.ts create mode 100644 exploit/time.js create mode 100644 gang/ascend.ts create mode 100644 gang/ascendMember.ts create mode 100644 gang/assign.js create mode 100644 gang/canRecruitMember.ts create mode 100644 gang/equip.js create mode 100644 gang/genEquipmentInfo.js create mode 100644 gang/genInfo/gang.ts create mode 100644 gang/genInfo/members.ts create mode 100644 gang/genInfo/otherGangs.ts create mode 100644 gang/genTaskInfo.ts create mode 100644 gang/get/gangInfo.ts create mode 100644 gang/get/member/ascensionResult.ts create mode 100644 gang/get/member/info.ts create mode 100644 gang/get/member/names.ts create mode 100644 gang/get/otherGangInfo.ts create mode 100644 gang/info/equipment.json create mode 100644 gang/info/tasks.json create mode 100644 gang/kingpin.ts create mode 100644 gang/launch.ts create mode 100644 gang/launch2.ts create mode 100644 gang/mode.ts create mode 100644 gang/recruit.ts create mode 100644 gang/recruitMember.ts create mode 100644 gang/rename.ts create mode 100644 gang/set/territoryWarfare.ts create mode 100644 go/getBoardState.js create mode 100644 go/getLiberties.js create mode 100644 go/move.js create mode 100644 go/play.js create mode 100644 hack/calcHackParams.js create mode 100644 hack/deployShotgunner.ts create mode 100644 hack/grow.ts create mode 100644 hack/hack.ts create mode 100644 hack/shotgunner.js create mode 100644 hack/weaken.ts create mode 100644 hacknet/deploy.ts create mode 100644 hacknet/launch.ts create mode 100644 hacknet/naiveLoop.js create mode 100644 hacknet/nodeFormulasLoop.ts create mode 100644 info/BDTargets.js create mode 100644 info/contractList.js create mode 100644 info/contractTypes.js create mode 100644 info/crimeEV.tsx create mode 100644 info/infilList.js create mode 100644 info/karma.js create mode 100644 info/pathToServer.js create mode 100644 init/cleanup.ts create mode 100644 init/collectLit.ts create mode 100644 init/init.ts create mode 100644 init/init2.ts create mode 100644 lib/deepScan.js create mode 100644 lib/genInfo/gameState.ts create mode 100644 lib/genInfo/reset.ts create mode 100644 lib/get/bitNodeMultipliers.ts create mode 100644 lib/get/moneySources.ts create mode 100644 lib/get/player.ts create mode 100644 lib/nsEnums.ts create mode 100644 lib/rm.ts create mode 100644 lib/tick.js create mode 100644 lib/util.ts create mode 100644 queenAdministrator.ts create mode 100644 readme.txt create mode 100644 rngesus.ts create mode 100644 share.ts create mode 100644 singularity/acceptFactionInvites.ts create mode 100644 singularity/applyForJobs.ts create mode 100644 singularity/applyToCompany.ts create mode 100644 singularity/buy/allPrograms.ts create mode 100644 singularity/buy/aug.ts create mode 100644 singularity/buy/core.ts create mode 100644 singularity/buy/maxNFG.ts create mode 100644 singularity/buy/program.ts create mode 100644 singularity/buy/ram.ts create mode 100644 singularity/buy/tor.ts create mode 100644 singularity/chooseWork.ts create mode 100644 singularity/connect.ts create mode 100644 singularity/doWork/company.ts create mode 100644 singularity/doWork/createProgram.ts create mode 100644 singularity/doWork/faction.ts create mode 100644 singularity/farmInt.ts create mode 100644 singularity/genInfo/companyPositions.ts create mode 100644 singularity/genInfo/companyRep.ts create mode 100644 singularity/genInfo/darkweb.ts create mode 100644 singularity/genInfo/factionEnemies.ts create mode 100644 singularity/get/aug/basePrice.ts create mode 100644 singularity/get/aug/factions.ts create mode 100644 singularity/get/aug/prereq.ts create mode 100644 singularity/get/aug/price.ts create mode 100644 singularity/get/aug/repReq.ts create mode 100644 singularity/get/aug/stats.ts create mode 100644 singularity/get/company/favor.ts create mode 100644 singularity/get/company/favorGain.ts create mode 100644 singularity/get/company/positionInfo.ts create mode 100644 singularity/get/company/positions.ts create mode 100644 singularity/get/company/rep.ts create mode 100644 singularity/get/currentServer.ts create mode 100644 singularity/get/currentWork.ts create mode 100644 singularity/get/darkweb/programCost.ts create mode 100644 singularity/get/darkweb/programs.ts create mode 100644 singularity/get/faction/enemies.ts create mode 100644 singularity/get/faction/favor.ts create mode 100644 singularity/get/faction/favorGain.ts create mode 100644 singularity/get/faction/inviteReqs.ts create mode 100644 singularity/get/faction/invites.ts create mode 100644 singularity/get/faction/rep.ts create mode 100644 singularity/get/faction/workTypes.ts create mode 100644 singularity/installAugs.ts create mode 100644 singularity/installBackdoor.ts create mode 100644 singularity/is/busy.ts create mode 100644 singularity/is/focused.ts create mode 100644 singularity/stem.ts create mode 100644 singularity/travel.ts create mode 100644 singularity/tryBackdoor.ts create mode 100644 singularity/upgradeHome/cores.ts create mode 100644 singularity/upgradeHome/ram.ts create mode 100644 sleeve/genAugInfo.js create mode 100644 sleeve/get/augPrice.js create mode 100644 sleeve/get/augRepReq.js create mode 100644 sleeve/get/augs.js create mode 100644 sleeve/get/numSleeves.js create mode 100644 sleeve/get/purchasableAugs.js create mode 100644 sleeve/get/sleeve.js create mode 100644 sleeve/setTo/commitCrime.js create mode 100644 sleeve/setTo/companyWork.js create mode 100644 sleeve/setTo/factionWork.js create mode 100644 sleeve/setTo/gymWorkout.js create mode 100644 sleeve/setTo/idle.js create mode 100644 sleeve/setTo/shockRecovery.js create mode 100644 sleeve/setTo/synchronize.js create mode 100644 sleeve/setTo/universityCourse.js create mode 100644 sleeve/travel.js create mode 100644 srv/deploy.ts create mode 100644 srv/get.js create mode 100644 srv/purchase.ts create mode 100644 srv/purchaseHackers.ts create mode 100644 srv/rename.js create mode 100644 srv/upgrade.ts create mode 100644 stock/buy.ts create mode 100644 stock/buyLoop.ts create mode 100644 stock/deploy.ts create mode 100644 stock/genSymbolInfo.ts create mode 100644 stock/get/askPrice.ts create mode 100644 stock/get/forecast.ts create mode 100644 stock/get/maxShares.ts create mode 100644 stock/get/organization.ts create mode 100644 stock/get/poorManForecast.ts create mode 100644 stock/get/position.ts create mode 100644 stock/get/symbols.ts create mode 100644 stock/get/volatility.ts create mode 100644 stock/init.ts create mode 100644 stock/init2.ts create mode 100644 stock/launch.ts create mode 100644 stock/purchase/4s.ts create mode 100644 stock/purchase/4sAPI.ts create mode 100644 stock/purchase/tixAPI.ts create mode 100644 stock/purchase/wseAccount.ts create mode 100644 stock/sell.ts create mode 100644 stock/sellLoop.ts create mode 100644 stock/sellShort.ts create mode 100644 stock/updateSymbolInfo.ts diff --git a/autoexec.ts b/autoexec.ts new file mode 100644 index 0000000..86fd3d4 --- /dev/null +++ b/autoexec.ts @@ -0,0 +1,8 @@ +import {initEnums} from "lib/nsEnums"; +import {main as genResetInfo} from "lib/genInfo/reset"; + +export async function main(ns: NS) { + // initEnums needs to be synchronous and called first to prevent errors + initEnums(ns); + await genResetInfo(ns); +} \ No newline at end of file diff --git a/bladeburner/chooseCity.ts b/bladeburner/chooseCity.ts new file mode 100644 index 0000000..c872955 --- /dev/null +++ b/bladeburner/chooseCity.ts @@ -0,0 +1,57 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; +import {BladeburnerChaosThreshold, BladeburnerPopThreshold} from "bladeburner/util"; +import {bladeburnerPlayer} from "bladeburner/genInfo/player"; +import {BladeburnerCity, bladeburnerCities} from "bladeburner/genInfo/cities"; + +type CityEntry = [CityName, BladeburnerCity]; + +function chaosLow(c: CityEntry) { + return c[1].chaos <= BladeburnerChaosThreshold * 1.1; +} + +function popAboveThreshold(c: CityEntry) { + return c[1].estimatedPopulation >= BladeburnerPopThreshold; +} + +function popComparator(a: CityEntry, b: CityEntry) { + return a[1].estimatedPopulation - b[1].estimatedPopulation +} + +export async function main(ns: NS) { + var destination; + + // First, prefer cities with at least one community + var candidates: Array = + Object.entries(bladeburnerCities).filter(c => c[1].communities) as typeof candidates; + if (!candidates.length) + candidates = Object.entries(bladeburnerCities) as typeof candidates; + + // Second, prefer cities with low chaos + // Is this actually a good idea? Liable to get stuck in one city if Incite Violence is ever used. + // Probably won't happen, but even so, maybe reconsider. + if (candidates.findIndex(chaosLow) >= 0) + candidates = candidates.filter(chaosLow); + + // Third, find the city closest to the threshold at which population affects success (above it if possible) + if (candidates.findIndex(popAboveThreshold) >= 0) { + candidates = candidates.filter(popAboveThreshold); + destination = candidates.toSorted(popComparator)[0][0]; + } + else + destination = candidates.toSorted(popComparator)[candidates.length - 1][0]; + + if (bladeburnerPlayer.bladeburner.city != destination) { + ns.print("INFO: Traveling to ", destination, " from ", bladeburnerPlayer.bladeburner.city, "."); + ns.writePort(Ports.Exec, { + "script": "/bladeburner/travel.ts", + "hostname": ns.self().server, + "args": [destination], + "threadOrOptions": {"temporary": true}, + "retry": true + } as ExecutorJob); + bladeburnerPlayer.bladeburner.city = destination; + } + else + ns.print("INFO: Staying in ", destination, "."); +} diff --git a/bladeburner/forge.ts b/bladeburner/forge.ts new file mode 100644 index 0000000..225c056 --- /dev/null +++ b/bladeburner/forge.ts @@ -0,0 +1,19 @@ +import {Ports} from "lib/util"; +import {main as genBladeburnerPlayer} from "bladeburner/genInfo/player"; +import {main as genBladeburnerActions} from "bladeburner/genInfo/actions"; +import {main as genBladeburnerCities} from "bladeburner/genInfo/cities"; +import {main as chooseCity} from "bladeburner/chooseCity"; +import {main as manageSkills} from "bladeburner/manageSkills"; + +export async function main(ns: NS) { + ns.clearPort(Ports.Bladeburner); + while (true) { + ns.clearLog(); + await genBladeburnerCities(ns); + await genBladeburnerPlayer(ns); + await chooseCity(ns); + await genBladeburnerActions(ns); + await manageSkills(ns); + await ns.bladeburner.nextUpdate(); + } +} \ No newline at end of file diff --git a/bladeburner/genInfo/actions.ts b/bladeburner/genInfo/actions.ts new file mode 100644 index 0000000..76b00bf --- /dev/null +++ b/bladeburner/genInfo/actions.ts @@ -0,0 +1,100 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; +import { + BladeburnerAutoLevelMap, + BladeburnerActionEstimatedChanceMap, + BladeburnerActionLevelMap, + BladeburnerActionRepMap, + BladeburnerActionSucessesMap, + BladeburnerActionTimeMap, + BladeburnerActionsRemainingMap, + BladeburnerGetDir, + mapBladeburnerActions +} from "bladeburner/util"; + +const GetDir = BladeburnerGetDir + "action/"; + +export interface BladeburnerActionInfo { + autoLevel?: boolean, + estimatedSuccessChance: [number, number], + level?: number, + maxLevel?: number, + repGain: number, + successes: number, + time: number, + countRemaining: number +} + +export var bladeburnerActions: Record; + +/** Shim for ns.bladeburner.getNextBlackOp. Name changed to distinguish it. */ +export function getNextBlackOpShim() { + // Assumes Black Ops are in order, which happens to be true, but that's still not ideal. + return Object.entries(bladeburnerActions).find( + action => action[1].countRemaining == 1 && action[0].startsWith("Operation") + ) as [BladeburnerBlackOpName, BladeburnerActionInfo]; +} + +export async function main(ns: NS) { + const hostname = ns.self().server; + + var executorJob: ExecutorJob = { + "script": GetDir + "autoLevel.ts", + "hostname": hostname, + "args": ["-a"], + "threadOrOptions": {"temporary": true}, + "retry": true + }; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const autoLevelMap = ns.readPort(Ports.Bladeburner) as BladeburnerAutoLevelMap; + + executorJob.script = GetDir + "countRemaining.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const countRemainingMap = ns.readPort(Ports.Bladeburner) as BladeburnerActionsRemainingMap; + + executorJob.script = GetDir + "currentLevel.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const levelMap = ns.readPort(Ports.Bladeburner) as BladeburnerActionLevelMap; + + executorJob.script = GetDir + "maxLevel.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const maxLevelMap = ns.readPort(Ports.Bladeburner) as BladeburnerActionLevelMap; + + executorJob.script = GetDir + "repGain.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const repGainMap = ns.readPort(Ports.Bladeburner) as BladeburnerActionRepMap; + + executorJob.script = GetDir + "estSuccessChance.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const estChanceMap = ns.readPort(Ports.Bladeburner) as BladeburnerActionEstimatedChanceMap; + + executorJob.script = GetDir + "successes.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const successesMap = ns.readPort(Ports.Bladeburner) as BladeburnerActionSucessesMap; + + executorJob.script = GetDir + "time.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const timeMap = ns.readPort(Ports.Bladeburner) as BladeburnerActionTimeMap; + + bladeburnerActions = Object.freeze(mapBladeburnerActions( + ns, + (typ, name) => ({ + "autoLevel": autoLevelMap[name], + "countRemaining": countRemainingMap[name], + "level": levelMap[name], + "maxLevel": maxLevelMap[name], + "repGain": repGainMap[name], + "estimatedSuccessChance": estChanceMap[name], + "successes": successesMap[name], + "time": timeMap[name] + } as BladeburnerActionInfo) + )); +} \ No newline at end of file diff --git a/bladeburner/genInfo/cities.ts b/bladeburner/genInfo/cities.ts new file mode 100644 index 0000000..979bc8c --- /dev/null +++ b/bladeburner/genInfo/cities.ts @@ -0,0 +1,44 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; +import {BladeburnerGetDir} from "bladeburner/util"; + +const GetDir = BladeburnerGetDir + "city/"; + +export type BladeburnerCity = { + estimatedPopulation: ReturnType + communities: ReturnType + chaos: ReturnType +}; + +export var bladeburnerCities: Record; + +export async function main(ns: NS) { + const hostname = ns.self().server; + + var executorJob: ExecutorJob = { + "script": GetDir + "chaos.ts", + "hostname": hostname, + "args": ["-a"], + "threadOrOptions": {"temporary": true}, + "retry": true + }; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const chaos = ns.readPort(Ports.Bladeburner) as Record; + + executorJob.script = GetDir + "communities.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const communities = ns.readPort(Ports.Bladeburner) as Record; + + executorJob.script = GetDir + "estPop.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const estPop = ns.readPort(Ports.Bladeburner) as Record; + + bladeburnerCities = Object.freeze(Object.fromEntries(Object.values(ns.enums.CityName).map(c => [c, { + "chaos": chaos[c], + "communities": communities[c], + "estimatedPopulation": estPop[c] + } as BladeburnerCity])) as typeof bladeburnerCities); +} \ No newline at end of file diff --git a/bladeburner/genInfo/player.ts b/bladeburner/genInfo/player.ts new file mode 100644 index 0000000..0efe423 --- /dev/null +++ b/bladeburner/genInfo/player.ts @@ -0,0 +1,100 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; +import {BladeburnerGetDir as GetDir} from "bladeburner/util"; + +/** Bladeburner skill info */ +export interface BladeburnerSkill { + /** Current skill level */ + level: ReturnType, + + /** Point cost of upgrading */ + cost: ReturnType +} + +export type BladeburnerSkills = Record; + +/** Extends the Player interface with player info for Bladeburner */ +export interface BladeburnerPlayer extends Player {bladeburner: { + city: ReturnType, + rank: ReturnType, + skills: BladeburnerSkills, + skillPoints: ReturnType, + + /** Current stamina, max stamina */ + stamina: ReturnType, + teamSize: ReturnType +}} + +export var bladeburnerPlayer: BladeburnerPlayer; + +export function getBladeburnerEffectiveSkills(player: BladeburnerPlayer) { + var skills = structuredClone(player.skills); + const reaperMult = 1 + (0.02 * player.bladeburner.skills[BladeburnerSkillName.Reaper].level); + const evasiveSysMult = 1 + (0.04 * player.bladeburner.skills[BladeburnerSkillName.EvasiveSystem].level); + + for (const s of ["dexterity", "agility"] as Array) + skills[s] *= evasiveSysMult; + for (const s of ["strength", "defense", "dexterity", "agility"] as Array) { + skills[s] *= reaperMult; + skills[s] = Math.floor(skills[s]); + } + return skills; +} + +export async function main(ns: NS) { + const hostname = ns.self().server; + var player = ns.getPlayer() as BladeburnerPlayer; + + var executorJob: ExecutorJob = { + "script": GetDir + "skill/points.ts", + "hostname": hostname, + "threadOrOptions": {"temporary": true}, + "retry": true + }; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const points = ns.readPort(Ports.Bladeburner) as BladeburnerPlayer["bladeburner"]["skillPoints"]; + + executorJob.script = GetDir + "city/current.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const city = ns.readPort(Ports.Bladeburner) as BladeburnerPlayer["bladeburner"]["city"]; + + executorJob.script = GetDir + "rank.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const rank = ns.readPort(Ports.Bladeburner) as BladeburnerPlayer["bladeburner"]["rank"]; + + executorJob.script = GetDir + "teamSize.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const teamSize = ns.readPort(Ports.Bladeburner) as BladeburnerPlayer["bladeburner"]["teamSize"]; + + executorJob.script = GetDir + "stamina.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const stamina = ns.readPort(Ports.Bladeburner) as BladeburnerPlayer["bladeburner"]["stamina"]; + + executorJob.script = GetDir + "skill/level.ts"; + executorJob.args = ["-a"]; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const levels = ns.readPort(Ports.Bladeburner) as Record; + + executorJob.script = GetDir + "skill/upgradeCost.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Bladeburner); + const costs = ns.readPort(Ports.Bladeburner) as Record; + + const skills = Object.fromEntries(ns.bladeburner.getSkillNames().map(s => [s, ({"level": levels[s], "cost": costs[s]} as BladeburnerSkill)])) as BladeburnerSkills; + + player.bladeburner = { + "city": city, + "rank": rank, + "skills": skills, + "skillPoints": points, + "stamina": stamina, + "teamSize": teamSize + }; + bladeburnerPlayer = player; +} \ No newline at end of file diff --git a/bladeburner/get/action/autoLevel.ts b/bladeburner/get/action/autoLevel.ts new file mode 100644 index 0000000..90876b2 --- /dev/null +++ b/bladeburner/get/action/autoLevel.ts @@ -0,0 +1,11 @@ +import {Ports} from "lib/util"; +import {mapBladeburnerActions} from "bladeburner/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["a", false]]); + const getAuto = ns.bladeburner.getActionAutolevel; + ns.writePort(Ports.Bladeburner, flags.a ? + mapBladeburnerActions(ns, getAuto): + getAuto(ns.args[0] as BladeburnerActionType, ns.args[1] as BladeburnerActionName) + ); +} \ No newline at end of file diff --git a/bladeburner/get/action/countRemaining.ts b/bladeburner/get/action/countRemaining.ts new file mode 100644 index 0000000..61e5713 --- /dev/null +++ b/bladeburner/get/action/countRemaining.ts @@ -0,0 +1,11 @@ +import {Ports} from "lib/util"; +import {mapBladeburnerActions} from "bladeburner/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["a", false]]); + const getCountRemaining = ns.bladeburner.getActionCountRemaining; + ns.writePort(Ports.Bladeburner, flags.a ? + mapBladeburnerActions(ns, getCountRemaining) : + getCountRemaining(ns.args[0] as BladeburnerActionType, ns.args[1] as BladeburnerActionName) + ); +} \ No newline at end of file diff --git a/bladeburner/get/action/currentLevel.ts b/bladeburner/get/action/currentLevel.ts new file mode 100644 index 0000000..60a6e66 --- /dev/null +++ b/bladeburner/get/action/currentLevel.ts @@ -0,0 +1,11 @@ +import {Ports} from "lib/util"; +import {mapBladeburnerActions} from "bladeburner/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["a", false]]); + const getLevel = ns.bladeburner.getActionCurrentLevel; + ns.writePort(Ports.Bladeburner, flags.a ? + mapBladeburnerActions(ns, getLevel): + getLevel(ns.args[0] as BladeburnerActionType, ns.args[1] as BladeburnerActionName) + ); +} \ No newline at end of file diff --git a/bladeburner/get/action/currentTime.ts b/bladeburner/get/action/currentTime.ts new file mode 100644 index 0000000..8e2b15b --- /dev/null +++ b/bladeburner/get/action/currentTime.ts @@ -0,0 +1,5 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + ns.writePort(Ports.Bladeburner, ns.bladeburner.getActionCurrentTime()); +} \ No newline at end of file diff --git a/bladeburner/get/action/estSuccessChance.ts b/bladeburner/get/action/estSuccessChance.ts new file mode 100644 index 0000000..887f7f1 --- /dev/null +++ b/bladeburner/get/action/estSuccessChance.ts @@ -0,0 +1,15 @@ +import {Ports} from "lib/util"; +import {mapBladeburnerActions} from "bladeburner/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["a", false], ["s", -1]]); + const args = flags._ as [BladeburnerActionType, BladeburnerActionName]; + const getEstimate = flags.s == -1 ? + ns.bladeburner.getActionEstimatedSuccessChance : + (actionType: BladeburnerActionType, actionName: BladeburnerActionName) => + ns.bladeburner.getActionEstimatedSuccessChance(actionType, actionName, flags.s as number); + ns.writePort(Ports.Bladeburner, flags.a ? + mapBladeburnerActions(ns, getEstimate): + getEstimate(args[0], args[1]) + ); +} \ No newline at end of file diff --git a/bladeburner/get/action/maxLevel.ts b/bladeburner/get/action/maxLevel.ts new file mode 100644 index 0000000..ba3cded --- /dev/null +++ b/bladeburner/get/action/maxLevel.ts @@ -0,0 +1,11 @@ +import {Ports} from "lib/util"; +import {mapBladeburnerActions} from "bladeburner/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["a", false]]); + const getMaxLevel = ns.bladeburner.getActionMaxLevel; + ns.writePort(Ports.Bladeburner, flags.a ? + mapBladeburnerActions(ns, getMaxLevel): + getMaxLevel(ns.args[0] as BladeburnerActionType, ns.args[1] as BladeburnerActionName) + ); +} \ No newline at end of file diff --git a/bladeburner/get/action/repGain.ts b/bladeburner/get/action/repGain.ts new file mode 100644 index 0000000..04435ed --- /dev/null +++ b/bladeburner/get/action/repGain.ts @@ -0,0 +1,11 @@ +import {Ports} from "lib/util"; +import {mapBladeburnerActions} from "bladeburner/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["a", false]]); + const getRepGain = ns.bladeburner.getActionRepGain; + ns.writePort(Ports.Bladeburner, flags.a ? + mapBladeburnerActions(ns, getRepGain): + getRepGain(ns.args[0] as BladeburnerActionType, ns.args[1] as BladeburnerActionName) + ); +} \ No newline at end of file diff --git a/bladeburner/get/action/successes.ts b/bladeburner/get/action/successes.ts new file mode 100644 index 0000000..310530c --- /dev/null +++ b/bladeburner/get/action/successes.ts @@ -0,0 +1,11 @@ +import {Ports} from "lib/util"; +import {mapBladeburnerActions} from "bladeburner/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["a", false]]); + const getSuccesses = ns.bladeburner.getActionSuccesses; + ns.writePort(Ports.Bladeburner, flags.a ? + mapBladeburnerActions(ns, getSuccesses): + getSuccesses(ns.args[0] as BladeburnerActionType, ns.args[1] as BladeburnerActionName) + ); +} \ No newline at end of file diff --git a/bladeburner/get/action/time.ts b/bladeburner/get/action/time.ts new file mode 100644 index 0000000..833341f --- /dev/null +++ b/bladeburner/get/action/time.ts @@ -0,0 +1,11 @@ +import {Ports} from "lib/util"; +import {mapBladeburnerActions} from "bladeburner/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["a", false]]); + const getTime = ns.bladeburner.getActionTime; + ns.writePort(Ports.Bladeburner, flags.a ? + mapBladeburnerActions(ns, getTime): + getTime(ns.args[0] as BladeburnerActionType, ns.args[1] as BladeburnerActionName) + ); +} \ No newline at end of file diff --git a/bladeburner/get/blackOp/next.ts b/bladeburner/get/blackOp/next.ts new file mode 100644 index 0000000..37b24b8 --- /dev/null +++ b/bladeburner/get/blackOp/next.ts @@ -0,0 +1,5 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + ns.writePort(Ports.Bladeburner, ns.bladeburner.getNextBlackOp()); +} \ No newline at end of file diff --git a/bladeburner/get/blackOp/rank.ts b/bladeburner/get/blackOp/rank.ts new file mode 100644 index 0000000..6882b4b --- /dev/null +++ b/bladeburner/get/blackOp/rank.ts @@ -0,0 +1,5 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + ns.writePort(Ports.Bladeburner, ns.bladeburner.getBlackOpRank(ns.args[0] as BladeburnerBlackOpName)); +} \ No newline at end of file diff --git a/bladeburner/get/city/chaos.ts b/bladeburner/get/city/chaos.ts new file mode 100644 index 0000000..b29059e --- /dev/null +++ b/bladeburner/get/city/chaos.ts @@ -0,0 +1,10 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["a", false]]); + const getChaos = ns.bladeburner.getCityChaos; + ns.writePort(Ports.Bladeburner, flags.a ? + Object.fromEntries(Object.values(ns.enums.CityName).map(c => [c, getChaos(c)])) : + getChaos(ns.args[0] as CityName) + ); +} \ No newline at end of file diff --git a/bladeburner/get/city/communities.ts b/bladeburner/get/city/communities.ts new file mode 100644 index 0000000..5fa7327 --- /dev/null +++ b/bladeburner/get/city/communities.ts @@ -0,0 +1,10 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["a", false]]); + const getComms = ns.bladeburner.getCityCommunities; + ns.writePort(Ports.Bladeburner, flags.a ? + Object.fromEntries(Object.values(ns.enums.CityName).map(c => [c, getComms(c)])) : + getComms(ns.args[0] as CityName) + ); +} \ No newline at end of file diff --git a/bladeburner/get/city/current.ts b/bladeburner/get/city/current.ts new file mode 100644 index 0000000..3324f7f --- /dev/null +++ b/bladeburner/get/city/current.ts @@ -0,0 +1,5 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + ns.writePort(Ports.Bladeburner, ns.bladeburner.getCity()); +} \ No newline at end of file diff --git a/bladeburner/get/city/estPop.ts b/bladeburner/get/city/estPop.ts new file mode 100644 index 0000000..965d183 --- /dev/null +++ b/bladeburner/get/city/estPop.ts @@ -0,0 +1,10 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["a", false]]); + const getEstPop = ns.bladeburner.getCityEstimatedPopulation; + ns.writePort(Ports.Bladeburner, flags.a ? + Object.fromEntries(Object.values(ns.enums.CityName).map(c => [c, getEstPop(c)])) : + getEstPop(ns.args[0] as CityName) + ); +} \ No newline at end of file diff --git a/bladeburner/get/rank.ts b/bladeburner/get/rank.ts new file mode 100644 index 0000000..8b2bc3e --- /dev/null +++ b/bladeburner/get/rank.ts @@ -0,0 +1,5 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + ns.writePort(Ports.Bladeburner, ns.bladeburner.getRank()); +} \ No newline at end of file diff --git a/bladeburner/get/skill/level.ts b/bladeburner/get/skill/level.ts new file mode 100644 index 0000000..10e738a --- /dev/null +++ b/bladeburner/get/skill/level.ts @@ -0,0 +1,10 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["a", false]]); + const getLevel = ns.bladeburner.getSkillLevel; + ns.writePort(Ports.Bladeburner, flags.a ? + Object.fromEntries(ns.bladeburner.getSkillNames().map(s => [s, getLevel(s)])): + getLevel(ns.args[0] as BladeburnerSkillName) + ); +} \ No newline at end of file diff --git a/bladeburner/get/skill/points.ts b/bladeburner/get/skill/points.ts new file mode 100644 index 0000000..e4e91b2 --- /dev/null +++ b/bladeburner/get/skill/points.ts @@ -0,0 +1,5 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + ns.writePort(Ports.Bladeburner, ns.bladeburner.getSkillPoints()); +} \ No newline at end of file diff --git a/bladeburner/get/skill/upgradeCost.ts b/bladeburner/get/skill/upgradeCost.ts new file mode 100644 index 0000000..6358bf1 --- /dev/null +++ b/bladeburner/get/skill/upgradeCost.ts @@ -0,0 +1,10 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["a", false]]); + const getCost = ns.bladeburner.getSkillUpgradeCost; + ns.writePort(Ports.Bladeburner, flags.a ? + Object.fromEntries(ns.bladeburner.getSkillNames().map(s => [s, getCost(s)])): + getCost(ns.args[0] as BladeburnerSkillName) + ); +} \ No newline at end of file diff --git a/bladeburner/get/stamina.ts b/bladeburner/get/stamina.ts new file mode 100644 index 0000000..24f3e8d --- /dev/null +++ b/bladeburner/get/stamina.ts @@ -0,0 +1,5 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + ns.writePort(Ports.Bladeburner, ns.bladeburner.getStamina()); +} \ No newline at end of file diff --git a/bladeburner/get/teamSize.ts b/bladeburner/get/teamSize.ts new file mode 100644 index 0000000..a519b6b --- /dev/null +++ b/bladeburner/get/teamSize.ts @@ -0,0 +1,5 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + ns.writePort(Ports.Bladeburner, ns.bladeburner.getTeamSize()); +} \ No newline at end of file diff --git a/bladeburner/join/division.js b/bladeburner/join/division.js new file mode 100644 index 0000000..d4f97e4 --- /dev/null +++ b/bladeburner/join/division.js @@ -0,0 +1,4 @@ +/** @param {NS} ns */ +export async function main(ns) { + ns.bladeburner.joinBladeburnerDivision(); +} \ No newline at end of file diff --git a/bladeburner/join/faction.js b/bladeburner/join/faction.js new file mode 100644 index 0000000..443dafe --- /dev/null +++ b/bladeburner/join/faction.js @@ -0,0 +1,4 @@ +/** @param {NS} ns */ +export async function main(ns) { + ns.bladeburner.joinBladeburnerFaction(); +} \ No newline at end of file diff --git a/bladeburner/launch.ts b/bladeburner/launch.ts new file mode 100644 index 0000000..2dd2b57 --- /dev/null +++ b/bladeburner/launch.ts @@ -0,0 +1,13 @@ +import {Ports} from "lib/util"; +import {extResetInfo, bladeburnerEnabled} from "lib/genInfo/reset"; +import {ExecutorJob} from "daemons/executor"; + +export async function main(ns: NS) { + if (bladeburnerEnabled(extResetInfo) && ns.bladeburner.joinBladeburnerDivision()) + ns.writePort(Ports.Exec, { + script: "bladeburner/forge.ts", + hostname: ns.self().server, + threadOrOptions: {preventDuplicates: true}, + retry: true + } as ExecutorJob); +} \ No newline at end of file diff --git a/bladeburner/manageSkills.ts b/bladeburner/manageSkills.ts new file mode 100644 index 0000000..25e548d --- /dev/null +++ b/bladeburner/manageSkills.ts @@ -0,0 +1,312 @@ +import {Ports} from "lib/util"; +import {extResetInfo} from "lib/genInfo/reset"; +import {ExecutorJob} from "daemons/executor"; +import {bladeburnerPlayer} from "bladeburner/genInfo/player"; +import {bladeburnerActions, getNextBlackOpShim} from "bladeburner/genInfo/actions"; + +const SuccessFactorBase = 1; +const EffectiveSkillFactor = 0.8; + +/** Affects the valuation of Blade's Intuition. */ +const Factor1 = SuccessFactorBase * 16; + +/** Affects the valuation of Reaper. */ +const Factor6 = EffectiveSkillFactor; + +/** Affects the valuation of Hands of Midas. */ +const Factor19 = 1e3; + +/** Affects the valuation of Digital Observer */ +const Factor24 = SuccessFactorBase * 16; + +/** Affects the valuation of Short-Circuit. */ +const Factor25 = SuccessFactorBase * 2; + +/** Affects the valuation of Evasive System. */ +const Factor33 = EffectiveSkillFactor; + +/** Affects the valuation of Cyber's Edge. */ +const Factor37 = 5e5; + +/** Affects the valuation of Datamancer. */ +const Factor53 = 16; + +/** Affects the valuation of Tracer. */ +const Factor56 = SuccessFactorBase; + +/** Affects the valuation of Overclock. */ +const Factor60 = 1; + +/** Affects the valuation of Cloak. */ +const Factor66 = SuccessFactorBase; + +/** Affects the valuation of Hyperdrive. */ +const Factor69 = 1e4; + +/** Affects the valuation of Cyber's Edge, Reaper, Evasive System, and Overclock. */ +const TargetStamina = 1e3; + +const MaxOverclockLevel = 90; + +/** Based on game code. Skills contribute to success sub-linearly. */ +const EstimatedDecay = 0.8; + +const BladesIntuitionMult = 0.03; +const CloakMult = 0.055; +const ShortCircuitMult = 0.055; +const DigitalObserverMult = 0.04; +const TracerMult = 0.04; +const ReaperMult = 0.02; +const EvasiveSystemMult = 0.04; +const HandsOfMidasMult = 0.1; +const HyperdriveMult = 0.1; + +const MinTimeSinceInstall = 600e3; + +const StealthContracts = new Set(["Tracking" as BladeburnerContractName]); + +// Sting is intentionally omitted +const StealthOperations = new Set([ + "Investigation", + "Undercover Operation", + "Stealth Retirement Operation", + "Assassination", +] as Array); + +const StealthBlackOps = new Set([ + "Operation Zero", + "Operation Shoulder of Orion", + "Operation Morpheus", +] as Array); + +const StealthActions = StealthContracts.union(StealthOperations).union(StealthBlackOps) as Set; + +const KillContracts = new Set(["Retirement", "Bounty Hunter"] as Array); + +const KillOperations = new Set( + ["Raid", "Stealth Retirement Operation", "Assassination"] as Array +); + +const KillBlackOps = new Set([ + "Operation Typhoon", + "Operation X", + "Operation Titan", + "Operation Ares", + "Operation Archangel", + "Operation Juggernaut", + "Operation Red Dragon", + "Operation K", + "Operation Deckard", + "Operation Tyrell", + "Operation Wallace", + "Operation Hyron", + "Operation Ion Storm", + "Operation Annihilus", + "Operation Ultron", +] as Array); + +const KillActions = KillContracts.union(KillOperations).union(KillBlackOps) as Set; + +type SkillAppraiser = () => number; + +/** Not *known* to not be guaranteed, anyway. */ +function successNotGuaranteed(action: BladeburnerActionName) { + // First disjunct ensures that impossible actions (i.e., Raid with 0 communities) don't count + return bladeburnerActions[action].estimatedSuccessChance[1] && + bladeburnerActions[action].estimatedSuccessChance[1] < 1; +} + +export async function main(ns: NS) { + /** Maxim 1: A Sergeant in motion outranks a Lieutenant who doesn't know what's going on. */ + function appraiseBladesIntuition() { + // To keep the calculation relatively simple, we consider only the next Black Operation, + // and use a high Factor1 to account for general utility and particular importance in the final trio of + // Black Operations. + if (!nextBlackOp || nextBlackOp[1].estimatedSuccessChance[1] == 1) return 0; + + let val = 1 + bladeburnerPlayer.bladeburner.skills["Digital Observer"].level * DigitalObserverMult; + if (nextBlackOp && nextBlackOp[1].estimatedSuccessChance[1] < 1) { + if (StealthBlackOps.has(nextBlackOp[0])) + val *= 1 + (bladeburnerPlayer.bladeburner.skills.Cloak.level * CloakMult); + else if (KillBlackOps.has(nextBlackOp[0])) + val *= 1 + (bladeburnerPlayer.bladeburner.skills["Short-Circuit"].level * ShortCircuitMult); + } + return val * Factor1 / bladeburnerPlayer.bladeburner.skills["Blade's Intuition"].cost; + } + + // FIXME: Cloak and Short-Circuit appraisers need to account for operations that are both stealth and kill + /** Maxim 66: Necessity is the mother of deception. */ + function appraiseCloak() { + const doMult = 1 + bladeburnerPlayer.bladeburner.skills["Digital Observer"].level * DigitalObserverMult; + let val = 0; + if (bladeburnerActions.Tracking.estimatedSuccessChance[1] < 1) + val = 1 + bladeburnerPlayer.bladeburner.skills.Tracer.level * TracerMult; + val += Array.from(StealthOperations.values()).filter(successNotGuaranteed).length * doMult; + if (nextBlackOp && nextBlackOp[1].estimatedSuccessChance[1] < 1 && StealthBlackOps.has(nextBlackOp[0])) + val += doMult; + if (!val) + return 0; + val *= 1 + bladeburnerPlayer.bladeburner.skills["Blade's Intuition"].level; + return val * Factor66 / bladeburnerPlayer.bladeburner.skills.Cloak.cost; + } + + /** Maxim 25: If a manufacturer's warranty covers the damage you do, you didn't do enough damage. */ + function appraiseShortCircuit() { + const doMult = 1 + bladeburnerPlayer.bladeburner.skills["Digital Observer"].level * DigitalObserverMult; + let val = Array.from(KillContracts.values()).filter(successNotGuaranteed).length * + (1 + bladeburnerPlayer.bladeburner.skills.Tracer.level * TracerMult); + val += Array.from(KillOperations.values()).filter(successNotGuaranteed).length * doMult; + if (nextBlackOp && nextBlackOp[1].estimatedSuccessChance[1] < 1 && KillBlackOps.has(nextBlackOp[0])) + val += doMult; + if (!val) + return 0; + val *= 1 + bladeburnerPlayer.bladeburner.skills["Blade's Intuition"].level; + return val * Factor25 / bladeburnerPlayer.bladeburner.skills["Short-Circuit"].cost; + } + + /** Maxim 24: Any sufficiently advanced technology is indistinguishable from a big gun. */ + function appraiseDigitalObserver() { + // To keep the calculation relatively simple, we consider only the next Black Operation, + // and use a high Factor24 to account for general utility and particular importance in the final trio of + // Black Operations. + if (!nextBlackOp || nextBlackOp[1].estimatedSuccessChance[1] == 1) return 0; + + let val = 1 + bladeburnerPlayer.bladeburner.skills["Blade's Intuition"].level * BladesIntuitionMult; + if (StealthBlackOps.has(nextBlackOp[0])) + val *= 1 + (bladeburnerPlayer.bladeburner.skills.Cloak.level * CloakMult); + else if (KillBlackOps.has(nextBlackOp[0])) + val *= 1 + (bladeburnerPlayer.bladeburner.skills["Short-Circuit"].level * ShortCircuitMult); + return val * Factor24 / bladeburnerPlayer.bladeburner.skills["Digital Observer"].cost; + } + + /** Maxim 56: Infantry exists to paint targets for people with real guns. */ + function appraiseTracer() { + let val = 0; + if (bladeburnerActions.Tracking.estimatedSuccessChance[1] < 1) + val = 1 + bladeburnerPlayer.bladeburner.skills.Cloak.level * CloakMult; + val += Array.from(KillContracts.values()).filter(successNotGuaranteed).length * + (1 + bladeburnerPlayer.bladeburner.skills["Short-Circuit"].level * ShortCircuitMult); + if (!val) // If contracts all have max 100% estimated success chance + return 0; + val *= 1 + bladeburnerPlayer.bladeburner.skills["Blade's Intuition"].level * BladesIntuitionMult; + return val * Factor56 / bladeburnerPlayer.bladeburner.skills.Tracer.cost; + } + + /** Maxim 60: Any weapon's rate of fire is inversely proportional to the number of available targets. */ + function appraiseOverclock() { + const level = bladeburnerPlayer.bladeburner.skills.Overclock.level; + if (level == MaxOverclockLevel) + return 0; + + const relevantActions = Object.values(bladeburnerActions).filter(a => a.successes > 2 && a.countRemaining > 10); + const timeReduction = relevantActions.reduce((sum, a) => a.time, 0) * 1 / (100 - level); + return Factor60 * timeReduction * bladeburnerPlayer.bladeburner.stamina[1] * ( + 1 + + Math.log2(1 + HandsOfMidasMult * bladeburnerPlayer.bladeburner.skills["Hands of Midas"].level) + + Math.log2(1 + HyperdriveMult * bladeburnerPlayer.bladeburner.skills["Hyperdrive"].level) + ) / (TargetStamina * bladeburnerPlayer.bladeburner.skills.Overclock.cost); + } + + // TODO: Account for stamina in appraisals of Reaper and Evasive System + /** Maxim 6: If violence wasn’t your last resort, you failed to resort to enough of it. */ + function appraiseReaper() { + let val = 0; + const mult = 1 + (EvasiveSystemMult * bladeburnerPlayer.bladeburner.skills["Evasive System"].level); + let adjustedSkills = structuredClone(bladeburnerPlayer.skills); + adjustedSkills.agility *= mult; + adjustedSkills.dexterity *= mult; + if (nextBlackOp && nextBlackOp[1].estimatedSuccessChance[1] < 1) + val = (["strength", "defense", "dexterity", "agility"] as Array).reduce((sum, stat) => { + return sum + + Math.pow(adjustedSkills[stat] * (1 + ReaperMult), EstimatedDecay) - + Math.pow(adjustedSkills[stat], EstimatedDecay); + }, 0); + return val * Factor6 / bladeburnerPlayer.bladeburner.skills.Reaper.cost; + } + + /** Maxim 33: If you're leaving tracks, you're being followed. */ + function appraiseEvasiveSystem() { + let val = 0; + const mult = 1 + (ReaperMult * bladeburnerPlayer.bladeburner.skills.Reaper.level); + let adjustedSkills = structuredClone(bladeburnerPlayer.skills); + if (nextBlackOp && nextBlackOp[1].estimatedSuccessChance[1] < 1) + val = (["dexterity", "agility"] as Array).reduce((sum, stat) => { + adjustedSkills[stat] *= mult; + return sum + + Math.pow(adjustedSkills[stat] * (1 + EvasiveSystemMult), EstimatedDecay) - + Math.pow(adjustedSkills[stat], EstimatedDecay); + }, 0); + return val * Factor33 / bladeburnerPlayer.bladeburner.skills["Evasive System"].cost; + } + + /** Maxim 53: The intel you've got is never the intel you want. */ + function appraiseDatamancer() { + return Factor53 * bladeburnerPlayer.mults.bladeburner_analysis / ( + Math.pow(1 + bladeburnerPlayer.bladeburner.skills.Datamancer.level, 0.5) * + bladeburnerPlayer.bladeburner.skills.Datamancer.cost + ); + } + + /** Maxim 37: There is no 'overkill.' There is only 'open fire' and 'reload.' */ + function appraiseCybersEdge() { + if (bladeburnerPlayer.bladeburner.stamina[1] > TargetStamina) + return 0; + return Factor37 / ( + Math.abs((TargetStamina >>> 1) - bladeburnerPlayer.bladeburner.stamina[1]) * + (100 - bladeburnerPlayer.bladeburner.skills.Overclock.level) * + bladeburnerPlayer.mults.bladeburner_stamina_gain * + bladeburnerPlayer.bladeburner.skills["Cyber's Edge"].cost + ); + } + + /** Maxim 19: The world is richer when you turn enemies into friends, but that's not the same as you being richer. */ + function appraiseHandsofMidas() { + return Factor19 / ( + Math.log2(bladeburnerPlayer.money) * + (100 - bladeburnerPlayer.bladeburner.skills.Overclock.level) * + bladeburnerPlayer.bladeburner.skills["Hands of Midas"].cost + ); + } + + /** Maxim 69: Sometimes rank is a function of firepower. */ + function appraiseHyperdrive() { + return Factor69 / ( + (1 + Math.log2(1 + bladeburnerPlayer.bladeburner.skills.Hyperdrive.level)) * + (100 - bladeburnerPlayer.bladeburner.skills.Overclock.level) * + bladeburnerPlayer.bladeburner.skills.Hyperdrive.cost + ); + } + + // If there's a stamina penalty or there hasn't been much time since install to accumulate exp, + // calculations are liable to be thrown off, so we hold off on spending skill points + if (bladeburnerPlayer.bladeburner.stamina[0] * 2 < bladeburnerPlayer.bladeburner.stamina[1]) { + ns.print("WARN: Stamina too low to evaluate skills."); + return; + } + + if (Date.now() - extResetInfo.lastAugReset < MinTimeSinceInstall) { + ns.print("INFO: Postponing skill buy."); + return; + } + + const nextBlackOp = getNextBlackOpShim(); + if (nextBlackOp) + ns.print("INFO: Next Black Op is ", nextBlackOp[0]); + else + ns.print("SUCCESS: All Black Ops completed!"); + const appraisals = ( + ns.bladeburner.getSkillNames() + .map(skill => [skill, (eval("appraise" + skill.replaceAll(/\W/g, "")) as SkillAppraiser)()]) as Array<[BladeburnerSkillName, number]> + ).sort((a, b) => b[1] - a[1]); + ns.print("INFO: Skill valuations: " + JSON.stringify(appraisals)); + if (bladeburnerPlayer.bladeburner.skillPoints >= bladeburnerPlayer.bladeburner.skills[appraisals[0][0]].cost) { + ns.print("Buying a level of ", appraisals[0][0]); + ns.writePort(Ports.Exec, { + "script": "/bladeburner/upgradeSkill.ts", + "hostname": ns.self().server, + "args": [appraisals[0][0]], + "threadOrOptions": {"temporary": true}, + "retry": true + } as ExecutorJob) + } +} \ No newline at end of file diff --git a/bladeburner/travel.ts b/bladeburner/travel.ts new file mode 100644 index 0000000..85167c5 --- /dev/null +++ b/bladeburner/travel.ts @@ -0,0 +1,3 @@ +export async function main(ns: NS) { + ns.bladeburner.switchCity(ns.args[0] as CityName); +} \ No newline at end of file diff --git a/bladeburner/upgradeSkill.ts b/bladeburner/upgradeSkill.ts new file mode 100644 index 0000000..581fe77 --- /dev/null +++ b/bladeburner/upgradeSkill.ts @@ -0,0 +1,3 @@ +export async function main(ns: NS) { + ns.bladeburner.upgradeSkill(ns.args[0] as BladeburnerSkillName); +} \ No newline at end of file diff --git a/bladeburner/util.ts b/bladeburner/util.ts new file mode 100644 index 0000000..e749a0b --- /dev/null +++ b/bladeburner/util.ts @@ -0,0 +1,43 @@ +export const BladeburnerGetDir = "/bladeburner/get/"; + +// Constants taken from game code +export const BladeburnerChaosThreshold = 50; +export const BladeburnerPopThreshold = 1e9; + +function getTypeFromName(ns: NS, actionName: BladeburnerActionName): BladeburnerActionType { + if (ns.bladeburner.getGeneralActionNames().includes(actionName as BladeburnerGeneralActionName)) return "General" as BladeburnerActionType; + if (ns.bladeburner.getContractNames().includes(actionName as BladeburnerContractName)) return "Contracts" as BladeburnerActionType; + if (ns.bladeburner.getOperationNames().includes(actionName as BladeburnerOperationName)) return "Operations" as BladeburnerActionType; + if (ns.bladeburner.getBlackOpNames().includes(actionName as BladeburnerBlackOpName)) return "Black Operations" as BladeburnerActionType; + throw new Error("\"${actionName}\" is not a valid BladeburnerActionName"); +} + +export function mapBladeburnerActions( + ns: NS, mapFunc: (actionType: BladeburnerActionType, actionName: BladeburnerActionName) => mapReturn +) { + const entryMapper = (actionName: BladeburnerActionName) => [actionName, mapFunc(getTypeFromName(ns, actionName), actionName)]; + let entries: Array> = []; + + for (const nameGetter of [ + ns.bladeburner.getGeneralActionNames, + ns.bladeburner.getContractNames, + ns.bladeburner.getOperationNames, + ns.bladeburner.getBlackOpNames + ]) { + try { entries = entries.concat(nameGetter().map(entryMapper)); } + catch (e) {} + } + + return Object.fromEntries(entries) as {[an in BladeburnerActionName]: mapReturn}; +} + +export type BladeburnerAutoLevelMap = ReturnType>; +export type BladeburnerActionEstimatedChanceMap = ReturnType>; +export type BladeburnerActionLevelMap = ReturnType>; +export type BladeburnerActionRepMap = ReturnType>; +export type BladeburnerActionsRemainingMap = ReturnType>; +export type BladeburnerActionSucessesMap = ReturnType>; +export type BladeburnerActionTimeMap = ReturnType>; + +export async function main(ns: NS) { +} \ No newline at end of file diff --git a/contract/2color.ts b/contract/2color.ts new file mode 100644 index 0000000..40c7573 --- /dev/null +++ b/contract/2color.ts @@ -0,0 +1,57 @@ +import {Ports} from "lib/util"; + +type Signature = CodingContractSignatures[CodingContractName.Proper2ColoringOfAGraph]; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("/contract/getData.js", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + const [size, edges] = ns.readPort(Ports.Contract) as Signature[0]; + var graph = new Array(size); + + for (let i = 0; i < size; ++i) + graph[i] = {"color": 0, "touched": false, "edges": []}; + + for (let i = 0; i < edges.length; ++i) { + graph[edges[i][0]].edges.push(graph[edges[i][1]]); + graph[edges[i][1]].edges.push(graph[edges[i][0]]); + } + + graph.find(a => a.edges.length).touched = true; + const isTouched = (a: any) => a.touched; + let edgeFound = true; + do { + if (!edgeFound) + graph.find(a => !a.touched).touched = true; + edgeFound = false; + + for (let i = 1; i < graph.length; ++i) + if (!graph[i].touched) { + if (graph[i].edges.length) { + const t = graph[i].edges.find(isTouched); + if (t !== undefined) { + graph[i].color = t.color ^ 1; + graph[i].touched = true; + edgeFound = true; + } + } + else + graph[i].touched = true; + } + } while (!graph.every(isTouched)); + + if (graph.every((a) => { + return a.edges.every((b: any) => a.color ^ b.color); + })) + graph = graph.map(a => a.color); + else + graph = []; + + ns.writePort(Ports.Contract, graph as Signature[1]); + ns.spawn("/contract/attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/arrayJumpI.ts b/contract/arrayJumpI.ts new file mode 100644 index 0000000..d74ee42 --- /dev/null +++ b/contract/arrayJumpI.ts @@ -0,0 +1,22 @@ +import {Ports} from "lib/util"; + +type Signature = CodingContractSignatures[CodingContractName.ArrayJumpingGame]; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("/contract/getData.js", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + var ar: Array = ns.readPort(Ports.Contract) as Signature[0]; + + ar[ar.length - 1] = true; + for (let i = ar.length - 2; i >= 0; --i) + ar[i] = ar.slice(i + 1, Math.min(i + (ar[i] as number) + 1, ar.length)).some(a => a); + + ns.writePort(Ports.Contract, (ar[0] ? 1 : 0) as Signature[1]); + ns.spawn("./attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/arrayJumpII.ts b/contract/arrayJumpII.ts new file mode 100644 index 0000000..25fd9ae --- /dev/null +++ b/contract/arrayJumpII.ts @@ -0,0 +1,25 @@ +import {Ports} from "lib/util"; + +type Signature = CodingContractSignatures[CodingContractName.ArrayJumpingGameII]; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("./getData.js", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + var ar = ns.readPort(Ports.Contract) as Signature[0]; + + for (let i = ar.length - 1; i >= 0; --i) { + if (i + ar[i] >= ar.length - 1) + ar[i] = 1; + else if (ar[i]) + ar[i] = 1 + Math.min(...ar.slice(i + 1, Math.min(ar[i] + i + 1, ar.length)).filter((a) => { return a; })); + } + + ns.writePort(Ports.Contract, (ar[0] < Number.MAX_SAFE_INTEGER ? ar[0] : 0) as Signature[1]); + ns.spawn("./attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/attempt.js b/contract/attempt.js new file mode 100644 index 0000000..8bd5209 --- /dev/null +++ b/contract/attempt.js @@ -0,0 +1,9 @@ +import {Ports} from "lib/util"; + +/** @param {NS} ns */ +export async function main(ns) { + const answer = ns.readPort(Ports.Contract); + + if (answer != "NULL PORT DATA") + ns.writePort(Ports.Contract, ns.codingcontract.attempt(answer, ns.args[0], ns.args[1])); +} \ No newline at end of file diff --git a/contract/caesar.ts b/contract/caesar.ts new file mode 100644 index 0000000..32d5a44 --- /dev/null +++ b/contract/caesar.ts @@ -0,0 +1,29 @@ +import {Ports} from "lib/util"; + +/** @param {NS} ns */ +export async function main(ns) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("/contract/getData.js", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + var msg = ns.readPort(Ports.Contract); + const shift = msg[1]; + var enc = []; + msg = msg[0]; + + for (let i = 0; i < msg.length; ++i) { + enc.push(msg.charCodeAt(i)); + if (enc[i] != 0x20) { + enc[i] -= shift; + if (enc[i] < 0x41) + enc[i] += 26; + } + } + + ns.writePort(Ports.Contract, String.fromCharCode(...enc)); + ns.spawn("/contract/attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/get/data.ts b/contract/get/data.ts new file mode 100644 index 0000000..84d05c9 --- /dev/null +++ b/contract/get/data.ts @@ -0,0 +1,12 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["p", false]]); + const args = flags._ as Array; + const data = ns.codingcontract.getData(args[0], args[1]); + + if (flags.p) + ns.writePort(Ports.Contract, data); + else + ns.tprint(data.toString()); +} \ No newline at end of file diff --git a/contract/get/type.ts b/contract/get/type.ts new file mode 100644 index 0000000..d02d54b --- /dev/null +++ b/contract/get/type.ts @@ -0,0 +1,12 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["p", false]]); + const args = flags._ as Array; + const type = ns.codingcontract.getContractType(args[0], args[1]); + + if (flags.p) + ns.writePort(Ports.Contract, type); + else + ns.tprint(type); +} \ No newline at end of file diff --git a/contract/gridPathsI.js b/contract/gridPathsI.js new file mode 100644 index 0000000..4f3549b --- /dev/null +++ b/contract/gridPathsI.js @@ -0,0 +1,29 @@ +import {Ports} from "lib/util"; + +/** @param {NS} ns */ +export async function main(ns) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("./getData.js", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + var dim = ns.readPort(Ports.Contract); + --dim[0]; + --dim[1]; + const len = dim[0] + dim[1]; + var factorial = [1, 1, 1]; + + for (let i = 1; i <= len; ++i) { + factorial[2] *= i; + if (dim[0] == i) + factorial[0] = factorial[2]; + if (dim[1] == i) + factorial[1] = factorial[2]; + } + const paths = factorial[2] / (factorial[0] * factorial[1]); + ns.writePort(Ports.Contract, paths); + ns.spawn("./attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/gridPathsII.ts b/contract/gridPathsII.ts new file mode 100644 index 0000000..248e562 --- /dev/null +++ b/contract/gridPathsII.ts @@ -0,0 +1,35 @@ +import {Ports} from "lib/util"; + +type Signature = CodingContractSignatures[CodingContractName.UniquePathsInAGridII]; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("./get/data.ts", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + var grid = ns.readPort(Ports.Contract) as Signature[0]; + const dim = [grid.length, grid[0].length]; + + if (!grid[dim[0] - 1][dim[1] - 1]) { + grid[dim[0] - 1][dim[1] - 1] = 1; + for (let i = dim[0] - 1; i >= 0; --i) + for (let j = dim[1] - (i == dim[0] - 1 ? 2 : 1); j >= 0; --j) { + if (grid[i][j]) + grid[i][j] = 0; + else { + grid[i][j] = 0; + if (i < dim[0] - 1) + grid[i][j] += grid[i + 1][j]; + if (j < dim[1] - 1) + grid[i][j] += grid[i][j + 1]; + } + } + } + + ns.writePort(Ports.Contract, grid[0][0] as Signature[1]); + ns.spawn("/contract/attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/intPartition.ts b/contract/intPartition.ts new file mode 100644 index 0000000..0a6cd7b --- /dev/null +++ b/contract/intPartition.ts @@ -0,0 +1,35 @@ +import {Ports} from "lib/util"; + +type Signature = CodingContractSignatures[CodingContractName.TotalWaysToSum]; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("/contract/get/data.ts", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + const x = ns.readPort(Ports.Contract) as Signature[0]; + var p = [1,1]; + + for (let n = 2; n <= x; ++n) { + p.push(0); + for (let k = 1, pent = 1; pent <= n; ++k, pent = (3 * k * k - k) >> 1) { + if (k & 1) + p[n] += p[n - pent]; + else + p[n] -= p[n - pent]; + pent += k; + if (pent > n) + break; + if (k & 1) + p[n] += p[n - pent]; + else + p[n] -= p[n - pent]; + } + } + ns.writePort(Ports.Contract, (p[x] - 1) as Signature[1]); + ns.spawn("/contract/attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/intPartitionSet.js b/contract/intPartitionSet.js new file mode 100644 index 0000000..6fa2786 --- /dev/null +++ b/contract/intPartitionSet.js @@ -0,0 +1,30 @@ +import {Ports} from "lib/util"; + +/** @param {NS} ns */ +export async function main(ns) { + const flags = ns.flags([["p", false], ["s", ""], ["x", 0]]); + /*if (!flags.p) { + ns.run("./getData.js", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + var x, s; + + if (flags.x && flags.s) { + x = flags.x; + s = JSON.parse(flags.s); + } + else + [x, s] = ns.readPort(Ports.Contract); + var p = new Array(x + 1); + p[0] = 1; + p.fill(0, 1); + + for (const k of s) + for (let n = k; n <= x; ++n) + p[n] += p[n - k]; + + ns.writePort(Ports.Contract, p[x]); + ns.spawn("./attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + if (!flags.p) + ns.clearPort(Ports.Contract); +} \ No newline at end of file diff --git a/contract/intToHamming.js b/contract/intToHamming.js new file mode 100644 index 0000000..1eefbb2 --- /dev/null +++ b/contract/intToHamming.js @@ -0,0 +1,24 @@ +import {Ports} from "/library/library.js"; + +/** @param {NS} ns */ +export async function main(ns) { + const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("/contract/getData.js", {"temporary": true}, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + } + + var n = ns.readPort(Ports.Contract); + var nBits, parityBits; + var code = 0; + var codePos, nPos; + + nBits = Math.ceil(Math.log2(n)); + parityBits = Math.ceil(Math.log2(nBits)); + while (Math.ceil(Math.log2(nBits + parityBits)) < parityBits) + ++parityBits; + n = BigInt(n); // Need to prevent automatic conversion to 32-bit signed + for (nPos = nBits - 1, codePos = 1; codePos <= nBits + parityBits; ++codePos) { + + } +} \ No newline at end of file diff --git a/contract/ipAddr.ts b/contract/ipAddr.ts new file mode 100644 index 0000000..8718aab --- /dev/null +++ b/contract/ipAddr.ts @@ -0,0 +1,46 @@ +import {Ports} from "lib/util"; + +type Signature = CodingContractSignatures[CodingContractName.GenerateIPAddresses]; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("/contract/getData.js", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + const str = ns.readPort(Ports.Contract) as Signature[0]; + var addrs = []; + + const isValid = function(octet: string) { + return (octet == "0" || octet[0] != "0") && (octet.length < 3 || octet <= "255"); + } + + for (let a = 1; a < 4; ++a) { + var octets = new Array(4); + + octets[0] = str.slice(0, a); + if (isValid(octets[0])) + for (let b = 1; b < 4 && a + b <= str.length - 2; ++b) { + octets[1] = str.slice(a, a + b); + if (isValid(octets[1])) + for (let c = 3; c; --c) { + const d = str.length - a - b - c; + + if (d >= 1 && d <= 3) { + octets[2] = str.slice(a + b, a + b + c); + if (isValid(octets[2])) { + octets[3] = str.slice(a + b + c); + if (isValid(octets[3])) + addrs.push(octets[0] + "." + octets[1] + "." + octets[2] + "." + octets[3]); + } + } + } + } + } + + ns.writePort(Ports.Contract, addrs as Signature[1]); + ns.spawn("/contract/attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/lpf.js b/contract/lpf.js new file mode 100644 index 0000000..f3bedcd --- /dev/null +++ b/contract/lpf.js @@ -0,0 +1,53 @@ +import {Ports} from "lib/util"; + +/** @param {NS} ns */ +export async function main(ns) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("/contract/getData.js", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + var n = ns.readPort(Ports.Contract); + var primes = [3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, 4073, 4079, 4091, 4093]; + var lpf = 2; + var cap; + + while (!(n & 1)) + n >>= 1; + + cap = Math.sqrt(n); + for (let i = 0; n > 1 && primes[i && i - 1] <= cap;) { + if (i == primes.length) { + var isPrime; + + primes.push(primes[i - 1] + 2); + do { + isPrime = true; + for (let j = 0; j < primes.length - 1 && isPrime; ++j) { + const div = primes[i] / primes[j]; + isPrime = (div != Math.trunc(div)); + } + if (!isPrime) + primes[i] += 2; + } while (!isPrime); + } + { + const div = n / primes[i]; + if (div == Math.trunc(div)) { + n = div; + cap = Math.sqrt(div); + lpf = primes[i]; + } + else + ++i; + } + } + if (n > 1) + lpf = n; + + ns.writePort(Ports.Contract, lpf); + ns.spawn("/contract/attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/lzCompression.js b/contract/lzCompression.js new file mode 100644 index 0000000..7aa7224 --- /dev/null +++ b/contract/lzCompression.js @@ -0,0 +1,24 @@ +/*Lempel-Ziv (LZ) compression is a data compression technique which encodes data using references to earlier parts of the data. In this variant of LZ, data is encoded in two types of chunk. Each chunk begins with a length L, encoded as a single ASCII digit from 1 to 9, followed by the chunk data, which is either: + +1. Exactly L characters, which are to be copied directly into the uncompressed data. +2. A reference to an earlier part of the uncompressed data. To do this, the length is followed by a second ASCII digit X: each of the L output characters is a copy of the character X places before it in the uncompressed data. + +For both chunk types, a length of 0 instead means the chunk ends immediately, and the next character is the start of a new chunk. The two chunk types alternate, starting with type 1, and the final chunk may be of either type.*/ + +import {Ports} from "lib/util"; + +/** @param {NS} ns */ +export async function main(ns) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("./getData.js", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + var str = ns.readPort(Ports.Contract); + + /*ns.writePort(Ports.Contract, paths); + ns.spawn("./attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/lzDecompression.ts b/contract/lzDecompression.ts new file mode 100644 index 0000000..8a76fd1 --- /dev/null +++ b/contract/lzDecompression.ts @@ -0,0 +1,42 @@ +/*Lempel-Ziv (LZ) compression is a data compression technique which encodes data using references to earlier parts of the data. In this variant of LZ, data is encoded in two types of chunk. Each chunk begins with a length L, encoded as a single ASCII digit from 1 to 9, followed by the chunk data, which is either: + +1. Exactly L characters, which are to be copied directly into the uncompressed data. +2. A reference to an earlier part of the uncompressed data. To do this, the length is followed by a second ASCII digit X: each of the L output characters is a copy of the character X places before it in the uncompressed data. + +For both chunk types, a length of 0 instead means the chunk ends immediately, and the next character is the start of a new chunk. The two chunk types alternate, starting with type 1, and the final chunk may be of either type.*/ + +import {Ports} from "lib/util"; + +type Signature = CodingContractSignatures[CodingContractName.CompressionIILZDecompression]; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("./getData.js", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + var comp = ns.readPort(Ports.Contract) as Signature[0]; + var decomp = ""; + + for (let i = 0, chunkType = 0; i < comp.length; chunkType ^= 1) { + const chunkLen = parseInt(comp[i++]); + + if (chunkLen) { + if (chunkType) { + const chunkStart = decomp.length - parseInt(comp[i++]); + for (let j = chunkStart; j < chunkStart + chunkLen; ++j) + decomp += decomp[j]; + } + else { + decomp += comp.substring(i, i + chunkLen); + i += chunkLen; + } + } + } + + ns.writePort(Ports.Contract, decomp as Signature[1]); + ns.spawn("./attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/mathExprs.js b/contract/mathExprs.js new file mode 100644 index 0000000..4740c41 --- /dev/null +++ b/contract/mathExprs.js @@ -0,0 +1,68 @@ +import {Ports} from "lib/util"; + +const Ops = "\0-+*"; + +/** + * op tuple format + * 0: Result accumulator + * 1: Digits accumulator + * 2: Mult accumulator + * 3: Opcode index + */ + +/** @param {NS} ns */ +export async function main(ns) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("./get/data.ts", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + const timestamp = new Date().getTime(); + + const [str, target] = ns.readPort(Ports.Contract); + var digits = new Array(str.length); + var opStack = new Array(str.length); + var exprs = []; + var expr = str[0]; + var result; + + digits[0] = parseInt(str[0]); + opStack[0] = [0, digits[0], 1, Ops.length - 1]; + result = digits[0]; + for (let i = 1; i < digits.length; ++i) { + digits[i] = parseInt(str[i + 1]); + result *= 10; + result += digits[i]; + opStack[i] = [0, result, 1, 0]; + expr += Ops[0] + str[i + 1]; + } + + while (false && opStack.length) { + let index; + + if (opStack.length == digits.length && result == target) + exprs.push(expr.replaceAll(Ops[0], "")); + + while (opStack[opStack.length - 1][2] == Ops.length - 1) { + const prevOp = opStack.pop(); + result = prevOp[0]; + } + while (opStack.length < digits.length) { + index = opStack.length - 1; + opStack.push([opStack[index][0], opStack[index][1] * 10, opStack[index][2], 0]); + if (opStack[++index][1] < 0) + opStack[index][1] -= digits[index]; + else + opStack[index][1] += digits[index]; + } + index = opStack.length - 1; + result = opStack[index][0] + opStack[index][1] * opStack[index][2]; + } + + //ns.tprintf("%d valid expressions found", exprs.length); + //ns.tprintf("%s seconds elapsed", ns.formatNumber((new Date().getTime() - timestamp) / 1000)); + //ns.writePort(Ports.Contract, exprs); + //ns.spawn("./attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/mergeIntervals.ts b/contract/mergeIntervals.ts new file mode 100644 index 0000000..808a278 --- /dev/null +++ b/contract/mergeIntervals.ts @@ -0,0 +1,31 @@ +import {Ports} from "lib/util"; + +type Signature = CodingContractSignatures[CodingContractName.MergeOverlappingIntervals]; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("/contract/get/data.ts", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + var intervals = ns.readPort(Ports.Contract) as Signature[0]; + var temp; + + intervals.sort((a, b) => { return a[0] - b[0]; }); + temp = [intervals[0]]; + for (let i = 1; i < intervals.length; ++i) { + if (intervals[i][0] <= temp[temp.length - 1][1]) { + if (intervals[i][1] > temp[temp.length - 1][1]) + temp[temp.length - 1][1] = intervals[i][1]; + } + else + temp.push(intervals[i]); + } + intervals = temp; + + ns.writePort(Ports.Contract, intervals as Signature[1]); + ns.spawn("/contract/attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/rleCompression.ts b/contract/rleCompression.ts new file mode 100644 index 0000000..4d9159b --- /dev/null +++ b/contract/rleCompression.ts @@ -0,0 +1,39 @@ +import {Ports} from "lib/util"; + +type Signature = CodingContractSignatures[CodingContractName.CompressionIRLECompression]; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("./get/data.ts", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + const str = ns.readPort(Ports.Contract) as Signature[0]; + + var compressed: Signature[1] = ""; + var c = "\0"; + var n = 0; + for (let i = 0; i < str.length; ++i) { + if (str[i] == c) { + if (n < 9) + ++n; + else { + compressed += "9" + c; + n = 1; + } + } + else { + if (n) + compressed += n.toString() + c; + c = str[i]; + n = 1; + } + } + compressed += n.toString() + c; + + ns.writePort(Ports.Contract, compressed); + ns.spawn("/contract/attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/shortestGridPath.ts b/contract/shortestGridPath.ts new file mode 100644 index 0000000..8435354 --- /dev/null +++ b/contract/shortestGridPath.ts @@ -0,0 +1,134 @@ +import {Ports} from "lib/util"; + +type Signature = CodingContractSignatures[CodingContractName.ShortestPathInAGrid]; + +enum Direction { + Down = "D", + Right = "R", + Up ="U", + Left = "L" +}; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("./get/data.ts", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + const grid = ns.readPort(Ports.Contract) as Signature[0]; + var pathStr: Signature[1] = "", shortest: Signature[1] = ""; + var pathGrid = structuredClone(grid); + var pos = [0, 0]; + + function errUnreachable() { + ns.alert("Error: shortestGridPath.ts executed code that should have been unreachable."); + ns.exit(); + }; + + function moveValid(y: number, x: number) { + return y >= 0 && y < grid.length && x >= 0 && x < grid[0].length && !grid[y][x] && !pathGrid[y][x]; + }; + + function move(dir: Direction) { + switch (dir) { + case Direction.Down: + pathGrid[pos[0]][pos[1]] = 1; + ++pos[0]; + break; + case Direction.Right: + pathGrid[pos[0]][pos[1]] = 1; + ++pos[1]; + break; + case Direction.Up: + pathGrid[pos[0]][pos[1]] = 1; + --pos[0]; + break; + case Direction.Left: + pathGrid[pos[0]][pos[1]] = 1; + --pos[1]; + break; + default: + errUnreachable(); + break; + } + pathStr += dir; + }; + + function backtrack() { + let nextMove: Direction | undefined; + + while (!nextMove && pathStr) { + switch (pathStr[pathStr.length - 1]) { + case Direction.Down: + --pos[0]; + if (moveValid(pos[0], pos[1] + 1)) + nextMove = Direction.Right; + else if (moveValid(pos[0] - 1, pos[1])) + nextMove = Direction.Up; + else if (moveValid(pos[0], pos[1] - 1)) + nextMove = Direction.Left; + break; + case Direction.Right: + --pos[1]; + if (moveValid(pos[0] - 1, pos[1])) + nextMove = Direction.Up; + else if (moveValid(pos[0], pos[1] - 1)) + nextMove = Direction.Left; + break; + case Direction.Up: + ++pos[0]; + if (moveValid(pos[0], pos[1] - 1)) + nextMove = Direction.Left; + break; + case Direction.Left: + ++pos[1]; + break; + default: + errUnreachable(); + break; + } + pathGrid[pos[0]][pos[1]] = 0; + pathStr = pathStr.slice(0, pathStr.length - 1); + } + if (nextMove) { + move(nextMove); + return true; + } + return false; + } + + for (let row of pathGrid) + row.fill(0); + + while (!shortest || shortest.length > grid.length + grid[0].length - 2) { + if (shortest && shortest.length <= pathStr.length) { + if (!backtrack()) + break; + } + else if (pos[0] == grid.length - 1 && pos[1] == grid[0].length - 1) { + shortest = structuredClone(pathStr); + if (!backtrack()) + break; + } + else { + if (moveValid(pos[0] + 1, pos[1])) + move(Direction.Down); + else if (moveValid(pos[0], pos[1] + 1)) + move(Direction.Right); + else if (moveValid(pos[0] - 1, pos[1])) + move(Direction.Up); + else if (moveValid(pos[0], pos[1] - 1)) + move(Direction.Left); + else if (!(pos[0] || pos[1])) + break; + else if (!backtrack()) + break; + } + } + + ns.writePort(Ports.Contract, shortest); + ns.spawn("./attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/solveLoop.tsx b/contract/solveLoop.tsx new file mode 100644 index 0000000..21eb852 --- /dev/null +++ b/contract/solveLoop.tsx @@ -0,0 +1,187 @@ +import {Ports} from "lib/util"; +import {CodingContractName} from "lib/nsEnums"; + +type CodingContractInfo = { + "hostname": string, + "filename": string + "type": CodingContractName +}; + +type CodingContractStats = { + "numSolved": number, + "moneyGained": number +}; + +const Solvers: Record = { + [CodingContractName.AlgorithmicStockTraderI]: "./stockTraderI.ts", + [CodingContractName.AlgorithmicStockTraderII]: "./stockTraderII.ts", + [CodingContractName.AlgorithmicStockTraderIII]: "./stockTraderIII.ts", + [CodingContractName.AlgorithmicStockTraderIV]: "./stockTraderIV.ts", + [CodingContractName.ArrayJumpingGame]: "./arrayJumpI.ts", + [CodingContractName.ArrayJumpingGameII]: "./arrayJumpII.ts", + [CodingContractName.CompressionIRLECompression]: "./rleCompression.ts", + [CodingContractName.CompressionIILZDecompression]: "./lzDecompression.ts", + [CodingContractName.CompressionIIILZCompression]: "./lzCompression.js", + [CodingContractName.EncryptionICaesarCipher]: "./caesar.ts", + [CodingContractName.EncryptionIIVigenereCipher]: "./vigenere.ts", + [CodingContractName.GenerateIPAddresses]: "./ipAddr.ts", + [CodingContractName.HammingCodesEncodedBinaryToInteger]: undefined, + [CodingContractName.HammingCodesIntegerToEncodedBinary]: "./intToHamming.js", + [CodingContractName.FindAllValidMathExpressions]: "./mathExprs.js", + [CodingContractName.FindLargestPrimeFactor]: "./lpf.js", + [CodingContractName.MergeOverlappingIntervals]: "./mergeIntervals.ts", + [CodingContractName.MinimumPathSumInATriangle]: "./triMinSum.ts", + [CodingContractName.Proper2ColoringOfAGraph]: "./2color.ts", + [CodingContractName.SanitizeParenthesesInExpression]: undefined, + [CodingContractName.ShortestPathInAGrid]: "./shortestGridPath.ts", + [CodingContractName.SpiralizeMatrix]: "./spiralize.ts", + [CodingContractName.SquareRoot]: "./sqrt.ts", + [CodingContractName.SubarrayWithMaximumSum]: "./subarrayMaxSum.ts", + [CodingContractName.TotalWaysToSum]: "./intPartition.ts", + [CodingContractName.TotalWaysToSumII]: "./intPartitionSet.js", + [CodingContractName.UniquePathsInAGridI]: "./gridPathsI.js", + [CodingContractName.UniquePathsInAGridII]: "./gridPathsII.ts" +}; + +export async function main(ns: NS) { + ns.atExit(() => { ns.clearPort(Ports.Contract); }); + while (!ns.run("/lib/deepScan.js", 1, "-p", Ports.Contract, "--include-host", "--exclude-purchased")) + await ns.sleep(1000); + await ns.nextPortWrite(Ports.Contract); + var styles: any = ns.ui.getTheme(); + styles = { + "error": {"color": styles.error}, + "success": {"color": styles.success}, + "money": {"color": styles.money} + }; + var hostnames = ns.readPort(Ports.Contract) as Array; + const host = ns.self().server; + + var failedTypes = new Set(); + var successStats: {[key in CodingContractName]?: CodingContractStats} = {}; + var moneyGained = 0; + + const attemptRam = ns.getScriptRam("./attempt.js"); + const freeRam = function() { return ns.getServerMaxRam(host) - ns.getServerUsedRam(host); }; + + function renderPrettyLog(outstanding: Array) { + var log = new Array(); + if (outstanding.length) { + const listItems = outstanding.map((c) => { + return
  • {c.filename} on {c.hostname} ({c["type"]})
  • ; + }); + log.push(

    Outstanding Contracts

      {listItems}
    ); + } + else + log.push(

    No outstanding contracts

    ); + + log.push(
    ); + if (failedTypes.size) { + var listItems = new Array(); + + for (const typ of failedTypes.values()) + listItems.push(
  • {typ}
  • ); + log.push(

    Failed Contract Types

      {listItems}
    ); + } + else + log.push(

    No failures

    ); + + var rows = new Array(); + + log.push(
    ); + for (const [typ, stats] of Object.entries(successStats)) + rows.push({typ}{stats.numSolved}${ns.formatNumber(stats.moneyGained)}); + if (rows.length) { + log.push( +
    + + + {rows} +
    Contract TypeSolvedMoney
    +

    Total money gained: ${ns.formatNumber(moneyGained)}

    +
    + ); + } + else + log.push(

    No contracts solved yet

    ); + return log; + } + + ns.disableLog("sleep"); + ns.disableLog("run"); + ns.disableLog("getServerMaxRam"); + ns.disableLog("getServerUsedRam"); + while (true) { + let outstanding = new Array(); + + for (let i = 0; i < hostnames.length && freeRam(); ++i) { + const ccs = ns.ls(hostnames[i], ".cct"); + for (let j = 0; j < ccs.length && freeRam(); ++j) { + ns.clearPort(Ports.Contract); + + if (ns.run("./get/type.ts", {"temporary": true}, ccs[j], hostnames[i], "-p")) { + await ns.nextPortWrite(Ports.Contract); + const typ = ns.readPort(Ports.Contract) as CodingContractName; + const contractInfo: CodingContractInfo = {"hostname": hostnames[i], "filename": ccs[j], "type": typ}; + + // Sleep before attempting to prevent immediate freeze on load + await ns.sleep(5000); + if (Solvers[typ] && attemptRam <= freeRam() && ns.run("./get/data.ts", {"temporary": true}, ccs[j], hostnames[i], "-p")) { + // Script writes data to port. Let the solver read it. + await ns.nextPortWrite(Ports.Contract); + //ns.alert(hostnames[i] + ": " + ccs[j] + ": " + type.toString()); + while (attemptRam > freeRam()) + await ns.sleep(1000); + const pid = ns.run(Solvers[typ] as string, {"temporary": true}, ccs[j], hostnames[i], "-p"); + if (pid) { + // Can't await port write because it might not happen while debugging + await ns.sleep(5000); + while (ns.isRunning(pid) || ns.isRunning("./attempt.js", undefined, ccs[j], hostnames[i])) + await ns.sleep(100); + let reward = ns.readPort(Ports.Contract); + if (reward != "NULL PORT DATA") { + if (reward == "") { + reward = "Contract failed!"; + failedTypes.add(typ); + outstanding.push(contractInfo); + ns.toast(reward, ns.enums.ToastVariant.ERROR); + } + else if (typeof reward == "string" && reward.startsWith("Gained")) { + let rewardMoney = reward[7] == "$" ? parseFloat(reward.slice(8)) : 0; + if (rewardMoney) + switch (reward[reward.length - 1]) { + case "k": rewardMoney *= 1000; break; + case "m": rewardMoney *= 1000000; break; + default: break; + } + + if (successStats[typ] === undefined) + successStats[typ] = {"numSolved": 0, "moneyGained": 0}; + ++(successStats[typ] as CodingContractStats).numSolved; + (successStats[typ] as CodingContractStats).moneyGained += rewardMoney; + moneyGained += rewardMoney; + ns.toast("Contract solved! " + reward + ".", ns.enums.ToastVariant.SUCCESS); + } + else + // attempt.js failed to run, and the 'reward' is actually the answer + // No need to toast in that case + outstanding.push(contractInfo); + } + else { + outstanding.push(contractInfo); + ns.toast("Solver for " + typ.toString() + " returned no reward", ns.enums.ToastVariant.WARNING); + } + } + } + else { + outstanding.push(contractInfo); + ns.toast("Couldn't attempt " + typ.toString() + " contract", ns.enums.ToastVariant.WARNING); + } + } + } + } + ns.clearLog(); + ns.printRaw(
    {renderPrettyLog(outstanding)}
    ); + await ns.sleep(300000); + } +} \ No newline at end of file diff --git a/contract/spiralize.ts b/contract/spiralize.ts new file mode 100644 index 0000000..5ae9688 --- /dev/null +++ b/contract/spiralize.ts @@ -0,0 +1,41 @@ +import {Ports} from "lib/util"; + +type Signature = CodingContractSignatures[CodingContractName.SpiralizeMatrix]; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("./get/data.ts", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + const mat = ns.readPort(Ports.Contract) as Signature[0]; + var walls = [mat[0].length, mat.length, -1, 0]; + const dirs = [[0, 1], [1, 0], [0, -1], [-1, 0]]; + var spiral: Signature[1] = []; + + for (let i = 0, j = 0, d = 0; true; i += dirs[d][0], j += dirs[d][1]) { + spiral.push(mat[i][j]); + if (dirs[d][0] != 0) { + if (i + dirs[d][0] == walls[d]) { + walls[d] -= dirs[d][0]; + ++d; + d &= 3; + if (j + dirs[d][1] == walls[d]) + break; + } + } + else if (j + dirs[d][1] == walls[d]) { + walls[d] -= dirs[d][1]; + ++d; + d &= 3; + if (i + dirs[d][0] == walls[d]) + break; + } + } + + ns.writePort(Ports.Contract, spiral); + ns.spawn("./attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/sqrt.ts b/contract/sqrt.ts new file mode 100644 index 0000000..20d711c --- /dev/null +++ b/contract/sqrt.ts @@ -0,0 +1,37 @@ +import {Ports} from "lib/util"; + +type Signature = CodingContractSignatures[CodingContractName.SquareRoot]; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("./get/data.ts", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + const n = ns.readPort(Ports.Contract) as Signature[0]; + var up = n, low = 1n, diff; + + // Binary search for the square root + while (low != n && (diff = up - low) > 1n) { + const bound = up - (diff >> 1n); + const sq = bound * bound; + + if (sq > n) + up = bound; + else + low = bound; + } + + ns.writePort(Ports.Contract, low as Signature[1]); + if (ns.run("./attempt.js", {"temporary": true}, ns.args[0], ns.args[1])) { + await ns.nextPortWrite(Ports.Contract); + if (!ns.peek(Ports.Contract)) { + ns.clearPort(Ports.Contract); + ns.writePort(Ports.Contract, up as Signature[1]); + ns.spawn("./attempt.js", {"spawnDelay": 20, "temporary": true}, ns.args[0], ns.args[1]); + } + } + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/stockTraderI.ts b/contract/stockTraderI.ts new file mode 100644 index 0000000..d8a7a8e --- /dev/null +++ b/contract/stockTraderI.ts @@ -0,0 +1,22 @@ +import {Ports} from "lib/util"; + +type Signature = CodingContractSignatures[CodingContractName.AlgorithmicStockTraderI]; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("./get/data.ts", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + const prices = ns.readPort(Ports.Contract) as Signature[0]; + var profit: Signature[1] = 0; + + for (let i = 0; i < prices.length - 1; ++i) + profit = Math.max(profit, Math.max(...prices.slice(i + 1)) - prices[i]); + + ns.writePort(Ports.Contract, profit); + ns.spawn("/contract/attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/stockTraderII.ts b/contract/stockTraderII.ts new file mode 100644 index 0000000..d59337f --- /dev/null +++ b/contract/stockTraderII.ts @@ -0,0 +1,19 @@ +import {Ports} from "lib/util"; +import {getProfit} from "contract/stockTraderIV"; + +type Signature = CodingContractSignatures[CodingContractName.AlgorithmicStockTraderII]; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("./get/data.ts", 1, ns.args[0], ns.args[1], "-p"); + await ns.sleep(20); + }*/ + + var prices = ns.readPort(Ports.Contract) as Signature[0]; + + ns.writePort(Ports.Contract, getProfit(prices) as Signature[1]); + ns.spawn("./attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/stockTraderIII.ts b/contract/stockTraderIII.ts new file mode 100644 index 0000000..efbc51c --- /dev/null +++ b/contract/stockTraderIII.ts @@ -0,0 +1,19 @@ +import {Ports} from "lib/util"; +import {getProfit} from "contract/stockTraderIV"; + +type Signature = CodingContractSignatures[CodingContractName.AlgorithmicStockTraderIII]; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("./get/data.ts", 1, ns.args[0], ns.args[1], "-p"); + await ns.sleep(20); + }*/ + + var prices = ns.readPort(Ports.Contract) as Signature[0]; + + ns.writePort(Ports.Contract, getProfit(prices, 2) as Signature[1]); + ns.spawn("./attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/stockTraderIV.ts b/contract/stockTraderIV.ts new file mode 100644 index 0000000..de94d77 --- /dev/null +++ b/contract/stockTraderIV.ts @@ -0,0 +1,73 @@ +import {Ports} from "lib/util"; + +type Signature = CodingContractSignatures[CodingContractName.AlgorithmicStockTraderIV]; + +export function getProfit (prices: Signature[0][1], k?: Signature[0][0]): Signature[1] { + var price = Number.MAX_SAFE_INTEGER; + var buy = true; + var profit = 0; + var temp: Signature[0][1] = []; + + // Loop throws out prices that buck the trend at the end, + // so fudge one + prices.push(prices[prices.length - 2]); + // Construct array of local minima and maxima + for (let i = 0; i < prices.length; ++i) { + if (buy) { + if (prices[i] > price) { + temp.push(price); + price = prices[i]; + buy = false; + } + else + price = prices[i]; + } + else { + if (prices[i] < price) { + temp.push(price); + price = prices[i]; + buy = true; + } + else + price = prices[i]; + } + } + + prices = temp; + if (prices.length & 1) + prices.pop(); + + if (k) { + // Throw out pair with least difference as needed + k <<= 1; + while (prices.length > k) { + let toToss = 0; + + for (let i = 1; i < prices.length - 1; ++i) + if (Math.abs(prices[i] - prices[i + 1]) < Math.abs(prices[toToss] - prices[toToss + 1])) + toToss = i; + + prices.splice(toToss, 2); + } + } + + for (let i = 0; i < prices.length; i += 2) + profit += prices[i + 1] - prices[i]; + + return profit; +} + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("./get/data.ts", 1, ns.args[0], ns.args[1], "-p"); + await ns.sleep(20); + }*/ + + var [k, prices] = ns.readPort(Ports.Contract) as Signature[0]; + + ns.writePort(Ports.Contract, getProfit(prices, k)); + ns.spawn("./attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/subarrayMaxSum.ts b/contract/subarrayMaxSum.ts new file mode 100644 index 0000000..98cbdde --- /dev/null +++ b/contract/subarrayMaxSum.ts @@ -0,0 +1,35 @@ +import {Ports} from "lib/util"; + +type Signature = CodingContractSignatures[CodingContractName.SubarrayWithMaximumSum]; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("./get/data.ts", 1, ns.args[0], ns.args[1], "-p"); + await ns.sleep(20); + }*/ + + var ar = ns.readPort(Ports.Contract) as Signature[0]; + var max: Signature[1] = Number.MIN_SAFE_INTEGER; + + for (let i = 0; i < ar.length;) { + let sum = ar[i++]; + + if (sum >= 0) + while (i < ar.length && ar[i] >= 0) + sum += ar[i++]; + if (sum > max) + max = sum; + + for (let j = i; j < ar.length; ++j) { + sum += ar[j]; + if (sum > max) + max = sum; + } + } + + ns.writePort(Ports.Contract, max); + ns.spawn("./attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + //if (!flags.p) + // ns.clearPort(Ports.Contract); +} \ No newline at end of file diff --git a/contract/triMinSum.ts b/contract/triMinSum.ts new file mode 100644 index 0000000..5448932 --- /dev/null +++ b/contract/triMinSum.ts @@ -0,0 +1,23 @@ +import {Ports} from "lib/util"; + +type Signature = CodingContractSignatures[CodingContractName.MinimumPathSumInATriangle]; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("/contract/getData.js", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + var tri = ns.readPort(Ports.Contract) as Signature[0]; + + for (let i = tri.length - 2; i >= 0; --i) { + for (let j = 0; j < tri[i].length; ++j) + tri[i][j] += Math.min(tri[i + 1][j], tri[i + 1][j + 1]); + } + + ns.writePort(Ports.Contract, tri[0][0] as Signature[1]); + ns.spawn("/contract/attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/contract/vigenere.ts b/contract/vigenere.ts new file mode 100644 index 0000000..d1504a0 --- /dev/null +++ b/contract/vigenere.ts @@ -0,0 +1,34 @@ +import {Ports} from "lib/util"; + +type Signature = CodingContractSignatures[CodingContractName.EncryptionIIVigenereCipher]; + +export async function main(ns: NS) { + /*const flags = ns.flags([["p", false]]); + if (!flags.p) { + ns.run("./get/data.js", 1, ns.args[0], ns.args[1], "-p"); + ns.sleep(20); + }*/ + + const [msg, key] = ns.readPort(Ports.Contract) as Signature[0]; + var charCodes: Array = []; + + for (let i = 0; i < key.length; ++i) + charCodes.push(key.charCodeAt(i) - 0x41); + const keyCodes = charCodes; + + charCodes = []; + for (let i = 0, j = 0; i < msg.length; ++i) { + charCodes.push(msg.charCodeAt(i)) + if (charCodes[i] != 0x20) { + charCodes[i] += keyCodes[j]; + if (charCodes[i] > 0x5A) + charCodes[i] -= 26; + j = (j + 1) % keyCodes.length; + } + } + + ns.writePort(Ports.Contract, String.fromCharCode(...charCodes) as Signature[1]); + ns.spawn("/contract/attempt.js", {"spawnDelay": 0, "temporary": true}, ns.args[0], ns.args[1]); + /*if (!flags.p) + ns.clearPort(Ports.Contract);*/ +} \ No newline at end of file diff --git a/crack/crack.ts b/crack/crack.ts new file mode 100644 index 0000000..ccb0c81 --- /dev/null +++ b/crack/crack.ts @@ -0,0 +1,41 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; + +export async function main(ns: NS) { + var executorJob: ExecutorJob = { + script: "/crack/getTargets.ts", + hostname: ns.self().server, + threadOrOptions: {temporary: true}, + retry: true + }; + ns.clearPort(Ports.Crack); + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Crack); + var targets: Set = ns.readPort(Ports.Crack); + + while (targets.size) { + let message = ""; + let cracked = new Set(); + + for (const target of targets.values()) { + ns.clearPort(Ports.Crack); + executorJob.script = "/crack/tryNuke.ts"; + executorJob.args = [target]; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Crack); + if (ns.readPort(Ports.Crack)) { + executorJob.script = "/crack/deployScripts.ts"; + ns.writePort(Ports.Exec, executorJob); + cracked.add(target); + message = message || "Cracked new targets:"; + message += "\n" + target; + } + } + targets = targets.difference(cracked); + if (message) { + message += "\n" + targets.size.toString() + " target servers remaining"; + ns.alert(message); + } + await ns.sleep(30000); + } +} \ No newline at end of file diff --git a/crack/deployScripts.ts b/crack/deployScripts.ts new file mode 100644 index 0000000..21b2357 --- /dev/null +++ b/crack/deployScripts.ts @@ -0,0 +1,44 @@ +import {deployDynamo} from "daemons/deployDynamo"; +import {deployExecutor} from "daemons/deployExecutor"; +import {deployShotgunner} from "hack/deployShotgunner"; +import {deployHacknet} from "hacknet/deploy"; +import {deployServerBuyer} from "srv/deploy"; + +export function deployScripts(ns: NS, target: string) { + function deployShare(t?: number) { + ns.scp("/share.ts", target); + try { + ns.exec("/share.ts", target, { + "threads": t || (ns.getServerMaxRam(target) - ns.getServerUsedRam(target)) >> 2, + "preventDuplicates": true + }); + } + catch (e) {} + } + + switch (target) { + case "n00dles": + deployDynamo(ns, target); + break; + + case "CSEC": + deployServerBuyer(ns, target); + break; + + case "nectar-net": + deployExecutor(ns, target); + deployShotgunner(ns, target); + break; + + case "harakiri-sushi": + ns.exec("/stock/deploy.ts", "home", 1, target); + break; + + case "sigma-cosmetics": + deployHacknet(ns, target); + deployShare(2); + break; + } +} + +export async function main(ns: NS) { deployScripts(ns, ns.args[0] as string); } \ No newline at end of file diff --git a/crack/getTargets.ts b/crack/getTargets.ts new file mode 100644 index 0000000..00c10a0 --- /dev/null +++ b/crack/getTargets.ts @@ -0,0 +1,13 @@ +import {Ports, deepScan} from "lib/util"; + +export async function main(ns: NS) { + var targets = deepScan(ns, "home", false, true); + var cracked = new Set(); + + for (const target of targets.values()) + if (ns.hasRootAccess(target)) + cracked.add(target); + + targets = targets.difference(cracked); + ns.writePort(Ports.Crack, targets); +} \ No newline at end of file diff --git a/crack/tryNuke.ts b/crack/tryNuke.ts new file mode 100644 index 0000000..41abde9 --- /dev/null +++ b/crack/tryNuke.ts @@ -0,0 +1,17 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + const target = ns.args[0] as string; + const srv = ns.getServer(target); + if (!srv.sshPortOpen && ns.fileExists("BruteSSH.exe")) + ns.brutessh(target); + if (!srv.ftpPortOpen && ns.fileExists("FTPCrack.exe")) + ns.ftpcrack(target); + if (!srv.smtpPortOpen && ns.fileExists("RelaySMTP.exe")) + ns.relaysmtp(target); + if (!srv.httpPortOpen && ns.fileExists("HTTPWorm.exe")) + ns.httpworm(target); + if (!srv.sqlPortOpen && ns.fileExists("SQLInject.exe")) + ns.sqlinject(target); + ns.writePort(Ports.Crack, (srv.openPortCount ?? 0) >= (srv.numOpenPortsRequired ?? 0) && ns.nuke(target)); +} \ No newline at end of file diff --git a/crack/worm.js b/crack/worm.js new file mode 100644 index 0000000..7e21a32 --- /dev/null +++ b/crack/worm.js @@ -0,0 +1,36 @@ +/** @param {NS} ns */ +export async function main(ns) { + var nodes = new Set(ns.scan()); + var nodesIter = nodes.values(); + + for (const target of nodesIter) { + let wormed = new Set(); + + if (ns.hasRootAccess(target)) + wormed.add(target); + + nodes = nodes.difference(wormed); + } + while (nodes.size) { + let wormed = new Set(); + let message = null; + nodesIter = nodes.values(); + + for (const target of nodesIter) { + ns.brutessh(target); + if (ns.fileExists("FTPCrack.exe")) + ns.ftpcrack(target); + if (ns.nuke(target)) { + ns.scp("worm.js", target); + ns.exec("worm.js", target); + wormed.add(target); + message = message || "Wormed new targets:"; + message += "\n" + target; + } + } + nodes = nodes.difference(wormed); + if (message) + ns.alert(message); + ns.sleep(30000); + } +} \ No newline at end of file diff --git a/daemons/deployDynamo.ts b/daemons/deployDynamo.ts new file mode 100644 index 0000000..88cdaef --- /dev/null +++ b/daemons/deployDynamo.ts @@ -0,0 +1,9 @@ +const DynamoScript = "/daemons/dynamo.ts"; + +export function deployDynamo(ns: NS, dest: string) { + ns.scp(["/lib/util.ts", ...ns.ls("home", "daemons")], dest); + ns.kill(DynamoScript, dest); + ns.exec(DynamoScript, dest); +} + +export async function main(ns: NS) { deployDynamo(ns, ns.args[0] as string); } \ No newline at end of file diff --git a/daemons/deployExecutor.ts b/daemons/deployExecutor.ts new file mode 100644 index 0000000..ed97c85 --- /dev/null +++ b/daemons/deployExecutor.ts @@ -0,0 +1,9 @@ +const ExecutorScript = "/daemons/executor.ts"; + +export function deployExecutor(ns: NS, dest: string) { + ns.scp(["/lib/util.ts", ExecutorScript], dest); + ns.kill(ExecutorScript, dest); + ns.exec(ExecutorScript, dest); +} + +export async function main(ns: NS) { deployExecutor(ns, ns.args[0] as string); } \ No newline at end of file diff --git a/daemons/dynamo.ts b/daemons/dynamo.ts new file mode 100644 index 0000000..18d4961 --- /dev/null +++ b/daemons/dynamo.ts @@ -0,0 +1,105 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; + +const StaticRam = 4; +const MaxRamCost = StaticRam - 1.6; + +export interface DynamoJob { + /** The fully qualified name of the function to call, without the leading `ns.` */ + functionName: string, + + /** An array of arguments to pass to the function (optional). */ + args?: Array, + + /** The port to which the function's return value will be written (optional). */ + cbPort?: number, + + /** Set to suppress messages about unhandled errors. Only used if cbPort is undefined. */ + ignoreErrors?: boolean +} + +export interface DynamoResult { + /** The return value, if the function returned normally. Otherwise, the error. */ + val: any, + + /** True if the function threw an error. */ + error: boolean +} + +/** Use this instead of writing to Ports.DynamicNS directly. It unpacks the result and + * either throws an error or returns the NS function's return value, as you'd expect. + */ +export async function doDynamoJob(ns: NS, job: DynamoJob) { + ns.writePort(Ports.DynamicNS, job); + if (job.cbPort) { + await ns.nextPortWrite(job.cbPort); + const result = ns.readPort(job.cbPort) as DynamoResult; + if (result.error) + throw(result.val); + return result.val; + } +} + +export async function main(ns: NS) { + if (ns.ramOverride(StaticRam) != StaticRam) { + ns.print("ERROR: ramOverride failed."); + ns.toast("ramOverride failed in dynamo", ns.enums.ToastVariant.ERROR); + ns.exit(); + } + + var usedFuncs = new Map(); + + while (true) { + while (ns.peek(Ports.DynamicNS) != "NULL PORT DATA") { + const job = ns.readPort(Ports.DynamicNS) as DynamoJob; + const self = ns.self(); + let ramCost: number; + + try { ramCost = ns.getFunctionRamCost(job.functionName); } + catch (e) { + ns.toast("Invalid dynamo job", ns.enums.ToastVariant.ERROR); + ns.print(e); + continue; + } + + if (ramCost > MaxRamCost) { + const err = "Dynamo job exceeded static RAM"; + ns.toast(err, ns.enums.ToastVariant.ERROR); + if (job.cbPort) + ns.writePort(job.cbPort, {val: err, error: true} as DynamoResult); + } + + if (!usedFuncs.has(job.functionName)) { + if (StaticRam - (self.dynamicRamUsage ?? 1.6) < ramCost) { + // Restart the daemon. Can't use ns.spawn, because that costs RAM, so do it with the executor. + ns.print("Respawning"); + ns.writePort(Ports.Exec, { + script: self.filename, + hostname: self.server, + args: self.args, + retry: true + } as ExecutorJob); + ns.exit(); + } + usedFuncs.set(job.functionName, eval("ns." + job.functionName) as Function); + ns.printf("INFO: %s added to used functions.\nDynamic RAM usage is now %s.", + job.functionName, ns.formatRam((self.dynamicRamUsage ?? 1.6) + ramCost)); + } + + try { + const ret = (usedFuncs.get(job.functionName) as Function)(...(job.args ?? [])); + if (job.cbPort) + ns.writePort(job.cbPort, {val: ret, error: false} as DynamoResult); + } + catch (e) { + if (job.cbPort) + ns.writePort(job.cbPort, {val: e, error: true} as DynamoResult); + else if (!job.ignoreErrors) { + ns.print(e); + ns.toast("Unhandled error in dynamo"); + } + } + } + await ns.nextPortWrite(Ports.DynamicNS); + } +} \ No newline at end of file diff --git a/daemons/executor.ts b/daemons/executor.ts new file mode 100644 index 0000000..b10f44c --- /dev/null +++ b/daemons/executor.ts @@ -0,0 +1,76 @@ +// This should not be used for high-volume purposes like batch hacking +// Otherwise, it's great for minimizing RAM usage + +import {Ports} from "lib/util"; + +const MaxRetries = 1e3; + +export interface ExecutorJob { + /** File path must be absolute, not relative. */ + script: string, + + /** Name of server to run on. */ + hostname: string, + + /** Same as in ns.run and ns.exec. */ + threadOrOptions?: number | RunOptions, + + /** Array of arguments to pass to the script. */ + args?: Array, + + /** Delay in ms. Mutually exclusive with retry. */ + delay?: number, + + /** Callback port. PID will be written to it. If retry is true, port write will happen only when exec succeeds. */ + cbPort?: number, + + /** True to retry until exec succeeds. Mutually exclusive with delay. Will retry at most 1000 times.*/ + retry?: boolean | number +}; + +function execute(ns: NS, job: ExecutorJob) { + try { + const pid = ns.exec(job.script, job.hostname, job.threadOrOptions, ...(job.args ?? [])); + if (job.cbPort && (pid || !job.retry)) + ns.writePort(job.cbPort, pid); + return pid; + } + catch (e) { + ns.print(e); + ns.alert("Executor job threw an error\nScript: " + job.script + "\n" + "args: " + JSON.stringify(job.args) + "\n\n" + e); + if (job.cbPort) + ns.writePort(job.cbPort, 0); + return -1; + } +} + +export async function main(ns: NS) { + // window.setTimeout is used for delays, so ramOverride is needed to dodge the 25 GiB cost + ns.ramOverride(2.9); + + function needsRetry(job: ExecutorJob) { return job.retry; } + + var toRetry: Array = []; + + // TODO: Implement search for server with available RAM + while (true) { + while (ns.peek(Ports.Exec) != "NULL PORT DATA") { + const job = ns.readPort(Ports.Exec) as ExecutorJob; + if (job.delay) + window.setTimeout(execute, job.delay, ns, job); + else if (!execute(ns, job) && job.retry) { + job.retry = MaxRetries; + toRetry.push(job); + } + } + + for (const job of toRetry) + if (execute(ns, job)) + job.retry = false; + else if (!--(job.retry as number) && job.cbPort) + ns.writePort(job.cbPort, 0); + toRetry = toRetry.filter(needsRetry); + + await (toRetry.length ? ns.sleep(20) : ns.nextPortWrite(Ports.Exec)); + } +} \ No newline at end of file diff --git a/debug/monitorPort.ts b/debug/monitorPort.ts new file mode 100644 index 0000000..d20c94c --- /dev/null +++ b/debug/monitorPort.ts @@ -0,0 +1,8 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.ui.openTail(); + while(true) { + await ns.nextPortWrite(flags.p as number); + ns.print(JSON.stringify(ns.peek(flags.p))); + } +} \ No newline at end of file diff --git a/deployShare.ts b/deployShare.ts new file mode 100644 index 0000000..10254d6 --- /dev/null +++ b/deployShare.ts @@ -0,0 +1,14 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; + +export async function main(ns: NS) { + const flags = ns.flags([["r", 0]]); + const dest = (flags._ as Array)[0]; + ns.scp("/share.ts", dest); + ns.kill("/share.ts", dest); + ns.writePort(Ports.Exec, { + "script": "/share.ts", + "hostname": dest, + "threadOrOptions": (ns.getServerMaxRam(dest) - ns.getServerUsedRam(dest) - (flags.r as number)) >> 2 + } as ExecutorJob); +} \ No newline at end of file diff --git a/exploit/bypass.ts b/exploit/bypass.ts new file mode 100644 index 0000000..5bd2de7 --- /dev/null +++ b/exploit/bypass.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + ns.ramOverride(1.6); + (ns as any).bypass(document); +} \ No newline at end of file diff --git a/exploit/devMenu.js b/exploit/devMenu.js new file mode 100644 index 0000000..77186b8 --- /dev/null +++ b/exploit/devMenu.js @@ -0,0 +1,5 @@ +/** @param {NS} ns */ +export async function main(ns) { + ns.exploit(); + ns.openDevMenu(); +} \ No newline at end of file diff --git a/exploit/rainbow.ts b/exploit/rainbow.ts new file mode 100644 index 0000000..8eec1f1 --- /dev/null +++ b/exploit/rainbow.ts @@ -0,0 +1,3 @@ +export async function main(ns: NS) { + (ns as any).rainbow("noodles"); +} \ No newline at end of file diff --git a/exploit/reality.js b/exploit/reality.js new file mode 100644 index 0000000..07f3235 --- /dev/null +++ b/exploit/reality.js @@ -0,0 +1,8 @@ +export const recur = function (depth) { + x = depth; +}; + +/** @param {NS} ns */ +export async function main(ns) { + ns.alterReality(); +} \ No newline at end of file diff --git a/exploit/tamper.ts b/exploit/tamper.ts new file mode 100644 index 0000000..f059b6b --- /dev/null +++ b/exploit/tamper.ts @@ -0,0 +1,4 @@ +/** @param {NS} ns */ +export async function main(ns: NS) { + Number.prototype.toExponential = Number.prototype.toString; +} \ No newline at end of file diff --git a/exploit/time.js b/exploit/time.js new file mode 100644 index 0000000..f20ece0 --- /dev/null +++ b/exploit/time.js @@ -0,0 +1,19 @@ +/** @param {NS} ns */ +export async function main(ns) { + ns.ramOverride(1.6); + var temp; + if (window.setTimeoutComp === undefined) { + window.setTimeoutComp = (handler, ms, ...args) => { + return window.setTimeoutComp(handler, ms >> 5, ...args); + }; + window.setIntervalComp = (handler, ms, ...args) => { + return window.setIntervalComp(handler, ms >> 5, ...args); + } + } + temp = window.setTimeout; + window.setTimeout = window.setTimeoutComp; + window.setTimeoutComp = temp; + temp = window.setInterval; + window.setInterval = window.setIntervalComp; + window.setIntervalComp = temp; +} \ No newline at end of file diff --git a/gang/ascend.ts b/gang/ascend.ts new file mode 100644 index 0000000..02a70eb --- /dev/null +++ b/gang/ascend.ts @@ -0,0 +1,54 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; +import {gangInfo} from "gang/genInfo/gang"; +import {ExtGangMemberInfo, members} from "gang/genInfo/members"; +import {isHacker} from "gang/recruit"; + +// From game code +const ExpMultFactor = 0.25; + +export async function main(ns: NS) { + function isCandidate(m: ExtGangMemberInfo) { + const res = m.ascensionResult; + let gains = m.expGain; + if (!(res && gains) || (res.respect * 2) > gangInfo.respect) + return false; + + for (const stat of ["hack", "str", "def", "dex", "agi", "cha"]) { + // Factor out the exp mult from equipment + const expKey = (stat + "_exp") as keyof GangMemberExpGain; + // mult is actually equipment mult only, so no need to factor out ascenscion mult + const mult = m[(stat + "_mult") as keyof ExtGangMemberInfo] as number; + m[expKey] /= (1 + (mult - 1) * ExpMultFactor); + } + + if (m.task.includes("Train")) { + // It's most efficient to train all relevant stats in each ascension + // So, fudge training exp to prevent premature ascension + if (isHacker(m)) + gains.cha_exp = gains.hack_exp = gains.cha_exp || gains.hack_exp; + else + gains.cha_exp = gains.hack_exp = gains.dex_exp = gains.cha_exp || gains.hack_exp || gains.dex_exp; + } + + return Object.entries(res).every(([stat, ascMult]) => { + if (stat == "respect") + return true; + + const gain = gains[(stat + "_exp") as keyof GangMemberExpGain]; + const exp = (m[(stat + "_exp") as keyof ExtGangMemberInfo] ?? 0) as number; + return !gain || ascMult * exp > exp + 1000; + }); + }; + + const candidate = members.find(isCandidate); + + if (candidate) + ns.writePort(Ports.Exec, { + "script": "/gang/ascendMember.ts", + "hostname": ns.self().server, + "args": [candidate.name], + "threadOrOptions": {"temporary": true}, + "retry": true + } as ExecutorJob); +} \ No newline at end of file diff --git a/gang/ascendMember.ts b/gang/ascendMember.ts new file mode 100644 index 0000000..cb8260c --- /dev/null +++ b/gang/ascendMember.ts @@ -0,0 +1,5 @@ +export async function main(ns: NS) { + const member = ns.args[0] as string; + if (ns.gang.ascendMember(member)) + ns.toast(member + " has ascended!", ns.enums.ToastVariant.SUCCESS); +} \ No newline at end of file diff --git a/gang/assign.js b/gang/assign.js new file mode 100644 index 0000000..bc7d915 --- /dev/null +++ b/gang/assign.js @@ -0,0 +1,169 @@ +import {getGangInfo} from "./genGangInfo"; +import {getMemberInfo} from "./genMemberInfo"; +import {getTaskInfo} from "./genTaskInfo"; +import {MaxMembers, isCombatant, isHacker} from "./recruit"; +import {GangModes, getMode} from "./mode"; + +const WantedPenaltyThreshold = 0.45; +const WantedLevelThreshold = 1e3; +const SafeClashChance = 0.1; // Max clash chance for hackers and weak combatants to risk engaging in territory warfare +const PrimaryStatMinBase = 10; +const PrimaryStatMinMultBase = 1.55; + +function raisesRespect(task) { return task.baseRespect; } +function earnsMoney(task) { return task.baseMoney; } + +/** @param {GangTaskStats} task + * @param {GangMemberInfo} member + */ +function taskStatWeight(member, task) { + // Task stats already incorporate stat multipliers for current gains, but + // gains also grow faster when stats with higher mults gain more exp, + // so we weight them again + + // We sum log(mults * difficulty) * statWeight for each stat to get the rate at which statWeight (in game code) + // increases. After moving difficulty outside the log and factoring out constants, this is equivalent to + // summing log(mults) * statWeight for each stat, then adding log(difficulty). + return ["hack", "str", "def", "dex", "agi", "cha"].reduce((total, stat) => total + ( + Math.log2(member[stat + "_mult"] * member[stat + "_asc_mult"]) + ) * task[stat + "Weight"], 0) + Math.log2(task.difficulty); +} + +/** @param {NS} ns */ +export async function main(ns) { + const gangInfo = getGangInfo(ns); + const taskInfo = getTaskInfo(ns); + const respectTasks = taskInfo.filter(raisesRespect); + const moneyTasks = taskInfo.filter(earnsMoney); + const members = getMemberInfo(ns); + const hackers = gangInfo.isHacking ? members.filter(isHacker) : []; + const combatants = gangInfo.isHacking ? members.filter(isCombatant) : members; + const primaryStatMin = Math.ceil(PrimaryStatMinBase * Math.pow(PrimaryStatMinMultBase, members.length)); + + /** Comes straight from game code, and is used as an exponent to scale money and rep gains */ + //const territoryPenalty = (0.2 * gangInfo.territory + 0.8) * currentNodeMults.GangSoftcap; + + function respectPerWanted(member, task) { + return ns.formulas.gang.respectGain(gangInfo, member, task) / + ns.formulas.gang.wantedLevelGain(gangInfo, member, task); + } + + function moneyPerWanted(member, task) { + return ns.formulas.gang.moneyGain(gangInfo, member, task) / + ns.formulas.gang.wantedLevelGain(gangInfo, member, task); + } + + // wantedPenalty is actually a multiplier, confusingly + function wantedAboveThreshold() { return gangInfo.wantedPenalty < WantedPenaltyThreshold || gangInfo.wantedLevel > WantedLevelThreshold; } + + function hackerGetTrainingTask(m) { + if (m["hack"] < primaryStatMin && m.hack_exp < m.cha_exp) + return "Train Hacking"; + else if (m.cha < primaryStatMin) + return "Train Charisma"; + } + + function combatantGetTrainingTask(m) { + if (m.cha < primaryStatMin && m.cha_exp < m.hack_exp) + return "Train Charisma"; + else if (m["hack"] < primaryStatMin && m.hack_exp < m.dex_exp) + return "Train Hacking"; + else if (["str", "def", "dex", "agi"].some(stat => m[stat] < primaryStatMin)) + return "Train Combat"; + } + + function getRespectTask(m) { + return respectTasks.filter(t => respectPerWanted(m, t) > 1).toSorted( + (a, b) => ns.formulas.gang.respectGain(gangInfo, m, b) * taskStatWeight(m, b) - + ns.formulas.gang.respectGain(gangInfo, m, a) * taskStatWeight(m, a) + )[0].name + } + + function getMoneyTask(m) { + return moneyTasks.filter(t => moneyPerWanted(m, t) > 1).toSorted( + (a, b) => ns.formulas.gang.moneyGain(gangInfo, m, b) * taskStatWeight(m, b) - + ns.formulas.gang.moneyGain(gangInfo, m, a) * taskStatWeight(m, a) + )[0].name + } + + function getRespectOrMoneyTask(m) { + return members.length < MaxMembers ? getRespectTask(m) : getMoneyTask(m); + } + + function hackerAssignDefault(m) { + ns.gang.setMemberTask(m.name, hackerGetTrainingTask(m) ?? wantedAboveThreshold() ? "Ethical Hacking" : getRespectOrMoneyTask(m)); + } + + function combatantAssignDefault(m) { + let task = combatantGetTrainingTask(m); + if (!task) { + if (wantedAboveThreshold()) + task = "Vigilante Justice"; + else if (!gangInfo.isHacking) + task = getRespectOrMoneyTask(m); + // TODO: Finish assingment for combatants in hacker gangs + } + + ns.gang.setMemberTask(m.name, task); + } + + function assignDefault() { + hackers.forEach(hackerAssignDefault); + combatants.forEach(combatantAssignDefault); + } + + function combatantAssignAmass(m) { + ns.gang.setMemberTask( + m.name, + ((gangInfo.territoryClashChance > SafeClashChance && m.def < primaryStatMin) && combatantGetTrainingTask(m)) || + "Territory Warfare" + ); + } + + function assignAmass() { + if (gangInfo.territoryClashChance > SafeClashChance) { + combatants.forEach(combatantAssignAmass); + hackers.forEach(hackerAssignDefault); + } + else + members.forEach(combatantAssignAmass); + } + + function assignClash() { + combatants.forEach(combatantAssignAmass); + hackers.forEach(hackerAssignDefault); + } + + function assignEarn(focusRespect) { + const getTrainingTask = gangInfo.isHacking ? hackerGetTrainingTask : combatantGetTrainingTask; + const justiceTask = gangInfo.isHacking ? "Ethical Hacking" : "Vigilante Justice"; + const getEarnTask = focusRespect ? getRespectTask : getMoneyTask; + const assignMemberEarn = (m) => { ns.gang.setMemberTask( + m.name, getTrainingTask(m) ?? (wantedAboveThreshold() ? justiceTask : getEarnTask(m)) + ); }; + members.forEach(assignMemberEarn); + } + + switch (getMode(ns)) { + case GangModes.Amass: + assignAmass(); + break; + + case GangModes.Clash: + assignClash(); + break; + + case GangModes.EarnMoney: + assignEarn(false); + break; + + case GangModes.EarnRespect: + assignEarn(true); + break; + + case GangModes.Default: + default: + assignDefault(); + break; + } +} \ No newline at end of file diff --git a/gang/canRecruitMember.ts b/gang/canRecruitMember.ts new file mode 100644 index 0000000..0361383 --- /dev/null +++ b/gang/canRecruitMember.ts @@ -0,0 +1,5 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + ns.writePort(Ports.Gang, ns.gang.canRecruitMember()); +} \ No newline at end of file diff --git a/gang/equip.js b/gang/equip.js new file mode 100644 index 0000000..0df9c1c --- /dev/null +++ b/gang/equip.js @@ -0,0 +1,93 @@ +import {getGangInfo} from "./genGangInfo"; +import {getEquipmentInfo} from "./genEquipmentInfo"; +import {getMemberInfo} from "./genMemberInfo"; +import {isCombatant, isHacker} from "./recruit"; +import {GangModes, RespectTarget, getMode} from "./mode"; + +/** @param {NS} ns */ +export async function main(ns) { + const gangInfo = getGangInfo(ns); + const equipment = getEquipmentInfo(ns); + const members = getMemberInfo(ns); + const hackers = gangInfo.isHacking ? members.filter(isHacker) : []; + const combatants = gangInfo.isHacking ? members.filter(isCombatant) : members; + + function augNotOwned(eq) { return !(this.augmentations && this.augmentations.includes(eq)); } + function eqNotOwned(eq) { return !(this.upgrades && this.upgrades.includes(eq)); } + function costNegligible(eq) { return Math.log2(money) - Math.log2(equipment.cost[eq]) > 8; } + + var money = ns.getServerMoneyAvailable("home"); + + function tryPurchase(eq) { + if (ns.gang.purchaseEquipment(this.name, eq)) + money -= equipment.cost[eq]; + } + + if (gangInfo.respect < RespectTarget) { + // Only buy with heavy discount unless we have Scrooge McDuck money + equipment.augCombat = equipment.augCombat.filter(costNegligible); + equipment.augHack = equipment.augHack.filter(costNegligible); + equipment.eqCombat = equipment.eqCombat.filter(costNegligible); + equipment.eqHack = equipment.eqHack.filter(costNegligible); + } + + function equipDefault() { + for (const m of hackers) + equipment.augHack.filter(augNotOwned, m).forEach(tryPurchase, m); + for (const m of combatants) + equipment.augCombat.filter(augNotOwned, m).forEach(tryPurchase, m); + for (const m of hackers) + equipment.eqHack.filter(eqNotOwned, m).forEach(tryPurchase, m); + for (const m of combatants) + equipment.eqCombat.filter(eqNotOwned, m).forEach(tryPurchase, m); + } + + function equipHack() { + for (const m of hackers.concat(combatants)) { + equipment.augHack.filter(augNotOwned, m).forEach(tryPurchase, m); + equipment.eqHack.filter(eqNotOwned, m).forEach(tryPurchase, m); + } + } + + function equipCombat() { + for (const m of combatants.concat(hackers)) { + equipment.augCombat.filter(augNotOwned, m).forEach(tryPurchase, m); + equipment.eqCombat.filter(eqNotOwned, m).forEach(tryPurchase, m); + } + } + + function equipClash() { + for (const m of combatants) { + equipment.augCombat.filter(augNotOwned, m).forEach(tryPurchase, m); + equipment.eqCombat.filter(eqNotOwned, m).forEach(tryPurchase, m); + } + for (const m of hackers) { + equipment.augHack.filter(augNotOwned, m).forEach(tryPurchase, m); + equipment.eqHack.filter(eqNotOwned, m).forEach(tryPurchase, m); + } + } + + switch (getMode(ns)) { + case GangModes.Amass: + equipCombat(); + break; + + case GangModes.AmassAndRecruit: + case GangModes.Clash: + equipClash(); + break; + + case GangModes.EarnMoney: + case GangModes.EarnRespect: + if (gangInfo.isHacking) + equipHack(); + else + equipCombat(); + break; + + case GangModes.Default: + default: + equipDefault(); + break; + } +} \ No newline at end of file diff --git a/gang/genEquipmentInfo.js b/gang/genEquipmentInfo.js new file mode 100644 index 0000000..7331fa9 --- /dev/null +++ b/gang/genEquipmentInfo.js @@ -0,0 +1,40 @@ +const EquipmentInfoFile = "/gang/info/equipment.json"; + +export function getEquipmentInfo(ns) { + return JSON.parse(ns.read(EquipmentInfoFile)); +} + +/** @param {NS} ns */ +export async function main(ns) { + var equipment = { + "stats": {}, + "cost": {}, + "augHack": [], + "augCombat": [], + "Weapon": [], + "Armor": [], + "Rootkit": [], + "Vehicle": [] + }; + ns.gang.getEquipmentNames().forEach((eq) => { + var type = ns.gang.getEquipmentType(eq); + const cost = ns.gang.getEquipmentCost(eq); + + equipment.stats[eq] = ns.gang.getEquipmentStats(eq); + equipment.cost[eq] = cost; + + if (type == "Augmentation") + type = equipment.stats[eq]["hack"] ? "augHack" : "augCombat"; + + equipment[type].push(eq); + }); + // Concatenation here is done in order of purchasing priority + equipment.eqHack = equipment.Rootkit.concat(equipment.Vehicle); + equipment.eqCombat = equipment.Armor.concat(equipment.Weapon, equipment.Vehicle, equipment.Rootkit); + equipment.augCombat = equipment.augCombat.concat(equipment.augHack); // All stats contribute to combat effectiveness + delete equipment.Weapon; + delete equipment.Armor; + delete equipment.Rootkit; + delete equipment.Vehicle; + ns.write(EquipmentInfoFile, JSON.stringify(equipment), "w"); +} \ No newline at end of file diff --git a/gang/genInfo/gang.ts b/gang/genInfo/gang.ts new file mode 100644 index 0000000..cf20014 --- /dev/null +++ b/gang/genInfo/gang.ts @@ -0,0 +1,15 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; + +export var gangInfo: GangGenInfo; + +export async function main(ns: NS) { + ns.writePort(Ports.Exec, { + "script": "/gang/get/gangInfo.ts", + "hostname": ns.self().server, + "threadOrOptions": {"temporary": true}, + "retry": true + } as ExecutorJob); + await ns.nextPortWrite(Ports.Gang); + gangInfo = Object.freeze(ns.readPort(Ports.Gang)); +} \ No newline at end of file diff --git a/gang/genInfo/members.ts b/gang/genInfo/members.ts new file mode 100644 index 0000000..09b49cf --- /dev/null +++ b/gang/genInfo/members.ts @@ -0,0 +1,24 @@ +import {getEquipmentInfo} from "gang/genEquipmentInfo"; + +export interface ExtGangMemberInfo extends GangMemberInfo { + ascensionResult?: GangMemberAscension +}; + +export var members: Array = []; + +export function allAugsOwned(ns: NS, combatants: Array, hackers?: Array, equipmentInfo?: any) { + const priceComparator = (a: any, b: any) => equipmentInfo.cost[b] - equipmentInfo.cost[a]; + equipmentInfo ??= getEquipmentInfo(ns); + const mostExpensiveCombatAug = equipmentInfo.augCombat.toSorted(priceComparator)[0]; + const mostExpensiveHackAug = hackers && equipmentInfo.augHack.toSorted(priceComparator)[0]; + return combatants.every(m => m.augmentations.includes(mostExpensiveCombatAug)) && + (!hackers || hackers.every(m => m.augmentations.includes(mostExpensiveHackAug))); +} + +export async function main(ns: NS) { + members = ns.gang.getMemberNames().map((m) => { + let info = ns.gang.getMemberInformation(m) as ExtGangMemberInfo; + info.ascensionResult = ns.gang.getAscensionResult(m); + return info; + }); +} \ No newline at end of file diff --git a/gang/genInfo/otherGangs.ts b/gang/genInfo/otherGangs.ts new file mode 100644 index 0000000..8222f32 --- /dev/null +++ b/gang/genInfo/otherGangs.ts @@ -0,0 +1,24 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; +import {gangInfo} from "gang/genInfo/gang"; + +export var others: Array = []; +export var powerChanged = false; + +export async function main(ns: NS) { + const prev = others; + + ns.writePort(Ports.Exec, { + "script": "gang/get/otherGangInfo.ts", + "hostname": ns.self().server, + "threadOrOptions": {"temporary": true}, + "retry": true + } as ExecutorJob); + await ns.nextPortWrite(Ports.Gang); + // getOtherGangInformation for some reason includes our own gang, so have to filter it out + others = Object.values(ns.readPort(Ports.Gang) as Array).filter( + og => og.territory && og.territory != gangInfo.territory && og.power != gangInfo.power + ); + // FIXME: Is it really safe to assume order won't change? + powerChanged = !!prev.length && others.some((og, i) => og.power != prev[i]?.power); +} \ No newline at end of file diff --git a/gang/genTaskInfo.ts b/gang/genTaskInfo.ts new file mode 100644 index 0000000..0d94bf8 --- /dev/null +++ b/gang/genTaskInfo.ts @@ -0,0 +1,9 @@ +export const TaskInfoFile = "/gang/info/tasks.json"; + +export function getTaskInfo(ns: NS) { + return JSON.parse(ns.read(TaskInfoFile)) as Array; +} + +export async function main(ns: NS) { + ns.write(TaskInfoFile, JSON.stringify(ns.gang.getTaskNames().map(ns.gang.getTaskStats)), "w"); +} \ No newline at end of file diff --git a/gang/get/gangInfo.ts b/gang/get/gangInfo.ts new file mode 100644 index 0000000..af97180 --- /dev/null +++ b/gang/get/gangInfo.ts @@ -0,0 +1,5 @@ +import {Ports} from"lib/util"; + +export async function main(ns: NS) { + ns.writePort(Ports.Gang, ns.gang.getGangInformation()); +} \ No newline at end of file diff --git a/gang/get/member/ascensionResult.ts b/gang/get/member/ascensionResult.ts new file mode 100644 index 0000000..2cc569a --- /dev/null +++ b/gang/get/member/ascensionResult.ts @@ -0,0 +1,5 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + ns.writePort(Ports.Gang, (ns.args as Array).map(ns.gang.getAscensionResult)); +} \ No newline at end of file diff --git a/gang/get/member/info.ts b/gang/get/member/info.ts new file mode 100644 index 0000000..79aa75e --- /dev/null +++ b/gang/get/member/info.ts @@ -0,0 +1,5 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + ns.writePort(Ports.Gang, (ns.args as Array).map(ns.gang.getMemberInformation)); +} \ No newline at end of file diff --git a/gang/get/member/names.ts b/gang/get/member/names.ts new file mode 100644 index 0000000..347ff84 --- /dev/null +++ b/gang/get/member/names.ts @@ -0,0 +1,5 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + ns.writePort(Ports.Gang, ns.gang.getMemberNames); +} \ No newline at end of file diff --git a/gang/get/otherGangInfo.ts b/gang/get/otherGangInfo.ts new file mode 100644 index 0000000..96bd7e2 --- /dev/null +++ b/gang/get/otherGangInfo.ts @@ -0,0 +1,5 @@ +import {Ports} from"lib/util"; + +export async function main(ns: NS) { + ns.writePort(Ports.Gang, ns.gang.getOtherGangInformation()); +} \ No newline at end of file diff --git a/gang/info/equipment.json b/gang/info/equipment.json new file mode 100644 index 0000000..9ce5e9b --- /dev/null +++ b/gang/info/equipment.json @@ -0,0 +1 @@ +{"stats":{"Baseball Bat":{"str":1.04,"def":1.04},"Katana":{"str":1.08,"def":1.08,"dex":1.08},"Glock 18C":{"str":1.1,"def":1.1,"dex":1.1,"agi":1.1},"P90C":{"str":1.12,"def":1.1,"agi":1.1},"Steyr AUG":{"str":1.2,"def":1.15},"AK-47":{"str":1.25,"def":1.2},"M15A10 Assault Rifle":{"str":1.3,"def":1.25},"AWM Sniper Rifle":{"str":1.3,"dex":1.25,"agi":1.3},"Bulletproof Vest":{"def":1.04},"Full Body Armor":{"def":1.08},"Liquid Body Armor":{"def":1.15,"agi":1.15},"Graphene Plating Armor":{"def":1.2},"Ford Flex V20":{"agi":1.04,"cha":1.04},"ATX1070 Superbike":{"agi":1.08,"cha":1.08},"Mercedes-Benz S9001":{"agi":1.12,"cha":1.12},"White Ferrari":{"agi":1.16,"cha":1.16},"NUKE Rootkit":{"hack":1.05},"Soulstealer Rootkit":{"hack":1.1},"Demon Rootkit":{"hack":1.15},"Hmap Node":{"hack":1.12},"Jack the Ripper":{"hack":1.15},"Bionic Arms":{"str":1.3,"dex":1.3},"Bionic Legs":{"agi":1.6},"Bionic Spine":{"str":1.15,"def":1.15,"dex":1.15,"agi":1.15},"BrachiBlades":{"str":1.4,"def":1.4},"Nanofiber Weave":{"str":1.2,"def":1.2},"Synthetic Heart":{"str":1.5,"agi":1.5},"Synfibril Muscle":{"str":1.3,"def":1.3},"BitWire":{"hack":1.05},"Neuralstimulator":{"hack":1.15},"DataJack":{"hack":1.1},"Graphene Bone Lacings":{"str":1.7,"def":1.7}},"cost":{"Baseball Bat":36163.92732481126,"Katana":433967.12789773504,"Glock 18C":904098.1831202814,"P90C":1808196.3662405629,"Steyr AUG":2169835.639488675,"AK-47":3616392.7324811257,"M15A10 Assault Rifle":5424589.098721689,"AWM Sniper Rifle":8136883.648082533,"Bulletproof Vest":72327.85464962252,"Full Body Armor":180819.6366240563,"Liquid Body Armor":904098.1831202814,"Graphene Plating Armor":1446557.0929924503,"Ford Flex V20":108491.78197443376,"ATX1070 Superbike":325475.3459233013,"Mercedes-Benz S9001":650950.6918466026,"White Ferrari":1084917.8197443376,"NUKE Rootkit":180819.6366240563,"Soulstealer Rootkit":904098.1831202814,"Demon Rootkit":2712294.5493608443,"Hmap Node":1446557.0929924503,"Jack the Ripper":2712294.5493608443,"Bionic Arms":361639273.24811256,"Bionic Legs":361639273.24811256,"Bionic Spine":542458909.8721689,"BrachiBlades":723278546.4962251,"Nanofiber Weave":433967127.89773506,"Synthetic Heart":904098183.1202815,"Synfibril Muscle":542458909.8721689,"BitWire":180819636.62405628,"Neuralstimulator":361639273.24811256,"DataJack":271229454.93608445,"Graphene Bone Lacings":1808196366.240563},"augHack":["BitWire","Neuralstimulator","DataJack"],"augCombat":["Bionic Arms","Bionic Legs","Bionic Spine","BrachiBlades","Nanofiber Weave","Synthetic Heart","Synfibril Muscle","Graphene Bone Lacings","BitWire","Neuralstimulator","DataJack"],"eqHack":["NUKE Rootkit","Soulstealer Rootkit","Demon Rootkit","Hmap Node","Jack the Ripper","Ford Flex V20","ATX1070 Superbike","Mercedes-Benz S9001","White Ferrari"],"eqCombat":["Bulletproof Vest","Full Body Armor","Liquid Body Armor","Graphene Plating Armor","Baseball Bat","Katana","Glock 18C","P90C","Steyr AUG","AK-47","M15A10 Assault Rifle","AWM Sniper Rifle","Ford Flex V20","ATX1070 Superbike","Mercedes-Benz S9001","White Ferrari","NUKE Rootkit","Soulstealer Rootkit","Demon Rootkit","Hmap Node","Jack the Ripper"]} \ No newline at end of file diff --git a/gang/info/tasks.json b/gang/info/tasks.json new file mode 100644 index 0000000..dbe82df --- /dev/null +++ b/gang/info/tasks.json @@ -0,0 +1 @@ +[{"name":"Unassigned","desc":"This gang member is currently idle","isHacking":true,"isCombat":true,"baseRespect":0,"baseWanted":0,"baseMoney":0,"hackWeight":100,"strWeight":0,"defWeight":0,"dexWeight":0,"agiWeight":0,"chaWeight":0,"difficulty":1,"territory":{"money":1,"respect":1,"wanted":1}},{"name":"Mug People","desc":"Assign this gang member to mug random people on the streets

    Earns money - Slightly increases respect - Very slightly increases wanted level","isHacking":false,"isCombat":true,"baseRespect":0.00005,"baseWanted":0.00005,"baseMoney":3.6,"hackWeight":0,"strWeight":25,"defWeight":25,"dexWeight":25,"agiWeight":10,"chaWeight":15,"difficulty":1,"territory":{"money":1,"respect":1,"wanted":1}},{"name":"Deal Drugs","desc":"Assign this gang member to sell drugs

    Earns money - Slightly increases respect - Slightly increases wanted level - Scales slightly with territory","isHacking":false,"isCombat":true,"baseRespect":0.00006,"baseWanted":0.002,"baseMoney":15,"hackWeight":0,"strWeight":0,"defWeight":0,"dexWeight":20,"agiWeight":20,"chaWeight":60,"difficulty":3.5,"territory":{"money":1.2,"respect":1,"wanted":1.15}},{"name":"Strongarm Civilians","desc":"Assign this gang member to extort civilians in your territory

    Earns money - Slightly increases respect - Increases wanted - Scales heavily with territory","isHacking":false,"isCombat":true,"baseRespect":0.00004,"baseWanted":0.02,"baseMoney":7.5,"hackWeight":10,"strWeight":25,"defWeight":25,"dexWeight":20,"agiWeight":10,"chaWeight":10,"difficulty":5,"territory":{"money":1.6,"respect":1.1,"wanted":1.5}},{"name":"Run a Con","desc":"Assign this gang member to run cons

    Earns money - Increases respect - Increases wanted level","isHacking":false,"isCombat":true,"baseRespect":0.00012,"baseWanted":0.05,"baseMoney":45,"hackWeight":0,"strWeight":5,"defWeight":5,"dexWeight":25,"agiWeight":25,"chaWeight":40,"difficulty":14,"territory":{"money":1,"respect":1,"wanted":1}},{"name":"Armed Robbery","desc":"Assign this gang member to commit armed robbery on stores, banks and armored cars

    Earns money - Increases respect - Increases wanted level","isHacking":false,"isCombat":true,"baseRespect":0.00014,"baseWanted":0.1,"baseMoney":114,"hackWeight":20,"strWeight":15,"defWeight":15,"dexWeight":20,"agiWeight":10,"chaWeight":20,"difficulty":20,"territory":{"money":1,"respect":1,"wanted":1}},{"name":"Traffick Illegal Arms","desc":"Assign this gang member to traffick illegal arms

    Earns money - Increases respect - Increases wanted level - Scales heavily with territory","isHacking":false,"isCombat":true,"baseRespect":0.0002,"baseWanted":0.24,"baseMoney":174,"hackWeight":15,"strWeight":20,"defWeight":20,"dexWeight":20,"agiWeight":0,"chaWeight":25,"difficulty":32,"territory":{"money":1.4,"respect":1.3,"wanted":1.25}},{"name":"Threaten & Blackmail","desc":"Assign this gang member to threaten and blackmail high-profile targets

    Earns money - Slightly increases respect - Slightly increases wanted level","isHacking":false,"isCombat":true,"baseRespect":0.0002,"baseWanted":0.125,"baseMoney":72,"hackWeight":25,"strWeight":25,"defWeight":0,"dexWeight":25,"agiWeight":0,"chaWeight":25,"difficulty":28,"territory":{"money":1,"respect":1,"wanted":1}},{"name":"Human Trafficking","desc":"Assign this gang member to engage in human trafficking operations

    Earns money - Increases respect - Increases wanted level - Scales heavily with territory","isHacking":false,"isCombat":true,"baseRespect":0.004,"baseWanted":1.25,"baseMoney":360,"hackWeight":30,"strWeight":5,"defWeight":5,"dexWeight":30,"agiWeight":0,"chaWeight":30,"difficulty":36,"territory":{"money":1.5,"respect":1.5,"wanted":1.6}},{"name":"Terrorism","desc":"Assign this gang member to commit acts of terrorism

    Greatly increases respect - Greatly increases wanted level - Scales heavily with territory","isHacking":false,"isCombat":true,"baseRespect":0.01,"baseWanted":6,"baseMoney":0,"hackWeight":20,"strWeight":20,"defWeight":20,"dexWeight":20,"agiWeight":0,"chaWeight":20,"difficulty":36,"territory":{"money":1,"respect":2,"wanted":2}},{"name":"Vigilante Justice","desc":"Assign this gang member to be a vigilante and protect the city from criminals

    Decreases wanted level","isHacking":true,"isCombat":true,"baseRespect":0,"baseWanted":-0.001,"baseMoney":0,"hackWeight":20,"strWeight":20,"defWeight":20,"dexWeight":20,"agiWeight":20,"chaWeight":0,"difficulty":1,"territory":{"money":1,"respect":1,"wanted":0.9}},{"name":"Train Combat","desc":"Assign this gang member to increase their combat stats (str, def, dex, agi)","isHacking":true,"isCombat":true,"baseRespect":0,"baseWanted":0,"baseMoney":0,"hackWeight":0,"strWeight":25,"defWeight":25,"dexWeight":25,"agiWeight":25,"chaWeight":0,"difficulty":100,"territory":{"money":1,"respect":1,"wanted":1}},{"name":"Train Hacking","desc":"Assign this gang member to train their hacking skills","isHacking":true,"isCombat":true,"baseRespect":0,"baseWanted":0,"baseMoney":0,"hackWeight":100,"strWeight":0,"defWeight":0,"dexWeight":0,"agiWeight":0,"chaWeight":0,"difficulty":45,"territory":{"money":1,"respect":1,"wanted":1}},{"name":"Train Charisma","desc":"Assign this gang member to train their charisma","isHacking":true,"isCombat":true,"baseRespect":0,"baseWanted":0,"baseMoney":0,"hackWeight":0,"strWeight":0,"defWeight":0,"dexWeight":0,"agiWeight":0,"chaWeight":100,"difficulty":8,"territory":{"money":1,"respect":1,"wanted":1}},{"name":"Territory Warfare","desc":"Members assigned to this task increase your gang's power. They will also fight for territory if 'Territory Clashes' are enabled.

    Gang members performing this task can be killed during clashes.","isHacking":true,"isCombat":true,"baseRespect":0,"baseWanted":0,"baseMoney":0,"hackWeight":15,"strWeight":20,"defWeight":20,"dexWeight":20,"agiWeight":20,"chaWeight":5,"difficulty":5,"territory":{"money":1,"respect":1,"wanted":1}}] \ No newline at end of file diff --git a/gang/kingpin.ts b/gang/kingpin.ts new file mode 100644 index 0000000..9b5e7aa --- /dev/null +++ b/gang/kingpin.ts @@ -0,0 +1,44 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; +import {main as genGangInfo} from "gang/genInfo/gang"; +import {main as genOtherGangInfo} from "gang/genInfo/otherGangs"; +import {main as recruit} from "gang/recruit"; +import {main as setMode} from "gang/mode"; +import {main as ascend} from "gang/ascend"; +import {addTime} from "gang/mode"; + +const MarginMillis = 20; + +export async function main(ns: NS) { + function addTimeWrapper(millis: number) { + ns.printf("INFO: Processed %d ms", millis); + addTime(ns, millis); + } + + const mdules = [ + genGangInfo, + genOtherGangInfo, + recruit, + setMode, + ascend + ]; + const hostname = ns.self().server; + const jobs: Array = [ + "genEquipmentInfo.j", + "genMemberInfo.t", + "equip.j", + "assign.j", + ].map(script => ({"script": "/gang/" + script + "s", "hostname": hostname, "threadOrOptions": {"temporary": true}} as ExecutorJob)); + + ns.disableLog("sleep"); + ns.clearPort(Ports.Gang); + while (true) { + for (const mdule of mdules) + await mdule(ns); + for (const job of jobs) { + ns.writePort(Ports.Exec, job); + await ns.sleep(MarginMillis); + } + await ns.gang.nextUpdate().then(addTimeWrapper); + } +} \ No newline at end of file diff --git a/gang/launch.ts b/gang/launch.ts new file mode 100644 index 0000000..8103bbe --- /dev/null +++ b/gang/launch.ts @@ -0,0 +1,7 @@ +export async function main(ns: NS) { + const flags = ns.flags([["q", false]]); + if (ns.gang.inGang()) + ns.spawn("./launch2.ts", {"spawnDelay": 0}); + else if (!flags.q) + ns.toast("Not in gang", ns.enums.ToastVariant.ERROR); +} \ No newline at end of file diff --git a/gang/launch2.ts b/gang/launch2.ts new file mode 100644 index 0000000..e243d26 --- /dev/null +++ b/gang/launch2.ts @@ -0,0 +1,7 @@ +export async function main(ns: NS) { + while (!ns.run("./genTaskInfo.ts", {"temporary": true})) + await ns.sleep(100); + while (!ns.run("./genMemberInfo.ts", {"temporary": true})) + await ns.sleep(100); + ns.spawn("./kingpin.ts", {"spawnDelay": 0}); +} \ No newline at end of file diff --git a/gang/mode.ts b/gang/mode.ts new file mode 100644 index 0000000..695a9aa --- /dev/null +++ b/gang/mode.ts @@ -0,0 +1,82 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; +import {gangInfo} from "gang/genInfo/gang"; +import {others, powerChanged} from "gang/genInfo/otherGangs"; +import {members, allAugsOwned} from "gang/genInfo/members"; +import {MaxMembers} from "gang/recruit"; + +export enum GangMode { + Default, + Amass, + Clash, + EarnMoney, + EarnRespect, + AugPrep +}; + +export var gangMode = GangMode.Default; + +export const RepTarget = 2.5e6; +export const RespectTarget = 7.5e6; // Tuned to achieve a high discount whithout going too far into diminishing returns +export const MoneyTarget = 1e12; + +const MinWinChance = .58; +const TargetWinChance = .96; + +// Taken from game code +const MillisPerCycle = 2000; +const MillisPerBonusTimeCycle = 5000; +const CyclesPerPowerUpdate = 10; +const MillisPerPowerUpdate = MillisPerCycle * CyclesPerPowerUpdate; + +var millisSincePowerUpdate = 0; + +export function addTime(ns: NS, millis: number) { + millisSincePowerUpdate += millis; +} + +export async function main(ns: NS) { + if (powerChanged || gangInfo.territory == 1) + millisSincePowerUpdate = 0; + const powerUpdateComing = millisSincePowerUpdate + (ns.gang.getBonusTime() ? MillisPerBonusTimeCycle : MillisPerCycle) >= MillisPerPowerUpdate; + var engage = false; + var winChance = 0; + + function setPeaceMode() { + // FIXME: Currently, this assumes combat gang + gangMode = members.length == MaxMembers && ( + (allAugsOwned(ns, members, undefined) && ns.getServerMoneyAvailable("home") < MoneyTarget) || + gangInfo.respect >= RespectTarget + ) ? GangMode.EarnMoney : GangMode.EarnRespect; + } + + if (gangInfo.territory < 1) { + winChance = others.map((og) => { + return gangInfo.power > og.power ? 1 - .5 * (og.power / gangInfo.power) : .5 * (gangInfo.power / og.power); + }).reduce((sum, p) => { return sum + p; }, 0) / others.length; + engage = winChance > MinWinChance; + + ns.writePort(Ports.Exec, { + "script": "/gang/set/territoryWarfare.ts", + "hostname": ns.self().server, + "args": [engage], + "threadOrOptions": {"temporary": true}, + "retry": true + } as ExecutorJob); + } + + if (powerUpdateComing) { + if (engage) { + if (winChance < TargetWinChance) + gangMode = GangMode.Clash; + else + setPeaceMode(); + } + else + gangMode = GangMode.Amass; + } + else + setPeaceMode(); + + ns.print("INFO: Mode is set to ", gangMode); +} \ No newline at end of file diff --git a/gang/recruit.ts b/gang/recruit.ts new file mode 100644 index 0000000..7fcb7fb --- /dev/null +++ b/gang/recruit.ts @@ -0,0 +1,44 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; +import {gangInfo} from "gang/genInfo/gang"; +import {members} from "gang/genInfo/members"; + +export const MemberNames = { + "Hack": new Set(["Shades", "TOMCAT", "Optics", "Tattletale", "Saint", "Pilar", "Nomi", "Kiwi", "Lucyna", "Makishima", "Zed", "Rad Ray"]), + "Combat": new Set(["Neo", "Trinity", "Bakuda", "Maine", "Falco", "Dorio", "David", "Rebecca", "Laserhawk", "Denton", "Jensen", "Kogami"]) +}; +export const MaxMembers = 12; + +export function isHacker(m: string | GangMemberInfo) { + return MemberNames.Hack.has(m as string) || MemberNames.Hack.has((m as GangMemberInfo).name); +} + +export function isCombatant(m: string | GangMemberInfo) { + return MemberNames.Combat.has(m as string) || MemberNames.Combat.has((m as GangMemberInfo).name); +} + +export async function main(ns: NS) { + var executorJob = { + "script": "/gang/canRecruitMember.ts", + "hostname": ns.self().server, + "threadOrOptions": {"temporary": true, "preventDuplicates": true}, + "retry": true + } as ExecutorJob; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Gang); + if (ns.readPort(Ports.Gang)) { + const memberNames = members.map((m: GangMemberInfo) => m.name); + const hackers = new Set(memberNames.filter(isHacker)); + const combatants = new Set(memberNames.filter(isCombatant)); + const candidates = Array.from(( + !gangInfo.isHacking || combatants.size < hackers.size ? + MemberNames.Combat.difference(combatants) : + MemberNames.Hack.difference(hackers)).values() + ); + const recruit = candidates[Math.floor(Math.random() * (candidates.length - .001))]; + executorJob.script = "/gang/recruitMember.ts"; + executorJob.args = [recruit]; + ns.print("Recruiting ", recruit); + ns.writePort(Ports.Exec, executorJob); + } +} \ No newline at end of file diff --git a/gang/recruitMember.ts b/gang/recruitMember.ts new file mode 100644 index 0000000..da924c6 --- /dev/null +++ b/gang/recruitMember.ts @@ -0,0 +1,5 @@ +export async function main(ns: NS) { + const recruit = ns.args[0] as string; + if (ns.gang.recruitMember(recruit)) + ns.toast("Recruited " + recruit); +} \ No newline at end of file diff --git a/gang/rename.ts b/gang/rename.ts new file mode 100644 index 0000000..5942888 --- /dev/null +++ b/gang/rename.ts @@ -0,0 +1,3 @@ +export async function main(ns: NS) { + ns.gang.renameMember(ns.args[0] as string, ns.args[1] as string); +} \ No newline at end of file diff --git a/gang/set/territoryWarfare.ts b/gang/set/territoryWarfare.ts new file mode 100644 index 0000000..30fad84 --- /dev/null +++ b/gang/set/territoryWarfare.ts @@ -0,0 +1,3 @@ +export async function main(ns: NS) { + ns.gang.setTerritoryWarfare(ns.args[0] as boolean); +} \ No newline at end of file diff --git a/go/getBoardState.js b/go/getBoardState.js new file mode 100644 index 0000000..f8b383c --- /dev/null +++ b/go/getBoardState.js @@ -0,0 +1,6 @@ +import {Ports} from "/library/library.js"; + +/** @param {NS} ns */ +export async function main(ns) { + ns.writePort(Ports.Go, ns.go.getBoardState()); +} \ No newline at end of file diff --git a/go/getLiberties.js b/go/getLiberties.js new file mode 100644 index 0000000..48bc8a6 --- /dev/null +++ b/go/getLiberties.js @@ -0,0 +1,7 @@ +import {Ports} from "/library/library.js"; + +/** @param {NS} ns */ +export async function main(ns) { + // TODO: Re-implement + ns.writePort(Ports.Go, ns.go.analysis.getLiberties()); +} \ No newline at end of file diff --git a/go/move.js b/go/move.js new file mode 100644 index 0000000..02b50c8 --- /dev/null +++ b/go/move.js @@ -0,0 +1,7 @@ +import {Ports} from "/library/library.js"; + +/** @param {NS} ns */ +export async function main(ns) { + var p = ns.go.makeMove(ns.readPort(Ports.Go), ns.readPort(Ports.Go)).then((response) => ns.writePort(Ports.Go, response)); + await p; +} \ No newline at end of file diff --git a/go/play.js b/go/play.js new file mode 100644 index 0000000..7de4a70 --- /dev/null +++ b/go/play.js @@ -0,0 +1,6 @@ +import {Ports} from "/library/library.js"; + +/** @param {NS} ns */ +export async function main(ns) { + +} \ No newline at end of file diff --git a/hack/calcHackParams.js b/hack/calcHackParams.js new file mode 100644 index 0000000..f8d1690 --- /dev/null +++ b/hack/calcHackParams.js @@ -0,0 +1,101 @@ +const HackParamsFile = "./hackParams.json"; +const FailExpRatio = 0.25; + +/** @param {NS} ns */ +export async function main(ns) { + const flags = ns.flags([["p", 0]]); + const weakenRam = ns.getScriptRam("./weaken.ts"); + const growRam = ns.getScriptRam("./grow.ts"); + const hackRam = ns.getScriptRam("./hack.ts"); + const [player, home, targets, opts = {}] = ns.readPort(flags.p); + const weakenAmt = {"home": ns.weakenAnalyze(1, home.cpuCores), "host": ns.weakenAnalyze(1)}; + var hackParams = {}; + + opts.minRam ??= 0; + opts.maxHostRam ??= Number.MAX_SAFE_INTEGER; + if (opts.minRam > opts.maxHostRam) + opts.minRam = 0; + + for (let target of targets) { + let params = { + "home": {"efficiency": -1, "expEfficiency": -1, "ramCost": {}, "threads": {}}, + "host": {"efficiency": -1, "expEfficiency": -1, "ramCost": {}, "threads": {}} + }; + target.hackDifficulty = target.minDifficulty; // Assume server will be fully weakened. + const hackChance = ns.formulas.hacking.hackChance(target, player); + // hackPercent returns 0 for servers with unmet hack requirement, which would cause infinite loop + const hackAmt = ns.formulas.hacking.hackPercent(target, player) || 1; + const weakenTime = ns.formulas.hacking.weakenTime(target, player); + params.home.ramCost.total = 1 << 20; + params.host.ramCost.total = 1 << 20; + + for (let tHack = 1, ratio = hackAmt, cont = true; cont; ++tHack, ratio = Math.min(ratio + hackAmt, 1)) { + // Seems this function can't be assumed to have only one inflection point + cont = /*(params.home.efficiency < 0 || params.host.efficiency < 0) &&*/ ratio < 1; + target.moneyAvailable = target.moneyMax * (1 - ratio); // Assume server will be depleted. + let tGrow = ns.formulas.hacking.growThreads(target, player, target.moneyMax, home.cpuCores); + let tWeaken = [ + Math.ceil(ns.hackAnalyzeSecurity(tHack) / weakenAmt.home), + Math.ceil(ns.growthAnalyzeSecurity(tGrow) / weakenAmt.home) + ]; + let ramCost = {"w": [tWeaken[0] * weakenRam, tWeaken[1] * weakenRam], "g": tGrow * growRam}; + ramCost.home = ramCost.w[0] + ramCost.w[1] + ramCost.g; + ramCost.host = ramCost.h = tHack * hackRam; + ramCost.total = ramCost.home + ramCost.host; + let efficiency = ratio / ramCost.total; + if (efficiency >= params.home.efficiency && + (ramCost.host <= opts.maxHostRam || tHack == 1) && + (ramCost.total >= opts.minRam || ratio > .95)) { + params.home.ratio = ratio; + params.home.efficiency = efficiency; + params.home.ramCost = structuredClone(ramCost); + params.home.threads.h = tHack; + params.home.threads.g = tGrow; + params.home.threads.w = tWeaken; + //cont = true; + } + tGrow = ns.formulas.hacking.growThreads(target, player, target.moneyMax); + tWeaken = [ + Math.ceil(ns.hackAnalyzeSecurity(tHack) / weakenAmt.host), + Math.ceil(ns.growthAnalyzeSecurity(tGrow) / weakenAmt.host) + ]; + ramCost.w = [tWeaken[0] * weakenRam, tWeaken[1] * weakenRam]; + ramCost.g = tGrow * growRam; + ramCost.total = tGrow * growRam + (tWeaken[0] + tWeaken[1]) * weakenRam + ramCost.host; + efficiency = ratio / ramCost.total; + cont &= ramCost.host <= opts.maxHostRam || ramCost.total <= opts.maxHostRam; + if (efficiency >= params.host.efficiency && + (ramCost.total <= opts.maxHostRam || tHack == 1) && + (ramCost.total >= opts.minRam || ratio > .95)) { + delete ramCost.home; + delete ramCost.host; + params.host.ratio = tHack * hackAmt; + params.host.efficiency = efficiency; + params.host.ramCost = ramCost; + params.host.threads.h = tHack; + params.host.threads.g = tGrow; + params.host.threads.w = tWeaken; + //cont = true; + } + } + let hackExp = ns.formulas.hacking.hackExp(target, player); + hackExp = hackExp * (hackChance + (1 - hackChance) * FailExpRatio); + // FIXME: Params should be set to *something* after the loop is done, but that sometimes fails (WHY???) + // So, need to guard against accessing properties on undefined + if (params.host.efficiency > 0) { + params.host.efficiency *= 1000 * target.moneyMax * hackChance / weakenTime; + const totalThreads = params.host.threads.h + params.host.threads.w[0] + params.host.threads.w[1] + params.host.threads.g; + params.host.expEfficiency = hackExp * totalThreads / (weakenTime * params.host.ramCost.total); + + } + if (params.home.efficiency > 0) { + params.home.efficiency *= 1000 * target.moneyMax * hackChance / weakenTime; + const totalThreads = params.home.threads.h + params.home.threads.w[0] + params.home.threads.w[1] + params.home.threads.g; + params.home.expEfficiency = hackExp * totalThreads / (weakenTime * params.home.ramCost.total); + } + hackParams[target.hostname] = params; + } + // JSON file is used only for debugging + ns.write(HackParamsFile, JSON.stringify(hackParams), "w"); + ns.writePort(flags.p, hackParams); +} \ No newline at end of file diff --git a/hack/deployShotgunner.ts b/hack/deployShotgunner.ts new file mode 100644 index 0000000..cf88b85 --- /dev/null +++ b/hack/deployShotgunner.ts @@ -0,0 +1,15 @@ +import {Ports, deepScan} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; + +export function deployShotgunner(ns: NS, dest: string) { + var hostnames = deepScan(ns, undefined, false, false) as Array; + + for (const hostname of hostnames.filter(ns.getServerMaxRam)) + ns.scp(ns.ls("home", "hack/").concat(ns.ls("home", "lib/"), ["/srv/get.js", "/stock/genSymbolInfo.ts"]), hostname); + + ns.scp(ns.ls("home", "daemons/"), dest); + ns.kill("/hack/shotgunner.js", dest); + ns.writePort(Ports.Exec, {script: "/hack/shotgunner.js", hostname: dest} as ExecutorJob); +} + +export async function main(ns: NS) { deployShotgunner(ns, ns.args[0] as string); } \ No newline at end of file diff --git a/hack/grow.ts b/hack/grow.ts new file mode 100644 index 0000000..fa113e2 --- /dev/null +++ b/hack/grow.ts @@ -0,0 +1,10 @@ +export async function main(ns: NS) { + const flags = ns.flags([["d", 0], ["n", 1], ["s", false]]); + const opts: BasicHGWOptions = { + "stock": flags.s as boolean, + "additionalMsec": flags.d as number + }; + + for (let i = 0; i < (flags.n as number); ++i) + await ns.grow((flags._ as Array)[0], opts); +} \ No newline at end of file diff --git a/hack/hack.ts b/hack/hack.ts new file mode 100644 index 0000000..3f4ec01 --- /dev/null +++ b/hack/hack.ts @@ -0,0 +1,10 @@ +export async function main(ns: NS) { + const flags = ns.flags([["d", 0], ["n", 1], ["s", false]]); + const opts: BasicHGWOptions = { + "stock": flags.s as boolean, + "additionalMsec": flags.d as number + }; + + for (let i = 0; i < (flags.n as number); ++i) + await ns.hack((flags._ as Array)[0], opts); +} \ No newline at end of file diff --git a/hack/shotgunner.js b/hack/shotgunner.js new file mode 100644 index 0000000..57c7785 --- /dev/null +++ b/hack/shotgunner.js @@ -0,0 +1,338 @@ +// Execution time ratios: 20 : 16 : 5 (weaken : grow : hack) + +import {BitNode} from "lib/nsEnums"; +import {ReservedRam, Ports, SpecialHostnames, deepScan, srvSecurityDelta} from "lib/util"; +import {extResetInfo} from "lib/genInfo/reset"; +import {symbolInfoRecord} from "stock/genSymbolInfo"; + +// Could do more, but launching even this many takes a significant chunk of time, +// affecting both hacking performance and IRL performance. +// And this isn't a hard cap. The shotgunner currently doesn't recalculate when available RAM +// changes, and after the initial blast, it will fill in the gaps with smaller batches. +const BatchSoftcap = 18e3; + +const BatchHardcap = 25e3; + +const BatchesPerSleep = 0x1FF; + +// No longer used +function ramComparator(a, b) { return (a.maxRam - a.ramUsed) - (b.maxRam - b.ramUsed); }; + +function getForecasts() { + var forecasts = {}; + if (symbolInfoRecord) { + for (const s of Object.values(symbolInfoRecord)) + forecasts[s.organization] = s.forecast; + } + return forecasts; +} + +/** @param {Server} a + * @param {Server} b + */ +function forecastComparator(a, b) { + var symbolInfo = Object.values(symbolInfoRecord); + return Math.abs(symbolInfo.find(s => s.organization == a.organizationName).forecast - 0.5) - + Math.abs(symbolInfo.find(s => s.organization == b.organizationName).forecast - 0.5); +} + +function shouldUseStockMode() { + // TODO: Go by BN mults when available + return [BitNode.GhostOfWallStreet, BitNode.BigCrash].includes(extResetInfo.currentNode); +} + +function isGrowable(server) { return server.moneyMax; } + +/** @param {NS} ns + * @param {Set} pids +*/ +function removeDeadPids(ns, pids) { + for (const pid of pids.values()) + if (!ns.isRunning(pid)) + pids.delete(pid); +} + +function printStockInfo(ns, forecasts, target) { + if (forecasts && forecasts[target.organizationName]) + ns.printf("INFO: Using stock manipulation flags. Forecast is %s.", ns.formatPercent(forecasts[target.organizationName])); + else + ns.printf("INFO: Forecast for %s unavailable. Not engaging in market manipulation.", target.organizationName); +} + +/** @param {NS} ns */ +async function shootShotgun(ns, target, hackParams, forecast, servers, player, prevPids) { + if (srvSecurityDelta(target)) + ns.printf("WARN: %s's security is above minimum", target.hostname); + + const timestamp = performance.now(); + const times = { + "h": ns.formulas.hacking.hackTime(target, player), + "g": ns.formulas.hacking.growTime(target, player), + "w": ns.formulas.hacking.weakenTime(target, player) + }; + const extraMillis = [times.w - times.h, times.w - times.g]; + var batchCnt = 0; + var pids = new Set(); + + // Single-step prep by skipping hacks is best we can do for shotgunning + const runGrow = true; //!srvSecurityDelta(target); + const runHack = !srvSecurityDelta(target) && player.skills.hacking >= target.requiredHackingSkill; + + if (!(runGrow && runHack)) { + hackParams = structuredClone(hackParams); + if (!runHack) { + hackParams.host.ramCost.total -= hackParams.host.ramCost.h; + hackParams.home.ramCost.host = 0; + } + if (!runGrow) { + hackParams.home.ramCost.total -= hackParams.home.ramCost.g; + hackParams.host.ramCost.total -= hackParams.host.ramCost.g; + } + } + + forecast ??= 0.5; + servers.home.ramUsed += ReservedRam; + var hackerIdx = 0; + do { + do { + // For best performance, this loop must contain no ns calls except the unavoidable execs + const useHome = hackParams.home.ramCost.home <= servers.home.maxRam - servers.home.ramUsed; // && (hackParams.home.ramCost.total > hackParams.host.ramCost.total || homeCores == 1); + while (hackerIdx < servers.hackers.length && + servers.hackers[hackerIdx].maxRam - servers.hackers[hackerIdx].ramUsed < + (useHome ? hackParams.home.ramCost.host : hackParams.host.ramCost.total)) + ++hackerIdx; + // If there's still space left on home after using it for grow/weaken, use what remains for hacking + if (hackerIdx == servers.hackers.length && hackParams.home.ramCost.total > servers.home.maxRam - servers.home.ramUsed) + ++hackerIdx; + if (hackerIdx <= servers.hackers.length) { + const hacker = hackerIdx == servers.hackers.length ? servers.home : servers.hackers[hackerIdx]; + let batchParams; + let wgServer; + // Scripts are restarted on load, so there's no point saving hacking scripts + // All are temporary + if (useHome) { + batchParams = hackParams.home; + wgServer = servers.home; + servers.home.ramUsed += batchParams.ramCost.home; + hacker.ramUsed += batchParams.ramCost.host; + } + else { + // Fall back to running all on hacker + batchParams = hackParams.host; + wgServer = hacker; + hacker.ramUsed += batchParams.ramCost.total; + } + if (runHack) + pids.add(ns.exec("./hack.ts", hacker.hostname, {"temporary": true, "threads": batchParams.threads.h}, target.hostname, "-d", extraMillis[0], ...(forecast < 0.5 ? ["-s"] : []))); + pids.add(ns.exec("./weaken.ts", wgServer.hostname, {"temporary": true, "threads": batchParams.threads.w[0]}, target.hostname)); + if (runGrow) + pids.add(ns.exec("./grow.ts", wgServer.hostname, {"temporary": true, "threads": batchParams.threads.g}, target.hostname, "-d", extraMillis[1], ...(forecast > 0.5 ? ["-s"] : []))); + pids.add(ns.exec("./weaken.ts", wgServer.hostname, {"temporary": true, "threads": batchParams.threads.w[1]}, target.hostname)); + ++batchCnt; + if (!(++batchCnt & BatchesPerSleep)) + await ns.sleep(0); + } + } while (hackerIdx <= servers.hackers.length && !pids.has(0) && batchCnt < BatchHardcap); + removeDeadPids(ns, prevPids); + if (prevPids.size) + await ns.sleep(20); + } while (prevPids.size); + if (pids.has(0)) { + ns.print("ERROR: Exec failed (probably bad RAM logic)"); + ns.enableLog("exec"); + ns.toast("Exec failed in batcher", ns.enums.ToastVariant.ERROR); + pids.delete(0); + } + servers.home.ramUsed -= ReservedRam; + ns.printf("SUCCESS: Launched (or attempted to launch) %d batches in %dms", batchCnt, performance.now() - timestamp); + return pids; +} + +/** @param {NS} ns */ +async function redeploy(ns) { + // Run deployment script not only to restart this batcher with an updated hostname list, + // but also to deploy hack scripts to new server(s); + ns.enableLog("exec"); + while (!ns.exec("./deployShotgunner.ts", "home", 1, ns.self().server)) + await ns.sleep(50); + return ns.exit(); +} + +/** @param {NS} ns */ +export async function main(ns) { + ns.disableLog("scan"); + const hostname = ns.self().server; + const useStockMode = shouldUseStockMode(); + /*const weakenRam = ns.getScriptRam("weaken.ts"); + const growRam = ns.getScriptRam("grow.ts"); + const hackRam = ns.getScriptRam("hack.ts");*/ + var hackParams = {}, gapFillerParams = {}; + var player; + var homeCores = undefined; + var hackSkill = undefined; + var targetCnt = 0; + var hostnames = deepScan(ns, "home", true); + var servers; + var moneyTargets = new Array(2), expTargets = new Array(2); + var farmingExp = true; + var forecasts; + var pids = new Set(); + + if (!ns.fileExists("Formulas.exe", "home")) { + // TODO: Write fallback for pre-Formulas.exe usage + ns.print("ERROR: Formulas.exe not found"); + ns.toast("Tried to run shotgunner without Formulas.exe", ns.enums.ToastVariant.ERROR); + exit(); + } + + // Currently, calcHackParams can't handle unhackable servers, so filter them out + function isHackable(server) { return ns.formulas.hacking.hackPercent(server, player); } + + async function calcHackParams() { + const usableRam = servers.hackers.reduce((total, s) => total + s.maxRam, 0) + servers.home.maxRam; + const maxHostRam = Math.max(...servers.hackers.map(s => s.maxRam)); + ns.clearPort(Ports.Hack); + + ns.writePort(Ports.Hack, [ + player, servers.home, servers.targets, {"maxHostRam": maxHostRam} + ]); + while (!ns.exec("./calcHackParams.js", hostname, {"temporary": true}, "-p", Ports.Hack)) + await ns.sleep(5); + await ns.nextPortWrite(Ports.Hack); + gapFillerParams = ns.readPort(Ports.Hack); + + ns.writePort(Ports.Hack, [player, servers.home, servers.targets, { + "minRam": usableRam / BatchSoftcap, "maxHostRam": maxHostRam + }]); + while (!ns.exec("./calcHackParams.js", hostname, {"temporary": true}, "-p", Ports.Hack)) + await ns.sleep(5); + let p = ns.nextPortWrite(Ports.Hack); + await p; + hackParams = ns.readPort(Ports.Hack); + return p; + }; + + const moneyEfficiencyComparators = [ + (a, b) => hackParams[b.hostname].host.efficiency - hackParams[a.hostname].host.efficiency, + (a, b) => gapFillerParams[b.hostname].host.efficiency - gapFillerParams[a.hostname].host.efficiency + ]; + + const expEfficiencyComparators = [ + (a, b) => hackParams[b.hostname].host.expEfficiency - hackParams[a.hostname].host.expEfficiency, + (a, b) => gapFillerParams[b.hostname].host.expEfficiency - gapFillerParams[a.hostname].host.expEfficiency + ]; + + function hasAssociatedStock(server) { return server.organizationName in forecasts; } + + ns.clearPort(Ports.ServerBroadcast); + // Sleeping before entering the loop prevents port-related errors on resume + await ns.sleep(5000); + //ns.disableLog("sleep"); + // Comment this out only for debugging. For routine operation, exec logging is not worth the performance hit. + ns.disableLog("exec"); + while (true) { + if (ns.peek(Ports.ServerBroadcast) != "NULL PORT DATA") + await redeploy(ns); + + forecasts = getForecasts(); + player = ns.getPlayer(); + + while (!ns.exec("/srv/get.js", hostname, {"temporary": true}, "-p", Ports.Hack, ...hostnames)) + ns.sleep(5); + await ns.nextPortWrite(Ports.Hack); + servers = {"all": ns.readPort(Ports.Hack)}; + servers.cracked = servers.all.filter(s => s.hasAdminRights) + servers.hackers = servers.cracked.filter(s => !SpecialHostnames.includes(s.hostname) && s.maxRam); + servers.targets = servers.cracked.filter(useStockMode ? hasAssociatedStock : isGrowable).filter(isHackable); + servers.home = servers.cracked.find(s => s.hostname == "home"); + targetCnt = servers.targets.length; + + if (!targetCnt) { + ns.print("ERROR: No targets found."); + await ns.sleep(10000); + continue; + } + + //ns.tprint(JSON.stringify(servers.targets)); + if (hackSkill != player.skills.hacking || homeCores != servers.home.cpuCores || targetCnt != servers.targets.length) { + // FIXME: This should also run when usable RAM changes, which is more complicated to check for + homeCores = servers.home.cpuCores; + hackSkill = player.skills.hacking; + + await calcHackParams(); + for (let i = 0; i < 2; ++i) { + servers.targets.sort(useStockMode ? forecastComparator : moneyEfficiencyComparators[i]); + moneyTargets[i] = servers.targets[0]; + servers.targets.sort(expEfficiencyComparators[i]); + expTargets[i] = servers.targets[0]; + } + + let params = hackParams[moneyTargets[0].hostname]; + ns.printf( + "INFO: Targeting %s for money\nUsing hack ratios: %s for home, %s for host\nEfficiency ($/GiB-s): %s for home, %s for host", + moneyTargets[0].hostname, + ns.formatNumber(params.home.ratio), + ns.formatNumber(params.host.ratio), + ns.formatNumber(params.home.efficiency), + ns.formatNumber(params.host.efficiency) + ); + printStockInfo(ns, forecasts, moneyTargets[0]); + + params = gapFillerParams[moneyTargets[1].hostname]; + ns.printf( + "INFO: Filling gaps with %s\nUsing hack ratios: %s for home, %s for host\nEfficiency ($/GiB-s): %s for home, %s for host", + moneyTargets[1].hostname, + ns.formatNumber(params.home.ratio), + ns.formatNumber(params.host.ratio), + ns.formatNumber(params.home.efficiency), + ns.formatNumber(params.host.efficiency) + ); + + params = hackParams[expTargets[0].hostname]; + ns.printf( + "INFO: Targeting %s for exp (and filling gaps with %s\nUsing hack ratios: %s for home, %s for host\nEfficiency (exp/GiB-s): %s for home, %s for host", + expTargets[0].hostname, + expTargets[1].hostname, + ns.formatNumber(params.home.ratio), + ns.formatNumber(params.host.ratio), + ns.formatNumber(params.home.expEfficiency), + ns.formatNumber(params.host.expEfficiency) + ); + printStockInfo(ns, forecasts, expTargets[0]); + } + else + // Still need to update target server info for correct execution times + for (let i = 0; i < 2; ++i) { + expTargets[i] = servers.targets.find(s => s.hostname == expTargets[i].hostname); + moneyTargets[i] = servers.targets.find(s => s.hostname == moneyTargets[i].hostname); + } + + const targets = farmingExp ? expTargets : moneyTargets; + let pids1; + ns.disableLog("sleep"); + await shootShotgun( + ns, + targets[0], + hackParams[targets[0].hostname], + forecasts[targets[0].organizationName], + servers, + player, + pids + ).then(val => pids1 = val); + await shootShotgun( + ns, + targets[1], + gapFillerParams[targets[1].hostname], + forecasts[targets[1].organizationName], + servers, + player, + pids + ).then(val => pids = pids1.union(val)); + ns.enableLog("sleep"); + await ns.sleep(pids.size ? Math.max( + ns.formulas.hacking.weakenTime(targets[0], player), + ns.formulas.hacking.weakenTime(targets[1], player) + ) : 1000); + farmingExp = !farmingExp; + } +} \ No newline at end of file diff --git a/hack/weaken.ts b/hack/weaken.ts new file mode 100644 index 0000000..a25bb8f --- /dev/null +++ b/hack/weaken.ts @@ -0,0 +1,7 @@ +export async function main(ns: NS) { + const flags = ns.flags([["d", 0], ["n", 1]]); + const opts: BasicHGWOptions = {"additionalMsec": flags.d as number}; + + for (let i = 0; i < (flags.n as number); ++i) + await ns.weaken((flags._ as Array)[0], opts); +} \ No newline at end of file diff --git a/hacknet/deploy.ts b/hacknet/deploy.ts new file mode 100644 index 0000000..9b046fd --- /dev/null +++ b/hacknet/deploy.ts @@ -0,0 +1,10 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; + +export function deployHacknet(ns: NS, dest: string) { + var execJob: ExecutorJob = {"script": "/hacknet/launch.ts", "hostname": dest}; + ns.scp(ns.ls("home", "/hacknet/").concat(["/share.ts"]), dest, "home"); + ns.writePort(Ports.Exec, execJob); +} + +export async function main(ns: NS) { deployHacknet(ns, ns.args[0] as string); } \ No newline at end of file diff --git a/hacknet/launch.ts b/hacknet/launch.ts new file mode 100644 index 0000000..39d0d48 --- /dev/null +++ b/hacknet/launch.ts @@ -0,0 +1,6 @@ +export async function main(ns: NS) { + ns.spawn( + ns.fileExists("Formulas.exe", "home") ? "./nodeFormulasLoop.js" : "./naiveLoop.js", + {"spawnDelay": 0} + ); +} \ No newline at end of file diff --git a/hacknet/naiveLoop.js b/hacknet/naiveLoop.js new file mode 100644 index 0000000..4d381d9 --- /dev/null +++ b/hacknet/naiveLoop.js @@ -0,0 +1,60 @@ +const SleepMillis = 5000; + +/** @param {NS} ns */ +export async function main(ns) { + var i; + var nodes = new Array(ns.hacknet.numNodes()); + var init = true, upgradesRemaining = undefined, nodePriceOK = undefined, maxPrice; + + function spendHashes() { + // TODO: Spend hashes + + }; + + ns.disableLog("sleep"); + + for (i = 0; i < ns.hacknet.numNodes(); ++i) + nodes[i] = ns.hacknet.getNodeStats(i); + + ns.print("Beginning hacknet acquisition"); + while (init || upgradesRemaining || nodePriceOK) { + if (upgradesRemaining) { + ns.print("Continuing with node upgrades"); + for (i = 0; i < nodes.length; ++i) + ns.hacknet.upgradeRam(i); + for (i = 0; i < nodes.length; ++i) + ns.hacknet.upgradeCore(i); + for (i = 0; i < nodes.length; ++i) + ns.hacknet.upgradeLevel(i, 16); + } + if (!init) + ns.printf("Node price deemed %sacceptable ($%s %s $%s)", nodePriceOK ? "" : "un", ns.formatNumber(ns.hacknet.getPurchaseNodeCost()), nodePriceOK ? "<" : ">", ns.formatNumber(maxPrice)); + if (nodePriceOK || init) { + i = ns.hacknet.purchaseNode(); + if (i >= 0) + nodes.push(ns.hacknet.getNodeStats(i)) + } + init = !ns.hacknet.numNodes(); + if (!init) { + maxPrice = ns.hacknet.getNodeStats(0).production << 19; + nodePriceOK = ns.hacknet.getPurchaseNodeCost() < maxPrice; + upgradesRemaining = ns.hacknet.getNodeStats(ns.hacknet.numNodes() - 1).cores < 16; + } + + if (ns.hacknet.hashCapacity()) + spendHashes(); + + await ns.sleep(SleepMillis); + } + + if (ns.hacknet.hashCapacity()) { + while (true) { + spendHashes(); + await ns.sleep(SleepMillis); + } + } + + ns.toast("Ceasing Hacknet acquisition"); + if (ns.self().server != "home") + ns.spawn("share.ts", 2); +} \ No newline at end of file diff --git a/hacknet/nodeFormulasLoop.ts b/hacknet/nodeFormulasLoop.ts new file mode 100644 index 0000000..90d8b23 --- /dev/null +++ b/hacknet/nodeFormulasLoop.ts @@ -0,0 +1,151 @@ +import {extResetInfo} from "lib/genInfo/reset"; + +const SleepMillis = 30000; +const ROIThreshold = 1/(4*3600); // Max 4 hours to recover investment + +const NetburnersRequirements = { + "Levels": 100, + "Ram": 8, + "Cores": 4 +}; + +enum PurchaseType { + Node = "Node", + Level = "Level", + Ram = "Ram", + Core = "Core" +}; + +/** [index, type, ROI] */ +type Purchase = [number, PurchaseType, number]; + +function roiComparator(a: Purchase, b: Purchase) { return a[2] - b[2]; } + +function netburnersRequirementsMet(nodes: Array) { + return nodes.reduce((sum, node) => sum + node.level, 0) >= NetburnersRequirements.Levels && + nodes.reduce((sum, node) => sum + node.ram, 0) >= NetburnersRequirements.Ram && + nodes.reduce((sum, node) => sum + node.cores, 0) >= NetburnersRequirements.Cores; +} + +export async function main(ns: NS) { + const consts = ns.formulas.hacknetNodes.constants(); + var multipliers = ns.getHacknetMultipliers(); + const fullUpgradeCost = + ns.formulas.hacknetNodes.levelUpgradeCost(1, consts.MaxLevel - 1, multipliers.levelCost) + + ns.formulas.hacknetNodes.ramUpgradeCost(1, Math.log2(consts.MaxRam), multipliers.ramCost) + + ns.formulas.hacknetNodes.coreUpgradeCost(1, consts.MaxCores - 1, multipliers.coreCost); + var baseProduction = 0, maxProduction = 0; + var nodes: Array = new Array(ns.hacknet.numNodes()); + + function calcProduction() { + baseProduction = ns.formulas.hacknetNodes.moneyGainRate(1, 1, 1, multipliers.production); + maxProduction = ns.formulas.hacknetNodes.moneyGainRate( + consts.MaxLevel, consts.MaxRam, consts.MaxCores, multipliers.production + ); + } + + function getLevelROI(index: number) { + const stats = nodes[index]; + return (ns.formulas.hacknetNodes.moneyGainRate( + stats.level + 1, stats.ram, stats.cores, multipliers.production + ) - stats.production) / ns.hacknet.getLevelUpgradeCost(index); + } + + function getRamROI(index: number) { + const stats = nodes[index]; + return (ns.formulas.hacknetNodes.moneyGainRate( + stats.level, stats.ram + 1, stats.cores, multipliers.production + ) - stats.production) / ns.hacknet.getRamUpgradeCost(index); + } + + function getCoreROI(index: number) { + const stats = nodes[index]; + return (ns.formulas.hacknetNodes.moneyGainRate( + stats.level, stats.ram, stats.cores + 1, multipliers.production + ) - stats.production) / ns.hacknet.getCoreUpgradeCost(index); + } + + function getNodeROI() { + const nodeCost = ns.hacknet.getPurchaseNodeCost(); + return Math.max( + maxProduction / (nodeCost + fullUpgradeCost), + baseProduction / nodeCost + ); + } + + const getROIFuncs = { // Used for dynamic calls + [PurchaseType.Level]: getLevelROI, + [PurchaseType.Ram]: getRamROI, + [PurchaseType.Core]: getCoreROI, + [PurchaseType.Node]: getNodeROI + }; + + function pushUpgrades(index: number) { + let roi; + if (roi = getLevelROI(index)) + purchaseQueue.push([index, PurchaseType.Level, roi]); + if (roi = getRamROI(index)) + purchaseQueue.push([index, PurchaseType.Ram, roi]); + if (roi = getCoreROI(index)) + purchaseQueue.push([index, PurchaseType.Core, roi]); + } + + function spawnShare() { + if (ns.self().server != "home") + ns.spawn("share.ts", 2); + } + + if (extResetInfo.currentNode == 11) + spawnShare(); + + ns.sleep(5000); + calcProduction(); + var purchaseQueue: Array = [[nodes.length, PurchaseType.Node, getNodeROI()]]; + for (let i = 0; i < nodes.length; ++i) { + nodes[i] = ns.hacknet.getNodeStats(i); + pushUpgrades(i); + } + purchaseQueue.sort(roiComparator); + + ns.print("Max production: " + ns.formatNumber(maxProduction)); + ns.print("Fully upgraded node cost: " + ns.formatNumber(ns.hacknet.getPurchaseNodeCost() + fullUpgradeCost)); + ns.print("Purchase queue: " + JSON.stringify(purchaseQueue)); + var next = purchaseQueue[purchaseQueue.length - 1]; + while (next[2] > ROIThreshold || !netburnersRequirementsMet(nodes)) { + const [index, typ] = next; + let success: boolean | number = 0; + if (index == nodes.length) { + success = ns.hacknet.purchaseNode(); + if (success >= 0) { + ++next[0]; + next[2] = getNodeROI(); + nodes.push(ns.hacknet.getNodeStats(success)); + pushUpgrades(success); + } + } + else { + if (success = (ns.hacknet[("upgrade" + typ) as keyof Hacknet] as (i: number) => boolean)(index)) { + nodes[index] = ns.hacknet.getNodeStats(index); + if (nodes[index][typ == PurchaseType.Core ? "cores" : typ.toLowerCase() as keyof NodeStats] == + consts["Max" + (typ == PurchaseType.Core ? "Cores" : typ) as keyof HacknetNodeConstants]) + purchaseQueue.pop(); + for (let i = 0; i < purchaseQueue.length; ++i) + if (purchaseQueue[i][0] == index) + purchaseQueue[i][2] = getROIFuncs[purchaseQueue[i][1]](index); + } + } + + if (success) + purchaseQueue.sort(roiComparator); + else { + await ns.sleep(SleepMillis); + multipliers = ns.getHacknetMultipliers(); + calcProduction(); + ns.print("Purchase queue: " + JSON.stringify(purchaseQueue)); + } + next = purchaseQueue[purchaseQueue.length - 1]; + } + + ns.toast("Ceasing Hacknet acquisition"); + spawnShare(); +} \ No newline at end of file diff --git a/info/BDTargets.js b/info/BDTargets.js new file mode 100644 index 0000000..871ca01 --- /dev/null +++ b/info/BDTargets.js @@ -0,0 +1,6 @@ +import {deepScan} from "library.js"; + +/** @param {NS} ns */ +export async function main(ns) { + +} \ No newline at end of file diff --git a/info/contractList.js b/info/contractList.js new file mode 100644 index 0000000..b4b31e4 --- /dev/null +++ b/info/contractList.js @@ -0,0 +1,14 @@ +import {deepScan} from "lib/util"; + +/** @param {NS} ns */ +export async function main(ns) { + var hostnames = deepScan(ns); + var msg = ""; + + for (let i = 0; i < hostnames.length; ++i) { + let ccs = ns.ls(hostnames[i], ".cct"); + if (ccs.length) + msg += hostnames[i] + ": " + ccs.toString() + "\n"; + } + ns.alert(msg); +} \ No newline at end of file diff --git a/info/contractTypes.js b/info/contractTypes.js new file mode 100644 index 0000000..ffb9042 --- /dev/null +++ b/info/contractTypes.js @@ -0,0 +1,4 @@ +/** @param {NS} ns */ +export async function main(ns) { + ns.alert(ns.codingcontract.getContractTypes().toString()); +} \ No newline at end of file diff --git a/info/crimeEV.tsx b/info/crimeEV.tsx new file mode 100644 index 0000000..2996037 --- /dev/null +++ b/info/crimeEV.tsx @@ -0,0 +1,74 @@ +import {CrimeType} from "lib/nsEnums"; + +const Times: Record = { + [CrimeType.assassination]: 300, + [CrimeType.bondForgery]: 300, + [CrimeType.dealDrugs]: 10, + [CrimeType.grandTheftAuto]: 80, + [CrimeType.heist]: 600, + [CrimeType.homicide]: 3, + [CrimeType.kidnap]: 120, + [CrimeType.larceny]: 90, + [CrimeType.mug]: 4, + [CrimeType.robStore]: 60, + [CrimeType.shoplift]: 2, + [CrimeType.traffickArms]: 40 +}; + +const Karma: Record = { + [CrimeType.assassination]: 10, + [CrimeType.bondForgery]: 0.1, + [CrimeType.dealDrugs]: 0.5, + [CrimeType.grandTheftAuto]: 5, + [CrimeType.heist]: 15, + [CrimeType.homicide]: 3, + [CrimeType.kidnap]: 6, + [CrimeType.larceny]: 1.5, + [CrimeType.mug]: 0.25, + [CrimeType.robStore]: 0.5, + [CrimeType.shoplift]: 0.1, + [CrimeType.traffickArms]: 1 +}; + +export async function main(ns: NS) { + const player = ns.getPlayer(); + + var styles: any = ns.ui.getTheme(); + styles = { + "money": {"color": styles.money}, + "hackExp": {"color": styles["hack"]}, + "combat": {"color": styles.combat}, + "chaExp": {"color": styles.cha}, + "intExp": {"color": styles.int} + }; + styles.strExp = styles.combat; + styles.defExp = styles.combat; + styles.dexExp = styles.combat; + styles.agiExp = styles.combat; + + function adjExp(typ: CrimeType, exp: number, chance: number, failRatio?: number) { + failRatio ??= 0.25; + return exp * (chance + (1 - chance) * failRatio) / Times[typ]; + } + + const evRows = Object.values(ns.enums.CrimeType).map((typ: CrimeType) => { + const chance = ns.formulas.work.crimeSuccessChance(player, typ); + let gains = ns.formulas.work.crimeGains(player, typ); + let cells = [{typ.toString()}, {ns.formatPercent(chance)}, {ns.formatNumber(adjExp(typ, gains.money, chance, 0))}]; + + cells = cells.concat(["hackExp", "strExp", "defExp", "dexExp", "agiExp", "chaExp", "intExp"].map((stat: string) => { + gains[stat as keyof WorkStats] = adjExp(typ, gains[stat as keyof WorkStats], chance, stat == "intExp" ? 0 : undefined); + return {ns.formatNumber(gains[stat as keyof WorkStats])}; + })); + cells.push({ns.formatNumber(adjExp(typ, Karma[typ as CrimeType], chance))}); + return {cells}; + }); + + ns.tprintRaw( +
    + + {evRows} +
    CrimeChanceMoneyHackStrDefDexAgiChaIntKarma
    + ); + ns.asleep(20); +} \ No newline at end of file diff --git a/info/infilList.js b/info/infilList.js new file mode 100644 index 0000000..17664ea --- /dev/null +++ b/info/infilList.js @@ -0,0 +1,12 @@ +/** @param {NS} ns */ +export async function main(ns) { + var i; + var targets = ns.infiltration.getPossibleLocations(); + var infilList = ""; + for (i = 0; i < targets.length; ++i) { + let infil = ns.infiltration.getInfiltration(targets[i].name); + + infilList += ns.sprintf("%s, %s\n%d, %d, %d, %d\n", targets[i].city, targets[i].name, infil.difficulty, infil.startingSecurityLevel, infil.maxClearanceLevel, infil.reward.SoARep); + } + ns.alert(infilList); +} \ No newline at end of file diff --git a/info/karma.js b/info/karma.js new file mode 100644 index 0000000..8928214 --- /dev/null +++ b/info/karma.js @@ -0,0 +1,5 @@ +/** @param {NS} ns */ +export async function main(ns) { + var player = ns.getPlayer(); + ns.alert("Karma: " + player.karma.toString() + "\nBody count: " + player.numPeopleKilled.toString()); +} \ No newline at end of file diff --git a/info/pathToServer.js b/info/pathToServer.js new file mode 100644 index 0000000..cdf662b --- /dev/null +++ b/info/pathToServer.js @@ -0,0 +1,28 @@ +/** @param {NS} ns */ +export async function main(ns) { + const target = ns.args[0]; + var path = []; + + function pathToServer(hostname) { + const connections = ns.scan(hostname); + + path.push(hostname); + if (hostname == target) + return true; + + for (const connection of connections) { + if (!path.includes(connection) && pathToServer(connection)) + break; + } + + if (path[path.length - 1] == target) + return true; + path.pop(); + return false; + } + + if (pathToServer(ns.getHostname())) + ns.tprint(JSON.stringify(path)); + else + ns.tprint("Could not find " + target); +} \ No newline at end of file diff --git a/init/cleanup.ts b/init/cleanup.ts new file mode 100644 index 0000000..3a72c6a --- /dev/null +++ b/init/cleanup.ts @@ -0,0 +1,19 @@ +//TODO: DRY this + +const TempFiles = [ + "/lib/resetInfo.json", + "/crack/deployment.json", + "/hack/hackParams.json", + "/gang/gangInfo.json", + "/gang/memberInfo.json", + "/gang/taskInfo.json", + "/gang/state.json", + "/stock/symbolInfo.json", + "/stock/get/askPrices.json", + "/stock/get/poorManForecast.json" +]; + +export async function main(ns: NS) { + for (const file of TempFiles) + ns.rm(file); +} \ No newline at end of file diff --git a/init/collectLit.ts b/init/collectLit.ts new file mode 100644 index 0000000..1dac477 --- /dev/null +++ b/init/collectLit.ts @@ -0,0 +1,10 @@ +import {deepScan} from "lib/util"; + +export async function main(ns: NS) { + var nodes = deepScan(ns); + + for (let i = 0; i < nodes.length; ++i) { + let lit = ns.ls(nodes[i], ".lit"); + ns.scp(lit, "home", nodes[i]); + } +} \ No newline at end of file diff --git a/init/init.ts b/init/init.ts new file mode 100644 index 0000000..c43dcd7 --- /dev/null +++ b/init/init.ts @@ -0,0 +1,19 @@ +import {FreebieHostnames} from "lib/util"; +import {deployScripts} from "crack/deployScripts"; + +export const MarginMillis = 50; + +export async function main(ns: NS) { + ns.exec("./cleanup.ts", "home"); + await ns.sleep(MarginMillis); + ns.exec("./collectLit.ts", "home"); + await ns.sleep(MarginMillis); + ns.exec("/lib/genInfo/reset.ts", "home"); + for (const target of FreebieHostnames) { + await ns.sleep(MarginMillis); + ns.nuke(target); + deployScripts(ns, target); + } + // A brief delay allows /lib/genInfo/reset to use all avilable RAM + ns.spawn("./init2.ts", {spawnDelay: 200}); +} \ No newline at end of file diff --git a/init/init2.ts b/init/init2.ts new file mode 100644 index 0000000..165b69a --- /dev/null +++ b/init/init2.ts @@ -0,0 +1,18 @@ +import {MarginMillis} from "init/init"; +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; + +const Jobs: Array = [ + {script: "/crack/crack.js", hostname: "home", threadOrOptions: {preventDuplicates: true}}, + {script: "/contract/solveLoop.tsx", hostname: "home", threadOrOptions: {preventDuplicates: true}}, + {script: "/gang/launch.ts", hostname: "home", args: ["-q"], threadOrOptions: {preventDuplicates: true}}, + {script: "/bladeburner/launch.ts", hostname: "home", threadOrOptions: {preventDuplicates: true}}, + {script: "/queenAdministrator.ts", hostname: "home", threadOrOptions: {preventDuplicates: true}} +]; + +export async function main(ns: NS) { + for (const job of Jobs) { + await ns.sleep(MarginMillis); + ns.writePort(Ports.Exec, job); + } +} \ No newline at end of file diff --git a/lib/deepScan.js b/lib/deepScan.js new file mode 100644 index 0000000..f56be44 --- /dev/null +++ b/lib/deepScan.js @@ -0,0 +1,12 @@ +import {deepScan} from "lib/util"; + +/** @param {NS} ns */ +export async function main(ns) { + const flags = ns.flags([["p", 0], ["host", ""], ["include-host", false], ["exclude-purchased", false]]); + const host = flags.host ? flags.host : undefined; + var hostnames = deepScan(ns, host, flags["include-host"]); + + if (flags["exclude-purchased"]) + hostnames = hostnames.filter((h) => { return !h.startsWith("hack"); }); + ns.writePort(flags.p, hostnames); +} \ No newline at end of file diff --git a/lib/genInfo/gameState.ts b/lib/genInfo/gameState.ts new file mode 100644 index 0000000..56a2468 --- /dev/null +++ b/lib/genInfo/gameState.ts @@ -0,0 +1,37 @@ +import {Ports} from "lib/util"; +import {DynamoJob, doDynamoJob} from "daemons/dynamo"; +import {ExecutorJob} from "daemons/executor"; + +export interface GameState { + player: Player, + moneySources: MoneySources +}; + +export var gameState: GameState; + +export async function main(ns: NS) { + const self = ns.self(); + const port = Ports.PIDBase + self.pid; + let newState: Partial = {}; + + ns.atExit(() => ns.clearPort(port)); + + let dynamoJob: DynamoJob = { + functionName: "getPlayer", + cbPort: port + }; + await doDynamoJob(ns, dynamoJob).then(val => newState.player = val); + + let executorJob: ExecutorJob = { + script: "/lib/get/moneySources.ts", + hostname: self.server, + args: ["-p", port], + threadOrOptions: {temporary: true}, + retry: true + }; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(port); + newState.moneySources = ns.readPort(port) as MoneySources; + + gameState = Object.freeze(newState as GameState); +} \ No newline at end of file diff --git a/lib/genInfo/reset.ts b/lib/genInfo/reset.ts new file mode 100644 index 0000000..1a7e23f --- /dev/null +++ b/lib/genInfo/reset.ts @@ -0,0 +1,108 @@ +import {BitNode} from "lib/nsEnums"; +import {Ports} from "lib/util"; +import {DynamoJob, doDynamoJob} from "daemons/dynamo"; +import {ExecutorJob} from "daemons/executor"; + +export enum BitNodePath { + Hacking, + Combat +} + +export interface ExtResetInfo extends ResetInfo { + favorToDonate: number, + purchasedServers: { + limit: number, + maxRam: number + }, + stock: { + canShort: boolean, + canLimit: boolean + }, + bitNodeMultipliers?: BitNodeMultipliers, + bitNodePath: BitNodePath, + companyFavor: Record, + bladeburnerEnabled: boolean, + gangEnabled: boolean, + singularityEnabled: boolean +} + +export var extResetInfo: ExtResetInfo; + +export async function main(ns: NS) { + const self = ns.self(); + const port = Ports.PIDBase + self.pid; + var info: Partial = {}; + + ns.clearPort(port); + ns.atExit(() => ns.clearPort(port)); + ns.print("INFO: Getting reset info"); + let dynamoJob: DynamoJob = { + functionName: "getResetInfo", + cbPort: port + }; + await doDynamoJob(ns, dynamoJob).then(val => info = val); + + ns.print("INFO: Getting favor to donate"); + dynamoJob.functionName = "getFavorToDonate"; + await doDynamoJob(ns, dynamoJob).then(val => info.favorToDonate = val); + + info.purchasedServers = {limit: 0, maxRam: 0}; + ns.print("INFO: Getting purchased server constants"); + dynamoJob.functionName = "getPurchasedServerLimit"; + await doDynamoJob(ns, dynamoJob).then(val => (info as ExtResetInfo).purchasedServers.limit = val); + dynamoJob.functionName = "getPurchasedServerMaxRam"; + await doDynamoJob(ns, dynamoJob).then(val => (info as ExtResetInfo).purchasedServers.maxRam = val); + + if ((info as ExtResetInfo).ownedSF.get(BitNode.ArtificialIntelligence) || + info.currentNode == BitNode.ArtificialIntelligence) { + // TODO: Run this on some server other than home + ns.print("INFO: Getting BN mults"); + let executorJob: ExecutorJob = { + script: "/lib/get/bitNodeMultipliers.ts", + hostname: self.server, + args: ["-p", port], + threadOrOptions: {temporary: true}, + retry: true + }; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(port); + info.bitNodeMultipliers = ns.readPort(port); + } + else + ns.print("WARN: BN mults unavailable"); + + info.bladeburnerEnabled = !info.bitNodeOptions?.disableBladeburner && + info.currentNode != BitNode.GhostOfWallStreet && ( + info.ownedSF?.has(BitNode.Bladeburners) || + info.ownedSF?.has(BitNode.Bladeburners2079) || + info.currentNode == BitNode.Bladeburners || + info.currentNode == BitNode.Bladeburners2079 + ); + + info.gangEnabled = !info.bitNodeOptions?.disableGang && + !!info.bitNodeMultipliers?.GangSoftcap && ( + info.ownedSF?.has(BitNode.RiseOfTheUnderworld) || + info.currentNode == BitNode.RiseOfTheUnderworld + ); + + info.stock = { + canShort: (info.ownedSF?.get(BitNode.GhostOfWallStreet) ?? 0) >= 2 || + info.currentNode == BitNode.GhostOfWallStreet, + canLimit: (info.ownedSF?.get(BitNode.GhostOfWallStreet) ?? 0) >= 3 || + info.currentNode == BitNode.GhostOfWallStreet + }; + + switch (info.currentNode) { + case BitNode.RiseOfTheUnderworld: + case BitNode.BigCrash: + case BitNode.Bladeburners: + case BitNode.Bladeburners2079: + info.bitNodePath = info.bladeburnerEnabled ? BitNodePath.Combat : BitNodePath.Hacking; + break; + default: + info.bitNodePath = BitNodePath.Hacking; + break; + } + + extResetInfo = Object.freeze(info as ExtResetInfo); +} \ No newline at end of file diff --git a/lib/get/bitNodeMultipliers.ts b/lib/get/bitNodeMultipliers.ts new file mode 100644 index 0000000..2a5b7d4 --- /dev/null +++ b/lib/get/bitNodeMultipliers.ts @@ -0,0 +1,6 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0], ["n", 0], ["l", 0]]); + ns.writePort(flags.p as number, ns.getBitNodeMultipliers( + (flags.n as number) || undefined, (flags.l as number) || undefined + )); +} \ No newline at end of file diff --git a/lib/get/moneySources.ts b/lib/get/moneySources.ts new file mode 100644 index 0000000..b7f8491 --- /dev/null +++ b/lib/get/moneySources.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, ns.getMoneySources()); +} \ No newline at end of file diff --git a/lib/get/player.ts b/lib/get/player.ts new file mode 100644 index 0000000..b8eec3c --- /dev/null +++ b/lib/get/player.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, ns.getPlayer()); +} \ No newline at end of file diff --git a/lib/nsEnums.ts b/lib/nsEnums.ts new file mode 100644 index 0000000..b0a081c --- /dev/null +++ b/lib/nsEnums.ts @@ -0,0 +1,46 @@ +export var CityName: NS["enums"]["CityName"]; +export var CodingContractName: NS["enums"]["CodingContractName"]; +export var CompanyName: NS["enums"]["CompanyName"]; +export var CrimeType: NS["enums"]["CrimeType"]; +export var FactionName: NS["enums"]["FactionName"]; +export var FactionWorkType: NS["enums"]["FactionWorkType"]; +export var JobField: NS["enums"]["JobField"]; +export var JobName: NS["enums"]["JobName"]; +export var LocationName: NS["enums"]["LocationName"]; + +export enum BitNode { + SourceGenesis = 1, + RiseOfTheUnderworld, + Corporotocracy, + Singularity, + ArtificialIntelligence, + Bladeburners, + Bladeburners2079, + GhostOfWallStreet, + Hacktocracy, + DigitalCarbon, + BigCrash, + Recursion, + Lunatics, + IPvGOSubnetTakeover +} + +export enum WorkType { + Company = "COMPANY", + CreateProgram = "CREATE_PROGRAM", + Faction = "FACTION" +} + +export function initEnums(ns: NS) { + CityName = ns.enums.CityName; + CodingContractName = ns.enums.CodingContractName; + CompanyName = ns.enums.CompanyName; + CrimeType = ns.enums.CrimeType; + FactionName = ns.enums.FactionName; + FactionWorkType = ns.enums.FactionWorkType; + JobField = ns.enums.JobField; + JobName = ns.enums.JobName; + LocationName = ns.enums.LocationName; +} + +export async function main(ns: NS) { initEnums(ns); } \ No newline at end of file diff --git a/lib/rm.ts b/lib/rm.ts new file mode 100644 index 0000000..3a07ce4 --- /dev/null +++ b/lib/rm.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + for (const filename of ns.args as Array) + ns.rm(filename); +} \ No newline at end of file diff --git a/lib/tick.js b/lib/tick.js new file mode 100644 index 0000000..e806e12 --- /dev/null +++ b/lib/tick.js @@ -0,0 +1,6 @@ +/** @param {NS} ns */ +export async function main(ns) { + while (true) { + await Promise.any([ns.gang.nextUpdate(), ns.stock.nextUpdate()]); + } +} \ No newline at end of file diff --git a/lib/util.ts b/lib/util.ts new file mode 100644 index 0000000..ee17362 --- /dev/null +++ b/lib/util.ts @@ -0,0 +1,109 @@ +export const ReservedRam = 97.6; //17; + +export enum Ports { + // System daemons + Exec = 1, + DynamicNS, + + // Basic mechanics + Crack, + Server, + ServerBroadcast, + Contract, + StockBuy, + StockSell, + StockBroadcast, + StockUpdate, + Hack, + Go, + + // SF-gated mechanics + Sleeve, + Gang, + GangUpdate, + Corp, + CorpUpdate, + Bladeburner, + Singularity, + + // Offset for using PIDs as port numbers + PIDBase = 100 +} + +export const SpecialHostnames = ["home", "CSEC", "n00dles", "nectar-net", "harakiri-sushi", "sigma-cosmetics"]; +export const FreebieHostnames = ["n00dles", "foodnstuff", "sigma-cosmetics", "joesguns", "hong-fang-tea", "nectar-net", "harakiri-sushi"]; + +export function replacer(key: any, value: any) { + if (value instanceof Map) { + return { + dataType: 'Map', + value: Array.from(value.entries()), // or with spread: value: [...value] + }; + } else { + return value; + } +} + +export function reviver(key: any, value: any) { + if(typeof value === 'object' && value !== null) { + if (value.dataType === 'Map') { + return new Map(value.value); + } + } + return value; +} + +/** @returns Array of all nodes excluding host +*/ +export function deepScan(ns: NS, host?: string, incHost?: boolean, asSet?: boolean) { + var nodes = new Set(), newNodes = new Set(); + host ??= ns.self().server; + +/* Recursive version crashes + var adj = new Set(ns.scan(host)); + var adjIter; + if (adj.has(host)) + adj.delete(host); + adjIter = adj.values(); + for (const adjHost of adjIter) { + nodes += deepScan(ns, adjHost); + } +*/ + + for (newNodes.add(host); !newNodes.isSubsetOf(nodes);) { + nodes = nodes.union(newNodes); + newNodes = new Set(); + + for (const node of nodes.values()) + newNodes = newNodes.union(new Set(ns.scan(node))); + } + if (!incHost) + nodes.delete(host); + + if (!asSet) + return Array.from(nodes.values()); + + return nodes; +} + +export function getSecurityDelta(ns: NS, hostname: string) { + hostname ??= ns.self().server; + return ns.getServerSecurityLevel(hostname) - ns.getServerMinSecurityLevel(hostname); +} + +export function srvSecurityDelta(srv: Server) { + return (srv.hackDifficulty ?? 0) - (srv.minDifficulty ?? 0); +} + +export function getMoneyRatio(ns: NS, hostname: string) { + hostname ??= ns.self().server; + return ns.getServerMoneyAvailable(hostname) / ns.getServerMaxMoney(hostname); +} + +export function srvMoneyRatio(srv: Server) { + return (srv.moneyAvailable ?? 0) / (srv.moneyMax ?? 1); +} + +export async function main(ns: NS) { + var nodes = deepScan(ns); +} \ No newline at end of file diff --git a/queenAdministrator.ts b/queenAdministrator.ts new file mode 100644 index 0000000..627d3fe --- /dev/null +++ b/queenAdministrator.ts @@ -0,0 +1,27 @@ +import {BitNode} from "lib/nsEnums"; +import {main as genResetInfo, extResetInfo} from "lib/genInfo/reset"; +import {main as genGameState} from "lib/genInfo/gameState"; +import {main as genFactionEnemies} from "singularity/genInfo/factionEnemies"; +import {main as procSTEM} from "singularity/stem"; + +const SleepMillis = 1000; + +export async function main(ns: NS) { + // Regenerating reset info here prevents errors while editing code + await genResetInfo(ns); + + const singularityAvailable = extResetInfo.ownedSF.has(BitNode.Singularity) || extResetInfo.currentNode == BitNode.Singularity; + if (singularityAvailable) + await genFactionEnemies(ns); + + ns.disableLog("sleep"); + while (true) { + ns.print("INFO: Generating game state."); + await genGameState(ns); + if (singularityAvailable) { + ns.print("INFO: Processing STEM.") + await procSTEM(ns); + } + await ns.sleep(SleepMillis); + } +} \ No newline at end of file diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..1c27f6f --- /dev/null +++ b/readme.txt @@ -0,0 +1,17 @@ +These are my personal Bitburner scripts. + +Scripts of primary interest: + +autoexec: For use with the game's autoexec option. Other scripts won't work if it hasn't been run. +init/init: For initializing after entering a BN or installing augs. +singularity/stem: Singularity manager. Rough WiP. (Name is a reference to the movie Upgrade.) +queenAdministrator: General manager. Calls STEM and does other things that aren't synchronized with game updates. +crack/crack: Server cracker. +crack/deployScripts: Deploys management scripts to servers other than home. +hacknet/naiveLoop: Buys Hacknet without Formulas. +hacknet/nodeFormulasLoop: Works much better than the naive loop. +hack/shotgunner: Shotgun-style batch hacker. +contract/solveLoop: Solves (most) coding contracts. +stock/sellLoop and stock/buyLoop: Stock trader. WiP, but works quite well for long trading with 4S access. +gang/kingpin: Gang manager. Very well optimized, but results not guaranteed with hacking gangs. +bladeburner/forge: Bladeburner manager. Rough WiP. Buys skills and switches cities, but doesn't do actions yet. diff --git a/rngesus.ts b/rngesus.ts new file mode 100644 index 0000000..a2357e3 --- /dev/null +++ b/rngesus.ts @@ -0,0 +1,12 @@ +// Run this, place a test bet in roulette, leave the casino, return, +// and bet everything on the number that won + +export async function main(ns: NS) { + const realGetTime = Date.prototype.getTime; + const dt = new Date().getTime(); + Date.prototype.getTime = () => dt; + ns.toast("RNGesus smiles upon you!"); + await ns.sleep(300000); + Date.prototype.getTime = realGetTime; + ns.toast("RNGesus disabled", "warning"); +} \ No newline at end of file diff --git a/share.ts b/share.ts new file mode 100644 index 0000000..d2300ad --- /dev/null +++ b/share.ts @@ -0,0 +1,6 @@ +export async function main(ns: NS) { + const flags = ns.flags([["c", 0]]); + + for (let cycles: number = flags.c as number || Number.MAX_SAFE_INTEGER; cycles; --cycles) + await ns.share(); +} \ No newline at end of file diff --git a/singularity/acceptFactionInvites.ts b/singularity/acceptFactionInvites.ts new file mode 100644 index 0000000..b224757 --- /dev/null +++ b/singularity/acceptFactionInvites.ts @@ -0,0 +1,3 @@ +export async function main(ns: NS) { + +} \ No newline at end of file diff --git a/singularity/applyForJobs.ts b/singularity/applyForJobs.ts new file mode 100644 index 0000000..11620ca --- /dev/null +++ b/singularity/applyForJobs.ts @@ -0,0 +1,107 @@ +import {BitNode, CompanyName, JobField} from "lib/nsEnums"; +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; +import {BitNodePath, extResetInfo} from "lib/genInfo/reset"; +import {gameState} from "lib/genInfo/gameState"; +import {symbolInfoRecord} from "stock/genSymbolInfo"; +import {companyPositions} from "singularity/genInfo/companyPositions"; +import {companyRep} from "singularity/genInfo/companyRep"; + +const Megacorps = [ + CompanyName.BachmanAndAssociates, + CompanyName.BladeIndustries, + CompanyName.ClarkeIncorporated, + CompanyName.ECorp, + CompanyName.FourSigma, + CompanyName.FulcrumTechnologies, + CompanyName.KuaiGongInternational, + CompanyName.MegaCorp, + CompanyName.NWO, + CompanyName.OmniTekIncorporated +]; + +const FieldPreferences: Record> = { + [BitNodePath.Hacking]: { + [JobField.softwareConsultant]: 10, + [JobField.it]: 9, + [JobField.software]: 8, + [JobField.networkEngineer]: 7, + [JobField.securityEngineer]: 6, + [JobField.businessConsultant]: 5, + [JobField.business]: 4, + [JobField.agent]: 3, + [JobField.security]: 2, + [JobField.employee]: 1, + [JobField.waiter]: 1, + [JobField.partTimeEmployee]: 0, + [JobField.partTimeWaiter]: 0 + }, + [BitNodePath.Combat]: { + [JobField.agent]: 10, + [JobField.security]: 9, + [JobField.businessConsultant]: 8, + [JobField.business]: 7, + [JobField.softwareConsultant]: 6, + [JobField.it]: 5, + [JobField.software]: 4, + [JobField.networkEngineer]: 3, + [JobField.securityEngineer]: 2, + [JobField.employee]: 1, + [JobField.waiter]: 1, + [JobField.partTimeEmployee]: 0, + [JobField.partTimeWaiter]: 0 + } +} as const; + +var companiesWithStock: Array = []; + +/** Applies to a job at the given company, if requirements for any position are met. + * Positions with higher rep requirements are preferred. In the case of a tie, FieldPreferences will be used + * to break it. + */ +async function pickJobAtCompany(ns: NS, company: CompanyName) { + const hostname = ns.self().server; + const fieldPreferences = FieldPreferences[extResetInfo.bitNodePath]; + // Can't just one-and-done the position info, even per install, because backdooring changes rep requirements + ns.writePort(Ports.Exec, { + script: "/singularity/get/company/positionInfo.ts", + hostname: hostname, + args: ["-p", Ports.Singularity, "-c", company].concat(companyPositions[company]), + threadOrOptions: {temporary: true}, + retry: true + } as ExecutorJob); + await ns.nextPortWrite(Ports.Singularity); + const positionInfo = (ns.readPort(Ports.Singularity) as Array).filter( + p => companyRep[company] >= p.requiredReputation && Object.entries(p.requiredSkills).every( + ([skill, req]) => gameState.player.skills[skill as keyof Skills] >= req + )).sort((a, b) => + b.requiredReputation == a.requiredReputation ? + fieldPreferences[b.field] - fieldPreferences[a.field] : + b.requiredReputation - a.requiredReputation + )[0]; + if (positionInfo) + ns.writePort(Ports.Exec, { + script: "/singularity/applyToCompany.ts", + hostname: hostname, + args: [company, positionInfo.field], + threadOrOptions: {temporary: true}, + retry: true + } as ExecutorJob); +} + +export async function main(ns: NS) { + var relevantCompanies: Array; + + if (extResetInfo.currentNode == BitNode.GhostOfWallStreet) { + if (!companiesWithStock.length && symbolInfoRecord) { + const symbolInfo = Object.values(symbolInfoRecord); + companiesWithStock = Object.values(CompanyName).filter(c => symbolInfo.find(s => s.organization == c)); + } + relevantCompanies = companiesWithStock; + } + else + relevantCompanies = Megacorps.filter(c => !gameState.player.factions.includes(c)); + + for (const company of relevantCompanies) + await pickJobAtCompany(ns, company); +} \ No newline at end of file diff --git a/singularity/applyToCompany.ts b/singularity/applyToCompany.ts new file mode 100644 index 0000000..8c2194c --- /dev/null +++ b/singularity/applyToCompany.ts @@ -0,0 +1,3 @@ +export async function main(ns: NS) { + ns.singularity.applyToCompany(ns.args[0] as CompanyName, ns.args[1] as JobField); +} \ No newline at end of file diff --git a/singularity/buy/allPrograms.ts b/singularity/buy/allPrograms.ts new file mode 100644 index 0000000..dea43d5 --- /dev/null +++ b/singularity/buy/allPrograms.ts @@ -0,0 +1,5 @@ +import {getDarkwebInfo} from "singularity/genInfo/darkweb"; + +export async function main(ns: NS) { + +} \ No newline at end of file diff --git a/singularity/buy/aug.ts b/singularity/buy/aug.ts new file mode 100644 index 0000000..f59ddf3 --- /dev/null +++ b/singularity/buy/aug.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const args = ns.args as Array; + ns.singularity.purchaseAugmentation(args[0], args[1]); +} \ No newline at end of file diff --git a/singularity/buy/core.ts b/singularity/buy/core.ts new file mode 100644 index 0000000..51a483c --- /dev/null +++ b/singularity/buy/core.ts @@ -0,0 +1,3 @@ +export async function main(ns: NS) { + ns.singularity.upgradeHomeCores(); +} \ No newline at end of file diff --git a/singularity/buy/maxNFG.ts b/singularity/buy/maxNFG.ts new file mode 100644 index 0000000..d49a014 --- /dev/null +++ b/singularity/buy/maxNFG.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const args = ns.args as Array; + while (ns.singularity.purchaseAugmentation(args[0], "NeuroFlux Governor")); +} \ No newline at end of file diff --git a/singularity/buy/program.ts b/singularity/buy/program.ts new file mode 100644 index 0000000..a99a7d7 --- /dev/null +++ b/singularity/buy/program.ts @@ -0,0 +1,7 @@ +export async function main(ns: NS) { + const program = (ns.args as Array)[0]; + if (ns.singularity.purchaseProgram(program)) + ns.toast("Bought " + program, ns.enums.ToastVariant.SUCCESS); + else if (!ns.self().parent) + ns.tprint("ERROR: Couldn't purchase " + program); +} \ No newline at end of file diff --git a/singularity/buy/ram.ts b/singularity/buy/ram.ts new file mode 100644 index 0000000..9b59d7b --- /dev/null +++ b/singularity/buy/ram.ts @@ -0,0 +1,3 @@ +export async function main(ns: NS) { + ns.singularity.upgradeHomeRam(); +} \ No newline at end of file diff --git a/singularity/buy/tor.ts b/singularity/buy/tor.ts new file mode 100644 index 0000000..6a5d8c2 --- /dev/null +++ b/singularity/buy/tor.ts @@ -0,0 +1,3 @@ +export async function main(ns: NS) { + ns.singularity.purchaseTor(); +} \ No newline at end of file diff --git a/singularity/chooseWork.ts b/singularity/chooseWork.ts new file mode 100644 index 0000000..1973e48 --- /dev/null +++ b/singularity/chooseWork.ts @@ -0,0 +1,83 @@ +import {BitNode, WorkType} from "lib/nsEnums"; +import {Ports} from "lib/util"; +import {extResetInfo} from "lib/genInfo/reset"; +import {gameState} from "lib/genInfo/gameState"; +import {ExecutorJob} from "daemons/executor"; +import {SymbolInfo, symbolInfoRecord} from "stock/genSymbolInfo"; +import {ProgramCreationRequirements, programsNotOwned} from "singularity/genInfo/darkweb"; + +function evalStockPriority(s: SymbolInfo) { + const pos = s.position as Array; + return pos[0] * pos[1] * (s.volatility ?? 1) * (1 - (s.forecast ?? 0)); +} + +export async function main(ns: NS) { + const employers = Object.keys(gameState.player.jobs); + var workChosen = false; + var executorJob: ExecutorJob = { + script: "/singularity/is/focused.ts", + hostname: ns.self().server, + threadOrOptions: {temporary: true}, + retry: true + }; + + function workOnProgram(onlyEasy: boolean) { + const maxHackReq = onlyEasy ? gameState.player.skills.hacking << 1 : + gameState.player.skills.hacking + (gameState.player.skills.intelligence >>> 1); + let prog = Array.from(programsNotOwned.values()).find(p => ProgramCreationRequirements[p] <= maxHackReq); + if (prog) { + ns.print("INFO: Will create ", prog, "."); + if (currentWork.type != WorkType.CreateProgram || (currentWork as CreateProgramWorkTask).programName != prog) { + executorJob.script += "createProgram.ts"; + executorJob.args = [prog]; + } + return true; + } + return false; + } + + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Singularity); + const focused: boolean = ns.readPort(Ports.Singularity); + + executorJob.script = "/singularity/get/currentWork.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Singularity); + const currentWork: Task = ns.readPort(Ports.Singularity); + + executorJob.script = "/singularity/doWork/"; + if (extResetInfo.currentNode == BitNode.GhostOfWallStreet ) { + // First, create any programs that can be created quickly + workChosen = workOnProgram(true); + + // Second, work for a company whose stock we hold in the long position + // This will drive the value of the stock up + if (!workChosen && symbolInfoRecord) { + const symbolInfo = Object.values(symbolInfoRecord) + .filter(s => s.position && s.position[0] && employers.includes(s.organization)) + .sort((a, b) => evalStockPriority(b) - evalStockPriority(a)); + if (symbolInfo.length) { + workChosen = true; + ns.print("INFO: Will do company work for ", symbolInfo[0].organization, "."); + if (currentWork.type != WorkType.Company || + (currentWork as CompanyWorkTask).companyName != symbolInfo[0].organization) { + executorJob.script += "company.ts"; + executorJob.args = [symbolInfo[0].organization]; + } + } + } + + // Third, create any program that can be created + if (!workChosen) + workChosen = workOnProgram(false); + } + + if (!workChosen) + ns.print("WARN: Failed to choose work task."); + + if (executorJob.args) { + if (focused) + executorJob.args.push("-f"); + ns.writePort(Ports.Exec, executorJob); + } +} \ No newline at end of file diff --git a/singularity/connect.ts b/singularity/connect.ts new file mode 100644 index 0000000..01c3a12 --- /dev/null +++ b/singularity/connect.ts @@ -0,0 +1,24 @@ +import {Ports} from "lib/util"; +import {doDynamoJob} from "daemons/dynamo"; + +export async function main(ns: NS) { + const flags = ns.flags([["connected-host", false]]); + const target = (flags._ as Array)[0]; + + if (flags["connected-host"]) { + const cbPort = Ports.PIDBase + ns.self().pid; + let hostname; + try { + await doDynamoJob(ns, { + functionName: "scan", + args: [target], + cbPort: cbPort + }).then((hostnames: Array) => hostname = hostnames.find(ns.singularity.connect)); + } + catch (e) { ns.writePort(Ports.Singularity, null); } + finally { ns.clearPort(cbPort); } + ns.writePort(Ports.Singularity, hostname ?? null); + } + else + ns.writePort(Ports.Singularity, ns.singularity.connect(target)); +} \ No newline at end of file diff --git a/singularity/doWork/company.ts b/singularity/doWork/company.ts new file mode 100644 index 0000000..abb58d7 --- /dev/null +++ b/singularity/doWork/company.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["f", false]]); + ns.singularity.workForCompany((flags._ as Array)[0], flags.f as boolean); +} \ No newline at end of file diff --git a/singularity/doWork/createProgram.ts b/singularity/doWork/createProgram.ts new file mode 100644 index 0000000..9417edf --- /dev/null +++ b/singularity/doWork/createProgram.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["f", false]]); + ns.singularity.createProgram((flags._ as Array)[0], flags.f as boolean); +} \ No newline at end of file diff --git a/singularity/doWork/faction.ts b/singularity/doWork/faction.ts new file mode 100644 index 0000000..9b165ca --- /dev/null +++ b/singularity/doWork/faction.ts @@ -0,0 +1,6 @@ +export async function main(ns: NS) { + const flags = ns.flags([["f", false]]); + ns.singularity.workForFaction( + (flags._ as Array)[0], (flags._ as Array)[1], flags.f as boolean + ); +} \ No newline at end of file diff --git a/singularity/farmInt.ts b/singularity/farmInt.ts new file mode 100644 index 0000000..e827d8d --- /dev/null +++ b/singularity/farmInt.ts @@ -0,0 +1,14 @@ +import {gameState} from "lib/genInfo/gameState"; + +const MinFunds = 10e9; + +export async function main(ns: NS) { + const cities = Object.values(ns.enums.CityName); + if (gameState.player.money >= MinFunds) + for (let i = 0; i < 4; ++i) { + for (let j = 0; j < 0xFF; ++j) + cities.forEach(ns.singularity.travelToCity); + await ns.sleep(0); + } + ns.singularity.travelToCity(gameState.player.city); +} \ No newline at end of file diff --git a/singularity/genInfo/companyPositions.ts b/singularity/genInfo/companyPositions.ts new file mode 100644 index 0000000..e8d0a26 --- /dev/null +++ b/singularity/genInfo/companyPositions.ts @@ -0,0 +1,20 @@ +import {CompanyName, JobName} from "lib/nsEnums"; +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; + +export var companyPositions: Record>; + +export async function main(ns: NS) { + ns.writePort(Ports.Exec, { + script: "/singularity/get/company/positions.ts", + hostname: ns.self().server, + args: ["-p", Ports.Singularity, "-a"], + threadOrOptions: {temporary: true}, + retry: true + } as ExecutorJob); + await ns.nextPortWrite(Ports.Singularity); + const positions = ns.readPort(Ports.Singularity) as Array>; + companyPositions = Object.freeze(Object.fromEntries(Object.values(CompanyName).map( + (company, index) => [company, positions[index]] + )) as typeof companyPositions); +} \ No newline at end of file diff --git a/singularity/genInfo/companyRep.ts b/singularity/genInfo/companyRep.ts new file mode 100644 index 0000000..663ba40 --- /dev/null +++ b/singularity/genInfo/companyRep.ts @@ -0,0 +1,20 @@ +import {CompanyName} from "lib/nsEnums"; +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; + +export var companyRep: Record; + +export async function main(ns: NS) { + ns.writePort(Ports.Exec, { + script: "singularity/get/company/rep.ts", + hostname: ns.self().server, + args: ["-p", Ports.Singularity, "-a"], + threadOrOptions: {temporary: true}, + retry: true + } as ExecutorJob); + await ns.nextPortWrite(Ports.Singularity); + const rep = ns.readPort(Ports.Singularity) as Array; + companyRep = Object.freeze(Object.fromEntries( + Object.values(CompanyName).map((c, i) => [c, rep[i]]) + ) as typeof companyRep); +} \ No newline at end of file diff --git a/singularity/genInfo/darkweb.ts b/singularity/genInfo/darkweb.ts new file mode 100644 index 0000000..3a6491a --- /dev/null +++ b/singularity/genInfo/darkweb.ts @@ -0,0 +1,34 @@ +import {Ports} from "lib/util"; +import {DynamoJob, doDynamoJob} from "daemons/dynamo"; + +export type DarkwebMenuType = Record; + +export var DarkwebMenuType: DarkwebMenuType; + +export const ProgramCreationRequirements: DarkwebMenuType = { + "AutoLink.exe": 25, + "BruteSSH.exe": 50, + "ServerProfiler.exe": 75, + "DeepScanV1.exe": 75, + "FTPCrack.exe": 100, + "relaySMTP.exe": 250, + "DeepScanV2.exe": 400, + "HTTPWorm.exe": 500, + "SQLInject.exe": 750 +} as const; + +export var programsNotOwned = new Set(Object.keys(ProgramCreationRequirements)); + +export async function main(ns: NS) { + var args = [null, "home"]; + var dynamoJob: DynamoJob = { + functionName: "fileExists", + cbPort: Ports.Singularity, + args: args + }; + + for (const file of Array.from(programsNotOwned.values())) { + args[0] = file; + await doDynamoJob(ns, dynamoJob).then(e => programsNotOwned.delete(file)); + } +} \ No newline at end of file diff --git a/singularity/genInfo/factionEnemies.ts b/singularity/genInfo/factionEnemies.ts new file mode 100644 index 0000000..e08f87c --- /dev/null +++ b/singularity/genInfo/factionEnemies.ts @@ -0,0 +1,21 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; + +export var factionEnemies: Map>; + +export async function main(ns: NS) { + const factions = Object.values(ns.enums.FactionName); + ns.writePort(Ports.Exec, { + script: "/singularity/get/faction/enemies.ts", + hostname: ns.self().server, + args: ["-a"], + threadOrOptions: {temporary: true}, + retry: true + } as ExecutorJob); + await ns.nextPortWrite(Ports.Singularity); + var enemies: Array> = ns.readPort(Ports.Singularity); + factionEnemies = new Map>(); + for (let i = 0; i < factions.length; ++i) + if (enemies[i].length) + factionEnemies.set(factions[i], enemies[i]); +} \ No newline at end of file diff --git a/singularity/get/aug/basePrice.ts b/singularity/get/aug/basePrice.ts new file mode 100644 index 0000000..bec354c --- /dev/null +++ b/singularity/get/aug/basePrice.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, (flags._ as Array).map(ns.singularity.getAugmentationBasePrice)); +} \ No newline at end of file diff --git a/singularity/get/aug/factions.ts b/singularity/get/aug/factions.ts new file mode 100644 index 0000000..152d95c --- /dev/null +++ b/singularity/get/aug/factions.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, (flags._ as Array).map(ns.singularity.getAugmentationFactions)); +} \ No newline at end of file diff --git a/singularity/get/aug/prereq.ts b/singularity/get/aug/prereq.ts new file mode 100644 index 0000000..bd6ac5b --- /dev/null +++ b/singularity/get/aug/prereq.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, (flags._ as Array).map(ns.singularity.getAugmentationPrereq)); +} \ No newline at end of file diff --git a/singularity/get/aug/price.ts b/singularity/get/aug/price.ts new file mode 100644 index 0000000..879ab86 --- /dev/null +++ b/singularity/get/aug/price.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, (flags._ as Array).map(ns.singularity.getAugmentationPrice)); +} \ No newline at end of file diff --git a/singularity/get/aug/repReq.ts b/singularity/get/aug/repReq.ts new file mode 100644 index 0000000..2ec6a1a --- /dev/null +++ b/singularity/get/aug/repReq.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, (flags._ as Array).map(ns.singularity.getAugmentationRepReq)); +} \ No newline at end of file diff --git a/singularity/get/aug/stats.ts b/singularity/get/aug/stats.ts new file mode 100644 index 0000000..c29d674 --- /dev/null +++ b/singularity/get/aug/stats.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, (flags._ as Array).map(ns.singularity.getAugmentationStats)); +} \ No newline at end of file diff --git a/singularity/get/company/favor.ts b/singularity/get/company/favor.ts new file mode 100644 index 0000000..0d44356 --- /dev/null +++ b/singularity/get/company/favor.ts @@ -0,0 +1,9 @@ +import {CompanyName} from "lib/nsEnums"; + +export async function main(ns: NS) { + const flags = ns.flags([["a", false], ["p", 0]]); + ns.writePort(flags.p as number, flags.a ? + Object.values(CompanyName).map(ns.singularity.getCompanyFavor) : + ns.singularity.getCompanyFavor((flags._ as Array)[0]) + ); +} \ No newline at end of file diff --git a/singularity/get/company/favorGain.ts b/singularity/get/company/favorGain.ts new file mode 100644 index 0000000..cf13f12 --- /dev/null +++ b/singularity/get/company/favorGain.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, (flags._ as Array).map(ns.singularity.getCompanyFavorGain)); +} \ No newline at end of file diff --git a/singularity/get/company/positionInfo.ts b/singularity/get/company/positionInfo.ts new file mode 100644 index 0000000..886ddb3 --- /dev/null +++ b/singularity/get/company/positionInfo.ts @@ -0,0 +1,6 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0], ["c", ""]]); + ns.writePort(flags.p as number, (flags._ as Array).map(job => ns.singularity.getCompanyPositionInfo( + flags.c as CompanyName, job + ))); +} \ No newline at end of file diff --git a/singularity/get/company/positions.ts b/singularity/get/company/positions.ts new file mode 100644 index 0000000..3999233 --- /dev/null +++ b/singularity/get/company/positions.ts @@ -0,0 +1,9 @@ +import {CompanyName} from "lib/nsEnums"; + +export async function main(ns: NS) { + const flags = ns.flags([["a", false], ["p", 0]]); + ns.writePort(flags.p as number, flags.a ? + Object.values(CompanyName).map(ns.singularity.getCompanyPositions) : + ns.singularity.getCompanyPositions((flags._ as Array)[0]) + ); +} \ No newline at end of file diff --git a/singularity/get/company/rep.ts b/singularity/get/company/rep.ts new file mode 100644 index 0000000..eb23234 --- /dev/null +++ b/singularity/get/company/rep.ts @@ -0,0 +1,8 @@ +import {CompanyName} from "lib/nsEnums"; + +export async function main(ns: NS) { + const flags = ns.flags([["p", 0], ["a", false]]); + ns.writePort(flags.p as number, ( + flags.a ? Object.values(CompanyName) : (flags._ as Array) + ).map(ns.singularity.getCompanyRep)); +} \ No newline at end of file diff --git a/singularity/get/currentServer.ts b/singularity/get/currentServer.ts new file mode 100644 index 0000000..1bc25d6 --- /dev/null +++ b/singularity/get/currentServer.ts @@ -0,0 +1,6 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["p", Ports.Singularity]]); + ns.writePort(flags.p as number, ns.singularity.getCurrentServer()); +} \ No newline at end of file diff --git a/singularity/get/currentWork.ts b/singularity/get/currentWork.ts new file mode 100644 index 0000000..9c1ca12 --- /dev/null +++ b/singularity/get/currentWork.ts @@ -0,0 +1,5 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + ns.writePort(Ports.Singularity, ns.singularity.getCurrentWork()); +} \ No newline at end of file diff --git a/singularity/get/darkweb/programCost.ts b/singularity/get/darkweb/programCost.ts new file mode 100644 index 0000000..626aa5a --- /dev/null +++ b/singularity/get/darkweb/programCost.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, (flags._ as Array).map(ns.singularity.getDarkwebProgramCost)); +} \ No newline at end of file diff --git a/singularity/get/darkweb/programs.ts b/singularity/get/darkweb/programs.ts new file mode 100644 index 0000000..07f759d --- /dev/null +++ b/singularity/get/darkweb/programs.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const port = ns.flags([["p", 0]]).p as number; + ns.writePort(port, ns.singularity.getDarkwebPrograms()); +} \ No newline at end of file diff --git a/singularity/get/faction/enemies.ts b/singularity/get/faction/enemies.ts new file mode 100644 index 0000000..a9e3b86 --- /dev/null +++ b/singularity/get/faction/enemies.ts @@ -0,0 +1,9 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["p", Ports.Singularity], ["a", false]]); + ns.writePort(flags.p as number, flags.a ? + Object.values(ns.enums.FactionName).map(ns.singularity.getFactionEnemies) : + ns.singularity.getFactionEnemies((flags._ as Array)[0]) + ); +} \ No newline at end of file diff --git a/singularity/get/faction/favor.ts b/singularity/get/faction/favor.ts new file mode 100644 index 0000000..eb2cd60 --- /dev/null +++ b/singularity/get/faction/favor.ts @@ -0,0 +1,6 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, (flags._ as Array).map(ns.singularity.getFactionFavor)); +} \ No newline at end of file diff --git a/singularity/get/faction/favorGain.ts b/singularity/get/faction/favorGain.ts new file mode 100644 index 0000000..270d7b4 --- /dev/null +++ b/singularity/get/faction/favorGain.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, (flags._ as Array).map(ns.singularity.getFactionFavorGain)); +} \ No newline at end of file diff --git a/singularity/get/faction/inviteReqs.ts b/singularity/get/faction/inviteReqs.ts new file mode 100644 index 0000000..34de6ee --- /dev/null +++ b/singularity/get/faction/inviteReqs.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, (flags._ as Array).map(ns.singularity.getFactionInviteRequirements)); +} \ No newline at end of file diff --git a/singularity/get/faction/invites.ts b/singularity/get/faction/invites.ts new file mode 100644 index 0000000..6884a0b --- /dev/null +++ b/singularity/get/faction/invites.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const port = ns.flags([["p", 0]]).p as number; + ns.writePort(port, ns.singularity.checkFactionInvitations()); +} \ No newline at end of file diff --git a/singularity/get/faction/rep.ts b/singularity/get/faction/rep.ts new file mode 100644 index 0000000..a3e5e8a --- /dev/null +++ b/singularity/get/faction/rep.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, (flags._ as Array).map(ns.singularity.getFactionRep)); +} \ No newline at end of file diff --git a/singularity/get/faction/workTypes.ts b/singularity/get/faction/workTypes.ts new file mode 100644 index 0000000..3e24fe2 --- /dev/null +++ b/singularity/get/faction/workTypes.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, (flags._ as Array).map(ns.singularity.getFactionWorkTypes)); +} \ No newline at end of file diff --git a/singularity/installAugs.ts b/singularity/installAugs.ts new file mode 100644 index 0000000..68623b5 --- /dev/null +++ b/singularity/installAugs.ts @@ -0,0 +1,3 @@ +export async function main(ns: NS) { + ns.singularity.installAugmentations("/init/init.ts"); +} \ No newline at end of file diff --git a/singularity/installBackdoor.ts b/singularity/installBackdoor.ts new file mode 100644 index 0000000..df3bc0e --- /dev/null +++ b/singularity/installBackdoor.ts @@ -0,0 +1,10 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + const port = ns.flags([["p", Ports.Singularity]]).p as number; + try { + await ns.singularity.installBackdoor(); + ns.writePort(port, true); + } + catch (e) { ns.writePort(port, false); } +} \ No newline at end of file diff --git a/singularity/is/busy.ts b/singularity/is/busy.ts new file mode 100644 index 0000000..6fdf84f --- /dev/null +++ b/singularity/is/busy.ts @@ -0,0 +1,5 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + ns.writePort(Ports.Singularity, ns.singularity.isBusy()); +} \ No newline at end of file diff --git a/singularity/is/focused.ts b/singularity/is/focused.ts new file mode 100644 index 0000000..5a5ceb6 --- /dev/null +++ b/singularity/is/focused.ts @@ -0,0 +1,5 @@ +import {Ports} from "lib/util"; + +export async function main(ns: NS) { + ns.writePort(Ports.Singularity, ns.singularity.isFocused()); +} \ No newline at end of file diff --git a/singularity/stem.ts b/singularity/stem.ts new file mode 100644 index 0000000..03e37c9 --- /dev/null +++ b/singularity/stem.ts @@ -0,0 +1,23 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; +import {main as genCompanyRep} from "singularity/genInfo/companyRep"; +import {main as genCompanyPositions} from "singularity/genInfo/companyPositions"; +import {main as genDarkwebInfo} from "singularity/genInfo/darkweb"; +import {main as applyForJobs} from "singularity/applyForJobs"; +import {main as chooseWork} from "singularity/chooseWork"; + +export async function main(ns: NS) { + ns.print("INFO: Attempting to farm Int."); + ns.writePort(Ports.Exec, { + script: "/singularity/farmInt.ts", + hostname: ns.self().server, + threadOrOptions: {temporary: true} + } as ExecutorJob); + await genCompanyRep(ns); + await genCompanyPositions(ns); + await genDarkwebInfo(ns); + ns.print("INFO: Applying for jobs.") + await applyForJobs(ns); + ns.print("INFO: Choosing work."); + await chooseWork(ns); +} \ No newline at end of file diff --git a/singularity/travel.ts b/singularity/travel.ts new file mode 100644 index 0000000..d36670e --- /dev/null +++ b/singularity/travel.ts @@ -0,0 +1,3 @@ +export async function main(ns: NS) { + ns.singularity.travelToCity((ns.args as Array)[0]); +} \ No newline at end of file diff --git a/singularity/tryBackdoor.ts b/singularity/tryBackdoor.ts new file mode 100644 index 0000000..9f0723c --- /dev/null +++ b/singularity/tryBackdoor.ts @@ -0,0 +1,44 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; + +export async function main(ns: NS) { + const target = (ns.args as Array)[0]; + var executorJob: ExecutorJob = { + script: "/singularity/getCurrentServer.ts", + hostname: ns.self().server, + threadOrOptions: {temporary: true}, + retry: true + }; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Singularity); + const host: string = ns.readPort(Ports.Singularity); + + executorJob.script = "/singularity/connect.ts"; + executorJob.args = [target, "--connected-host"]; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Singularity); + if (ns.readPort(Ports.Singularity)) { + ns.tprintf("INFO: Backdooring %s. Please stand by.", target); + + executorJob.args.pop(); + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Singularity); + ns.clearPort(Ports.Singularity); + + executorJob.script = "/singularity/installBackdoor.ts"; + delete executorJob.args; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.Singularity); + ns.tprintf( + ns.readPort(Ports.Singularity) ? + "SUCCESS: Backdoored %s." : + "ERROR: Couldn't install backdoor on %s.", + target + ); + executorJob.script = "/singularity/connect.ts"; + executorJob.args = [host]; + ns.writePort(Ports.Exec, executorJob); + } + else + ns.tprint("ERROR: Couldn't find connected host.") +} \ No newline at end of file diff --git a/singularity/upgradeHome/cores.ts b/singularity/upgradeHome/cores.ts new file mode 100644 index 0000000..1e63baf --- /dev/null +++ b/singularity/upgradeHome/cores.ts @@ -0,0 +1,6 @@ +export async function main(ns: NS) { + if (ns.singularity.upgradeHomeCores()) + ns.toast("Upgraded home cores", ns.enums.ToastVariant.SUCCESS); + else if (!ns.self().parent) + ns.tprint("ERROR: Couldn't upgrade home cores"); +} \ No newline at end of file diff --git a/singularity/upgradeHome/ram.ts b/singularity/upgradeHome/ram.ts new file mode 100644 index 0000000..a88a278 --- /dev/null +++ b/singularity/upgradeHome/ram.ts @@ -0,0 +1,6 @@ +export async function main(ns: NS) { + if (ns.singularity.upgradeHomeRam()) + ns.toast("Upgraded home RAM", ns.enums.ToastVariant.SUCCESS); + else if (!ns.self().parent) + ns.tprint("ERROR: Couldn't upgrade home RAM"); +} \ No newline at end of file diff --git a/sleeve/genAugInfo.js b/sleeve/genAugInfo.js new file mode 100644 index 0000000..55d4363 --- /dev/null +++ b/sleeve/genAugInfo.js @@ -0,0 +1,18 @@ +import {Ports} from "/library/library.js"; + +/** @param {NS} ns */ +export async function main(ns) { + var augs, augInfo; + + ns.run("./get/purchasableAugs.js", {"temporary": true}); + await ns.nextPortWrite(Ports.Sleeve); + augs = ns.readPort(Ports.Sleeve); + for (const aug of augs) { + ns.run("./get/augPrice.js", {"temporary": true}, aug); + await ns.nextPortWrite(Ports.Sleeve); + ns.run("./get/augRepReq.js", {"temporary": true}, aug); + await ns.nextPortWrite(Ports.Sleeve); + augInfo[aug] = [ns.readPort(Ports.Sleeve), ns.readPort(Ports.Sleeve)]; + } + ns.write("./augInfo.json", JSON.stringify(augInfo), "w"); +} \ No newline at end of file diff --git a/sleeve/get/augPrice.js b/sleeve/get/augPrice.js new file mode 100644 index 0000000..04df919 --- /dev/null +++ b/sleeve/get/augPrice.js @@ -0,0 +1,6 @@ +import {Ports} from "/library/library.js"; + +/** @param {NS} ns */ +export async function main(ns) { + ns.writePort(Ports.Sleeve, ns.sleeve.getSleeveAugmentationPrice(ns.args[0])); +} \ No newline at end of file diff --git a/sleeve/get/augRepReq.js b/sleeve/get/augRepReq.js new file mode 100644 index 0000000..47c0a60 --- /dev/null +++ b/sleeve/get/augRepReq.js @@ -0,0 +1,6 @@ +import {Ports} from "/library/library.js"; + +/** @param {NS} ns */ +export async function main(ns) { + ns.writePort(Ports.Sleeve, ns.sleeve.getSleeveAugmentationRepReq(ns.args[0])); +} \ No newline at end of file diff --git a/sleeve/get/augs.js b/sleeve/get/augs.js new file mode 100644 index 0000000..e8ed315 --- /dev/null +++ b/sleeve/get/augs.js @@ -0,0 +1,7 @@ +import {Ports} from "/library/library.js"; + +/** @param {NS} ns */ +export async function main(ns) { + const flags = ns.flags([["n", 0]]) + ns.writePort(Ports.Sleeve, ns.sleeve.getSleeveAugmentations(flags.n)); +} \ No newline at end of file diff --git a/sleeve/get/numSleeves.js b/sleeve/get/numSleeves.js new file mode 100644 index 0000000..01b04af --- /dev/null +++ b/sleeve/get/numSleeves.js @@ -0,0 +1,6 @@ +import {Ports} from "/library/library.js"; + +/** @param {NS} ns */ +export async function main(ns) { + ns.writePort(Ports.Sleeve, ns.sleeve.getNumSleeves()); +} \ No newline at end of file diff --git a/sleeve/get/purchasableAugs.js b/sleeve/get/purchasableAugs.js new file mode 100644 index 0000000..b3dd288 --- /dev/null +++ b/sleeve/get/purchasableAugs.js @@ -0,0 +1,6 @@ +import {Ports} from "/library/library.js"; + +/** @param {NS} ns */ +export async function main(ns) { + ns.writePort(Ports.Sleeve, ns.sleeve.getSleevePurchasableAugs(ns.args[0])); +} \ No newline at end of file diff --git a/sleeve/get/sleeve.js b/sleeve/get/sleeve.js new file mode 100644 index 0000000..99d07e3 --- /dev/null +++ b/sleeve/get/sleeve.js @@ -0,0 +1,7 @@ +import {Ports} from "/library/library.js"; + +/** @param {NS} ns */ +export async function main(ns) { + const flags = ns.flags([["n", 0]]); + ns.writePort(Ports.Sleeve, ns.getSleeve(flags.n)); +} \ No newline at end of file diff --git a/sleeve/setTo/commitCrime.js b/sleeve/setTo/commitCrime.js new file mode 100644 index 0000000..2ca4aac --- /dev/null +++ b/sleeve/setTo/commitCrime.js @@ -0,0 +1,5 @@ +/** @param {NS} ns */ +export async function main(ns) { + const flags = ns.flags([["n", 0]]); + ns.sleeve.setToCommitCrime(n, ns.args[0]); +} \ No newline at end of file diff --git a/sleeve/setTo/companyWork.js b/sleeve/setTo/companyWork.js new file mode 100644 index 0000000..4aacd8b --- /dev/null +++ b/sleeve/setTo/companyWork.js @@ -0,0 +1,5 @@ +/** @param {NS} ns */ +export async function main(ns) { + const flags = ns.flags([["n", 0]]); + ns.sleeve.setToCompanyWork(n, ns.args[0]); +} \ No newline at end of file diff --git a/sleeve/setTo/factionWork.js b/sleeve/setTo/factionWork.js new file mode 100644 index 0000000..dbd9544 --- /dev/null +++ b/sleeve/setTo/factionWork.js @@ -0,0 +1,5 @@ +/** @param {NS} ns */ +export async function main(ns) { + const flags = ns.flags([["n", 0]]); + ns.sleeve.setToFactionWork(n, ns.args[0], ns.args[1]); +} \ No newline at end of file diff --git a/sleeve/setTo/gymWorkout.js b/sleeve/setTo/gymWorkout.js new file mode 100644 index 0000000..5837cf9 --- /dev/null +++ b/sleeve/setTo/gymWorkout.js @@ -0,0 +1,5 @@ +/** @param {NS} ns */ +export async function main(ns) { + const flags = ns.flags([["n", 0]]); + ns.sleeve.setToGymWorkout(n, ns.args[0], ns.args[1]); +} \ No newline at end of file diff --git a/sleeve/setTo/idle.js b/sleeve/setTo/idle.js new file mode 100644 index 0000000..e9a023d --- /dev/null +++ b/sleeve/setTo/idle.js @@ -0,0 +1,5 @@ +/** @param {NS} ns */ +export async function main(ns) { + const flags = ns.flags([["n", 0]]); + ns.sleeve.setToIdle(n); +} \ No newline at end of file diff --git a/sleeve/setTo/shockRecovery.js b/sleeve/setTo/shockRecovery.js new file mode 100644 index 0000000..f990109 --- /dev/null +++ b/sleeve/setTo/shockRecovery.js @@ -0,0 +1,5 @@ +/** @param {NS} ns */ +export async function main(ns) { + const flags = ns.flags([["n", 0]]); + ns.sleeve.setToShockRecovery(n); +} \ No newline at end of file diff --git a/sleeve/setTo/synchronize.js b/sleeve/setTo/synchronize.js new file mode 100644 index 0000000..d9bea53 --- /dev/null +++ b/sleeve/setTo/synchronize.js @@ -0,0 +1,5 @@ +/** @param {NS} ns */ +export async function main(ns) { + const flags = ns.flags([["n", 0]]); + ns.sleeve.setToSynchronize(n); +} \ No newline at end of file diff --git a/sleeve/setTo/universityCourse.js b/sleeve/setTo/universityCourse.js new file mode 100644 index 0000000..c5ec7c9 --- /dev/null +++ b/sleeve/setTo/universityCourse.js @@ -0,0 +1,5 @@ +/** @param {NS} ns */ +export async function main(ns) { + const flags = ns.flags([["n", 0]]); + ns.sleeve.setToUniversityCourse(n, ns.args[0], ns.args[1]); +} \ No newline at end of file diff --git a/sleeve/travel.js b/sleeve/travel.js new file mode 100644 index 0000000..ef32422 --- /dev/null +++ b/sleeve/travel.js @@ -0,0 +1,5 @@ +/** @param {NS} ns */ +export async function main(ns) { + const flags = ns.flags([["n", 0]]); + ns.sleeve.travel(n, ns.args[0]); +} \ No newline at end of file diff --git a/srv/deploy.ts b/srv/deploy.ts new file mode 100644 index 0000000..2d6523a --- /dev/null +++ b/srv/deploy.ts @@ -0,0 +1,6 @@ +export function deployServerBuyer(ns: NS, dest: string) { + ns.scp(ns.ls("home", "srv/").concat(ns.ls("home", "lib/"), ns.ls("home", "daemons/")), dest); + ns.exec("/srv/purchaseHackers.ts", dest); +} + +export async function main(ns: NS) { deployServerBuyer(ns, ns.args[0] as string); } \ No newline at end of file diff --git a/srv/get.js b/srv/get.js new file mode 100644 index 0000000..066e8e7 --- /dev/null +++ b/srv/get.js @@ -0,0 +1,11 @@ +/** @param {NS} ns */ +export async function main(ns) { + const flags = ns.flags([["p", 0]]) + const servers = flags._.map(ns.getServer); + + if (flags.p) + ns.writePort(flags.p, servers.length > 1 ? servers : servers[0]) + else + for (const srv of servers) + ns.write("/srv/info/" + hostname + ".json", JSON.stringify(srv), "w"); +} \ No newline at end of file diff --git a/srv/purchase.ts b/srv/purchase.ts new file mode 100644 index 0000000..8647bcd --- /dev/null +++ b/srv/purchase.ts @@ -0,0 +1,6 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0], ["r", 0]]); + const hostname = ns.purchaseServer((flags._ as Array)[0], flags.r as number || 256); + if (flags.p && hostname) + ns.writePort(flags.p as number, hostname); +} \ No newline at end of file diff --git a/srv/purchaseHackers.ts b/srv/purchaseHackers.ts new file mode 100644 index 0000000..674182d --- /dev/null +++ b/srv/purchaseHackers.ts @@ -0,0 +1,44 @@ +import {BitNode} from "lib/nsEnums"; +import {Ports} from "lib/util"; +import {extResetInfo} from "lib/genInfo/reset"; + +const InitRam = 1 << 8; +const SleepMillis = 15000; +const RequiredSeedMoney = extResetInfo.currentNode == BitNode.GhostOfWallStreet ? 1e9 : 0; + +export async function main(ns: NS) { + const cbPort = ns.self().pid + Ports.PIDBase; + + function getPurchasedSrvs() { + return ns.scan("home").filter(h => h.includes("hack")); + } + + function done(hostnames: Array) { + return hostnames.length == extResetInfo.purchasedServers.limit && + hostnames.every(h => ns.getServerMaxRam(h) == extResetInfo.purchasedServers.maxRam); + } + + // Shotgunner now expands to fill available space, so this filter is not in use + function halfFull(h: string) { return ns.getServerUsedRam(h) > (ns.getServerMaxRam(h) >>> 1); } + + for (let hostnames = getPurchasedSrvs(); !done(hostnames); hostnames = getPurchasedSrvs()) { + while (!ns.run("/lib/get/moneySources.ts", {"temporary": true}, "-p", cbPort)) + await ns.sleep(20); + await ns.nextPortWrite(cbPort); + const moneySinceInstall = (ns.readPort(cbPort) as MoneySources).sinceInstall; + + // Servers must pay for themselves in hacking money and stock manipulation + if (moneySinceInstall.hacking + moneySinceInstall.stock - RequiredSeedMoney > Math.abs(moneySinceInstall.servers)) { + if (hostnames.length < extResetInfo.purchasedServers.limit) + while (!ns.run("./purchase.ts", {"temporary": true}, "hacker", "-r", InitRam, "-p", Ports.ServerBroadcast)) + await ns.sleep(20); + else { + hostnames.sort((a, b) => { return ns.getServerMaxRam(a) - ns.getServerMaxRam(b); }); + while (!ns.run("./upgrade.ts", {"temporary": true}, hostnames[0], ns.getServerMaxRam(hostnames[0]) << 1)) + await ns.sleep(20); + } + } + await ns.sleep(SleepMillis); + } + ns.toast("Finished upgrading hackers", "success"); +} \ No newline at end of file diff --git a/srv/rename.js b/srv/rename.js new file mode 100644 index 0000000..e12e215 --- /dev/null +++ b/srv/rename.js @@ -0,0 +1,4 @@ +/** @param {NS} ns */ +export async function main(ns) { + ns.renamePurchasedServer(ns.args[0], ns.args[1]); +} \ No newline at end of file diff --git a/srv/upgrade.ts b/srv/upgrade.ts new file mode 100644 index 0000000..a14f108 --- /dev/null +++ b/srv/upgrade.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const hostname = ns.args[0] as string; + ns.upgradePurchasedServer(hostname, ns.getServerMaxRam(hostname) << 1); +} \ No newline at end of file diff --git a/stock/buy.ts b/stock/buy.ts new file mode 100644 index 0000000..0155d52 --- /dev/null +++ b/stock/buy.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["q", 0]]); + ns.stock.buyStock((flags._ as Array)[0], flags.q as number); +} \ No newline at end of file diff --git a/stock/buyLoop.ts b/stock/buyLoop.ts new file mode 100644 index 0000000..e578625 --- /dev/null +++ b/stock/buyLoop.ts @@ -0,0 +1,73 @@ +import {BitNode} from "lib/nsEnums"; +import {Ports} from "lib/util"; +import {extResetInfo} from "lib/genInfo/reset"; +import {ExecutorJob} from "daemons/executor"; +import {FullSymbolInfo, symbolInfoRecord} from "stock/genSymbolInfo"; +import {sellAllStocks} from "stock/sellLoop"; + +const MinPurchase = extResetInfo.currentNode == BitNode.GhostOfWallStreet ? 1e7 : 1e8; +const MinFunds = extResetInfo.currentNode == BitNode.GhostOfWallStreet ? MinPurchase : 1e9; +const MinForecast = 0.5; +const MinPoorManForecast = 0.82; + +export async function main(ns: NS) { + const hostname = ns.self().server; + const commission = ns.stock.getConstants().StockMarketCommission; + var usePoorMan: boolean; + const symbols = Object.keys(symbolInfoRecord); + var valuations: Record = {}; + + function evalStock(sym: string) { + const info = symbolInfoRecord[sym] as FullSymbolInfo; + const forecast = info.forecast - MinForecast; + return Math.abs(forecast) * forecast * info.volatility; + } + + function isRising(sym: string) { return (symbolInfoRecord[sym].forecast ?? 0.5) > (usePoorMan ? MinPoorManForecast : MinForecast); } + + ns.atExit(() => { sellAllStocks(ns); }); + while (true) { + // sellLoop.js writes to Ports.StockInfo when it's finished each iteration. + // Doing things this way reduces RAM usage and conflicts, and ensures that we never + // buy when we're not prepared to sell. + await ns.nextPortWrite(Ports.StockBroadcast); + + usePoorMan = !("volatility" in symbolInfoRecord[symbols[0]]); + let risingSymbols = symbols.filter(isRising); + if (usePoorMan) + for (const sym of risingSymbols) + valuations[sym] = (symbolInfoRecord[sym].forecast ?? 0.5) - MinPoorManForecast; + else + for (const sym of risingSymbols) + valuations[sym] = evalStock(sym); + risingSymbols.sort((a, b) => { return valuations[b] - valuations[a]; }); + ns.print("INFO: Rising stocks sorted by eval: ", JSON.stringify(risingSymbols)); + + let executorJob: ExecutorJob = { + script: "/stock/get/askPrice.ts", + hostname: hostname, + args: ["-p", Ports.StockBuy, ...risingSymbols], + threadOrOptions: {temporary: true}, + retry: true + }; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.StockBuy); + const askPrices = ns.readPort(Ports.StockBuy); + + executorJob.script = "/stock/buy.ts"; + for (let i = 0; i < risingSymbols.length && ns.getServerMoneyAvailable("home") >= MinFunds; ++i) { + const sym = risingSymbols[i]; + const shareDelta = symbolInfoRecord[sym].maxShares - (symbolInfoRecord[sym].position ?? [0])[0]; + if (shareDelta) { + const quantity = Math.min( + Math.floor((ns.getServerMoneyAvailable("home") - commission) / askPrices[i]), + shareDelta + ); + if (quantity * askPrices[i] >= MinPurchase) { + executorJob.args = [sym, "-q", quantity]; + ns.writePort(Ports.Exec, executorJob); + } + } + } + } +} \ No newline at end of file diff --git a/stock/deploy.ts b/stock/deploy.ts new file mode 100644 index 0000000..6b2b1f2 --- /dev/null +++ b/stock/deploy.ts @@ -0,0 +1,16 @@ +export async function main(ns: NS) { + const dest: string = ns.args[0] as string; + + ns.scp(ns.ls("home", "stock/").concat(ns.ls("home", "lib/"), ns.ls("home", "daemons/"), ["/share.ts"]), dest); + ns.kill("./init.ts", dest); + ns.kill("./init2.ts", dest); + ns.kill("./launch.ts", dest); + ns.kill("./buyLoop.ts", dest); + await ns.sleep(5000); // Allow time for sell loop to sell all stocks + ns.kill("./sellLoop.ts", dest); + ns.kill("/share.ts", dest); + ns.exec("./init.ts", dest); + const freeRam = ns.getServerMaxRam(dest) - ns.getServerUsedRam(dest); + if (freeRam >= 4) + ns.exec("/share.ts", dest, freeRam >> 2); +} \ No newline at end of file diff --git a/stock/genSymbolInfo.ts b/stock/genSymbolInfo.ts new file mode 100644 index 0000000..70c34e7 --- /dev/null +++ b/stock/genSymbolInfo.ts @@ -0,0 +1,66 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; + +export interface SymbolInfo { + maxShares: number, + volatility?: number, + forecast?: number, + organization: string, + + /** The first element in the array is the number of shares the player owns of the stock in the Long position. The second element in the array is the average price of the player’s shares in the Long position. + The third element in the array is the number of shares the player owns of the stock in the Short position. The fourth element in the array is the average price of the player’s Short position. */ + position?: ReturnType +} + +export interface FullSymbolInfo extends SymbolInfo { + volatility: number, + forecast: number +} + +export type SymbolInfoRecord = Record; + +export var symbolInfoRecord: SymbolInfoRecord = {}; + +export async function main(ns: NS) { + const hostname = ns.self().server; + var args = ["-p", Ports.StockSell]; + var executorJob: ExecutorJob = { + "script": "/stock/get/symbols.ts", + "hostname": hostname, + "args": args, + "threadOrOptions": {"temporary": true}, + "retry": true + }; + + ns.clearPort(Ports.StockSell); + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.StockSell); + const symbols: Array = ns.readPort(Ports.StockSell); + args = args.concat(symbols); + executorJob.args = args; + + executorJob.script = "/stock/get/maxShares.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.StockSell); + const maxShares: Array = ns.readPort(Ports.StockSell); + + executorJob.script = "/stock/get/volatility.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.StockSell); + const volatilities: Array = ns.readPort(Ports.StockSell); + + executorJob.script = "/stock/get/organization.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.StockSell); + const orgs: Array = ns.readPort(Ports.StockSell); + + for (let i = 0; i < symbols.length; ++i) + symbolInfoRecord[symbols[i]] = { + "maxShares": maxShares[i], + "organization": orgs[i] + }; + + if (volatilities) + for (let i = 0; i < symbols.length; ++i) + symbolInfoRecord[symbols[i]].volatility = volatilities[i]; +} \ No newline at end of file diff --git a/stock/get/askPrice.ts b/stock/get/askPrice.ts new file mode 100644 index 0000000..aa49d71 --- /dev/null +++ b/stock/get/askPrice.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, (flags._ as Array).map(ns.stock.getAskPrice)); +} \ No newline at end of file diff --git a/stock/get/forecast.ts b/stock/get/forecast.ts new file mode 100644 index 0000000..fbcc195 --- /dev/null +++ b/stock/get/forecast.ts @@ -0,0 +1,6 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + const port = flags.p as number; + try { ns.writePort(port, (flags._ as Array).map(ns.stock.getForecast)); } + catch (e) { ns.writePort(port, null); } +} \ No newline at end of file diff --git a/stock/get/maxShares.ts b/stock/get/maxShares.ts new file mode 100644 index 0000000..8943b3d --- /dev/null +++ b/stock/get/maxShares.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, (flags._ as Array).map(ns.stock.getMaxShares)); +} \ No newline at end of file diff --git a/stock/get/organization.ts b/stock/get/organization.ts new file mode 100644 index 0000000..0096104 --- /dev/null +++ b/stock/get/organization.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, (flags._ as Array).map(ns.stock.getOrganization)); +} \ No newline at end of file diff --git a/stock/get/poorManForecast.ts b/stock/get/poorManForecast.ts new file mode 100644 index 0000000..5df64c2 --- /dev/null +++ b/stock/get/poorManForecast.ts @@ -0,0 +1,26 @@ +// This script should be run *only* once per stock update, with every symbol, in the same order each time + +const PoorManForecastFile = "/stock/get/poorManForecast.json"; +const AskPricesFile = "/stock/get/askPrices.json"; + +const RecencyWeight = 0.16; + +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + const askPrices = (flags._ as Array).map(ns.stock.getAskPrice); + let poorManForecasts; + try { poorManForecasts = JSON.parse(ns.read(PoorManForecastFile)) as Array; } + catch (e) { poorManForecasts = new Array(askPrices.length).fill(0.5); } + + try { + const prevAskPrices = JSON.parse(ns.read(AskPricesFile)); + poorManForecasts = poorManForecasts.map((forecast: number, index: number) => { + forecast *= 1 - RecencyWeight; + return forecast += askPrices[index] > prevAskPrices[index] ? RecencyWeight : 0; + }); + } + catch (e) {} + ns.write(AskPricesFile, JSON.stringify(askPrices), "w"); + ns.write(PoorManForecastFile, JSON.stringify(poorManForecasts), "w"); + ns.writePort(flags.p as number, poorManForecasts); +} \ No newline at end of file diff --git a/stock/get/position.ts b/stock/get/position.ts new file mode 100644 index 0000000..65d1c7c --- /dev/null +++ b/stock/get/position.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, (flags._ as Array).map(ns.stock.getPosition)); +} \ No newline at end of file diff --git a/stock/get/symbols.ts b/stock/get/symbols.ts new file mode 100644 index 0000000..0116f6d --- /dev/null +++ b/stock/get/symbols.ts @@ -0,0 +1,4 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + ns.writePort(flags.p as number, ns.stock.getSymbols()); +} \ No newline at end of file diff --git a/stock/get/volatility.ts b/stock/get/volatility.ts new file mode 100644 index 0000000..22e0f75 --- /dev/null +++ b/stock/get/volatility.ts @@ -0,0 +1,6 @@ +export async function main(ns: NS) { + const flags = ns.flags([["p", 0]]); + const port = flags.p as number; + try { ns.writePort(port, (flags._ as Array).map(ns.stock.getVolatility)); } + catch (e) { ns.writePort(port, null); } +} \ No newline at end of file diff --git a/stock/init.ts b/stock/init.ts new file mode 100644 index 0000000..df11758 --- /dev/null +++ b/stock/init.ts @@ -0,0 +1,8 @@ +export const SleepTime = 300000; + +export async function main(ns: NS) { + while (!ns.stock.purchaseWseAccount()) + await ns.sleep(SleepTime); + + ns.spawn("./init2.ts", {"spawnDelay": 0}); +} \ No newline at end of file diff --git a/stock/init2.ts b/stock/init2.ts new file mode 100644 index 0000000..43b37f8 --- /dev/null +++ b/stock/init2.ts @@ -0,0 +1,9 @@ +import {SleepTime} from "stock/init"; + +export async function main(ns: NS) { + while (!ns.stock.purchaseTixApi()) + await ns.sleep(SleepTime); + + ns.kill("/share.ts"); + ns.spawn("./launch.ts", {"spawnDelay": 0}); +} \ No newline at end of file diff --git a/stock/launch.ts b/stock/launch.ts new file mode 100644 index 0000000..424c0a8 --- /dev/null +++ b/stock/launch.ts @@ -0,0 +1,16 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; +import {main as genSymbolInfo} from "stock/genSymbolInfo"; + +export async function main(ns: NS) { + await genSymbolInfo(ns); + + var executorJob: ExecutorJob = { + "script": "/stock/sellLoop.ts", + "hostname": ns.self().server, + "retry": true + } + ns.writePort(Ports.Exec, executorJob); + executorJob.script = "/stock/buyLoop.ts"; + ns.writePort(Ports.Exec, executorJob); +} \ No newline at end of file diff --git a/stock/purchase/4s.ts b/stock/purchase/4s.ts new file mode 100644 index 0000000..c9a38e3 --- /dev/null +++ b/stock/purchase/4s.ts @@ -0,0 +1,3 @@ +export async function main(ns: NS) { + ns.stock.purchase4SMarketData(); +} \ No newline at end of file diff --git a/stock/purchase/4sAPI.ts b/stock/purchase/4sAPI.ts new file mode 100644 index 0000000..29d8de3 --- /dev/null +++ b/stock/purchase/4sAPI.ts @@ -0,0 +1,3 @@ +export async function main(ns: NS) { + ns.stock.purchase4SMarketDataTixApi(); +} \ No newline at end of file diff --git a/stock/purchase/tixAPI.ts b/stock/purchase/tixAPI.ts new file mode 100644 index 0000000..7e4ffbf --- /dev/null +++ b/stock/purchase/tixAPI.ts @@ -0,0 +1,3 @@ +export async function main(ns: NS) { + ns.stock.purchaseTixApi(); +} \ No newline at end of file diff --git a/stock/purchase/wseAccount.ts b/stock/purchase/wseAccount.ts new file mode 100644 index 0000000..00a849b --- /dev/null +++ b/stock/purchase/wseAccount.ts @@ -0,0 +1,3 @@ +export async function main(ns: NS) { + ns.stock.purchaseWseAccount(); +} \ No newline at end of file diff --git a/stock/sell.ts b/stock/sell.ts new file mode 100644 index 0000000..0423e8c --- /dev/null +++ b/stock/sell.ts @@ -0,0 +1,6 @@ +export const Quantity = 1e8; + +export async function main(ns: NS) { + for (const sym of ns.args as Array) + ns.stock.sellStock(sym, Quantity); +} \ No newline at end of file diff --git a/stock/sellLoop.ts b/stock/sellLoop.ts new file mode 100644 index 0000000..fed9918 --- /dev/null +++ b/stock/sellLoop.ts @@ -0,0 +1,47 @@ +import {Ports} from "lib/util"; +import {extResetInfo} from "lib/genInfo/reset"; +import {ExecutorJob} from "daemons/executor"; +import {symbolInfoRecord} from "stock/genSymbolInfo"; +import {main as updateSymbolInfo, usePoorMan} from "stock/updateSymbolInfo"; + +const MinForecast = 0.5; +const MinPoorManForecast = 0.5; +const SellAllCmd = "Sell all"; + +export function sellAllStocks(ns: NS) { ns.writePort(Ports.StockSell, SellAllCmd); } + +export async function main(ns: NS) { + const hostname = ns.self().server; + const symbols = Object.keys(symbolInfoRecord); + + while (true) { + let toSell: Array = []; + + if (ns.peek(Ports.StockSell) == SellAllCmd) { + toSell = symbols; + ns.clearPort(Ports.StockSell); + } + else { + await updateSymbolInfo(ns); + + const minForecast = usePoorMan ? MinPoorManForecast : MinForecast; + for (const sym of symbols.filter(s => symbolInfoRecord[s].position)) + if ((symbolInfoRecord[sym].forecast ?? 0.5) <= minForecast) + toSell.push(sym); + } + if (toSell.length) { + ns.print("INFO: Selling ", JSON.stringify(toSell)); + ns.writePort(Ports.Exec, { + "script": "/stock/sell.ts", + "hostname": hostname, + "threadOrOptions": {"temporary": true}, + "args": toSell, + "retry": true + } as ExecutorJob); + } + + ns.clearPort(Ports.StockBroadcast); + ns.writePort(Ports.StockBroadcast, true); + await ns.stock.nextUpdate(); + } +} \ No newline at end of file diff --git a/stock/sellShort.ts b/stock/sellShort.ts new file mode 100644 index 0000000..ccac002 --- /dev/null +++ b/stock/sellShort.ts @@ -0,0 +1,6 @@ +import {Quantity} from "stock/sell"; + +export async function main(ns: NS) { + for (const sym of ns.args as Array) + ns.stock.sellShort(sym, Quantity); +} \ No newline at end of file diff --git a/stock/updateSymbolInfo.ts b/stock/updateSymbolInfo.ts new file mode 100644 index 0000000..6a127eb --- /dev/null +++ b/stock/updateSymbolInfo.ts @@ -0,0 +1,57 @@ +import {Ports} from "lib/util"; +import {ExecutorJob} from "daemons/executor"; +import {main as genSymbolInfo, symbolInfoRecord} from "stock/genSymbolInfo"; + +export var usePoorMan = true; + +export async function main(ns: NS) { + const symbols = Object.keys(symbolInfoRecord); + var executorJob: ExecutorJob = { + script: "", + hostname: ns.self().server, + threadOrOptions: {temporary: true}, + retry: true + }; + + if (usePoorMan) { + ns.print("INFO: Attempting to buy 4S access."); + for (const script of ["4s.ts", "4sAPI.ts"]) { + executorJob.script = "/stock/purchase/" + script; + ns.writePort(Ports.Exec, executorJob); + } + } + + ns.print("INFO: Attempting to get 4S forecasts."); + executorJob.script = "/stock/get/forecast.ts"; + executorJob.args = ["-p", Ports.StockSell].concat(symbols); + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.StockSell); + let forecasts: Array = ns.readPort(Ports.StockSell); + + if (usePoorMan = !forecasts) { + ns.print("INFO: Using poor man forecasts."); + usePoorMan = true; + executorJob.script = "/stock/get/poorManForecast.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.StockSell); + forecasts = ns.readPort(Ports.StockSell); + } + else if (!symbolInfoRecord[symbols[0]].volatility) { + ns.print("INFO: Regenerating symbol info with volatility."); + await genSymbolInfo(ns); + } + + ns.print("INFO: Getting positions."); + executorJob.script = "/stock/get/position.ts"; + ns.writePort(Ports.Exec, executorJob); + await ns.nextPortWrite(Ports.StockSell); + const pos: Array> = ns.readPort(Ports.StockSell); + + symbols.forEach((sym, i) => { + symbolInfoRecord[sym].forecast = forecasts[i]; + if (pos[i][0] || pos[i][2]) + symbolInfoRecord[sym].position = pos[i]; + else if ("position" in symbolInfoRecord[sym]) + delete symbolInfoRecord[sym].position; + }); +} \ No newline at end of file From 0a28b2e1032aa1689e75cd6f9cc858f6cbdce70c Mon Sep 17 00:00:00 2001 From: "Um... Figure it out!" <33612115+CTH999@users.noreply.github.com> Date: Thu, 21 May 2026 15:15:13 -0500 Subject: [PATCH 2/4] to sort the plan is to eventually, with permission, integrate these scripts conceptually into mine. To do that, it was moved to a "to sort" folder --- autoexec.ts => To Sort/autoexec.ts | 0 {bladeburner => To Sort/bladeburner}/chooseCity.ts | 0 {bladeburner => To Sort/bladeburner}/forge.ts | 0 {bladeburner => To Sort/bladeburner}/genInfo/actions.ts | 0 {bladeburner => To Sort/bladeburner}/genInfo/cities.ts | 0 {bladeburner => To Sort/bladeburner}/genInfo/player.ts | 0 {bladeburner => To Sort/bladeburner}/get/action/autoLevel.ts | 0 {bladeburner => To Sort/bladeburner}/get/action/countRemaining.ts | 0 {bladeburner => To Sort/bladeburner}/get/action/currentLevel.ts | 0 {bladeburner => To Sort/bladeburner}/get/action/currentTime.ts | 0 .../bladeburner}/get/action/estSuccessChance.ts | 0 {bladeburner => To Sort/bladeburner}/get/action/maxLevel.ts | 0 {bladeburner => To Sort/bladeburner}/get/action/repGain.ts | 0 {bladeburner => To Sort/bladeburner}/get/action/successes.ts | 0 {bladeburner => To Sort/bladeburner}/get/action/time.ts | 0 {bladeburner => To Sort/bladeburner}/get/blackOp/next.ts | 0 {bladeburner => To Sort/bladeburner}/get/blackOp/rank.ts | 0 {bladeburner => To Sort/bladeburner}/get/city/chaos.ts | 0 {bladeburner => To Sort/bladeburner}/get/city/communities.ts | 0 {bladeburner => To Sort/bladeburner}/get/city/current.ts | 0 {bladeburner => To Sort/bladeburner}/get/city/estPop.ts | 0 {bladeburner => To Sort/bladeburner}/get/rank.ts | 0 {bladeburner => To Sort/bladeburner}/get/skill/level.ts | 0 {bladeburner => To Sort/bladeburner}/get/skill/points.ts | 0 {bladeburner => To Sort/bladeburner}/get/skill/upgradeCost.ts | 0 {bladeburner => To Sort/bladeburner}/get/stamina.ts | 0 {bladeburner => To Sort/bladeburner}/get/teamSize.ts | 0 {bladeburner => To Sort/bladeburner}/join/division.js | 0 {bladeburner => To Sort/bladeburner}/join/faction.js | 0 {bladeburner => To Sort/bladeburner}/launch.ts | 0 {bladeburner => To Sort/bladeburner}/manageSkills.ts | 0 {bladeburner => To Sort/bladeburner}/travel.ts | 0 {bladeburner => To Sort/bladeburner}/upgradeSkill.ts | 0 {bladeburner => To Sort/bladeburner}/util.ts | 0 {contract => To Sort/contract}/2color.ts | 0 {contract => To Sort/contract}/arrayJumpI.ts | 0 {contract => To Sort/contract}/arrayJumpII.ts | 0 {contract => To Sort/contract}/attempt.js | 0 {contract => To Sort/contract}/caesar.ts | 0 {contract => To Sort/contract}/get/data.ts | 0 {contract => To Sort/contract}/get/type.ts | 0 {contract => To Sort/contract}/gridPathsI.js | 0 {contract => To Sort/contract}/gridPathsII.ts | 0 {contract => To Sort/contract}/intPartition.ts | 0 {contract => To Sort/contract}/intPartitionSet.js | 0 {contract => To Sort/contract}/intToHamming.js | 0 {contract => To Sort/contract}/ipAddr.ts | 0 {contract => To Sort/contract}/lpf.js | 0 {contract => To Sort/contract}/lzCompression.js | 0 {contract => To Sort/contract}/lzDecompression.ts | 0 {contract => To Sort/contract}/mathExprs.js | 0 {contract => To Sort/contract}/mergeIntervals.ts | 0 {contract => To Sort/contract}/rleCompression.ts | 0 {contract => To Sort/contract}/shortestGridPath.ts | 0 {contract => To Sort/contract}/solveLoop.tsx | 0 {contract => To Sort/contract}/spiralize.ts | 0 {contract => To Sort/contract}/sqrt.ts | 0 {contract => To Sort/contract}/stockTraderI.ts | 0 {contract => To Sort/contract}/stockTraderII.ts | 0 {contract => To Sort/contract}/stockTraderIII.ts | 0 {contract => To Sort/contract}/stockTraderIV.ts | 0 {contract => To Sort/contract}/subarrayMaxSum.ts | 0 {contract => To Sort/contract}/triMinSum.ts | 0 {contract => To Sort/contract}/vigenere.ts | 0 {crack => To Sort/crack}/crack.ts | 0 {crack => To Sort/crack}/deployScripts.ts | 0 {crack => To Sort/crack}/getTargets.ts | 0 {crack => To Sort/crack}/tryNuke.ts | 0 {crack => To Sort/crack}/worm.js | 0 {daemons => To Sort/daemons}/deployDynamo.ts | 0 {daemons => To Sort/daemons}/deployExecutor.ts | 0 {daemons => To Sort/daemons}/dynamo.ts | 0 {daemons => To Sort/daemons}/executor.ts | 0 {debug => To Sort/debug}/monitorPort.ts | 0 deployShare.ts => To Sort/deployShare.ts | 0 {exploit => To Sort/exploit}/bypass.ts | 0 {exploit => To Sort/exploit}/devMenu.js | 0 {exploit => To Sort/exploit}/rainbow.ts | 0 {exploit => To Sort/exploit}/reality.js | 0 {exploit => To Sort/exploit}/tamper.ts | 0 {exploit => To Sort/exploit}/time.js | 0 {gang => To Sort/gang}/ascend.ts | 0 {gang => To Sort/gang}/ascendMember.ts | 0 {gang => To Sort/gang}/assign.js | 0 {gang => To Sort/gang}/canRecruitMember.ts | 0 {gang => To Sort/gang}/equip.js | 0 {gang => To Sort/gang}/genEquipmentInfo.js | 0 {gang => To Sort/gang}/genInfo/gang.ts | 0 {gang => To Sort/gang}/genInfo/members.ts | 0 {gang => To Sort/gang}/genInfo/otherGangs.ts | 0 {gang => To Sort/gang}/genTaskInfo.ts | 0 {gang => To Sort/gang}/get/gangInfo.ts | 0 {gang => To Sort/gang}/get/member/ascensionResult.ts | 0 {gang => To Sort/gang}/get/member/info.ts | 0 {gang => To Sort/gang}/get/member/names.ts | 0 {gang => To Sort/gang}/get/otherGangInfo.ts | 0 {gang => To Sort/gang}/info/equipment.json | 0 {gang => To Sort/gang}/info/tasks.json | 0 {gang => To Sort/gang}/kingpin.ts | 0 {gang => To Sort/gang}/launch.ts | 0 {gang => To Sort/gang}/launch2.ts | 0 {gang => To Sort/gang}/mode.ts | 0 {gang => To Sort/gang}/recruit.ts | 0 {gang => To Sort/gang}/recruitMember.ts | 0 {gang => To Sort/gang}/rename.ts | 0 {gang => To Sort/gang}/set/territoryWarfare.ts | 0 {go => To Sort/go}/getBoardState.js | 0 {go => To Sort/go}/getLiberties.js | 0 {go => To Sort/go}/move.js | 0 {go => To Sort/go}/play.js | 0 {hack => To Sort/hack}/calcHackParams.js | 0 {hack => To Sort/hack}/deployShotgunner.ts | 0 {hack => To Sort/hack}/grow.ts | 0 {hack => To Sort/hack}/hack.ts | 0 {hack => To Sort/hack}/shotgunner.js | 0 {hack => To Sort/hack}/weaken.ts | 0 {hacknet => To Sort/hacknet}/deploy.ts | 0 {hacknet => To Sort/hacknet}/launch.ts | 0 {hacknet => To Sort/hacknet}/naiveLoop.js | 0 {hacknet => To Sort/hacknet}/nodeFormulasLoop.ts | 0 {info => To Sort/info}/BDTargets.js | 0 {info => To Sort/info}/contractList.js | 0 {info => To Sort/info}/contractTypes.js | 0 {info => To Sort/info}/crimeEV.tsx | 0 {info => To Sort/info}/infilList.js | 0 {info => To Sort/info}/karma.js | 0 {info => To Sort/info}/pathToServer.js | 0 {init => To Sort/init}/cleanup.ts | 0 {init => To Sort/init}/collectLit.ts | 0 {init => To Sort/init}/init.ts | 0 {init => To Sort/init}/init2.ts | 0 {lib => To Sort/lib}/deepScan.js | 0 {lib => To Sort/lib}/genInfo/gameState.ts | 0 {lib => To Sort/lib}/genInfo/reset.ts | 0 {lib => To Sort/lib}/get/bitNodeMultipliers.ts | 0 {lib => To Sort/lib}/get/moneySources.ts | 0 {lib => To Sort/lib}/get/player.ts | 0 {lib => To Sort/lib}/nsEnums.ts | 0 {lib => To Sort/lib}/rm.ts | 0 {lib => To Sort/lib}/tick.js | 0 {lib => To Sort/lib}/util.ts | 0 queenAdministrator.ts => To Sort/queenAdministrator.ts | 0 readme.txt => To Sort/readme.txt | 0 rngesus.ts => To Sort/rngesus.ts | 0 share.ts => To Sort/share.ts | 0 {singularity => To Sort/singularity}/acceptFactionInvites.ts | 0 {singularity => To Sort/singularity}/applyForJobs.ts | 0 {singularity => To Sort/singularity}/applyToCompany.ts | 0 {singularity => To Sort/singularity}/buy/allPrograms.ts | 0 {singularity => To Sort/singularity}/buy/aug.ts | 0 {singularity => To Sort/singularity}/buy/core.ts | 0 {singularity => To Sort/singularity}/buy/maxNFG.ts | 0 {singularity => To Sort/singularity}/buy/program.ts | 0 {singularity => To Sort/singularity}/buy/ram.ts | 0 {singularity => To Sort/singularity}/buy/tor.ts | 0 {singularity => To Sort/singularity}/chooseWork.ts | 0 {singularity => To Sort/singularity}/connect.ts | 0 {singularity => To Sort/singularity}/doWork/company.ts | 0 {singularity => To Sort/singularity}/doWork/createProgram.ts | 0 {singularity => To Sort/singularity}/doWork/faction.ts | 0 {singularity => To Sort/singularity}/farmInt.ts | 0 {singularity => To Sort/singularity}/genInfo/companyPositions.ts | 0 {singularity => To Sort/singularity}/genInfo/companyRep.ts | 0 {singularity => To Sort/singularity}/genInfo/darkweb.ts | 0 {singularity => To Sort/singularity}/genInfo/factionEnemies.ts | 0 {singularity => To Sort/singularity}/get/aug/basePrice.ts | 0 {singularity => To Sort/singularity}/get/aug/factions.ts | 0 {singularity => To Sort/singularity}/get/aug/prereq.ts | 0 {singularity => To Sort/singularity}/get/aug/price.ts | 0 {singularity => To Sort/singularity}/get/aug/repReq.ts | 0 {singularity => To Sort/singularity}/get/aug/stats.ts | 0 {singularity => To Sort/singularity}/get/company/favor.ts | 0 {singularity => To Sort/singularity}/get/company/favorGain.ts | 0 {singularity => To Sort/singularity}/get/company/positionInfo.ts | 0 {singularity => To Sort/singularity}/get/company/positions.ts | 0 {singularity => To Sort/singularity}/get/company/rep.ts | 0 {singularity => To Sort/singularity}/get/currentServer.ts | 0 {singularity => To Sort/singularity}/get/currentWork.ts | 0 {singularity => To Sort/singularity}/get/darkweb/programCost.ts | 0 {singularity => To Sort/singularity}/get/darkweb/programs.ts | 0 {singularity => To Sort/singularity}/get/faction/enemies.ts | 0 {singularity => To Sort/singularity}/get/faction/favor.ts | 0 {singularity => To Sort/singularity}/get/faction/favorGain.ts | 0 {singularity => To Sort/singularity}/get/faction/inviteReqs.ts | 0 {singularity => To Sort/singularity}/get/faction/invites.ts | 0 {singularity => To Sort/singularity}/get/faction/rep.ts | 0 {singularity => To Sort/singularity}/get/faction/workTypes.ts | 0 {singularity => To Sort/singularity}/installAugs.ts | 0 {singularity => To Sort/singularity}/installBackdoor.ts | 0 {singularity => To Sort/singularity}/is/busy.ts | 0 {singularity => To Sort/singularity}/is/focused.ts | 0 {singularity => To Sort/singularity}/stem.ts | 0 {singularity => To Sort/singularity}/travel.ts | 0 {singularity => To Sort/singularity}/tryBackdoor.ts | 0 {singularity => To Sort/singularity}/upgradeHome/cores.ts | 0 {singularity => To Sort/singularity}/upgradeHome/ram.ts | 0 {sleeve => To Sort/sleeve}/genAugInfo.js | 0 {sleeve => To Sort/sleeve}/get/augPrice.js | 0 {sleeve => To Sort/sleeve}/get/augRepReq.js | 0 {sleeve => To Sort/sleeve}/get/augs.js | 0 {sleeve => To Sort/sleeve}/get/numSleeves.js | 0 {sleeve => To Sort/sleeve}/get/purchasableAugs.js | 0 {sleeve => To Sort/sleeve}/get/sleeve.js | 0 {sleeve => To Sort/sleeve}/setTo/commitCrime.js | 0 {sleeve => To Sort/sleeve}/setTo/companyWork.js | 0 {sleeve => To Sort/sleeve}/setTo/factionWork.js | 0 {sleeve => To Sort/sleeve}/setTo/gymWorkout.js | 0 {sleeve => To Sort/sleeve}/setTo/idle.js | 0 {sleeve => To Sort/sleeve}/setTo/shockRecovery.js | 0 {sleeve => To Sort/sleeve}/setTo/synchronize.js | 0 {sleeve => To Sort/sleeve}/setTo/universityCourse.js | 0 {sleeve => To Sort/sleeve}/travel.js | 0 {srv => To Sort/srv}/deploy.ts | 0 {srv => To Sort/srv}/get.js | 0 {srv => To Sort/srv}/purchase.ts | 0 {srv => To Sort/srv}/purchaseHackers.ts | 0 {srv => To Sort/srv}/rename.js | 0 {srv => To Sort/srv}/upgrade.ts | 0 {stock => To Sort/stock}/buy.ts | 0 {stock => To Sort/stock}/buyLoop.ts | 0 {stock => To Sort/stock}/deploy.ts | 0 {stock => To Sort/stock}/genSymbolInfo.ts | 0 {stock => To Sort/stock}/get/askPrice.ts | 0 {stock => To Sort/stock}/get/forecast.ts | 0 {stock => To Sort/stock}/get/maxShares.ts | 0 {stock => To Sort/stock}/get/organization.ts | 0 {stock => To Sort/stock}/get/poorManForecast.ts | 0 {stock => To Sort/stock}/get/position.ts | 0 {stock => To Sort/stock}/get/symbols.ts | 0 {stock => To Sort/stock}/get/volatility.ts | 0 {stock => To Sort/stock}/init.ts | 0 {stock => To Sort/stock}/init2.ts | 0 {stock => To Sort/stock}/launch.ts | 0 {stock => To Sort/stock}/purchase/4s.ts | 0 {stock => To Sort/stock}/purchase/4sAPI.ts | 0 {stock => To Sort/stock}/purchase/tixAPI.ts | 0 {stock => To Sort/stock}/purchase/wseAccount.ts | 0 {stock => To Sort/stock}/sell.ts | 0 {stock => To Sort/stock}/sellLoop.ts | 0 {stock => To Sort/stock}/sellShort.ts | 0 {stock => To Sort/stock}/updateSymbolInfo.ts | 0 241 files changed, 0 insertions(+), 0 deletions(-) rename autoexec.ts => To Sort/autoexec.ts (100%) rename {bladeburner => To Sort/bladeburner}/chooseCity.ts (100%) rename {bladeburner => To Sort/bladeburner}/forge.ts (100%) rename {bladeburner => To Sort/bladeburner}/genInfo/actions.ts (100%) rename {bladeburner => To Sort/bladeburner}/genInfo/cities.ts (100%) rename {bladeburner => To Sort/bladeburner}/genInfo/player.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/action/autoLevel.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/action/countRemaining.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/action/currentLevel.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/action/currentTime.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/action/estSuccessChance.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/action/maxLevel.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/action/repGain.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/action/successes.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/action/time.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/blackOp/next.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/blackOp/rank.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/city/chaos.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/city/communities.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/city/current.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/city/estPop.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/rank.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/skill/level.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/skill/points.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/skill/upgradeCost.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/stamina.ts (100%) rename {bladeburner => To Sort/bladeburner}/get/teamSize.ts (100%) rename {bladeburner => To Sort/bladeburner}/join/division.js (100%) rename {bladeburner => To Sort/bladeburner}/join/faction.js (100%) rename {bladeburner => To Sort/bladeburner}/launch.ts (100%) rename {bladeburner => To Sort/bladeburner}/manageSkills.ts (100%) rename {bladeburner => To Sort/bladeburner}/travel.ts (100%) rename {bladeburner => To Sort/bladeburner}/upgradeSkill.ts (100%) rename {bladeburner => To Sort/bladeburner}/util.ts (100%) rename {contract => To Sort/contract}/2color.ts (100%) rename {contract => To Sort/contract}/arrayJumpI.ts (100%) rename {contract => To Sort/contract}/arrayJumpII.ts (100%) rename {contract => To Sort/contract}/attempt.js (100%) rename {contract => To Sort/contract}/caesar.ts (100%) rename {contract => To Sort/contract}/get/data.ts (100%) rename {contract => To Sort/contract}/get/type.ts (100%) rename {contract => To Sort/contract}/gridPathsI.js (100%) rename {contract => To Sort/contract}/gridPathsII.ts (100%) rename {contract => To Sort/contract}/intPartition.ts (100%) rename {contract => To Sort/contract}/intPartitionSet.js (100%) rename {contract => To Sort/contract}/intToHamming.js (100%) rename {contract => To Sort/contract}/ipAddr.ts (100%) rename {contract => To Sort/contract}/lpf.js (100%) rename {contract => To Sort/contract}/lzCompression.js (100%) rename {contract => To Sort/contract}/lzDecompression.ts (100%) rename {contract => To Sort/contract}/mathExprs.js (100%) rename {contract => To Sort/contract}/mergeIntervals.ts (100%) rename {contract => To Sort/contract}/rleCompression.ts (100%) rename {contract => To Sort/contract}/shortestGridPath.ts (100%) rename {contract => To Sort/contract}/solveLoop.tsx (100%) rename {contract => To Sort/contract}/spiralize.ts (100%) rename {contract => To Sort/contract}/sqrt.ts (100%) rename {contract => To Sort/contract}/stockTraderI.ts (100%) rename {contract => To Sort/contract}/stockTraderII.ts (100%) rename {contract => To Sort/contract}/stockTraderIII.ts (100%) rename {contract => To Sort/contract}/stockTraderIV.ts (100%) rename {contract => To Sort/contract}/subarrayMaxSum.ts (100%) rename {contract => To Sort/contract}/triMinSum.ts (100%) rename {contract => To Sort/contract}/vigenere.ts (100%) rename {crack => To Sort/crack}/crack.ts (100%) rename {crack => To Sort/crack}/deployScripts.ts (100%) rename {crack => To Sort/crack}/getTargets.ts (100%) rename {crack => To Sort/crack}/tryNuke.ts (100%) rename {crack => To Sort/crack}/worm.js (100%) rename {daemons => To Sort/daemons}/deployDynamo.ts (100%) rename {daemons => To Sort/daemons}/deployExecutor.ts (100%) rename {daemons => To Sort/daemons}/dynamo.ts (100%) rename {daemons => To Sort/daemons}/executor.ts (100%) rename {debug => To Sort/debug}/monitorPort.ts (100%) rename deployShare.ts => To Sort/deployShare.ts (100%) rename {exploit => To Sort/exploit}/bypass.ts (100%) rename {exploit => To Sort/exploit}/devMenu.js (100%) rename {exploit => To Sort/exploit}/rainbow.ts (100%) rename {exploit => To Sort/exploit}/reality.js (100%) rename {exploit => To Sort/exploit}/tamper.ts (100%) rename {exploit => To Sort/exploit}/time.js (100%) rename {gang => To Sort/gang}/ascend.ts (100%) rename {gang => To Sort/gang}/ascendMember.ts (100%) rename {gang => To Sort/gang}/assign.js (100%) rename {gang => To Sort/gang}/canRecruitMember.ts (100%) rename {gang => To Sort/gang}/equip.js (100%) rename {gang => To Sort/gang}/genEquipmentInfo.js (100%) rename {gang => To Sort/gang}/genInfo/gang.ts (100%) rename {gang => To Sort/gang}/genInfo/members.ts (100%) rename {gang => To Sort/gang}/genInfo/otherGangs.ts (100%) rename {gang => To Sort/gang}/genTaskInfo.ts (100%) rename {gang => To Sort/gang}/get/gangInfo.ts (100%) rename {gang => To Sort/gang}/get/member/ascensionResult.ts (100%) rename {gang => To Sort/gang}/get/member/info.ts (100%) rename {gang => To Sort/gang}/get/member/names.ts (100%) rename {gang => To Sort/gang}/get/otherGangInfo.ts (100%) rename {gang => To Sort/gang}/info/equipment.json (100%) rename {gang => To Sort/gang}/info/tasks.json (100%) rename {gang => To Sort/gang}/kingpin.ts (100%) rename {gang => To Sort/gang}/launch.ts (100%) rename {gang => To Sort/gang}/launch2.ts (100%) rename {gang => To Sort/gang}/mode.ts (100%) rename {gang => To Sort/gang}/recruit.ts (100%) rename {gang => To Sort/gang}/recruitMember.ts (100%) rename {gang => To Sort/gang}/rename.ts (100%) rename {gang => To Sort/gang}/set/territoryWarfare.ts (100%) rename {go => To Sort/go}/getBoardState.js (100%) rename {go => To Sort/go}/getLiberties.js (100%) rename {go => To Sort/go}/move.js (100%) rename {go => To Sort/go}/play.js (100%) rename {hack => To Sort/hack}/calcHackParams.js (100%) rename {hack => To Sort/hack}/deployShotgunner.ts (100%) rename {hack => To Sort/hack}/grow.ts (100%) rename {hack => To Sort/hack}/hack.ts (100%) rename {hack => To Sort/hack}/shotgunner.js (100%) rename {hack => To Sort/hack}/weaken.ts (100%) rename {hacknet => To Sort/hacknet}/deploy.ts (100%) rename {hacknet => To Sort/hacknet}/launch.ts (100%) rename {hacknet => To Sort/hacknet}/naiveLoop.js (100%) rename {hacknet => To Sort/hacknet}/nodeFormulasLoop.ts (100%) rename {info => To Sort/info}/BDTargets.js (100%) rename {info => To Sort/info}/contractList.js (100%) rename {info => To Sort/info}/contractTypes.js (100%) rename {info => To Sort/info}/crimeEV.tsx (100%) rename {info => To Sort/info}/infilList.js (100%) rename {info => To Sort/info}/karma.js (100%) rename {info => To Sort/info}/pathToServer.js (100%) rename {init => To Sort/init}/cleanup.ts (100%) rename {init => To Sort/init}/collectLit.ts (100%) rename {init => To Sort/init}/init.ts (100%) rename {init => To Sort/init}/init2.ts (100%) rename {lib => To Sort/lib}/deepScan.js (100%) rename {lib => To Sort/lib}/genInfo/gameState.ts (100%) rename {lib => To Sort/lib}/genInfo/reset.ts (100%) rename {lib => To Sort/lib}/get/bitNodeMultipliers.ts (100%) rename {lib => To Sort/lib}/get/moneySources.ts (100%) rename {lib => To Sort/lib}/get/player.ts (100%) rename {lib => To Sort/lib}/nsEnums.ts (100%) rename {lib => To Sort/lib}/rm.ts (100%) rename {lib => To Sort/lib}/tick.js (100%) rename {lib => To Sort/lib}/util.ts (100%) rename queenAdministrator.ts => To Sort/queenAdministrator.ts (100%) rename readme.txt => To Sort/readme.txt (100%) rename rngesus.ts => To Sort/rngesus.ts (100%) rename share.ts => To Sort/share.ts (100%) rename {singularity => To Sort/singularity}/acceptFactionInvites.ts (100%) rename {singularity => To Sort/singularity}/applyForJobs.ts (100%) rename {singularity => To Sort/singularity}/applyToCompany.ts (100%) rename {singularity => To Sort/singularity}/buy/allPrograms.ts (100%) rename {singularity => To Sort/singularity}/buy/aug.ts (100%) rename {singularity => To Sort/singularity}/buy/core.ts (100%) rename {singularity => To Sort/singularity}/buy/maxNFG.ts (100%) rename {singularity => To Sort/singularity}/buy/program.ts (100%) rename {singularity => To Sort/singularity}/buy/ram.ts (100%) rename {singularity => To Sort/singularity}/buy/tor.ts (100%) rename {singularity => To Sort/singularity}/chooseWork.ts (100%) rename {singularity => To Sort/singularity}/connect.ts (100%) rename {singularity => To Sort/singularity}/doWork/company.ts (100%) rename {singularity => To Sort/singularity}/doWork/createProgram.ts (100%) rename {singularity => To Sort/singularity}/doWork/faction.ts (100%) rename {singularity => To Sort/singularity}/farmInt.ts (100%) rename {singularity => To Sort/singularity}/genInfo/companyPositions.ts (100%) rename {singularity => To Sort/singularity}/genInfo/companyRep.ts (100%) rename {singularity => To Sort/singularity}/genInfo/darkweb.ts (100%) rename {singularity => To Sort/singularity}/genInfo/factionEnemies.ts (100%) rename {singularity => To Sort/singularity}/get/aug/basePrice.ts (100%) rename {singularity => To Sort/singularity}/get/aug/factions.ts (100%) rename {singularity => To Sort/singularity}/get/aug/prereq.ts (100%) rename {singularity => To Sort/singularity}/get/aug/price.ts (100%) rename {singularity => To Sort/singularity}/get/aug/repReq.ts (100%) rename {singularity => To Sort/singularity}/get/aug/stats.ts (100%) rename {singularity => To Sort/singularity}/get/company/favor.ts (100%) rename {singularity => To Sort/singularity}/get/company/favorGain.ts (100%) rename {singularity => To Sort/singularity}/get/company/positionInfo.ts (100%) rename {singularity => To Sort/singularity}/get/company/positions.ts (100%) rename {singularity => To Sort/singularity}/get/company/rep.ts (100%) rename {singularity => To Sort/singularity}/get/currentServer.ts (100%) rename {singularity => To Sort/singularity}/get/currentWork.ts (100%) rename {singularity => To Sort/singularity}/get/darkweb/programCost.ts (100%) rename {singularity => To Sort/singularity}/get/darkweb/programs.ts (100%) rename {singularity => To Sort/singularity}/get/faction/enemies.ts (100%) rename {singularity => To Sort/singularity}/get/faction/favor.ts (100%) rename {singularity => To Sort/singularity}/get/faction/favorGain.ts (100%) rename {singularity => To Sort/singularity}/get/faction/inviteReqs.ts (100%) rename {singularity => To Sort/singularity}/get/faction/invites.ts (100%) rename {singularity => To Sort/singularity}/get/faction/rep.ts (100%) rename {singularity => To Sort/singularity}/get/faction/workTypes.ts (100%) rename {singularity => To Sort/singularity}/installAugs.ts (100%) rename {singularity => To Sort/singularity}/installBackdoor.ts (100%) rename {singularity => To Sort/singularity}/is/busy.ts (100%) rename {singularity => To Sort/singularity}/is/focused.ts (100%) rename {singularity => To Sort/singularity}/stem.ts (100%) rename {singularity => To Sort/singularity}/travel.ts (100%) rename {singularity => To Sort/singularity}/tryBackdoor.ts (100%) rename {singularity => To Sort/singularity}/upgradeHome/cores.ts (100%) rename {singularity => To Sort/singularity}/upgradeHome/ram.ts (100%) rename {sleeve => To Sort/sleeve}/genAugInfo.js (100%) rename {sleeve => To Sort/sleeve}/get/augPrice.js (100%) rename {sleeve => To Sort/sleeve}/get/augRepReq.js (100%) rename {sleeve => To Sort/sleeve}/get/augs.js (100%) rename {sleeve => To Sort/sleeve}/get/numSleeves.js (100%) rename {sleeve => To Sort/sleeve}/get/purchasableAugs.js (100%) rename {sleeve => To Sort/sleeve}/get/sleeve.js (100%) rename {sleeve => To Sort/sleeve}/setTo/commitCrime.js (100%) rename {sleeve => To Sort/sleeve}/setTo/companyWork.js (100%) rename {sleeve => To Sort/sleeve}/setTo/factionWork.js (100%) rename {sleeve => To Sort/sleeve}/setTo/gymWorkout.js (100%) rename {sleeve => To Sort/sleeve}/setTo/idle.js (100%) rename {sleeve => To Sort/sleeve}/setTo/shockRecovery.js (100%) rename {sleeve => To Sort/sleeve}/setTo/synchronize.js (100%) rename {sleeve => To Sort/sleeve}/setTo/universityCourse.js (100%) rename {sleeve => To Sort/sleeve}/travel.js (100%) rename {srv => To Sort/srv}/deploy.ts (100%) rename {srv => To Sort/srv}/get.js (100%) rename {srv => To Sort/srv}/purchase.ts (100%) rename {srv => To Sort/srv}/purchaseHackers.ts (100%) rename {srv => To Sort/srv}/rename.js (100%) rename {srv => To Sort/srv}/upgrade.ts (100%) rename {stock => To Sort/stock}/buy.ts (100%) rename {stock => To Sort/stock}/buyLoop.ts (100%) rename {stock => To Sort/stock}/deploy.ts (100%) rename {stock => To Sort/stock}/genSymbolInfo.ts (100%) rename {stock => To Sort/stock}/get/askPrice.ts (100%) rename {stock => To Sort/stock}/get/forecast.ts (100%) rename {stock => To Sort/stock}/get/maxShares.ts (100%) rename {stock => To Sort/stock}/get/organization.ts (100%) rename {stock => To Sort/stock}/get/poorManForecast.ts (100%) rename {stock => To Sort/stock}/get/position.ts (100%) rename {stock => To Sort/stock}/get/symbols.ts (100%) rename {stock => To Sort/stock}/get/volatility.ts (100%) rename {stock => To Sort/stock}/init.ts (100%) rename {stock => To Sort/stock}/init2.ts (100%) rename {stock => To Sort/stock}/launch.ts (100%) rename {stock => To Sort/stock}/purchase/4s.ts (100%) rename {stock => To Sort/stock}/purchase/4sAPI.ts (100%) rename {stock => To Sort/stock}/purchase/tixAPI.ts (100%) rename {stock => To Sort/stock}/purchase/wseAccount.ts (100%) rename {stock => To Sort/stock}/sell.ts (100%) rename {stock => To Sort/stock}/sellLoop.ts (100%) rename {stock => To Sort/stock}/sellShort.ts (100%) rename {stock => To Sort/stock}/updateSymbolInfo.ts (100%) diff --git a/autoexec.ts b/To Sort/autoexec.ts similarity index 100% rename from autoexec.ts rename to To Sort/autoexec.ts diff --git a/bladeburner/chooseCity.ts b/To Sort/bladeburner/chooseCity.ts similarity index 100% rename from bladeburner/chooseCity.ts rename to To Sort/bladeburner/chooseCity.ts diff --git a/bladeburner/forge.ts b/To Sort/bladeburner/forge.ts similarity index 100% rename from bladeburner/forge.ts rename to To Sort/bladeburner/forge.ts diff --git a/bladeburner/genInfo/actions.ts b/To Sort/bladeburner/genInfo/actions.ts similarity index 100% rename from bladeburner/genInfo/actions.ts rename to To Sort/bladeburner/genInfo/actions.ts diff --git a/bladeburner/genInfo/cities.ts b/To Sort/bladeburner/genInfo/cities.ts similarity index 100% rename from bladeburner/genInfo/cities.ts rename to To Sort/bladeburner/genInfo/cities.ts diff --git a/bladeburner/genInfo/player.ts b/To Sort/bladeburner/genInfo/player.ts similarity index 100% rename from bladeburner/genInfo/player.ts rename to To Sort/bladeburner/genInfo/player.ts diff --git a/bladeburner/get/action/autoLevel.ts b/To Sort/bladeburner/get/action/autoLevel.ts similarity index 100% rename from bladeburner/get/action/autoLevel.ts rename to To Sort/bladeburner/get/action/autoLevel.ts diff --git a/bladeburner/get/action/countRemaining.ts b/To Sort/bladeburner/get/action/countRemaining.ts similarity index 100% rename from bladeburner/get/action/countRemaining.ts rename to To Sort/bladeburner/get/action/countRemaining.ts diff --git a/bladeburner/get/action/currentLevel.ts b/To Sort/bladeburner/get/action/currentLevel.ts similarity index 100% rename from bladeburner/get/action/currentLevel.ts rename to To Sort/bladeburner/get/action/currentLevel.ts diff --git a/bladeburner/get/action/currentTime.ts b/To Sort/bladeburner/get/action/currentTime.ts similarity index 100% rename from bladeburner/get/action/currentTime.ts rename to To Sort/bladeburner/get/action/currentTime.ts diff --git a/bladeburner/get/action/estSuccessChance.ts b/To Sort/bladeburner/get/action/estSuccessChance.ts similarity index 100% rename from bladeburner/get/action/estSuccessChance.ts rename to To Sort/bladeburner/get/action/estSuccessChance.ts diff --git a/bladeburner/get/action/maxLevel.ts b/To Sort/bladeburner/get/action/maxLevel.ts similarity index 100% rename from bladeburner/get/action/maxLevel.ts rename to To Sort/bladeburner/get/action/maxLevel.ts diff --git a/bladeburner/get/action/repGain.ts b/To Sort/bladeburner/get/action/repGain.ts similarity index 100% rename from bladeburner/get/action/repGain.ts rename to To Sort/bladeburner/get/action/repGain.ts diff --git a/bladeburner/get/action/successes.ts b/To Sort/bladeburner/get/action/successes.ts similarity index 100% rename from bladeburner/get/action/successes.ts rename to To Sort/bladeburner/get/action/successes.ts diff --git a/bladeburner/get/action/time.ts b/To Sort/bladeburner/get/action/time.ts similarity index 100% rename from bladeburner/get/action/time.ts rename to To Sort/bladeburner/get/action/time.ts diff --git a/bladeburner/get/blackOp/next.ts b/To Sort/bladeburner/get/blackOp/next.ts similarity index 100% rename from bladeburner/get/blackOp/next.ts rename to To Sort/bladeburner/get/blackOp/next.ts diff --git a/bladeburner/get/blackOp/rank.ts b/To Sort/bladeburner/get/blackOp/rank.ts similarity index 100% rename from bladeburner/get/blackOp/rank.ts rename to To Sort/bladeburner/get/blackOp/rank.ts diff --git a/bladeburner/get/city/chaos.ts b/To Sort/bladeburner/get/city/chaos.ts similarity index 100% rename from bladeburner/get/city/chaos.ts rename to To Sort/bladeburner/get/city/chaos.ts diff --git a/bladeburner/get/city/communities.ts b/To Sort/bladeburner/get/city/communities.ts similarity index 100% rename from bladeburner/get/city/communities.ts rename to To Sort/bladeburner/get/city/communities.ts diff --git a/bladeburner/get/city/current.ts b/To Sort/bladeburner/get/city/current.ts similarity index 100% rename from bladeburner/get/city/current.ts rename to To Sort/bladeburner/get/city/current.ts diff --git a/bladeburner/get/city/estPop.ts b/To Sort/bladeburner/get/city/estPop.ts similarity index 100% rename from bladeburner/get/city/estPop.ts rename to To Sort/bladeburner/get/city/estPop.ts diff --git a/bladeburner/get/rank.ts b/To Sort/bladeburner/get/rank.ts similarity index 100% rename from bladeburner/get/rank.ts rename to To Sort/bladeburner/get/rank.ts diff --git a/bladeburner/get/skill/level.ts b/To Sort/bladeburner/get/skill/level.ts similarity index 100% rename from bladeburner/get/skill/level.ts rename to To Sort/bladeburner/get/skill/level.ts diff --git a/bladeburner/get/skill/points.ts b/To Sort/bladeburner/get/skill/points.ts similarity index 100% rename from bladeburner/get/skill/points.ts rename to To Sort/bladeburner/get/skill/points.ts diff --git a/bladeburner/get/skill/upgradeCost.ts b/To Sort/bladeburner/get/skill/upgradeCost.ts similarity index 100% rename from bladeburner/get/skill/upgradeCost.ts rename to To Sort/bladeburner/get/skill/upgradeCost.ts diff --git a/bladeburner/get/stamina.ts b/To Sort/bladeburner/get/stamina.ts similarity index 100% rename from bladeburner/get/stamina.ts rename to To Sort/bladeburner/get/stamina.ts diff --git a/bladeburner/get/teamSize.ts b/To Sort/bladeburner/get/teamSize.ts similarity index 100% rename from bladeburner/get/teamSize.ts rename to To Sort/bladeburner/get/teamSize.ts diff --git a/bladeburner/join/division.js b/To Sort/bladeburner/join/division.js similarity index 100% rename from bladeburner/join/division.js rename to To Sort/bladeburner/join/division.js diff --git a/bladeburner/join/faction.js b/To Sort/bladeburner/join/faction.js similarity index 100% rename from bladeburner/join/faction.js rename to To Sort/bladeburner/join/faction.js diff --git a/bladeburner/launch.ts b/To Sort/bladeburner/launch.ts similarity index 100% rename from bladeburner/launch.ts rename to To Sort/bladeburner/launch.ts diff --git a/bladeburner/manageSkills.ts b/To Sort/bladeburner/manageSkills.ts similarity index 100% rename from bladeburner/manageSkills.ts rename to To Sort/bladeburner/manageSkills.ts diff --git a/bladeburner/travel.ts b/To Sort/bladeburner/travel.ts similarity index 100% rename from bladeburner/travel.ts rename to To Sort/bladeburner/travel.ts diff --git a/bladeburner/upgradeSkill.ts b/To Sort/bladeburner/upgradeSkill.ts similarity index 100% rename from bladeburner/upgradeSkill.ts rename to To Sort/bladeburner/upgradeSkill.ts diff --git a/bladeburner/util.ts b/To Sort/bladeburner/util.ts similarity index 100% rename from bladeburner/util.ts rename to To Sort/bladeburner/util.ts diff --git a/contract/2color.ts b/To Sort/contract/2color.ts similarity index 100% rename from contract/2color.ts rename to To Sort/contract/2color.ts diff --git a/contract/arrayJumpI.ts b/To Sort/contract/arrayJumpI.ts similarity index 100% rename from contract/arrayJumpI.ts rename to To Sort/contract/arrayJumpI.ts diff --git a/contract/arrayJumpII.ts b/To Sort/contract/arrayJumpII.ts similarity index 100% rename from contract/arrayJumpII.ts rename to To Sort/contract/arrayJumpII.ts diff --git a/contract/attempt.js b/To Sort/contract/attempt.js similarity index 100% rename from contract/attempt.js rename to To Sort/contract/attempt.js diff --git a/contract/caesar.ts b/To Sort/contract/caesar.ts similarity index 100% rename from contract/caesar.ts rename to To Sort/contract/caesar.ts diff --git a/contract/get/data.ts b/To Sort/contract/get/data.ts similarity index 100% rename from contract/get/data.ts rename to To Sort/contract/get/data.ts diff --git a/contract/get/type.ts b/To Sort/contract/get/type.ts similarity index 100% rename from contract/get/type.ts rename to To Sort/contract/get/type.ts diff --git a/contract/gridPathsI.js b/To Sort/contract/gridPathsI.js similarity index 100% rename from contract/gridPathsI.js rename to To Sort/contract/gridPathsI.js diff --git a/contract/gridPathsII.ts b/To Sort/contract/gridPathsII.ts similarity index 100% rename from contract/gridPathsII.ts rename to To Sort/contract/gridPathsII.ts diff --git a/contract/intPartition.ts b/To Sort/contract/intPartition.ts similarity index 100% rename from contract/intPartition.ts rename to To Sort/contract/intPartition.ts diff --git a/contract/intPartitionSet.js b/To Sort/contract/intPartitionSet.js similarity index 100% rename from contract/intPartitionSet.js rename to To Sort/contract/intPartitionSet.js diff --git a/contract/intToHamming.js b/To Sort/contract/intToHamming.js similarity index 100% rename from contract/intToHamming.js rename to To Sort/contract/intToHamming.js diff --git a/contract/ipAddr.ts b/To Sort/contract/ipAddr.ts similarity index 100% rename from contract/ipAddr.ts rename to To Sort/contract/ipAddr.ts diff --git a/contract/lpf.js b/To Sort/contract/lpf.js similarity index 100% rename from contract/lpf.js rename to To Sort/contract/lpf.js diff --git a/contract/lzCompression.js b/To Sort/contract/lzCompression.js similarity index 100% rename from contract/lzCompression.js rename to To Sort/contract/lzCompression.js diff --git a/contract/lzDecompression.ts b/To Sort/contract/lzDecompression.ts similarity index 100% rename from contract/lzDecompression.ts rename to To Sort/contract/lzDecompression.ts diff --git a/contract/mathExprs.js b/To Sort/contract/mathExprs.js similarity index 100% rename from contract/mathExprs.js rename to To Sort/contract/mathExprs.js diff --git a/contract/mergeIntervals.ts b/To Sort/contract/mergeIntervals.ts similarity index 100% rename from contract/mergeIntervals.ts rename to To Sort/contract/mergeIntervals.ts diff --git a/contract/rleCompression.ts b/To Sort/contract/rleCompression.ts similarity index 100% rename from contract/rleCompression.ts rename to To Sort/contract/rleCompression.ts diff --git a/contract/shortestGridPath.ts b/To Sort/contract/shortestGridPath.ts similarity index 100% rename from contract/shortestGridPath.ts rename to To Sort/contract/shortestGridPath.ts diff --git a/contract/solveLoop.tsx b/To Sort/contract/solveLoop.tsx similarity index 100% rename from contract/solveLoop.tsx rename to To Sort/contract/solveLoop.tsx diff --git a/contract/spiralize.ts b/To Sort/contract/spiralize.ts similarity index 100% rename from contract/spiralize.ts rename to To Sort/contract/spiralize.ts diff --git a/contract/sqrt.ts b/To Sort/contract/sqrt.ts similarity index 100% rename from contract/sqrt.ts rename to To Sort/contract/sqrt.ts diff --git a/contract/stockTraderI.ts b/To Sort/contract/stockTraderI.ts similarity index 100% rename from contract/stockTraderI.ts rename to To Sort/contract/stockTraderI.ts diff --git a/contract/stockTraderII.ts b/To Sort/contract/stockTraderII.ts similarity index 100% rename from contract/stockTraderII.ts rename to To Sort/contract/stockTraderII.ts diff --git a/contract/stockTraderIII.ts b/To Sort/contract/stockTraderIII.ts similarity index 100% rename from contract/stockTraderIII.ts rename to To Sort/contract/stockTraderIII.ts diff --git a/contract/stockTraderIV.ts b/To Sort/contract/stockTraderIV.ts similarity index 100% rename from contract/stockTraderIV.ts rename to To Sort/contract/stockTraderIV.ts diff --git a/contract/subarrayMaxSum.ts b/To Sort/contract/subarrayMaxSum.ts similarity index 100% rename from contract/subarrayMaxSum.ts rename to To Sort/contract/subarrayMaxSum.ts diff --git a/contract/triMinSum.ts b/To Sort/contract/triMinSum.ts similarity index 100% rename from contract/triMinSum.ts rename to To Sort/contract/triMinSum.ts diff --git a/contract/vigenere.ts b/To Sort/contract/vigenere.ts similarity index 100% rename from contract/vigenere.ts rename to To Sort/contract/vigenere.ts diff --git a/crack/crack.ts b/To Sort/crack/crack.ts similarity index 100% rename from crack/crack.ts rename to To Sort/crack/crack.ts diff --git a/crack/deployScripts.ts b/To Sort/crack/deployScripts.ts similarity index 100% rename from crack/deployScripts.ts rename to To Sort/crack/deployScripts.ts diff --git a/crack/getTargets.ts b/To Sort/crack/getTargets.ts similarity index 100% rename from crack/getTargets.ts rename to To Sort/crack/getTargets.ts diff --git a/crack/tryNuke.ts b/To Sort/crack/tryNuke.ts similarity index 100% rename from crack/tryNuke.ts rename to To Sort/crack/tryNuke.ts diff --git a/crack/worm.js b/To Sort/crack/worm.js similarity index 100% rename from crack/worm.js rename to To Sort/crack/worm.js diff --git a/daemons/deployDynamo.ts b/To Sort/daemons/deployDynamo.ts similarity index 100% rename from daemons/deployDynamo.ts rename to To Sort/daemons/deployDynamo.ts diff --git a/daemons/deployExecutor.ts b/To Sort/daemons/deployExecutor.ts similarity index 100% rename from daemons/deployExecutor.ts rename to To Sort/daemons/deployExecutor.ts diff --git a/daemons/dynamo.ts b/To Sort/daemons/dynamo.ts similarity index 100% rename from daemons/dynamo.ts rename to To Sort/daemons/dynamo.ts diff --git a/daemons/executor.ts b/To Sort/daemons/executor.ts similarity index 100% rename from daemons/executor.ts rename to To Sort/daemons/executor.ts diff --git a/debug/monitorPort.ts b/To Sort/debug/monitorPort.ts similarity index 100% rename from debug/monitorPort.ts rename to To Sort/debug/monitorPort.ts diff --git a/deployShare.ts b/To Sort/deployShare.ts similarity index 100% rename from deployShare.ts rename to To Sort/deployShare.ts diff --git a/exploit/bypass.ts b/To Sort/exploit/bypass.ts similarity index 100% rename from exploit/bypass.ts rename to To Sort/exploit/bypass.ts diff --git a/exploit/devMenu.js b/To Sort/exploit/devMenu.js similarity index 100% rename from exploit/devMenu.js rename to To Sort/exploit/devMenu.js diff --git a/exploit/rainbow.ts b/To Sort/exploit/rainbow.ts similarity index 100% rename from exploit/rainbow.ts rename to To Sort/exploit/rainbow.ts diff --git a/exploit/reality.js b/To Sort/exploit/reality.js similarity index 100% rename from exploit/reality.js rename to To Sort/exploit/reality.js diff --git a/exploit/tamper.ts b/To Sort/exploit/tamper.ts similarity index 100% rename from exploit/tamper.ts rename to To Sort/exploit/tamper.ts diff --git a/exploit/time.js b/To Sort/exploit/time.js similarity index 100% rename from exploit/time.js rename to To Sort/exploit/time.js diff --git a/gang/ascend.ts b/To Sort/gang/ascend.ts similarity index 100% rename from gang/ascend.ts rename to To Sort/gang/ascend.ts diff --git a/gang/ascendMember.ts b/To Sort/gang/ascendMember.ts similarity index 100% rename from gang/ascendMember.ts rename to To Sort/gang/ascendMember.ts diff --git a/gang/assign.js b/To Sort/gang/assign.js similarity index 100% rename from gang/assign.js rename to To Sort/gang/assign.js diff --git a/gang/canRecruitMember.ts b/To Sort/gang/canRecruitMember.ts similarity index 100% rename from gang/canRecruitMember.ts rename to To Sort/gang/canRecruitMember.ts diff --git a/gang/equip.js b/To Sort/gang/equip.js similarity index 100% rename from gang/equip.js rename to To Sort/gang/equip.js diff --git a/gang/genEquipmentInfo.js b/To Sort/gang/genEquipmentInfo.js similarity index 100% rename from gang/genEquipmentInfo.js rename to To Sort/gang/genEquipmentInfo.js diff --git a/gang/genInfo/gang.ts b/To Sort/gang/genInfo/gang.ts similarity index 100% rename from gang/genInfo/gang.ts rename to To Sort/gang/genInfo/gang.ts diff --git a/gang/genInfo/members.ts b/To Sort/gang/genInfo/members.ts similarity index 100% rename from gang/genInfo/members.ts rename to To Sort/gang/genInfo/members.ts diff --git a/gang/genInfo/otherGangs.ts b/To Sort/gang/genInfo/otherGangs.ts similarity index 100% rename from gang/genInfo/otherGangs.ts rename to To Sort/gang/genInfo/otherGangs.ts diff --git a/gang/genTaskInfo.ts b/To Sort/gang/genTaskInfo.ts similarity index 100% rename from gang/genTaskInfo.ts rename to To Sort/gang/genTaskInfo.ts diff --git a/gang/get/gangInfo.ts b/To Sort/gang/get/gangInfo.ts similarity index 100% rename from gang/get/gangInfo.ts rename to To Sort/gang/get/gangInfo.ts diff --git a/gang/get/member/ascensionResult.ts b/To Sort/gang/get/member/ascensionResult.ts similarity index 100% rename from gang/get/member/ascensionResult.ts rename to To Sort/gang/get/member/ascensionResult.ts diff --git a/gang/get/member/info.ts b/To Sort/gang/get/member/info.ts similarity index 100% rename from gang/get/member/info.ts rename to To Sort/gang/get/member/info.ts diff --git a/gang/get/member/names.ts b/To Sort/gang/get/member/names.ts similarity index 100% rename from gang/get/member/names.ts rename to To Sort/gang/get/member/names.ts diff --git a/gang/get/otherGangInfo.ts b/To Sort/gang/get/otherGangInfo.ts similarity index 100% rename from gang/get/otherGangInfo.ts rename to To Sort/gang/get/otherGangInfo.ts diff --git a/gang/info/equipment.json b/To Sort/gang/info/equipment.json similarity index 100% rename from gang/info/equipment.json rename to To Sort/gang/info/equipment.json diff --git a/gang/info/tasks.json b/To Sort/gang/info/tasks.json similarity index 100% rename from gang/info/tasks.json rename to To Sort/gang/info/tasks.json diff --git a/gang/kingpin.ts b/To Sort/gang/kingpin.ts similarity index 100% rename from gang/kingpin.ts rename to To Sort/gang/kingpin.ts diff --git a/gang/launch.ts b/To Sort/gang/launch.ts similarity index 100% rename from gang/launch.ts rename to To Sort/gang/launch.ts diff --git a/gang/launch2.ts b/To Sort/gang/launch2.ts similarity index 100% rename from gang/launch2.ts rename to To Sort/gang/launch2.ts diff --git a/gang/mode.ts b/To Sort/gang/mode.ts similarity index 100% rename from gang/mode.ts rename to To Sort/gang/mode.ts diff --git a/gang/recruit.ts b/To Sort/gang/recruit.ts similarity index 100% rename from gang/recruit.ts rename to To Sort/gang/recruit.ts diff --git a/gang/recruitMember.ts b/To Sort/gang/recruitMember.ts similarity index 100% rename from gang/recruitMember.ts rename to To Sort/gang/recruitMember.ts diff --git a/gang/rename.ts b/To Sort/gang/rename.ts similarity index 100% rename from gang/rename.ts rename to To Sort/gang/rename.ts diff --git a/gang/set/territoryWarfare.ts b/To Sort/gang/set/territoryWarfare.ts similarity index 100% rename from gang/set/territoryWarfare.ts rename to To Sort/gang/set/territoryWarfare.ts diff --git a/go/getBoardState.js b/To Sort/go/getBoardState.js similarity index 100% rename from go/getBoardState.js rename to To Sort/go/getBoardState.js diff --git a/go/getLiberties.js b/To Sort/go/getLiberties.js similarity index 100% rename from go/getLiberties.js rename to To Sort/go/getLiberties.js diff --git a/go/move.js b/To Sort/go/move.js similarity index 100% rename from go/move.js rename to To Sort/go/move.js diff --git a/go/play.js b/To Sort/go/play.js similarity index 100% rename from go/play.js rename to To Sort/go/play.js diff --git a/hack/calcHackParams.js b/To Sort/hack/calcHackParams.js similarity index 100% rename from hack/calcHackParams.js rename to To Sort/hack/calcHackParams.js diff --git a/hack/deployShotgunner.ts b/To Sort/hack/deployShotgunner.ts similarity index 100% rename from hack/deployShotgunner.ts rename to To Sort/hack/deployShotgunner.ts diff --git a/hack/grow.ts b/To Sort/hack/grow.ts similarity index 100% rename from hack/grow.ts rename to To Sort/hack/grow.ts diff --git a/hack/hack.ts b/To Sort/hack/hack.ts similarity index 100% rename from hack/hack.ts rename to To Sort/hack/hack.ts diff --git a/hack/shotgunner.js b/To Sort/hack/shotgunner.js similarity index 100% rename from hack/shotgunner.js rename to To Sort/hack/shotgunner.js diff --git a/hack/weaken.ts b/To Sort/hack/weaken.ts similarity index 100% rename from hack/weaken.ts rename to To Sort/hack/weaken.ts diff --git a/hacknet/deploy.ts b/To Sort/hacknet/deploy.ts similarity index 100% rename from hacknet/deploy.ts rename to To Sort/hacknet/deploy.ts diff --git a/hacknet/launch.ts b/To Sort/hacknet/launch.ts similarity index 100% rename from hacknet/launch.ts rename to To Sort/hacknet/launch.ts diff --git a/hacknet/naiveLoop.js b/To Sort/hacknet/naiveLoop.js similarity index 100% rename from hacknet/naiveLoop.js rename to To Sort/hacknet/naiveLoop.js diff --git a/hacknet/nodeFormulasLoop.ts b/To Sort/hacknet/nodeFormulasLoop.ts similarity index 100% rename from hacknet/nodeFormulasLoop.ts rename to To Sort/hacknet/nodeFormulasLoop.ts diff --git a/info/BDTargets.js b/To Sort/info/BDTargets.js similarity index 100% rename from info/BDTargets.js rename to To Sort/info/BDTargets.js diff --git a/info/contractList.js b/To Sort/info/contractList.js similarity index 100% rename from info/contractList.js rename to To Sort/info/contractList.js diff --git a/info/contractTypes.js b/To Sort/info/contractTypes.js similarity index 100% rename from info/contractTypes.js rename to To Sort/info/contractTypes.js diff --git a/info/crimeEV.tsx b/To Sort/info/crimeEV.tsx similarity index 100% rename from info/crimeEV.tsx rename to To Sort/info/crimeEV.tsx diff --git a/info/infilList.js b/To Sort/info/infilList.js similarity index 100% rename from info/infilList.js rename to To Sort/info/infilList.js diff --git a/info/karma.js b/To Sort/info/karma.js similarity index 100% rename from info/karma.js rename to To Sort/info/karma.js diff --git a/info/pathToServer.js b/To Sort/info/pathToServer.js similarity index 100% rename from info/pathToServer.js rename to To Sort/info/pathToServer.js diff --git a/init/cleanup.ts b/To Sort/init/cleanup.ts similarity index 100% rename from init/cleanup.ts rename to To Sort/init/cleanup.ts diff --git a/init/collectLit.ts b/To Sort/init/collectLit.ts similarity index 100% rename from init/collectLit.ts rename to To Sort/init/collectLit.ts diff --git a/init/init.ts b/To Sort/init/init.ts similarity index 100% rename from init/init.ts rename to To Sort/init/init.ts diff --git a/init/init2.ts b/To Sort/init/init2.ts similarity index 100% rename from init/init2.ts rename to To Sort/init/init2.ts diff --git a/lib/deepScan.js b/To Sort/lib/deepScan.js similarity index 100% rename from lib/deepScan.js rename to To Sort/lib/deepScan.js diff --git a/lib/genInfo/gameState.ts b/To Sort/lib/genInfo/gameState.ts similarity index 100% rename from lib/genInfo/gameState.ts rename to To Sort/lib/genInfo/gameState.ts diff --git a/lib/genInfo/reset.ts b/To Sort/lib/genInfo/reset.ts similarity index 100% rename from lib/genInfo/reset.ts rename to To Sort/lib/genInfo/reset.ts diff --git a/lib/get/bitNodeMultipliers.ts b/To Sort/lib/get/bitNodeMultipliers.ts similarity index 100% rename from lib/get/bitNodeMultipliers.ts rename to To Sort/lib/get/bitNodeMultipliers.ts diff --git a/lib/get/moneySources.ts b/To Sort/lib/get/moneySources.ts similarity index 100% rename from lib/get/moneySources.ts rename to To Sort/lib/get/moneySources.ts diff --git a/lib/get/player.ts b/To Sort/lib/get/player.ts similarity index 100% rename from lib/get/player.ts rename to To Sort/lib/get/player.ts diff --git a/lib/nsEnums.ts b/To Sort/lib/nsEnums.ts similarity index 100% rename from lib/nsEnums.ts rename to To Sort/lib/nsEnums.ts diff --git a/lib/rm.ts b/To Sort/lib/rm.ts similarity index 100% rename from lib/rm.ts rename to To Sort/lib/rm.ts diff --git a/lib/tick.js b/To Sort/lib/tick.js similarity index 100% rename from lib/tick.js rename to To Sort/lib/tick.js diff --git a/lib/util.ts b/To Sort/lib/util.ts similarity index 100% rename from lib/util.ts rename to To Sort/lib/util.ts diff --git a/queenAdministrator.ts b/To Sort/queenAdministrator.ts similarity index 100% rename from queenAdministrator.ts rename to To Sort/queenAdministrator.ts diff --git a/readme.txt b/To Sort/readme.txt similarity index 100% rename from readme.txt rename to To Sort/readme.txt diff --git a/rngesus.ts b/To Sort/rngesus.ts similarity index 100% rename from rngesus.ts rename to To Sort/rngesus.ts diff --git a/share.ts b/To Sort/share.ts similarity index 100% rename from share.ts rename to To Sort/share.ts diff --git a/singularity/acceptFactionInvites.ts b/To Sort/singularity/acceptFactionInvites.ts similarity index 100% rename from singularity/acceptFactionInvites.ts rename to To Sort/singularity/acceptFactionInvites.ts diff --git a/singularity/applyForJobs.ts b/To Sort/singularity/applyForJobs.ts similarity index 100% rename from singularity/applyForJobs.ts rename to To Sort/singularity/applyForJobs.ts diff --git a/singularity/applyToCompany.ts b/To Sort/singularity/applyToCompany.ts similarity index 100% rename from singularity/applyToCompany.ts rename to To Sort/singularity/applyToCompany.ts diff --git a/singularity/buy/allPrograms.ts b/To Sort/singularity/buy/allPrograms.ts similarity index 100% rename from singularity/buy/allPrograms.ts rename to To Sort/singularity/buy/allPrograms.ts diff --git a/singularity/buy/aug.ts b/To Sort/singularity/buy/aug.ts similarity index 100% rename from singularity/buy/aug.ts rename to To Sort/singularity/buy/aug.ts diff --git a/singularity/buy/core.ts b/To Sort/singularity/buy/core.ts similarity index 100% rename from singularity/buy/core.ts rename to To Sort/singularity/buy/core.ts diff --git a/singularity/buy/maxNFG.ts b/To Sort/singularity/buy/maxNFG.ts similarity index 100% rename from singularity/buy/maxNFG.ts rename to To Sort/singularity/buy/maxNFG.ts diff --git a/singularity/buy/program.ts b/To Sort/singularity/buy/program.ts similarity index 100% rename from singularity/buy/program.ts rename to To Sort/singularity/buy/program.ts diff --git a/singularity/buy/ram.ts b/To Sort/singularity/buy/ram.ts similarity index 100% rename from singularity/buy/ram.ts rename to To Sort/singularity/buy/ram.ts diff --git a/singularity/buy/tor.ts b/To Sort/singularity/buy/tor.ts similarity index 100% rename from singularity/buy/tor.ts rename to To Sort/singularity/buy/tor.ts diff --git a/singularity/chooseWork.ts b/To Sort/singularity/chooseWork.ts similarity index 100% rename from singularity/chooseWork.ts rename to To Sort/singularity/chooseWork.ts diff --git a/singularity/connect.ts b/To Sort/singularity/connect.ts similarity index 100% rename from singularity/connect.ts rename to To Sort/singularity/connect.ts diff --git a/singularity/doWork/company.ts b/To Sort/singularity/doWork/company.ts similarity index 100% rename from singularity/doWork/company.ts rename to To Sort/singularity/doWork/company.ts diff --git a/singularity/doWork/createProgram.ts b/To Sort/singularity/doWork/createProgram.ts similarity index 100% rename from singularity/doWork/createProgram.ts rename to To Sort/singularity/doWork/createProgram.ts diff --git a/singularity/doWork/faction.ts b/To Sort/singularity/doWork/faction.ts similarity index 100% rename from singularity/doWork/faction.ts rename to To Sort/singularity/doWork/faction.ts diff --git a/singularity/farmInt.ts b/To Sort/singularity/farmInt.ts similarity index 100% rename from singularity/farmInt.ts rename to To Sort/singularity/farmInt.ts diff --git a/singularity/genInfo/companyPositions.ts b/To Sort/singularity/genInfo/companyPositions.ts similarity index 100% rename from singularity/genInfo/companyPositions.ts rename to To Sort/singularity/genInfo/companyPositions.ts diff --git a/singularity/genInfo/companyRep.ts b/To Sort/singularity/genInfo/companyRep.ts similarity index 100% rename from singularity/genInfo/companyRep.ts rename to To Sort/singularity/genInfo/companyRep.ts diff --git a/singularity/genInfo/darkweb.ts b/To Sort/singularity/genInfo/darkweb.ts similarity index 100% rename from singularity/genInfo/darkweb.ts rename to To Sort/singularity/genInfo/darkweb.ts diff --git a/singularity/genInfo/factionEnemies.ts b/To Sort/singularity/genInfo/factionEnemies.ts similarity index 100% rename from singularity/genInfo/factionEnemies.ts rename to To Sort/singularity/genInfo/factionEnemies.ts diff --git a/singularity/get/aug/basePrice.ts b/To Sort/singularity/get/aug/basePrice.ts similarity index 100% rename from singularity/get/aug/basePrice.ts rename to To Sort/singularity/get/aug/basePrice.ts diff --git a/singularity/get/aug/factions.ts b/To Sort/singularity/get/aug/factions.ts similarity index 100% rename from singularity/get/aug/factions.ts rename to To Sort/singularity/get/aug/factions.ts diff --git a/singularity/get/aug/prereq.ts b/To Sort/singularity/get/aug/prereq.ts similarity index 100% rename from singularity/get/aug/prereq.ts rename to To Sort/singularity/get/aug/prereq.ts diff --git a/singularity/get/aug/price.ts b/To Sort/singularity/get/aug/price.ts similarity index 100% rename from singularity/get/aug/price.ts rename to To Sort/singularity/get/aug/price.ts diff --git a/singularity/get/aug/repReq.ts b/To Sort/singularity/get/aug/repReq.ts similarity index 100% rename from singularity/get/aug/repReq.ts rename to To Sort/singularity/get/aug/repReq.ts diff --git a/singularity/get/aug/stats.ts b/To Sort/singularity/get/aug/stats.ts similarity index 100% rename from singularity/get/aug/stats.ts rename to To Sort/singularity/get/aug/stats.ts diff --git a/singularity/get/company/favor.ts b/To Sort/singularity/get/company/favor.ts similarity index 100% rename from singularity/get/company/favor.ts rename to To Sort/singularity/get/company/favor.ts diff --git a/singularity/get/company/favorGain.ts b/To Sort/singularity/get/company/favorGain.ts similarity index 100% rename from singularity/get/company/favorGain.ts rename to To Sort/singularity/get/company/favorGain.ts diff --git a/singularity/get/company/positionInfo.ts b/To Sort/singularity/get/company/positionInfo.ts similarity index 100% rename from singularity/get/company/positionInfo.ts rename to To Sort/singularity/get/company/positionInfo.ts diff --git a/singularity/get/company/positions.ts b/To Sort/singularity/get/company/positions.ts similarity index 100% rename from singularity/get/company/positions.ts rename to To Sort/singularity/get/company/positions.ts diff --git a/singularity/get/company/rep.ts b/To Sort/singularity/get/company/rep.ts similarity index 100% rename from singularity/get/company/rep.ts rename to To Sort/singularity/get/company/rep.ts diff --git a/singularity/get/currentServer.ts b/To Sort/singularity/get/currentServer.ts similarity index 100% rename from singularity/get/currentServer.ts rename to To Sort/singularity/get/currentServer.ts diff --git a/singularity/get/currentWork.ts b/To Sort/singularity/get/currentWork.ts similarity index 100% rename from singularity/get/currentWork.ts rename to To Sort/singularity/get/currentWork.ts diff --git a/singularity/get/darkweb/programCost.ts b/To Sort/singularity/get/darkweb/programCost.ts similarity index 100% rename from singularity/get/darkweb/programCost.ts rename to To Sort/singularity/get/darkweb/programCost.ts diff --git a/singularity/get/darkweb/programs.ts b/To Sort/singularity/get/darkweb/programs.ts similarity index 100% rename from singularity/get/darkweb/programs.ts rename to To Sort/singularity/get/darkweb/programs.ts diff --git a/singularity/get/faction/enemies.ts b/To Sort/singularity/get/faction/enemies.ts similarity index 100% rename from singularity/get/faction/enemies.ts rename to To Sort/singularity/get/faction/enemies.ts diff --git a/singularity/get/faction/favor.ts b/To Sort/singularity/get/faction/favor.ts similarity index 100% rename from singularity/get/faction/favor.ts rename to To Sort/singularity/get/faction/favor.ts diff --git a/singularity/get/faction/favorGain.ts b/To Sort/singularity/get/faction/favorGain.ts similarity index 100% rename from singularity/get/faction/favorGain.ts rename to To Sort/singularity/get/faction/favorGain.ts diff --git a/singularity/get/faction/inviteReqs.ts b/To Sort/singularity/get/faction/inviteReqs.ts similarity index 100% rename from singularity/get/faction/inviteReqs.ts rename to To Sort/singularity/get/faction/inviteReqs.ts diff --git a/singularity/get/faction/invites.ts b/To Sort/singularity/get/faction/invites.ts similarity index 100% rename from singularity/get/faction/invites.ts rename to To Sort/singularity/get/faction/invites.ts diff --git a/singularity/get/faction/rep.ts b/To Sort/singularity/get/faction/rep.ts similarity index 100% rename from singularity/get/faction/rep.ts rename to To Sort/singularity/get/faction/rep.ts diff --git a/singularity/get/faction/workTypes.ts b/To Sort/singularity/get/faction/workTypes.ts similarity index 100% rename from singularity/get/faction/workTypes.ts rename to To Sort/singularity/get/faction/workTypes.ts diff --git a/singularity/installAugs.ts b/To Sort/singularity/installAugs.ts similarity index 100% rename from singularity/installAugs.ts rename to To Sort/singularity/installAugs.ts diff --git a/singularity/installBackdoor.ts b/To Sort/singularity/installBackdoor.ts similarity index 100% rename from singularity/installBackdoor.ts rename to To Sort/singularity/installBackdoor.ts diff --git a/singularity/is/busy.ts b/To Sort/singularity/is/busy.ts similarity index 100% rename from singularity/is/busy.ts rename to To Sort/singularity/is/busy.ts diff --git a/singularity/is/focused.ts b/To Sort/singularity/is/focused.ts similarity index 100% rename from singularity/is/focused.ts rename to To Sort/singularity/is/focused.ts diff --git a/singularity/stem.ts b/To Sort/singularity/stem.ts similarity index 100% rename from singularity/stem.ts rename to To Sort/singularity/stem.ts diff --git a/singularity/travel.ts b/To Sort/singularity/travel.ts similarity index 100% rename from singularity/travel.ts rename to To Sort/singularity/travel.ts diff --git a/singularity/tryBackdoor.ts b/To Sort/singularity/tryBackdoor.ts similarity index 100% rename from singularity/tryBackdoor.ts rename to To Sort/singularity/tryBackdoor.ts diff --git a/singularity/upgradeHome/cores.ts b/To Sort/singularity/upgradeHome/cores.ts similarity index 100% rename from singularity/upgradeHome/cores.ts rename to To Sort/singularity/upgradeHome/cores.ts diff --git a/singularity/upgradeHome/ram.ts b/To Sort/singularity/upgradeHome/ram.ts similarity index 100% rename from singularity/upgradeHome/ram.ts rename to To Sort/singularity/upgradeHome/ram.ts diff --git a/sleeve/genAugInfo.js b/To Sort/sleeve/genAugInfo.js similarity index 100% rename from sleeve/genAugInfo.js rename to To Sort/sleeve/genAugInfo.js diff --git a/sleeve/get/augPrice.js b/To Sort/sleeve/get/augPrice.js similarity index 100% rename from sleeve/get/augPrice.js rename to To Sort/sleeve/get/augPrice.js diff --git a/sleeve/get/augRepReq.js b/To Sort/sleeve/get/augRepReq.js similarity index 100% rename from sleeve/get/augRepReq.js rename to To Sort/sleeve/get/augRepReq.js diff --git a/sleeve/get/augs.js b/To Sort/sleeve/get/augs.js similarity index 100% rename from sleeve/get/augs.js rename to To Sort/sleeve/get/augs.js diff --git a/sleeve/get/numSleeves.js b/To Sort/sleeve/get/numSleeves.js similarity index 100% rename from sleeve/get/numSleeves.js rename to To Sort/sleeve/get/numSleeves.js diff --git a/sleeve/get/purchasableAugs.js b/To Sort/sleeve/get/purchasableAugs.js similarity index 100% rename from sleeve/get/purchasableAugs.js rename to To Sort/sleeve/get/purchasableAugs.js diff --git a/sleeve/get/sleeve.js b/To Sort/sleeve/get/sleeve.js similarity index 100% rename from sleeve/get/sleeve.js rename to To Sort/sleeve/get/sleeve.js diff --git a/sleeve/setTo/commitCrime.js b/To Sort/sleeve/setTo/commitCrime.js similarity index 100% rename from sleeve/setTo/commitCrime.js rename to To Sort/sleeve/setTo/commitCrime.js diff --git a/sleeve/setTo/companyWork.js b/To Sort/sleeve/setTo/companyWork.js similarity index 100% rename from sleeve/setTo/companyWork.js rename to To Sort/sleeve/setTo/companyWork.js diff --git a/sleeve/setTo/factionWork.js b/To Sort/sleeve/setTo/factionWork.js similarity index 100% rename from sleeve/setTo/factionWork.js rename to To Sort/sleeve/setTo/factionWork.js diff --git a/sleeve/setTo/gymWorkout.js b/To Sort/sleeve/setTo/gymWorkout.js similarity index 100% rename from sleeve/setTo/gymWorkout.js rename to To Sort/sleeve/setTo/gymWorkout.js diff --git a/sleeve/setTo/idle.js b/To Sort/sleeve/setTo/idle.js similarity index 100% rename from sleeve/setTo/idle.js rename to To Sort/sleeve/setTo/idle.js diff --git a/sleeve/setTo/shockRecovery.js b/To Sort/sleeve/setTo/shockRecovery.js similarity index 100% rename from sleeve/setTo/shockRecovery.js rename to To Sort/sleeve/setTo/shockRecovery.js diff --git a/sleeve/setTo/synchronize.js b/To Sort/sleeve/setTo/synchronize.js similarity index 100% rename from sleeve/setTo/synchronize.js rename to To Sort/sleeve/setTo/synchronize.js diff --git a/sleeve/setTo/universityCourse.js b/To Sort/sleeve/setTo/universityCourse.js similarity index 100% rename from sleeve/setTo/universityCourse.js rename to To Sort/sleeve/setTo/universityCourse.js diff --git a/sleeve/travel.js b/To Sort/sleeve/travel.js similarity index 100% rename from sleeve/travel.js rename to To Sort/sleeve/travel.js diff --git a/srv/deploy.ts b/To Sort/srv/deploy.ts similarity index 100% rename from srv/deploy.ts rename to To Sort/srv/deploy.ts diff --git a/srv/get.js b/To Sort/srv/get.js similarity index 100% rename from srv/get.js rename to To Sort/srv/get.js diff --git a/srv/purchase.ts b/To Sort/srv/purchase.ts similarity index 100% rename from srv/purchase.ts rename to To Sort/srv/purchase.ts diff --git a/srv/purchaseHackers.ts b/To Sort/srv/purchaseHackers.ts similarity index 100% rename from srv/purchaseHackers.ts rename to To Sort/srv/purchaseHackers.ts diff --git a/srv/rename.js b/To Sort/srv/rename.js similarity index 100% rename from srv/rename.js rename to To Sort/srv/rename.js diff --git a/srv/upgrade.ts b/To Sort/srv/upgrade.ts similarity index 100% rename from srv/upgrade.ts rename to To Sort/srv/upgrade.ts diff --git a/stock/buy.ts b/To Sort/stock/buy.ts similarity index 100% rename from stock/buy.ts rename to To Sort/stock/buy.ts diff --git a/stock/buyLoop.ts b/To Sort/stock/buyLoop.ts similarity index 100% rename from stock/buyLoop.ts rename to To Sort/stock/buyLoop.ts diff --git a/stock/deploy.ts b/To Sort/stock/deploy.ts similarity index 100% rename from stock/deploy.ts rename to To Sort/stock/deploy.ts diff --git a/stock/genSymbolInfo.ts b/To Sort/stock/genSymbolInfo.ts similarity index 100% rename from stock/genSymbolInfo.ts rename to To Sort/stock/genSymbolInfo.ts diff --git a/stock/get/askPrice.ts b/To Sort/stock/get/askPrice.ts similarity index 100% rename from stock/get/askPrice.ts rename to To Sort/stock/get/askPrice.ts diff --git a/stock/get/forecast.ts b/To Sort/stock/get/forecast.ts similarity index 100% rename from stock/get/forecast.ts rename to To Sort/stock/get/forecast.ts diff --git a/stock/get/maxShares.ts b/To Sort/stock/get/maxShares.ts similarity index 100% rename from stock/get/maxShares.ts rename to To Sort/stock/get/maxShares.ts diff --git a/stock/get/organization.ts b/To Sort/stock/get/organization.ts similarity index 100% rename from stock/get/organization.ts rename to To Sort/stock/get/organization.ts diff --git a/stock/get/poorManForecast.ts b/To Sort/stock/get/poorManForecast.ts similarity index 100% rename from stock/get/poorManForecast.ts rename to To Sort/stock/get/poorManForecast.ts diff --git a/stock/get/position.ts b/To Sort/stock/get/position.ts similarity index 100% rename from stock/get/position.ts rename to To Sort/stock/get/position.ts diff --git a/stock/get/symbols.ts b/To Sort/stock/get/symbols.ts similarity index 100% rename from stock/get/symbols.ts rename to To Sort/stock/get/symbols.ts diff --git a/stock/get/volatility.ts b/To Sort/stock/get/volatility.ts similarity index 100% rename from stock/get/volatility.ts rename to To Sort/stock/get/volatility.ts diff --git a/stock/init.ts b/To Sort/stock/init.ts similarity index 100% rename from stock/init.ts rename to To Sort/stock/init.ts diff --git a/stock/init2.ts b/To Sort/stock/init2.ts similarity index 100% rename from stock/init2.ts rename to To Sort/stock/init2.ts diff --git a/stock/launch.ts b/To Sort/stock/launch.ts similarity index 100% rename from stock/launch.ts rename to To Sort/stock/launch.ts diff --git a/stock/purchase/4s.ts b/To Sort/stock/purchase/4s.ts similarity index 100% rename from stock/purchase/4s.ts rename to To Sort/stock/purchase/4s.ts diff --git a/stock/purchase/4sAPI.ts b/To Sort/stock/purchase/4sAPI.ts similarity index 100% rename from stock/purchase/4sAPI.ts rename to To Sort/stock/purchase/4sAPI.ts diff --git a/stock/purchase/tixAPI.ts b/To Sort/stock/purchase/tixAPI.ts similarity index 100% rename from stock/purchase/tixAPI.ts rename to To Sort/stock/purchase/tixAPI.ts diff --git a/stock/purchase/wseAccount.ts b/To Sort/stock/purchase/wseAccount.ts similarity index 100% rename from stock/purchase/wseAccount.ts rename to To Sort/stock/purchase/wseAccount.ts diff --git a/stock/sell.ts b/To Sort/stock/sell.ts similarity index 100% rename from stock/sell.ts rename to To Sort/stock/sell.ts diff --git a/stock/sellLoop.ts b/To Sort/stock/sellLoop.ts similarity index 100% rename from stock/sellLoop.ts rename to To Sort/stock/sellLoop.ts diff --git a/stock/sellShort.ts b/To Sort/stock/sellShort.ts similarity index 100% rename from stock/sellShort.ts rename to To Sort/stock/sellShort.ts diff --git a/stock/updateSymbolInfo.ts b/To Sort/stock/updateSymbolInfo.ts similarity index 100% rename from stock/updateSymbolInfo.ts rename to To Sort/stock/updateSymbolInfo.ts From 9ab6eac5d90615af8dd02b82e79a83417ecdffff Mon Sep 17 00:00:00 2001 From: "Um... Figure it out!" <33612115+CTH999@users.noreply.github.com> Date: Thu, 21 May 2026 17:12:49 -0500 Subject: [PATCH 3/4] fork link to the fork --- Fork.url | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 Fork.url diff --git a/Fork.url b/Fork.url new file mode 100644 index 0000000..1d9e4bc --- /dev/null +++ b/Fork.url @@ -0,0 +1,5 @@ +[{000214A0-0000-0000-C000-000000000046}] +Prop3=19,11 +[InternetShortcut] +IDList= +URL=https://github.com/CTH999/bitburner-solution From 30f768cc8e259d0af63a18bc676e14e0cc395ff5 Mon Sep 17 00:00:00 2001 From: "Um... Figure it out!" <33612115+CTH999@users.noreply.github.com> Date: Thu, 21 May 2026 17:13:08 -0500 Subject: [PATCH 4/4] pre-fork link to pre-fork --- Original.url | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 Original.url diff --git a/Original.url b/Original.url new file mode 100644 index 0000000..0856a35 --- /dev/null +++ b/Original.url @@ -0,0 +1,5 @@ +[{000214A0-0000-0000-C000-000000000046}] +Prop3=19,11 +[InternetShortcut] +IDList= +URL=https://github.com/snchlsn/bitburner-solution