tweak and bugfix gang management script
This commit is contained in:
parent
d851d84c05
commit
93c201ed1f
216
src/gang.js
216
src/gang.js
|
@ -4,12 +4,33 @@ export async function main(ns) {
|
||||||
ns.tail()
|
ns.tail()
|
||||||
ns.disableLog('ALL')
|
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 MAKE_MONEY = "Human Trafficking"
|
||||||
const VIGILANTE_JUSTICE = "Vigilante Justice"
|
const VIGILANTE_JUSTICE = "Vigilante Justice"
|
||||||
|
const MUG_PEOPLE = "Mug People"
|
||||||
const TERRORISM = "Terrorism"
|
const TERRORISM = "Terrorism"
|
||||||
const TRAIN_COMBAT = "Train Combat"
|
const TRAIN_COMBAT = "Train Combat"
|
||||||
const TERRITORY_WARFARE = "Territory Warfare"
|
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 = [
|
const memberNamePool = [
|
||||||
"CJ",
|
"CJ",
|
||||||
"Big Smoke",
|
"Big Smoke",
|
||||||
|
@ -25,26 +46,81 @@ export async function main(ns) {
|
||||||
"Madd Dogg",
|
"Madd Dogg",
|
||||||
"Little Weasel",
|
"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)
|
const memberInfo = ns.gang.getMemberInformation(memberName)
|
||||||
return Math.floor(Math.min(
|
return Math.min(
|
||||||
memberInfo.str_asc_mult,
|
memberInfo.str_asc_mult,
|
||||||
memberInfo.def_asc_mult,
|
memberInfo.def_asc_mult,
|
||||||
memberInfo.dex_asc_mult,
|
memberInfo.dex_asc_mult,
|
||||||
memberInfo.agi_asc_mult,
|
// memberInfo.agi_asc_mult,
|
||||||
) / 1.5)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
// Credit: Mysteyes. https://discord.com/channels/415207508303544321/415207923506216971/940379724214075442
|
||||||
function getAscendTreshold(memberName) {
|
function getAscendTreshold(memberName) {
|
||||||
const memberInfo = ns.gang.getMemberInformation(memberName)
|
const memberInfo = ns.gang.getMemberInformation(memberName)
|
||||||
const mult = Math.min(
|
const mult = getMinAscMult(memberName)
|
||||||
memberInfo.str_asc_mult,
|
|
||||||
memberInfo.def_asc_mult,
|
|
||||||
memberInfo.dex_asc_mult,
|
|
||||||
memberInfo.agi_asc_mult,
|
|
||||||
)
|
|
||||||
if (mult < 1.632) return 1.6326;
|
if (mult < 1.632) return 1.6326;
|
||||||
if (mult < 2.336) return 1.4315;
|
if (mult < 2.336) return 1.4315;
|
||||||
if (mult < 2.999) return 1.284;
|
if (mult < 2.999) return 1.284;
|
||||||
|
@ -61,29 +137,20 @@ export async function main(ns) {
|
||||||
return 1.0591;
|
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) {
|
function isAscensionReady(memberName) {
|
||||||
return getMinAscensionResult(memberName) >= getAscendTreshold(memberName)
|
return getMinAscensionResult(memberName) >= getAscendTreshold(memberName)
|
||||||
}
|
}
|
||||||
|
|
||||||
function isRecruitementReady(relativePower, memberCount) {
|
function isRecruitementReady(relativePower, memberCount) {
|
||||||
const res = relativePower / 3 > memberCount - 3 // TODO
|
const res = relativePower * 1.25 + 2 > memberCount // TODO
|
||||||
// ns.print(ascentionCount / 3)
|
ns.print(relativePower * 1.25 + 2)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
function getGangWarWinChances() {
|
function getGangWarWinChance() {
|
||||||
const gangInfo = ns.gang.getGangInformation()
|
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))
|
const chancesToWin = otherGangNames.map(x => ns.gang.getChanceToWinClash(x))
|
||||||
return Math.min(...chancesToWin)
|
return Math.min(...chancesToWin)
|
||||||
}
|
}
|
||||||
|
@ -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
|
// gang war
|
||||||
if (getGangWarWinChances() >= 0.6) {
|
if (getGangWarWinChance() >= GANG_WAR_START_TRESH) {
|
||||||
// go to war
|
// go to war
|
||||||
ns.gang.setTerritoryWarfare(true)
|
ns.gang.setTerritoryWarfare(true)
|
||||||
}
|
}
|
||||||
else if (getGangWarWinChances() <= 0.55) {
|
else if (getGangWarWinChance() <= GANG_WAR_STOP_TRESH) {
|
||||||
// disengage
|
// disengage
|
||||||
ns.gang.setTerritoryWarfare(false)
|
ns.gang.setTerritoryWarfare(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// score each member by their estimated power level
|
// score each member by their estimated power level
|
||||||
let memberDatas = gangMembers.map(x => [x, estimateMemberPower(x)])
|
let memberDatas = gangMembers.map(x => [x, {
|
||||||
memberDatas.sort((a, b) => b[1] - a[1])
|
"relativePower": estimateMemberPower(x),
|
||||||
const targetRelativePower = memberDatas[0][1] - 1
|
"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) {
|
for (const memberData of memberDatas) {
|
||||||
const memberName = memberData[0]
|
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) {
|
// compute the phase of each members
|
||||||
ns.print("assignment: catchup")
|
if (relativePower < targetRelativePower) {
|
||||||
// bring up newcomers to the same level than established members
|
// 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
|
else if (gangMembers.length < MAX_MEMBER_COUNT && isRecruitementReady(targetRelativePower, gangMembers.length)) {
|
||||||
ns.print("assignment: level up after ascention")
|
// 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
|
// train a member back up after ascention
|
||||||
ns.gang.setMemberTask(memberName, TRAIN_COMBAT)
|
ns.gang.setMemberTask(memberName, TRAIN_COMBAT)
|
||||||
|
memberData[1]["training"] = true
|
||||||
}
|
}
|
||||||
else if (gangInfo.wantedPenalty < 0) {
|
else if (gangInfo.wantedPenalty < 0) {
|
||||||
ns.print("assignment: reduce wanted level")
|
ns.print("assignment: reduce wanted level")
|
||||||
// reduce wanted level
|
// try to keep the wanted penalty at 0%
|
||||||
ns.print(gangInfo.wantedPenalty)
|
|
||||||
ns.gang.setMemberTask(memberName, VIGILANTE_JUSTICE)
|
ns.gang.setMemberTask(memberName, VIGILANTE_JUSTICE)
|
||||||
|
memberData[1]["vigilante"] = true
|
||||||
}
|
}
|
||||||
else if (gangMembers.length < maxMemberCount && isRecruitementReady(relativePower, gangMembers.length)) {
|
else if (memberData[1]["phase"] == PHASE_RECRUIT) {
|
||||||
ns.print("assignment: gain respect")
|
ns.print("assignment: gain respect to recruit new members")
|
||||||
// gain respect to recruit new member
|
if (minSkillLevel < 500) {
|
||||||
|
ns.gang.setMemberTask(memberName, MUG_PEOPLE)
|
||||||
|
}
|
||||||
|
else {
|
||||||
ns.gang.setMemberTask(memberName, TERRORISM)
|
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
|
// train up to next ascention
|
||||||
ns.gang.setMemberTask(memberName, TRAIN_COMBAT)
|
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")
|
ns.print("assignment: gang war")
|
||||||
// prepare for gang war
|
|
||||||
ns.gang.setMemberTask(memberName, TERRITORY_WARFARE)
|
ns.gang.setMemberTask(memberName, TERRITORY_WARFARE)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ns.print("assignment: make money")
|
ns.print("assignment: make money")
|
||||||
// go for the money
|
ns.gang.setMemberTask(memberName, MAKE_MONEY)
|
||||||
ns.gang.setMemberTask(memberName, MAKE_MONEY) // TODO
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
await ns.sleep(5000)
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue