diff --git a/src/gang.js b/src/gang/gang-manage.js similarity index 65% rename from src/gang.js rename to src/gang/gang-manage.js index ebae919..d16e077 100644 --- a/src/gang.js +++ b/src/gang/gang-manage.js @@ -4,21 +4,21 @@ export async function main(ns) { ns.tail() ns.disableLog('ALL') - // config + /** CCONFIG **/ /* 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 + const MIN_ASCENSION_PERCENT = 0.95 /* 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 */ + /* Minimum 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 + const GANG_WAR_TARGET = 0.95 - // enum + /** CONSTANTS **/ const MAKE_MONEY = "Human Trafficking" const VIGILANTE_JUSTICE = "Vigilante Justice" const MUG_PEOPLE = "Mug People" @@ -28,8 +28,9 @@ export async function main(ns) { const PHASE_CATCHUP = "catchup" const PHASE_RECRUIT = "recruit" - const PHASE_WAR = "war" - const PHASE_MONEY = "money" + const PHASE_TRAIN = "train" + const PHASE_WAR = "gang war" + const PHASE_MONEY = "make money" const memberNamePool = [ "CJ", @@ -119,7 +120,6 @@ export async function main(ns) { // Credit: Mysteyes. https://discord.com/channels/415207508303544321/415207923506216971/940379724214075442 function getAscendTreshold(memberName) { - const memberInfo = ns.gang.getMemberInformation(memberName) const mult = getMinAscMult(memberName) if (mult < 1.632) return 1.6326; if (mult < 2.336) return 1.4315; @@ -137,14 +137,11 @@ export async function main(ns) { return 1.0591; } - function isAscensionReady(memberName) { - return getMinAscensionResult(memberName) >= getAscendTreshold(memberName) - } - - function isRecruitementReady(relativePower, memberCount) { - const res = relativePower * 1.25 + 2 > memberCount // TODO - ns.print(relativePower * 1.25 + 2) - return res + function isRecruitmentReady(power, memberCount) { + // the formula here is kinda arbitrary + // increase the number of members in the gang linearly with the power of its members + // ns.print(power * 1.5 + 3) + return power * 1.5 + 3 > memberCount } function getGangWarWinChance() { @@ -158,106 +155,109 @@ export async function main(ns) { while (true) { const gangInfo = ns.gang.getGangInformation() - ns.print("running loop") - // check for and recruit new members if available while (ns.gang.canRecruitMember()) { const availableNames = memberNamePool.filter(x => memberNamePool.includes(x)) const memberName = availableNames[Math.floor(Math.random() * availableNames.length)] ns.gang.recruitMember(memberName) - ns.gang.setMemberTask(memberName, TRAIN_COMBAT) - } - - const gangMembers = ns.gang.getMemberNames() - - // check for and ascend members who are ready - for (const memberName of gangMembers) { - if (isAscensionReady(memberName)) { - // ns.print("ascending " + memberName) - ns.gang.ascendMember(memberName) - } } // gang war - if (getGangWarWinChance() >= GANG_WAR_START_TRESH) { + const gangWarWinChance = getGangWarWinChance() + if (gangWarWinChance >= GANG_WAR_START_TRESH) { // go to war ns.gang.setTerritoryWarfare(true) } - else if (getGangWarWinChance() <= GANG_WAR_STOP_TRESH) { + else if (gangWarWinChance <= GANG_WAR_STOP_TRESH) { // disengage ns.gang.setTerritoryWarfare(false) } - // score each member by their estimated power level - let memberDatas = gangMembers.map(x => [x, { - "relativePower": estimateMemberPower(x), + // create an 2-dimensional array of member data. We use an array because we need it to be sorted + // 1st dimension is for each member + // index 0 of 2nd dimension is the name of the member + // index 1 of 2nd dimension is the data + let gangMembers = ns.gang.getMemberNames().map(x => [x, { + "power": 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 + // sort by their power + gangMembers.sort((a, b) => b[1]["power"] - a[1]["power"]) + // the highest power level is the target new members needs to catchup to + const targetPower = gangMembers[0][1]["power"] - 2 + // check if we are ready to recruit a new member + const recruitmentReady = isRecruitmentReady(targetPower, gangMembers.length) - for (const memberData of memberDatas) { - const memberName = memberData[0] - const relativePower = memberData[1]["relativePower"] - const minSkillLevel = memberData[1]["minSkillLevel"] - const ascensionPercent = memberData[1]["ascensionPercent"] - + for (const member of gangMembers) { + const memberName = member[0] + const memberData = member[1] + const power = memberData["power"] + const minSkillLevel = memberData["minSkillLevel"] + const ascensionPercent = memberData["ascensionPercent"] // compute the phase of each members - if (relativePower < targetRelativePower) { + if (power < targetPower) { // bring up newcomers to the same level than established members - memberData[1]["phase"] = PHASE_CATCHUP + memberData["phase"] = PHASE_CATCHUP } - else if (gangMembers.length < MAX_MEMBER_COUNT && isRecruitementReady(targetRelativePower, gangMembers.length)) { + else if (gangMembers.length < MAX_MEMBER_COUNT) { // gain respect to recruit new member - memberData[1]["phase"] = PHASE_RECRUIT + memberData["phase"] = PHASE_RECRUIT } - else if (gangInfo.territory < 1 && getGangWarWinChance() < GANG_WAR_TARGET) { + else if (power < MIN_POWER_LEVEL) { + // power up until useful enough + memberData["phase"] = PHASE_TRAIN + } + else if (gangInfo.territory < 1 && gangWarWinChance < GANG_WAR_TARGET) { // prepare for gang war - memberData[1]["phase"] = PHASE_WAR + memberData["phase"] = PHASE_WAR } else { // if everything else is done, make money - memberData[1]["phase"] = PHASE_MONEY + memberData["phase"] = PHASE_MONEY } - if (memberData[1]["phase"] == PHASE_CATCHUP) { + // assign tasks + // strategy is: + // catchup -> recruit <-> train -> gang war <-> make money + // reduce wanted level as needed + if (memberData["phase"] == PHASE_CATCHUP) { ns.print("assignment: catchup") ns.gang.setMemberTask(memberName, TRAIN_COMBAT) - memberData[1]["training"] = true + memberData["training"] = true } - else if (ascensionPercent < MIN_ASCENSION_PERCENT) { // TODO: arbitrary constant to be ajusted + else if (isNaN(ascensionPercent) || 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 + memberData["training"] = true } else if (gangInfo.wantedPenalty < 0) { ns.print("assignment: reduce wanted level") // try to keep the wanted penalty at 0% ns.gang.setMemberTask(memberName, VIGILANTE_JUSTICE) - memberData[1]["vigilante"] = true + memberData["vigilante"] = true } - else if (memberData[1]["phase"] == PHASE_RECRUIT) { + else if (memberData["phase"] == PHASE_RECRUIT && recruitmentReady) { ns.print("assignment: gain respect to recruit new members") - if (minSkillLevel < 500) { + if (minSkillLevel < 250) { ns.gang.setMemberTask(memberName, MUG_PEOPLE) } else { ns.gang.setMemberTask(memberName, TERRORISM) } } - else if (relativePower < MIN_POWER_LEVEL) { + else if (memberData["phase"] == PHASE_RECRUIT || memberData["phase"] == PHASE_TRAIN) { ns.print("assignment: train") // train up to next ascention ns.gang.setMemberTask(memberName, TRAIN_COMBAT) - memberData[1]["training"] = true + memberData["training"] = true } - else if (memberData[1]["phase"] == PHASE_WAR) { + else if (memberData["phase"] == PHASE_WAR) { ns.print("assignment: gang war") ns.gang.setMemberTask(memberName, TERRITORY_WARFARE) } @@ -267,14 +267,20 @@ export async function main(ns) { } // buy equipement - if (memberData[1]["phase"] == PHASE_WAR || memberData[1]["phase"] == PHASE_MONEY) { + if (memberData["phase"] == PHASE_WAR || memberData["phase"] == PHASE_MONEY) { for (const equipment of equipments) { ns.gang.purchaseEquipment(memberName, equipment) } } - ns.print(getMinAscensionResult(memberName) / getAscendTreshold(memberName)) + + // ascend members who are ready + if (ascensionPercent >= 1 && + !(memberData["phase"] == PHASE_RECRUIT && memberData["training"] == false)) { // avoid ascending a member that has built up respect to recruit a new member + // ns.print("ascending " + memberName) + ns.gang.ascendMember(memberName) + } } - ns.print(memberDatas) + ns.print(gangMembers) await ns.sleep(5000) } } \ No newline at end of file diff --git a/src/gang/gang-prepare.js b/src/gang/gang-prepare.js new file mode 100644 index 0000000..21a9348 --- /dev/null +++ b/src/gang/gang-prepare.js @@ -0,0 +1,50 @@ +/** @param {NS} ns */ +export async function main(ns) { + ns.clearLog() + ns.tail() + ns.disableLog('sleep') + + const START_SKILL_LEVEL = 80 + const GYM_NAME = "powerhouse gym" + const FACTION = "Slum Snakes" + + const skills = [ + "strength", + "defense", + "dexterity", + "agility" + ] + + // skillup + for (const skill of skills) { + let player = ns.getPlayer() + if (player.skills[skill] < START_SKILL_LEVEL) { + ns.singularity.gymWorkout(GYM_NAME, skill, ns.singularity.isFocused()) + while(player.skills[skill] < START_SKILL_LEVEL) { + await ns.sleep(1000) + player = ns.getPlayer() + } + } + } + + // generate bad karma + ns.singularity.commitCrime("Homicide", ns.singularity.isFocused()) + + // join faction + if(!ns.getPlayer().factions.includes(FACTION)) { + while(!ns.singularity.checkFactionInvitations().includes(FACTION)) { + await ns.sleep(1000) + } + ns.singularity.joinFaction(FACTION) + } + + // create gang + while(!ns.gang.inGang()) { + await ns.sleep(1000) + ns.gang.createGang(FACTION) + } + + // Start gang management script + ns.singularity.commitCrime("Kidnap", ns.singularity.isFocused()) + ns.spawn("/gang/gang-manage.js") +} \ No newline at end of file