From 93c201ed1fa27eac9778446c24b89d8229a96b80 Mon Sep 17 00:00:00 2001 From: Massaki Archambault Date: Sun, 17 Sep 2023 12:07:59 -0400 Subject: [PATCH] tweak and bugfix gang management script --- src/gang.js | 222 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 158 insertions(+), 64 deletions(-) diff --git a/src/gang.js b/src/gang.js index 1476909..ebae919 100644 --- a/src/gang.js +++ b/src/gang.js @@ -4,12 +4,33 @@ export async function main(ns) { ns.tail() ns.disableLog('ALL') + // config + /* Maximum number of members in a gang */ + const MAX_MEMBER_COUNT = 12 + /* Percent of training to the next ascension a member must do after ascending */ + const MIN_ASCENSION_PERCENT = 0.9 + /* Minimum power level a member must reach before transitioning to gang war/money making */ + const MIN_POWER_LEVEL = 15 + /* Win chance required to start a gang war */ + const GANG_WAR_START_TRESH = 0.6 + /* Minimum win chance before pulling out of a gang war */ + const GANG_WAR_STOP_TRESH = 0.55 + /* Target win chance */ + const GANG_WAR_TARGET = 0.9 + + // enum const MAKE_MONEY = "Human Trafficking" const VIGILANTE_JUSTICE = "Vigilante Justice" + const MUG_PEOPLE = "Mug People" const TERRORISM = "Terrorism" const TRAIN_COMBAT = "Train Combat" const TERRITORY_WARFARE = "Territory Warfare" - const maxMemberCount = 12 + + const PHASE_CATCHUP = "catchup" + const PHASE_RECRUIT = "recruit" + const PHASE_WAR = "war" + const PHASE_MONEY = "money" + const memberNamePool = [ "CJ", "Big Smoke", @@ -25,26 +46,81 @@ export async function main(ns) { "Madd Dogg", "Little Weasel", ] + const equipments = [ + "Baseball Bat", + "Katana", + "Glock 18C", + "P90C", "Steyr AUG", + "AK-47", + "M15A10 Assault Rifle", + "AWM Sniper Rifle", + "Bulletproof Vest", + "Full Body Armor", + "Liquid Body Armor", + "Graphene Plating Armor", + "Ford Flex V20", + "ATX1070 Superbike", + "Mercedes-Benz S9001", + "White Ferrari", + "Bionic Arms", + "Bionic Legs", + "Bionic Spine", + "BrachiBlades", + "Nanofiber Weave", + "Synthetic Heart", + "Synfibril Muscle", + "Graphene Bone Lacings", - function estimateMemberPower(memberName) { + // Hacking stuff, low priority so they go to the end of the list + "BitWire", + "Neuralstimulator", + "DataJack", + "NUKE Rootkit", + "Soulstealer Rootkit", + "Demon Rootkit", + "Hmap Node", + "Jack the Ripper", + ] + + function getMinAscMult(memberName) { const memberInfo = ns.gang.getMemberInformation(memberName) - return Math.floor(Math.min( + return Math.min( memberInfo.str_asc_mult, memberInfo.def_asc_mult, memberInfo.dex_asc_mult, - memberInfo.agi_asc_mult, - ) / 1.5) + // memberInfo.agi_asc_mult, + ) + } + + + function getMinAscensionResult(memberName) { + const memberAscensionResult = ns.gang.getAscensionResult(memberName) + return memberAscensionResult === undefined ? undefined : Math.min( + memberAscensionResult.str, + memberAscensionResult.def, + memberAscensionResult.dex, + // memberAscensionResult.agi + ) + } + + function estimateMemberPower(memberName) { + return getMinAscMult(memberName) / 1.6 + } + + function getMinSkillLevel(memberName) { + const memberInfo = ns.gang.getMemberInformation(memberName) + return Math.min( + memberInfo.str, + memberInfo.def, + memberInfo.dex, + memberInfo.agi, + ) } // Credit: Mysteyes. https://discord.com/channels/415207508303544321/415207923506216971/940379724214075442 function getAscendTreshold(memberName) { const memberInfo = ns.gang.getMemberInformation(memberName) - const mult = Math.min( - memberInfo.str_asc_mult, - memberInfo.def_asc_mult, - memberInfo.dex_asc_mult, - memberInfo.agi_asc_mult, - ) + const mult = getMinAscMult(memberName) if (mult < 1.632) return 1.6326; if (mult < 2.336) return 1.4315; if (mult < 2.999) return 1.284; @@ -61,29 +137,20 @@ export async function main(ns) { return 1.0591; } - function getMinAscensionResult(memberName) { - const memberAscensionResult = ns.gang.getAscensionResult(memberName) - return memberAscensionResult === undefined ? undefined : Math.min( - memberAscensionResult.str, - memberAscensionResult.def, - memberAscensionResult.dex, - memberAscensionResult.agi - ) - } - function isAscensionReady(memberName) { return getMinAscensionResult(memberName) >= getAscendTreshold(memberName) } function isRecruitementReady(relativePower, memberCount) { - const res = relativePower / 3 > memberCount - 3 // TODO - // ns.print(ascentionCount / 3) + const res = relativePower * 1.25 + 2 > memberCount // TODO + ns.print(relativePower * 1.25 + 2) return res } - function getGangWarWinChances() { + function getGangWarWinChance() { const gangInfo = ns.gang.getGangInformation() - const otherGangNames = Object.keys(ns.gang.getOtherGangInformation()).filter(x => gangInfo.faction != x && x.territory > 0) + const otherGangInfo = ns.gang.getOtherGangInformation() + const otherGangNames = Object.keys(otherGangInfo).filter(x => x != gangInfo.faction && otherGangInfo[x]["territory"] > 0) const chancesToWin = otherGangNames.map(x => ns.gang.getChanceToWinClash(x)) return Math.min(...chancesToWin) } @@ -100,7 +167,7 @@ export async function main(ns) { ns.gang.recruitMember(memberName) ns.gang.setMemberTask(memberName, TRAIN_COMBAT) } - + const gangMembers = ns.gang.getMemberNames() // check for and ascend members who are ready @@ -111,76 +178,103 @@ export async function main(ns) { } } - for (const memberName of gangMembers) { - if (isAscensionReady(memberName)) { - // ns.print("ascending " + memberName) - ns.gang.ascendMember(memberName) - } - } - - // TODO: equipement - - // gang war - if (getGangWarWinChances() >= 0.6) { + if (getGangWarWinChance() >= GANG_WAR_START_TRESH) { // go to war ns.gang.setTerritoryWarfare(true) } - else if (getGangWarWinChances() <= 0.55) { + else if (getGangWarWinChance() <= GANG_WAR_STOP_TRESH) { // disengage ns.gang.setTerritoryWarfare(false) } // score each member by their estimated power level - let memberDatas = gangMembers.map(x => [x, estimateMemberPower(x)]) - memberDatas.sort((a, b) => b[1] - a[1]) - const targetRelativePower = memberDatas[0][1] - 1 + let memberDatas = gangMembers.map(x => [x, { + "relativePower": estimateMemberPower(x), + "minSkillLevel": getMinSkillLevel(x), + "ascensionPercent": getMinAscensionResult(x) / getAscendTreshold(x), + "phase": undefined, + "training": false, + "vigilante": false, + }]) + memberDatas.sort((a, b) => b[1]["relativePower"] - a[1]["relativePower"]) + const targetRelativePower = memberDatas[0][1]["relativePower"] - 2 + for (const memberData of memberDatas) { const memberName = memberData[0] - const relativePower = memberData[1] + const relativePower = memberData[1]["relativePower"] + const minSkillLevel = memberData[1]["minSkillLevel"] + const ascensionPercent = memberData[1]["ascensionPercent"] + - ns.print(memberName) - // ns.print(getMinAscensionResult(memberName) / getAscendTreshold(memberName)) - // ns.print(isAscensionReady(memberName)) - - if (relativePower < targetRelativePower && relativePower < 40) { - ns.print("assignment: catchup") + // compute the phase of each members + if (relativePower < targetRelativePower) { // bring up newcomers to the same level than established members - ns.gang.setMemberTask(memberName, TRAIN_COMBAT) + memberData[1]["phase"] = PHASE_CATCHUP } - else if (getMinAscensionResult(memberName) / getAscendTreshold(memberName) < 0.9) { // TODO: arbitrary constant to be ajusted - ns.print("assignment: level up after ascention") + else if (gangMembers.length < MAX_MEMBER_COUNT && isRecruitementReady(targetRelativePower, gangMembers.length)) { + // gain respect to recruit new member + memberData[1]["phase"] = PHASE_RECRUIT + } + else if (gangInfo.territory < 1 && getGangWarWinChance() < GANG_WAR_TARGET) { + // prepare for gang war + memberData[1]["phase"] = PHASE_WAR + } + else { + // if everything else is done, make money + memberData[1]["phase"] = PHASE_MONEY + } + + if (memberData[1]["phase"] == PHASE_CATCHUP) { + ns.print("assignment: catchup") + ns.gang.setMemberTask(memberName, TRAIN_COMBAT) + memberData[1]["training"] = true + } + else if (ascensionPercent < MIN_ASCENSION_PERCENT) { // TODO: arbitrary constant to be ajusted + ns.print("assignment: level up after ascension") // train a member back up after ascention ns.gang.setMemberTask(memberName, TRAIN_COMBAT) + memberData[1]["training"] = true } else if (gangInfo.wantedPenalty < 0) { ns.print("assignment: reduce wanted level") - // reduce wanted level - ns.print(gangInfo.wantedPenalty) + // try to keep the wanted penalty at 0% ns.gang.setMemberTask(memberName, VIGILANTE_JUSTICE) + memberData[1]["vigilante"] = true } - else if (gangMembers.length < maxMemberCount && isRecruitementReady(relativePower, gangMembers.length)) { - ns.print("assignment: gain respect") - // gain respect to recruit new member - ns.gang.setMemberTask(memberName, TERRORISM) + else if (memberData[1]["phase"] == PHASE_RECRUIT) { + ns.print("assignment: gain respect to recruit new members") + if (minSkillLevel < 500) { + ns.gang.setMemberTask(memberName, MUG_PEOPLE) + } + else { + ns.gang.setMemberTask(memberName, TERRORISM) + } } - else if (relativePower < 20) { // TODO - ns.print("assignment: train to next ascension") + else if (relativePower < MIN_POWER_LEVEL) { + ns.print("assignment: train") // train up to next ascention ns.gang.setMemberTask(memberName, TRAIN_COMBAT) + memberData[1]["training"] = true } - else if (gangInfo.territory < 1 && getGangWarWinChances() < 0.95) { + else if (memberData[1]["phase"] == PHASE_WAR) { ns.print("assignment: gang war") - // prepare for gang war ns.gang.setMemberTask(memberName, TERRITORY_WARFARE) } else { ns.print("assignment: make money") - // go for the money - ns.gang.setMemberTask(memberName, MAKE_MONEY) // TODO + ns.gang.setMemberTask(memberName, MAKE_MONEY) } - } + // buy equipement + if (memberData[1]["phase"] == PHASE_WAR || memberData[1]["phase"] == PHASE_MONEY) { + for (const equipment of equipments) { + ns.gang.purchaseEquipment(memberName, equipment) + } + } + ns.print(getMinAscensionResult(memberName) / getAscendTreshold(memberName)) + } + ns.print(memberDatas) await ns.sleep(5000) } } \ No newline at end of file