r/Bitburner

Mobile Game inspired by Lain Aesthetics and Bitburner
▲ 8 r/Bitburner+2 crossposts

Mobile Game inspired by Lain Aesthetics and Bitburner

Hy everyone i am making this game inspired by Lain and by Bitburner, if anyone want to try It at this link you can find both Linux and Android versions. I already planned of publishing It on the stores but First i want to enhance user experience more and make It better before i make It fully public. If anybody is willing to try It and let me know what he thinks of It, i'd be very grateful. Thanks everyone☺️

github.com
u/CmonMazzu — 17 hours ago

Did the server connections change with the update?

This could just be me not paying attention it seems like what servers are connected to each other have changed. Did this happen with the newest update or is it always changing? I have a network map built out in obsidian and it's now incorrect. I just want to know if the server connections are going to keep changing.

reddit.com
u/StrangeCat77 — 8 hours ago

I made a terraforming/incremental game where the Python code you write IS the gameplay

Hi all,

I built a game where the Python code you write is the gameplay, and people who've played it keep telling me it scratches a Bitburner itch. I'd love an honest read from this community: does this concept sound like it would land for you?

I created a terraforming game with a deep (and sinister) story behind it, where every device/vehicle/drone/machine has to be automated/scripted to work with the planet, allowing the player to do whatever they want with them.

You do not just click to mine and smelt iron ore. You need to get a vehicle(called Pioneer), install modules to it(programmatically possible to, dynamically load/unload), program the drill module to drill the ore, program the feeders to transfer items to inventory, storage bin, warehouse, wherever you want, and program the smelters/fabricators using the storages. Everything can be fully automated with code and your end goal is to awaken the planet.

Your solar generators don't just work, they have to be adjusted to track the sun's position, via code. Everything can be managed with code, even the actual shop where you buy things with credits earned from Earth contracts.

I have released a FREE demo on Steam here: https://store.steampowered.com/app/868160/Code_Terraform

Discord: https://discord.gg/hUrK2MRn8s (I'd love to help if you are stuck)

I'd love to hear your opinions about it, thanks!

u/RecursivelyYours — 2 days ago

Curious but afraid

Hey whatsup guys Im super comfortable with tech concepts and computers, but don't have any Javascript experience whatsoever. Should I only start the game when I have a basic grasp, is there some kind of ultra beginner's guide outside the tutorials in the game? Is it possible to get into the game without coding experience?

edit: Thanks for all the wonderful responses everyone, I'm gonna dive right in

reddit.com
u/Zorian_Vale — 23 hours ago

How do I automate accepting Stanek's gift?

I can move myself to Chongqing with a script, but how do I investigate the church to get invited to "Church of the Machine God" with a script?

reddit.com
u/Puzzleheaded_Toe6299 — 14 hours ago

update broke corporations

Corporations no longer progress when you tab out of the browser tab for the game, meaning that in order to progress your corporation, you need to sit and watch the game for however long it takes the corporation to progress to where you want it.

Because of this, corporations are now completely useless if you play the game as an actual idle game.

reddit.com
u/DevouringBeast — 4 days ago

Been a while since we posted a version update announcement on Reddit, but here it is!

Version 3.0 is released (on Steam, on the web here, or downloadable from the GitHub release). As a major version, this release has some breaking changes.

When you load in for the first time, you will be prompted to save a backup of your pre-3.0.0 savegame, in case you want to temporarily revert to an older version due to issues with your scripts. A text file APIBreakInfo-3.0.0.txt will be created with information to help you address any of your scripts which are impacted by an API break.

If you do need to revert to a previous version, Steam has other selectable versions under Properties -> Game Versions and Betas.

Changelog can be viewed ingame or at the linked GitHub release

u/Omelet — 12 days ago

Working on a darknet script (very much a work in progress) and I was refactoring everything to use an object to represent a darknet and I started getting game crashes after about ~5 seconds of running the script? Been staring at it for hours and can't figure out why. Maybe something to do with nested async functions? Any help would be appreciated. I hope its not something too obvious...

Its pretty poorly commented, but think most stuff is pretty explanatorily named. Lmk if there are questions.

***Solved! I was being an idiot. Issue was: dnets = dnets.slice(i, 1); i--; in the second for loop of the while(true) loop. I meant to use splice....... and i don't need the =. So that line is just changed to: dnets.splice(i, 1); i--; And it works great! It just ended up stuck in that for loop forever before.

Can test with just a call to:

ns.exec("dnet/darknet.js","darkweb",1,"home");

File:

const dnetFiles = ["dnet/darknet.js",];
const dnetFile = 0;
/**  {NS} ns */
export async function main(ns) {
  ns.disableLog("ALL");
  const parent = ns.args[0];
  var host = new DarkNet(ns, ns.getHostname(), "");
  ns.print("\n---Darknet Processing Begin---" + host.name);
  host.checkFiles();
  var dnets = [];
  while (true) {
    ns.print("Beginning new loop \n");
    host.openCaches();
    //Update the dnets array with new servers
    const servers = ns.dnet.probe();
    for (const server of servers) {
      if (!dnets.some((darknet) => (darknet.name == server))) {
        dnets.push(new DarkNet(ns, server, host.name));
      }
    }
    //Removes no longer connected servers
    for (let i = 0; i < dnets.length; i++) {
      if (!dnets[i].isValid()) {
        dnets = dnets.slice(i, 1); i--;
      }
    }
    //Connects to and handles connected dnet servers
    for (let darknet of dnets) {
      if (darknet.isValid()) {
        await darknet.authenticateServer();
      }
    }
    await ns.dnet.nextMutation();
  }
}


class DarkNet {
  /** u/param {NS} ns */
  constructor(ns, name, host) {
    this.ns = ns;
    this.name = name;
    this.host = host;
    this.password = null;
    this.possible = null;
    this.result = null;
    this.details = this.ns.dnet.getServerAuthDetails(this.name);
    this.isDynamic = this.getPasswordType();
    if (!this.isDynamic) { this.possible = this.getStaticPassword(); }
  }
  async authenticateServer() {
    if (this.isConnected()) { return true; }
    if (this.isDynamic) { this.result = await this.authenticateDynamic(); }
    else { this.result = await this.authenticateStatic(); }
    if (!this.result.success) {
      this.ns.print("Failed to connect to dnet server " + this.name +
        "\nHint: " + this.details.passwordHint + " Data: " + this.details.data +
        "\nFormat: " + this.details.passwordFormat + " Length: " + this.details.passwordLength +
        " Attempted: " + this.possible + "\nIs Valid: " + this.isValid());
      //ns.exit();
    }
    else {
      this.ns.print("Connected Server:" + this.name + " Password:" + this.password);
      this.setupNewServer();
      await this.ns.sleep(10);
    }
    return this.result.success;
  }
  setupNewServer() {
    this.startProcess(dnetFiles[dnetFile], 1, this.host);
  }
  startProcess(file, threads, args) {
    if (!this.ns.scriptRunning(file, this.name)) {
      this.ns.scp(dnetFiles, this.name, "home");
      const id = this.ns.exec(file, this.name, threads, args);
      if (id == 0) { this.ns.print("Failed to exec " + file + " on " + this.name + " from " + this.host); }
      else { this.ns.print("Running " + file + " on " + this.name); }
      return id;
    }
    return 0;
  }
  async authenticateDynamic() {
    await this.ns.sleep(100);
    return { success: false };
  }
  async authenticateStatic() {
    if (this.possible == null) { return { success: false }; }
    for (const password of this.possible) {
      let result = await this.tryAuth(password);
      if (result.success) { return result; }
    }
    return { success: false };
  }
  async tryAuth(password) {
    if (!this.isValid() || password == null) { return { success: false }; }
    let result = await this.ns.dnet.authenticate(this.name, password);
    if (result.success) { this.password = password; }
    return result;
  }
  getDynamicPassword() {
    this.ns.alert("Test!");
    return null;
  }
  getStaticPassword() {
    if (this.details.passwordLength == 0) { return [""]; }
    if (this.details.passwordHint.length <= 0) { return null; }
    if (this.details.modelId == "ZeroLogon") { return ["0"]; }
    const hintSplit = this.details.passwordHint.split(" ");
    if (hintSplit.includes("default") || hintSplit.includes("factory") || hintSplit.includes("never")) {
      return ["0000", "12345", "admin", "password"];
    }
    if (this.details.data == "" && !isNaN(hintSplit.at(-1))) {
      return [hintSplit.at(-1)];
    }
    if (hintSplit.includes("human")) {
      let password = "";
      for (const char of this.details.data) { if (!isNaN(char)) { password += char; } }
      return [password];
    }
    if (hintSplit.includes("made") || hintSplit.includes("sorted") || hintSplit.includes("shuffled") || hintSplit.includes("uses")) {
      const data = this.details.data;
      if (data.length > 3) { ns.alert("Bad Assumption! shuffled " + this.name); return null; }
      return [data, data[0] + data[2] + data[1], data[1] + data[2] + data[0], data[1] + data[0] + data[2], data[2] + data[0] + data[1], data[2] + data[1] + data[0]];
    }
    if (hintSplit.includes("dog")) { return ["fido", "spot", "rover", "max"]; }
    if (hintSplit.includes("value")) {
      const roman = { I: 1, V: 5, X: 10, L: 50, C: 100, D: 500, M: 1000 };
      let password = 0;
      for (let i = 0; i < this.details.data.length; i++) {
        if (roman[this.details.data[i]] < roman[this.details.data[i + 1]]) { password -= roman[this.details.data[i]]; }
        else { password += roman[this.details.data[i]]; }
      }
      return [password];
    }
    if (hintSplit.includes("base")) {
      if (hintSplit.at(-1) != "10") { ns.alert("Bad Assumption! base " + this.name); return null; }
      let parseData = this.details.data.split(",");
      return [parseInt(parseData[1], parseData[0])];
    }
    if (hintSplit.includes("between")) {
      let password = [];
      for (let i = Number(hintSplit[hintSplit.length - 3]) + 1; i < Number(hintSplit[hintSplit.length - 1]); i++) { password.push(i); }
      return password;
    }
    if (hintSplit.includes("divisible")) {
      if (hintSplit[hintSplit.length - 2] != "1") { ns.alert("Bad Assumption! divisible"); return null; }
      let password = [];
      for (let i = 1; i < Math.pow(10, this.details.passwordLength); i++) { password.push(i); }
      return password
    }
    return null;
  }
  getPasswordType() {
    return false;
  }
  isValid() {
    this.details = this.ns.dnet.getServerAuthDetails(this.name);
    if (this.details.isOnline && this.details.isConnectedToCurrentServer) { return true; }
    return false;
  }
  isConnected() {
    this.details = this.ns.dnet.getServerAuthDetails(this.name);
    return this.details.hasSession;
  }
  openCaches() {
    const files = this.ns.ls(this.name);
    for (const file of files) {
      const type = file.split(".").at(-1);
      if (type == "cache") {
        let result = this.ns.dnet.openCache(file);
        this.ns.print("\nCache result: " + result.message);
      }
    }
  }
  checkFiles() {
    const files = this.ns.ls(this.name);
    for (const file of files) {
      const type = file.split(".").at(-1);
      if (type == "lit" || type == "txt") {
        this.ns.scp(file, "darkweb");
        //if (this.name != "darkweb") { this.ns.rm(file,this.name); }
      }
      if (type == "exe") {
        this.ns.print("Executable file found: " + file);
      }
    }
  }
}
reddit.com
u/adjectived — 6 days ago
▲ 6 r/Bitburner+1 crossposts

I got tired of writing scripts for every edge case, so I built an AI agent that reads the full game state every 60 seconds and decides what to do next — buy programs, deploy hacks, commit crimes, manage sleeves, join factions. It runs on a local Ollama instance (Llama 3.1 8B on a Jetson in my case, but any Ollama host works).

What it actually does:

The AI receives your complete game state as structured JSON — hack level, money, server fleet, gang info, bladeburner status, augmentations, faction rep, running scripts — and returns 1-5 action commands per cycle. Each action maps to a real NS Singularity call. It doesn't just suggest — it executes.

The safety rails are the interesting part.

Every action the AI proposes passes through a validator before execution. It can't install augmentations with fewer than 5 pending (wasteful reset). It can't spend more than 90% of your cash in one cycle. It can't soft reset without a confirmation file. It can't kill scripts or delete files. Blocked actions are logged with the reason. The AI also gets its previous action results fed back each cycle, so it learns from failures — if the safety rail blocks an upgrade_server three times in a row because of cash reserve, it eventually pivots to something else.

You can see it working in the screenshots — the AI kept trying to upgrade a server to 8 TB but the cash reserve safety rail blocked it every time (SKIP: upgrade_server would violate cash reserve). That's the system working as intended.

The stack:

scb.js is the master orchestrator — one script to run everything. It handles scanning, rooting, parallel backdooring, TOR/program auto-purchasing, incremental server upgrades, and companion script launching. The Ollama player is a spawned child process that handles strategy while scb.js handles infrastructure.

There's also a watchdog (scb-watchdog.js) that monitors the Ollama connection, auto-reconnects when it drops, and can hot-reload scb.js when you save changes through bitburner-filesync.

https://preview.redd.it/tvrp9n52x1zg1.png?width=696&format=png&auto=webp&s=02caef6c6ffc76d131867897f53c7d2e012fb1f6

Flip any flag to disable a feature. The Ollama player has its own config for backend (ollama or claude), host, model, poll interval, and safety limits.

What I'm not claiming:

This won't optimally speedrun a BitNode. A well-tuned autopilot.js with hardcoded heuristics will outperform it on pure efficiency. What this does is make the game playable while you're AFK without writing a specific script for every situation. The AI adapts to whatever state it finds — early game, mid game, post-aug, new BitNode.

Repo: github.com/Subzero121800/BitBurner-AI

MIT licensed. PRs welcome. If you have a local Ollama instance running, you can be up in under 5 minutes.

reddit.com
u/Subzero121800 — 10 days ago

I wrote one script to crawl the darknet (dnet/exploreScript.js) to update the server databases >!and crack passcodes, copy itself to the new server, and run itself on the new server. !<

>!Because I didn't know how best to handle servers I had already cracked, I used hasSession. If hasSession = true, I didn't want to go through the whole database update and code cracking. Instead I would copy and execute dnet/second-passExplore.js. This script will see if the neighbors are in the database, and keep crawling until it finds a neighbor with hasSession = false, and then will run the first script on the current server to continue the database update. !<

>!Originally, instead of copying and running the second-pass script, I just had //TBD. exploreScript was running and propagating just fine. !<

>!After I got the scripts into the shape they are below, when I ran either script, the log for exploreScript.js started popping up everywhere. I had //ns.ui.openTail() at the top of the script leftover from when I started, so I removed that just in case it was triggering the tail to open still? It is crashing my game so I added a sleep before executing a new script so I had time to kill the script as logs popped up.!<

>!Please help, I have no idea what to do. I don't think I'm using the right words to google to find the right forum with the answer. !<

Please ignore my very rough script, it's not ready for public viewing but I am too annoyed by this problem to not ask for help.

dnet/exploreScript.js:

/** u/param {NS} ns */
export async function main(ns) {


  //First we want to read the database file
  const database = "dnet/database/dnetDB.json"
  //make an empty object in case the file doesn't exist yet/is empty
  let db = {};
  //read the file as a string
  const rawRead = ns.read(database);
  //Only parse the file if it contains something 
  //If it was empty then we would continue with db being an empty object
  if (rawRead &amp;&amp; rawRead.trim() !== "") {
    db = JSON.parse(rawRead);
  }


  //Probe the dark net
  //by getting the servers that are immediate neighbors to the 
  //currently connected server
  //MUST BE RUN ON THE SERVER YOU WANT THE NEIGHBORS OF
  const nearbyDarknetServers = ns.dnet.probe();
  for (const darkServ of nearbyDarknetServers) {
    //Check if we already have the server in the database
    //We are looking at an object type
    //Prototype has the methods for objects
    //HasOwnProperty is a mehtod that checks if a property key exists in the object
    //Call runs that method on the object db to see if the server name 
    //(property key) is present
    if (!Object.prototype.hasOwnProperty.call(db, darkServ)) {
      //Get data about the server
      const details = ns.dnet.getServerAuthDetails(darkServ);


      //Add server into the database, 
      //Append the object "details"
      //And make a "subproperty" in the darkServ object
      //(nested object)
      db[darkServ] = {
        hostname: darkServ,
        ...details,
        password: ""
      };
      ns.print(`INFO: Added ${darkServ} to database.`);
      //Grab and print new info to log
      const det = JSON.stringify(details, null, 2);
      ns.print(`Server Name: ${darkServ}`);
      ns.print(`${det}`);
    } else {
      ns.print(`${darkServ} is already in database.`);
    }


    //We use "w" here because we are reading the JSON, 
    //updating it if needed
    //so we can overwrite whatever is in the file. 
    ns.write(database, JSON.stringify(db, null, 2), "w")
  }


  //Once we update the database, go back through neighbors and propogate
  //Because we have a list of neighbors, we don't have to reconnect to 
  //the "seed" server before continuing. 
  for (const darkServ of nearbyDarknetServers) {
    const entry = db[darkServ];


    //If the server is not online and is not a neighbor, skip server
    if (!entry.isOnline || !entry.isConnectedToCurrentServer) {
      continue;
    }


    //If we already cracked the code (and thus continued to propogate)
    //do not recrack and reproppogate
    //but connect and continue to look for neighbors
    if (entry.hasSession) {
      //connect
      //copy and execute a propogator that continues until it finds
      //something with !entry.hasSession and runs this script again
      ns.print("FUTURE WORK")
      
      const result = await ns.dnet.authenticate(darkServ, "");
      //If it fails, move on to the next server
      if (!result) {
        //ERROR
        continue;
      } else {
        //Run secondPass crawl script script
        const script = "dnet/second-passExplore.js"
        ns.scp(script, darkServ);
        ns.exec(script, darkServ, {
          preventDuplicates: true, //This prevents running multiple copies of this script
        });
      }
      continue;
    };



    //If the password length is 0, no password is needed
    //Connect to the server, propogate, and reconnect to the initial server
    if (entry.passwordLength === 0) {
      const result = await ns.dnet.authenticate(darkServ, "");
      //If it fails, move on to the next server
      if (!result) {
        //ERROR
        continue;
      } else {
        //If we have successfully authenticated, 
        //we can now propogate
        ns.scp(ns.getScriptName(), darkServ);
        ns.exec(ns.getScriptName(), darkServ, {
          preventDuplicates: true, //This prevents running multiple copies of this script
        });


        continue;
      }


      //If the password is not straightforward, then use the solver
      //then copy and paste the propogation step above. 
    } else {
      //TBD


      //RECONNECT TO THE ORIGINAL SERVER SEED
      //IDK IF THIS IS RIGHT
      //BC YOU CANT CONNECT TO SESSION (HOME)
      //BUT IDK HOW TO GET THE CURRENT HOSTNAME
      //FUTURE WORK
      continue;
    }
  }
}

dnet/second-passExplore.js

/** u/param {NS} ns */
export async function main(ns) {
  //Purpose: passivley crawl through servers until you find a server
  //with a neighbor you haven't cracked the code and connected to already
  //(hasSession = false)


  //First we want to read the database file
  const database = "dnet/database/dnetDB.json"
  //make an empty object in case the file doesn't exist yet/is empty
  let db = {};
  //read the file as a string
  const rawRead = ns.read(database);
  //Only parse the file if it contains something 
  //If it was empty then we would continue with db being an empty object
  if (rawRead &amp;&amp; rawRead.trim() !== "") {
    db = JSON.parse(rawRead);
  }


  //Probe the dark net
  //by getting the servers that are immediate neighbors to the 
  //currently connected server
  //MUST BE RUN ON THE SERVER YOU WANT THE NEIGHBORS OF
  const nearbyDarknetServers = ns.dnet.probe();
  for (const darkServ of nearbyDarknetServers) {
    const exists = Object.prototype.hasOwnProperty.call(db, darkServ);


    //If the neighbor is not in the database
    //or if it has not been connected to,
    //run the explore script
    //that will continue to update the database and propogate password cracking
    if (!exists || db[darkServ].hasSession === false) {
      //Account for if the script is starting from home
      let hostName = ns.getHostname();
      if (hostName === "home") { hostName = "darkweb"}
      
      const scriptDB = "dnet/exploreScript.js"
      ns.scp(scriptDB, hostName);
      ns.exec(scriptDB, hostName, {
        preventDuplicates: true, //This prevents running multiple copies of this script
      });


      ns.print(`Running database update and code cracking script: ${scriptDB}`)


    } else {
      //If we have connected to the server previously, then we want to 
      //continue to more passively continue our journey until we find
      //something new
      const password = db[darkServ].password;
      ns.dnet.connectToSession(darkServ, password);
      ns.scp(ns.getScriptName(), darkServ);
      await ns.sleep(0. * 60 * 1000)
      ns.exec(ns.getScriptName(), darkServ, {
        preventDuplicates: true, //This prevents running multiple copies of this script
      });



    }
  }
}
reddit.com
u/Krispcrap — 9 days ago

I have await ns.sleep in all my scripts, before anyone asks.

My game keeps freezing. My guess is that it's because I have too many threads on too many different servers, but I'm not entirely sure, becuase I only notice the freezes after hours of inactivity. I play mainly on browsers, and the freeze I keep getting is opening onto the "while you were gone" screen, closing it, and then being unable to interact with the site at all, like it's a still image.

Anyone got any ideas here?

reddit.com
u/Top-Occasion-1300 — 13 days ago

i’ve maxed all servers and written autodeployable code that continuously roots and places scripts on any new servers that i can unlock with hacking level increases. going to the city is mostly boring because i’m not working a job to make 1200 more a second when i already make trillion an hour. i feel like my hack weaken grow loop is as close to optimal as i care to get, i have all scripts and the formulas unlocked and integrated, what do i do now other than just wait for higher hacking level? Anything cool i can do with my money? i have 30 hackneys maxed out already

reddit.com
u/ChefNaughty — 13 days ago