CORPORATION: Add documentation (#1121)

This ports the PDF manual by @catloversg into .md form, so it can be used in-game and via browsing on GitHub.

It also adds MathJax rendering to in-game docs, so that the math formulas can be rendered. (GitHub already supports this natively.)
This commit is contained in:
catloversg 2024-03-04 18:10:18 +07:00 committed by GitHub
parent 6a3d22d7bd
commit ffc34387f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 2238 additions and 84 deletions

@ -7,6 +7,7 @@ module.exports = {
transformIgnorePatterns: ["node_modules/(?!react-markdown)/"],
testPathIgnorePatterns: [".cypress", "node_modules", "dist"],
testEnvironment: "./FixJSDOMEnvironment.ts",
setupFiles: ["./jest.polyfills.js"],
moduleNameMapper: {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$":
"<rootDir>/test/__mocks__/fileMock.js",

6
jest.polyfills.js Normal file

@ -0,0 +1,6 @@
const { TextEncoder, TextDecoder } = require("node:util");
Object.defineProperties(globalThis, {
TextEncoder: { value: TextEncoder },
TextDecoder: { value: TextDecoder },
});

207
package-lock.json generated

@ -90,6 +90,8 @@
"prettier": "^2.8.8",
"raw-loader": "^4.0.2",
"react-refresh": "^0.14.0",
"rehype-mathjax": "^4.0.3",
"remark-math": "^5.1.1",
"source-map": "^0.7.4",
"start-server-and-test": "^1.15.4",
"style-loader": "^3.3.3",
@ -4465,6 +4467,12 @@
"integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==",
"dev": true
},
"node_modules/@types/katex": {
"version": "0.16.7",
"resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz",
"integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==",
"dev": true
},
"node_modules/@types/keyv": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
@ -4480,6 +4488,12 @@
"integrity": "sha512-Vrjz5N5Ia4SEzWWgIVwnHNEnb1UE1XMkvY5DGXrAeOGE9imk0hgTHh5GyDjLDJi9OTCn9oo9dXH1uToK1VRfrg==",
"dev": true
},
"node_modules/@types/mathjax": {
"version": "0.0.37",
"resolved": "https://registry.npmjs.org/@types/mathjax/-/mathjax-0.0.37.tgz",
"integrity": "sha512-y0WSZBtBNQwcYipTU/BhgeFu1EZNlFvUNCmkMXV9kBQZq7/o5z82dNVyH3yy2Xv5zzeNeQoHSL4Xm06+EQiH+g==",
"dev": true
},
"node_modules/@types/mdast": {
"version": "3.0.13",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.13.tgz",
@ -9518,6 +9532,64 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hast-util-from-dom": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-4.2.0.tgz",
"integrity": "sha512-t1RJW/OpJbCAJQeKi3Qrj1cAOLA0+av/iPFori112+0X7R3wng+jxLA+kXec8K4szqPRGI8vPxbbpEYvvpwaeQ==",
"dev": true,
"dependencies": {
"hastscript": "^7.0.0",
"web-namespaces": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/hast-util-from-dom/node_modules/hast-util-parse-selector": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz",
"integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==",
"dev": true,
"dependencies": {
"@types/hast": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/hast-util-from-dom/node_modules/hastscript": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz",
"integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==",
"dev": true,
"dependencies": {
"@types/hast": "^2.0.0",
"comma-separated-tokens": "^2.0.0",
"hast-util-parse-selector": "^3.0.0",
"property-information": "^6.0.0",
"space-separated-tokens": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/hast-util-is-element": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-2.1.3.tgz",
"integrity": "sha512-O1bKah6mhgEq2WtVMk+Ta5K7pPMqsBBlmzysLdcwKVrqzZQ0CHqUPiIVspNhAG1rvxpvJjtGee17XfauZYKqVA==",
"dev": true,
"dependencies": {
"@types/hast": "^2.0.0",
"@types/unist": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/hast-util-parse-selector": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz",
@ -9527,6 +9599,22 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/hast-util-to-text": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-3.1.2.tgz",
"integrity": "sha512-tcllLfp23dJJ+ju5wCCZHVpzsQQ43+moJbqVX3jNWPB7z/KFC4FyZD6R7y94cHL6MQ33YtMZL8Z0aIXXI4XFTw==",
"dev": true,
"dependencies": {
"@types/hast": "^2.0.0",
"@types/unist": "^2.0.0",
"hast-util-is-element": "^2.0.0",
"unist-util-find-after": "^4.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/hast-util-whitespace": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz",
@ -12713,6 +12801,31 @@
"node": ">=8"
}
},
"node_modules/katex": {
"version": "0.16.9",
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.9.tgz",
"integrity": "sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ==",
"dev": true,
"funding": [
"https://opencollective.com/katex",
"https://github.com/sponsors/katex"
],
"dependencies": {
"commander": "^8.3.0"
},
"bin": {
"katex": "cli.js"
}
},
"node_modules/katex/node_modules/commander": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
"dev": true,
"engines": {
"node": ">= 12"
}
},
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@ -13245,6 +13358,21 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/mdast-util-math": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-2.0.2.tgz",
"integrity": "sha512-8gmkKVp9v6+Tgjtq6SYx9kGPpTf6FVYRa53/DLh479aldR9AyP48qeVOgNZ5X7QUK7nOy4yw7vg6mbiGcs9jWQ==",
"dev": true,
"dependencies": {
"@types/mdast": "^3.0.0",
"longest-streak": "^3.0.0",
"mdast-util-to-markdown": "^1.3.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/mdast-util-phrasing": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz",
@ -13550,6 +13678,25 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/micromark-extension-math": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-2.1.2.tgz",
"integrity": "sha512-es0CcOV89VNS9wFmyn+wyFTKweXGW4CEvdaAca6SWRWPyYCbBisnjaHLjWO4Nszuiud84jCpkHsqAJoa768Pvg==",
"dev": true,
"dependencies": {
"@types/katex": "^0.16.0",
"katex": "^0.16.0",
"micromark-factory-space": "^1.0.0",
"micromark-util-character": "^1.0.0",
"micromark-util-symbol": "^1.0.0",
"micromark-util-types": "^1.0.0",
"uvu": "^0.5.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/micromark-factory-destination": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz",
@ -15732,6 +15879,26 @@
"jsesc": "bin/jsesc"
}
},
"node_modules/rehype-mathjax": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/rehype-mathjax/-/rehype-mathjax-4.0.3.tgz",
"integrity": "sha512-QIwWH9U+r54nMQklVkT1qluxhKyzdPWz9dFwgel3BrseQsWZafRTDTUj8VR8/14nFuRIV2ChuCMz4zpACPoYvg==",
"dev": true,
"dependencies": {
"@types/hast": "^2.0.0",
"@types/mathjax": "^0.0.37",
"hast-util-from-dom": "^4.0.0",
"hast-util-to-text": "^3.1.0",
"jsdom": "^20.0.0",
"mathjax-full": "^3.0.0",
"unified": "^10.0.0",
"unist-util-visit": "^4.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/relateurl": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
@ -15756,6 +15923,22 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/remark-math": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/remark-math/-/remark-math-5.1.1.tgz",
"integrity": "sha512-cE5T2R/xLVtfFI4cCePtiRn+e6jKMtFDR3P8V3qpv8wpKjwvHoBA4eJzvX+nVrnlNy0911bdGmuspCSwetfYHw==",
"dev": true,
"dependencies": {
"@types/mdast": "^3.0.0",
"mdast-util-math": "^2.0.0",
"micromark-extension-math": "^2.0.0",
"unified": "^10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/remark-parse": {
"version": "10.0.2",
"resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz",
@ -17371,6 +17554,20 @@
"node": ">= 0.8.0"
}
},
"node_modules/unist-util-find-after": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-4.0.1.tgz",
"integrity": "sha512-QO/PuPMm2ERxC6vFXEPtmAutOopy5PknD+Oq64gGwxKtk4xwo9Z97t9Av1obPmGU0IyTa6EKYUfTrK2QJS3Ozw==",
"dev": true,
"dependencies": {
"@types/unist": "^2.0.0",
"unist-util-is": "^5.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/unist-util-generated": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz",
@ -17710,6 +17907,16 @@
"minimalistic-assert": "^1.0.0"
}
},
"node_modules/web-namespaces": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
"integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==",
"dev": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/webidl-conversions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",

@ -97,7 +97,9 @@
"typescript": "^5.2.2",
"webpack": "^5.88.2",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
"webpack-dev-server": "^4.15.1",
"remark-math": "^5.1.1",
"rehype-mathjax": "^4.0.3"
},
"engines": {
"node": ">=14"

@ -0,0 +1,44 @@
# Basic gameplay Terms
## Basic gameplay
There is a `corporation-management-handbook.lit` on your home server. Read it.
Go to City Hall in Sector-12 and create a Corporation through the UI if you want. However, you really should do everything by scripting.
You can use seed money when creating a corporation in BN3.
There are multiple industries that you can expand into. In order to do that, you need to choose an industry and create a division. Agriculture is the best starting industry. Check this [section](./industry-supply-chain.md) for details.
Each division can expand to 6 cities.
Each industry has different input materials and output materials/products. For example: Agriculture needs Water and Chemicals to produce Plants and Food. The number next to each material is its "coefficient" (You can call it "weight" or "factor" if you want).
$$0.5\ \textit{Water}+0.2\ \textit{Chemicals} \Rightarrow 1\ \textit{Plants}+1\ \textit{Food}$$
There is no "offline progress" in corporation. When you go offline, the corporation accumulates bonus time.
A corporation continuously transitions between 5 states: START → PURCHASE → PRODUCTION → EXPORT → SALE → START. The action occurs when the state is _entered_, i.e., when the state is PURCHASE, it means purchasing has just occurred. One cycle (going through one of these transitions) takes 10 seconds. If you have enough bonus time, it takes one second. Check this [section](./miscellany.md) for details.
Each division has its "division product multiplier". This multiplier can be increased by buying [boost materials](./boost-material.md): AI Cores, Hardware, Real Estate, and Robots.
You should look around to get familiar with the UI. One confusing thing for newbies is how to setup the "buy value" to buy materials. We have "Purchase" and "Bulk purchase":
- Purchase: This is "buy per second" value. For example: In cycle 1, you enter "100", then in cycle 2, at PURCHASE state, you'll have 100\*10 units in your inventory. You can buy more than what your funds allows (and go into debt) with this option. Important note: when you have enough units that you want, you must press "Clear purchase", otherwise it'll buy forever until you run out of storage space.
- Bulk purchase: You buy exactly what you want. Must pay upfront.
If you want to buy something, write a script to do that. It's too error-prone to do it manually.
When you hover your mouse over a warehouse, you'll see the space that those materials take up in the warehouse. They are _not_ the numbers of units.
## Terms
Smart Supply: Automatically buy optimal quantities of input material units.
Export: Allow export/import materials between divisions.
Wilson: Wilson Analytics upgrade.
Market-TA2: Automatically set optimal prices for your output materials/products.
RP: Research point.

@ -0,0 +1,170 @@
# Boost material
## Division production multiplier
Each industry has a different set of boost material's coefficients. For example:
- Agriculture:
- AI Cores: 0.3
- Hardware: 0.2
- Real Estate: 0.72
- Robots: 0.3
- Chemical:
- AI Cores: 0.2
- Hardware: 0.2
- Real Estate: 0.25
- Robots: 0.25
- Tobacco:
- AI Cores: 0.15
- Hardware: 0.15
- Real Estate: 0.15
- Robots: 0.25
The division production multiplier is used for calculating [division raw production](./division-raw-production.md) in the PRODUCTION state. It's sum of each warehouse's `cityMult`, and `cityMult` is calculated by combining the quantity of each boost material with the boost material's coefficient.
This multiplier is `this.productionMult` in `Division.ts`.
```typescript
calculateProductionFactors(): void {
let multSum = 0;
for (const warehouse of getRecordValues(this.warehouses)) {
const materials = warehouse.materials;
const cityMult =
Math.pow(0.002 * materials["Real Estate"].stored + 1, this.realEstateFactor) *
Math.pow(0.002 * materials.Hardware.stored + 1, this.hardwareFactor) *
Math.pow(0.002 * materials.Robots.stored + 1, this.robotFactor) *
Math.pow(0.002 * materials["AI Cores"].stored + 1, this.aiCoreFactor);
multSum += Math.pow(cityMult, 0.73);
}
multSum < 1 ? (this.productionMult = 1) : (this.productionMult = multSum);
}
```
This is the reason we must expand to all 6 cities. More cities → Higher `this.productionMult` → Higher raw production → More produced materials/products → Higher profit per city → Higher total profit.
Expanding to 6 cities means `this.productionMult` is multiplied by 6, and we have 6 cities, so effectively production is multiplied by 36. This is not exactly true because there are more things that affect the raw production value of each city, but x36 can be seen as a rough estimate of benefit, especially in early rounds. In those rounds, division production multiplier is the most important thing.
## Optimizer
In order to increase `this.productionMult`, we need to buy boost materials. The problem is how much we should buy each material, given a specific constraint on storage space.
Each boost material has a coefficient ("factor" in source code) and a base size (size for 1 unit in storage).
Let's define:
- 4 coefficients: ${c_{1}}$, ${c_{2}}$, ${c_{3}}$, ${c_{4}}$
- 4 base sizes: ${s_{1}}$, ${s_{2}}$, ${s_{3}}$, ${s_{4}}$
- Quantities of each boost materials: x, y, z, w
Assuming the same warehouse setup in all cities, the division production multiplier is:
$$F(x,y,z,w) = \sum_{i = 1}^{6}\left( (1 + 0.002\ast x)^{c_{1}}\ast(1 + 0.002\ast y)^{c_{2}}{\ast(1 + 0.002\ast z)}^{c_{3}}{\ast(1 + 0.002\ast w)}^{c_{4}} \right)^{0.73}$$
In order to find the maximum of the function above, we can find the maximum of this function:
$$F(x,y,z,w) = (1 + 0.002\ast x)^{c_{1}}\ast(1 + 0.002\ast y)^{c_{2}}{\ast(1 + 0.002\ast z)}^{c_{3}}{\ast(1 + 0.002\ast w)}^{c_{4}}$$
Constraint function (S is storage space):
$$G(x,y,z,w) = s_{1}\ast x + s_{2}\ast y + s_{3}\ast z + s_{4}\ast w = S$$
Problem: Find the maximum of $F(x,y,z,w)$ with constraint $G(x,y,z,w)$.
## Solution
### Lagrange multiplier method
Disclaimer: This is based on discussion between \@Jesus and \@yichizhng on Discord. All credit goes to them.
By using the [Lagrange multiplier](https://en.wikipedia.org/wiki/Lagrange_multiplier) method, we have this system:
$$\begin{cases} \frac{\partial F}{\partial x} &= \lambda\frac{\partial G}{\partial x} \newline \frac{\partial F}{\partial y} &= \lambda\frac{\partial G}{\partial y} \newline \frac{\partial F}{\partial z} &= \lambda\frac{\partial G}{\partial z} \newline \frac{\partial F}{\partial w} &= \lambda\frac{\partial G}{\partial w} \newline G(x,y,z,w) &= S\end{cases}$$
In order to solve this system, we have 2 choices:
- Solve that system with [Ceres Solver](./miscellany.md).
- Do the hard work with basic calculus and algebra. This is the optimal way in both accuracy and performance, so we'll focus on it. In the following sections, I'll show the proof for this solution.
$$x\ast s_{1} = \frac{S - 500\ast\left( \frac{s_{1}}{c_{1}}\ast\left( c_{2} + c_{3} + c_{4} \right) - \left( s_{2} + s_{3} + s_{4} \right) \right)}{\frac{c_{1} + c_{2} + c_{3} + c_{4}}{c_{1}}}$$
$$y\ast s_{2} = \frac{S - 500\ast\left( \frac{s_{2}}{c_{2}}\ast\left( c_{1} + c_{3} + c_{4} \right) - \left( s_{1} + s_{3} + s_{4} \right) \right)}{\frac{c_{1} + c_{2} + c_{3} + c_{4}}{c_{2}}}$$
$$z\ast s_{3} = \frac{S - 500\ast\left( \frac{s_{3}}{c_{3}}\ast\left( c_{1} + c_{2} + c_{4} \right) - \left( s_{1} + s_{2} + s_{4} \right) \right)}{\frac{c_{1} + c_{2} + c_{3} + c_{4}}{c_{3}}}$$
$$w\ast s_{4} = \frac{S - 500\ast\left( \frac{s_{4}}{c_{4}}\ast\left( c_{1} + c_{2} + c_{3} \right) - \left( s_{1} + s_{2} + s_{3} \right) \right)}{\frac{c_{1} + c_{2} + c_{3} + c_{4}}{c_{4}}}$$
## Proof
Define: $k = 0.002$
$$\begin{cases}\frac{\partial F}{\partial x} = \left( k\ast c_{1}\ast(1 + k\ast x)^{c_{1} - 1} \right)\ast(1 + k\ast y)^{c_{2}}\ast(1 + k\ast z)^{c_{3}}\ast(1 + k\ast w)^{c_{4}} = \lambda\ast s_{1} \newline \frac{\partial F}{\partial y} = (1 + k\ast x)^{c_{1}}\ast\left( k\ast c_{1}\ast(1 + k\ast y)^{c_{2} - 1} \right)\ast(1 + k\ast z)^{c_{3}}\ast(1 + k\ast w)^{c_{4}} = \lambda\ast s_{2} \end{cases}$$
$$k\ast c_{1}\ast(1 + k\ast x)^{- 1}\ast s_{2} = k\ast c_{2}\ast(1 + k\ast y)^{- 1}\ast s_{1}$$
$$c_{1}\ast s_{2}\ast(1 + k\ast y) = c_{2}\ast s_{1}\ast(1 + k\ast x)$$
$$1 + k\ast y = \frac{c_{2}\ast s_{1}}{c_{1}\ast s_{2}}\ast(1 + k\ast x)$$
$$y = \frac{c_{2}\ast s_{1} + k\ast x\ast c_{2}\ast s_{1} - c_{1}\ast s_{2}}{k\ast c_{1}\ast s_{2}}$$
$$y\ast s_{2} = \frac{c_{2}\ast s_{1}\ast s_{2} + k\ast x\ast c_{2}\ast s_{1}\ast s_{2} - c_{1}\ast s_{2}\ast s_{2}}{k\ast c_{1}\ast s_{2}}$$
$$y\ast s_{2} = \frac{c_{2}\ast s_{1}}{k\ast c_{1}} + \frac{x\ast c_{2}\ast s_{1}}{c_{1}} - \frac{s_{2}}{k}$$
$$y\ast s_{2} = \frac{c_{2}}{c_{1}}\ast x\ast s_{1} + \frac{1}{k}\ast\frac{c_{2}\ast s_{1} - c_{1}\ast s_{2}}{c_{1}}$$
$$y\ast s_{2} = \frac{c_{2}}{c_{1}}\ast x\ast s_{1} + 500\ast\frac{c_{2}\ast s_{1} - c_{1}\ast s_{2}}{c_{1}}$$
Repeating the above steps, we have:
$$z\ast s_{3} = \frac{c_{3}}{c_{1}}\ast x\ast s_{1} + 500\ast\frac{c_{3}\ast s_{1} - c_{1}\ast s_{3}}{c_{1}}$$
$$w\ast s_{4} = \frac{c_{4}}{c_{1}}\ast x\ast s_{1} + 500\ast\frac{c_{4}\ast s_{1} - c_{1}\ast s_{4}}{c_{1}}$$
Substituting into the constraint function:
$$x\ast s_{1} + y\ast s_{2} + z\ast s_{3} + w\ast s_{4} = S$$
$$x\ast s_{1} + \frac{c_{2}}{c_{1}}\ast x\ast s_{1} + 500\ast\frac{c_{2}\ast s_{1} - c_{1}\ast s_{2}}{c_{1}} + \frac{c_{3}}{c_{1}}\ast x\ast s_{1} + 500\ast\frac{c_{3}\ast s_{1} - c_{1}\ast s_{3}}{c_{1}} + \frac{c_{4}}{c_{1}}\ast x\ast s_{1} + 500\ast\frac{c_{4}\ast s_{1} - c_{1}\ast s_{4}}{c_{1}} = S$$
$$\frac{x\ast s_{1}\ast\left( c_{1} + c_{2} + c_{3} + c_{4} \right)}{c_{1}} + \frac{500}{c_{1}}\ast\left( c_{2}\ast s_{1} - c_{1}\ast s_{2} + c_{3}\ast s_{1} - c_{1}\ast s_{3} + c_{4}\ast s_{1} - c_{1}\ast s_{4} \right) = S$$
$$\frac{x\ast s_{1}\ast\left( c_{1} + c_{2} + c_{3} + c_{4} \right)}{c_{1}} + \frac{500}{c_{1}}\ast\left( s_{1}\ast\left( c_{2} + c_{3} + c_{4}\ \right) - c_{1}\ast\left( s_{2} + s_{3} + s_{4} \right) \right) = S$$
$$x\ast s_{1}\ast\frac{c_{1} + c_{2} + c_{3} + c_{4}}{c_{1}} + \frac{500}{c_{1}}\ast\left( s_{1}\ast\left( c_{2} + c_{3} + c_{4}\ \right) - c_{1}\ast\left( s_{2} + s_{3} + s_{4} \right) \right) = S$$
$$x\ast s_{1} = \frac{S - 500\ast\left( \frac{s_{1}}{c_{1}}\ast\left( c_{2} + c_{3} + c_{4} \right) - \left( s_{2} + s_{3} + s_{4} \right) \right)}{\frac{c_{1} + c_{2} + c_{3} + c_{4}}{c_{1}}}$$
We can do the same steps for y,z,w.
## Handle low storage space
With small S, any variable (x,y,z,w) can be negative. In that case, we remove the variable that ends up being negative and then redo the steps above. When implementing this solution, we can use a recursive function to handle those cases.

@ -0,0 +1,47 @@
# Demand - Competition
## Usage
They are used for calculating `MaxSalesVolume` of materials and products.
`Market Research - Demand` grants access to `Demand` data.
`Market Data - Competition` grants access to `Competition` data.
## Material
Each material has its own `demandBase`, `demandRange`, `competitionBase`, `competitionRange`, and `maxVolatility`. Both `demand` and `competition` start at their bases and are always in their respective ranges.
This is non-intuitive: `demand` and `competition` are _not_ used for calculating `marketPrice`.
During the START state, the game calculates 6 variables:
```typescript
const priceVolatility: number = (Math.random() * this.maxVolatility) / 300;
const priceChange: number = 1 + priceVolatility;
const compVolatility: number = (Math.random() * this.maxVolatility) / 100;
const compChange: number = 1 + compVolatility;
const dmdVolatility: number = (Math.random() * this.maxVolatility) / 100;
const dmdChange: number = 1 + dmdVolatility;
```
`priceChange`, `compChange` and `dmdChange` are the amount of `marketPrice`, `competition` and `demand` changed in the next steps.
After that, it randomizes twice:
- First: `Math.random()` < 0.5. If yes, increase `competition` and `marketPrice`. If not, decrease them.
- Second: `Math.random()` < 0.5. If yes, increase `demand` and `marketPrice`. If not, decrease them.
## Product
Initial values are set when the product is finished. Check the next [section](./product.md) for the formulas.
During the START state, the game decreases `demand` and increases `competition` of the product.
- Amount of change:
$$AmountOfChange = Random(0,3)*0.0004$$
- This amount is multiplied by 3 if the industry is Pharmaceutical, Software or Robotics.
`Demand`'s minimum value is 0.001. `Competition`'s maximum value is 99.99.

@ -0,0 +1,40 @@
# Division raw production
## Definition
Each industry requires different input materials. Each required material has its own coefficient. This value is not the same as the boost material's coefficient; they are different things. For example:
- Agriculture: { Water: 0.5, Chemicals: 0.2 }
- Chemical: { Plants: 1, Water: 0.5 }
- Tobacco: { Plants: 1 }
Each division has a number that I call "Division raw production". This raw value is the division's production capability. Let's call it `RawProduction`. It's used for:
- Calculating how much input material that we need. It's multiplied by the input material's coefficient to find the required quantity of that input material.
- Calculating how much material/product that division can produce. It's multiplied by `ProducibleFrac`. `ProducibleFrac` starts at 1 and is reduced if there are not enough input materials.
For example, with Agriculture, if `RawProduction` is 1000, we need 500 units of Water and 200 units of Chemicals. With these input materials, we can produce 1000 units of Plants and 1000 units of Food.
### Formula
`RawProduction` is the product of 4 multipliers:
- Office multiplier:
- Employee production in 3 jobs (Operations, Engineer, Management) and their sum:
- `OperationsProd = office.employeeProductionByJob.Operations`
- `EngineerProd = office.employeeProductionByJob.Engineer`
- `ManagementProd = office.employeeProductionByJob.Management`
- $TotalEmployeesProd = OperationsProd + EngineerProd + ManagementProd$
- Management factor:
$$ManagementFactor = 1 + \frac{ManagementProd}{1.2\ast TotalEmployeesProd}$$
- Employee production multiplier:
$$EmployeeProductionMultiplier = \left( (OperationsProd)^{0.4} + (EngineerProd)^{0.3} \right)\ast ManagementFactor$$
- Balancing multiplier:
$$BalancingMultiplier = 0.05$$
- If output is material:
$$OfficeMultiplier = BalancingMultiplier\ast EmployeeProductionMultiplier$$
- If output is product:
$$OfficeMultiplier = 0.5\ast BalancingMultiplier\ast EmployeeProductionMultiplier$$
- Division production multiplier: see previous [section](./boost-material.md).
- Upgrade multiplier: multiplier from [Smart Factories](./unlocks-upgrade-research.md).
- Research multiplier: multiplier from [researches](./unlocks-upgrade-research.md).

@ -0,0 +1,213 @@
# FAQ
#### What is a corporation good for?
Generating ridiculously massive income. With this income, you can buy whatever you want, e.g., augmentations or bribing factions for reputation.
#### How many investment rounds should I take?
There are 4 rounds, and you should take all of them. Investment funds greatly boost your corporation development. Your corporation won't be able to reach its full potential in a reasonable time without them.
#### Investors take too many shares. Can I buy them back later?
No.
#### The government takes too many shares when I use the "Seed money" option. Can I buy them back later?
No.
#### Why can I not buy back anything? It's ridiculous.
You can only buy back shares that were issued. The shares that were owned by the government (when we use seed money) and investors cannot be bought back.
One thing to remember: this is a game, not real life. In this game, you always have total control over your corporation. When your profit hits exponential growth, the share percentage means nothing. If your corporation's profit is 1e90/s and you only have 1% shares, you still have 1e88/s.
#### My corporation generates profit. Why does my money not increase?
Go public and set dividend.
#### How many shares should I issue?
0
#### Why is my "earnings as a shareholder" lower than my calculation ("Dividends per share" \* "Owned Stock Shares")?
You have to pay tax. ShadyAccounting and GovernmentPartnership reduce tax. Check this [section](./financial-statement.md) for details.
#### All corporation APIs require too much RAM. How do I deal with it?
Earn money by any normal means: hacking, committing crimes, cheating in casino, etc. There is also another way:
- Follow the [advices](./general-advice.md) on round 1, but at the end, do not accept the investment offer.
- Go public immediately.
- Sell all your shares immediately.
- Sell CEO position and start a new corporation.
Also note that you don't have to do everything in one script. You can make smaller scripts that do less and use fewer APIs to keep the RAM usage down, and use `run()` to chain them together.
#### Why can I not create a corporation with the government's seed money ("Use seed money")?
That option is only available in BN3.
#### Can I sell my corporation via API?
No.
#### Can I transfer my (personal) money to the corporation's funds?
No. However, with SF9, you can sell hashes for corporation funds or RP.
#### Why can I not bribe factions for reputations? What's the exchange rate?
Your corporation's valuation must be at least 100e12 to bribe. Exchange rate: 1e9/reputation.
#### What's the maximum number of divisions?
In BN3, it's 20.
#### Why does my division not produce anything?
Check these things:
- Have at least 1 employee in Operations or Engineer
- Have enough storage space. Warehouse congestion is a common problem.
#### Which industry should I focus on?
Check this [section](./industry-supply-chain.md).
#### Should I create more divisions for the same industry? For example: multiple Agriculture.
No, focus your funds on one division for each industry.
#### Which "feature" (Export, Smart Supply, etc.) should I unlock?
Check this [section](./unlocks-upgrade-research.md).
#### Which upgrade should I buy?
Check this [section](./unlocks-upgrade-research.md).
#### Which research should I buy?
Check this [section](./unlocks-upgrade-research.md).
#### I unlocked "Smart Supply", but it does not work or its behavior is weird.
Check these things:
- You have to enable it if you unlock it after purchasing a warehouse.
- Always choose "Use leftovers". "Use imported" is only useful in special cases.
#### How do I implement a custom Smart Supply script?
Check this [section](./smart-supply.md).
#### How do I setup the quantity of exported materials?
Specify an export string. The optimal export string is `(IPROD+IINV/10)*(-1)`. Check this [section](./miscellany.md) for details.
#### Is research's benefit shared between different divisions?
It's shared if those divisions are in the same industry. However, the RP pool is not shared.
#### Should I expand to all 6 cities?
Yes. In fact, you must do that for maximum efficiency. Check this [section](./boost-material.md).
#### What are boost materials?
They are the materials that boost [division production multiplier](./boost-material.md). There are 4 boost materials: AI Cores, Hardware, Real Estate, and Robots.
#### How many boost materials that I should buy?
Check the optimizer in this [section](./boost-material.md).
#### Why does setAutoJobAssignment not take effect immediately?
It only takes effect in next cycle's START state.
#### Why does energy and morale matter?
They are used for calculating `employeeProductionByJob`, then that property is used for calculating other things: RP, material's quality, product's stats, division raw production and material/product's `MaxSalesVolume`.
#### What do Interns do?
Don't bother with that job. Its only purpose is to maintain energy and morale. A tea/party script can do it for you, and it is very simple to implement.
#### Everybody tells me to use 1/9 as Intern ratio, but when I use it, energy and morale still drop.
You can only use that ratio when your corporation works fine (funds #### 0 or profit #### 0). If it does not, use 1/6.
#### Are there any other ways to maintain these 2 stats?
There are researches for that. However, you should never buy them, it's always better spending your RP elsewhere or just stock up on RP.
#### Buying tea and throwing parties cost me too much money. Why are they so expensive?
Tea and parties are cheap. If your budget is so low that they cost you too much money, it means you wasted too much of your funds.
#### How much money should I spend to throw parties? How often should I buy tea / throw party?
Check this [section](./office.md).
#### How do I know if the qualities of my input materials are too low and need to be improved?
Check this [section](./quality.md).
#### What are Awareness and Popularity?
Check this [section](./wilson-analytics-advert.md).
#### Should I buy Dream Sense?
No. Check this [section](./wilson-analytics-advert.md) for the reason.
#### Is Wilson retroactive?
No.
#### Does that mean I should buy Wilson as soon as possible? If yes, then why don't you buy any Wilson in round 1 and 2?
You should buy Wilson as soon as possible, but not too soon. Round 1 and 2 are those cases. Check this [section](./wilson-analytics-advert.md) for details.
#### What are Demand and Competition?
Check this [section](./demand-competition.md).
#### How much should I spend for "Design investment" and "Marketing investment" when I create new product? How do they affect the product?
They are not too important. It's fine to spend 1% of your current funds for them. Check this [section](./product.md) for details.
#### Should I buy Market-TA1?
No, wait for Market-TA2. Market-TA1 is useless on its own.
#### When should I buy Market-TA2?
As soon as possible, it greatly increases your profit because it can find the optimal price. However, that research is expensive, it costs a total of 75e3 RP (Hi-Tech R&D Laboratory + Market-TA1 + Market-TA2). Depleting the entire RP pool extremely degrades your product rating, so I recommend saving up 150e3 RP before buying it. Depleting half of the RP pool is acceptable, considering the positive effect of Market-TA2.
#### What is the difference between Market-TA1 and Market-TA2?
Market-TA1: set a price that ensures that you can sell all produced goods in storage.
Market-TA2: set the highest possible price that ensures that you can sell all produced goods in storage.
#### I bought Market-TA2, but it does not set optimal price for me.
You have to enable it.
#### Is there a workaround for Market-TA2? Waiting for RP takes too long.
Yes, you can reimplement Market-TA2. Implementing a custom Market-TA2 script is the best optimization in round 3+. Check this [section](./optimal-selling-price-market-ta2.md) to see how to do it.
#### How do I discard materials/products?
Set the selling price to 0.
#### What is a dummy division?
Check this [section](./miscellany.md).
#### Can I skip Chemicals in round 2 and invest all funds in Agriculture?
No. Without a Chemical division, the quality of your Agriculture's output materials will be too low, and you cannot sell all those low-quality materials at good price.

@ -0,0 +1,181 @@
# Financial statement
## Total assets
`TotalAssets` is the sum of:
- Funds.
- With each division:
- Division's `RecoupableValue`. It's half of the sum of:
- Industry's starting cost.
- With each city that division has expanded to (exclude Sector-12):
- Office's initial cost.
- Warehouse's initial cost.
- Output material: `material.stored * material.averagePrice`.
- Product: `product.stored * product.productionCost`.
This value is kept track by `TotalAssets` and `PreviousTotalAssets`.
Funds is increased/decreased by `gainFunds`/`loseFunds` for each "action" (buying tea, throwing party, buying upgrade, etc.). Each action is either "long-term" (`FundsSourceLongTerm`) or "short-term" (`FundsSourceShortTerm`). If an action is "long-term", it modifies `totalAssets`.
```typescript
if (LongTermFundsSources.has(source)) {
this.totalAssets += amt;
}
this.funds += amt;
```
`FundsSourceLongTerm` and `FundsSourceShortTerm` are in `FundsSource.ts`.
## Valuation
Cycle's valuation:
- AssetDelta:
$$AssetDelta = \frac{TotalAssets - PreviousTotalAssets}{10}$$
- Pre-IPO:
- If `AssetDelta` is greater than 0, it's used for calculating valuation.
- Formula:
$$Valuation = \left( 10^{10} + \frac{Funds}{3} + AssetDelta\ast 315000 \right)\ast\left( \sqrt[12]{1.1} \right)^{NumberOfOfficesAndWarehouses}$$
- Valuation is rounded down to nearest million.
- Post-IPO:
- `AssetDelta` is affected by `DividendRate`:
$$AssetDelta = AssetDelta\ast(1 - DividendRate)$$
- Formula:
$$Valuation = (Funds + AssetDelta\ast 85000)\ast\left(\sqrt[12]{1.1}\right)^{NumberOfOfficesAndWarehouses}$$
- Minimum value of valuation is $10^{10}$.
- Valuation is multiplied by `CorporationValuation`. Many BitNodes cripple Corporation via this multiplier.
Corporation's valuation is the mean of last 10 cycles' valuations.
Bribing faction for reputation is unlocked when corporation's valuation is greater than or equal to 100e12. Exchange rate: 1e9/reputation.
## Investment offer
There are 4 investment rounds.
Each round has its own `FundingRoundShares` and `FundingRoundMultiplier`.
- $FundingRoundShares = [0.1, 0.35, 0.25, 0.2]$
- $FundingRoundMultiplier = [3, 2, 2, 1.5]$
Formula:
$$Offer = CorporationValuation\ast FundingRoundShares\ast FundingRoundMultiplier$$
Analyses:
- Offer depends on `Funds`, `AssetDelta` and `NumberOfOfficesAndWarehouses`.
- `Funds` are usually depleted to improve divisions.
- `NumberOfOfficesAndWarehouses` is the exponent of the multiplier, it can be increased by creating [dummy division](./miscellany.md). It is an easy way to get higher offer in round 3+, when we have enough funds to do that.
- `AssetDelta` is multiplied by 315000, so it is the main source of offer.
- Assuming that we can sell all produced units and not buy more boost materials, `AssetDelta` is delta of funds, and delta of funds is delta of profit. This is why we try our best to improve profit.
## Dividend
`DividendTax` depends on `CorporationSoftcap`. In BN3, `CorporationSoftcap` is 1.
$$DividendTax = 1 - CorporationSoftcap + 0.15$$
`ShadyAccounting` reduces `DividendTax` by 0.05.
`GovernmentPartnership` reduces `DividendTax` by 0.1.
Formula:
$$TotalDividends = DividendRate\ast(Revenue - Expenses)\ast 10$$
$$Dividend = \left(OwnedShares\ast\frac{TotalDividends}{TotalShares}\right)^{1 - DividendTax}$$
Retained earning:
$$RetainedEarning = (1 - DividendRate)\ast(Revenue - Expenses)\ast 10$$
Dividend is added to player's money. Retained earning is added to corporation's funds. This means if we increase `DividendRate`, corporation's valuation is dwindled.
## Shares
Self-fund:
- Cost 150b.
- Total shares: 1b.
- Initial owned shares: 1b.
Use seed money:
- Does not cost money.
- Total shares: 1.5b.
- Initial owned shares: 1b.
In each investment round, investors take a percentage of initial owned shares. The percentage of each round is in `FundingRoundShares`.
If your corporation is self-funded and you sell CEO position, you only need 50b to create next corporation.
`TargetSharePrice`:
$$OwnershipPercentage = \frac{OwnedShares}{TotalShares}$$
$$TargetSharePrice = \frac{CorporationValuation*\left(0.5+\sqrt{OwnershipPercentage}\right)}{TotalShares}$$
When corporation goes public, the initial share price is `TargetSharePrice`.
Share price is updated in START state.
$$SharePrice = \begin{cases} SharePrice\ast(1 + Math.random()\ast 0.01), & SharePrice \leq TargetSharePrice \newline SharePrice\ast(1 - Math.random()\ast 0.01), & SharePrice > TargetSharePrice\end{cases}$$
Minimum share price is 0.01.
Issue new shares:
- Maximum number of new shares is 20% of total shares.
- The number of new shares issued must be a multiple of 10 million.
- New share price:
$$NewOwnershipPercentage = \frac{OwnedShares}{TotalShares+NewShares}$$
$$NewSharePrice = \frac{CorporationValuation\ast\left(0.5+\sqrt{NewOwnershipPercentage}\right)}{TotalShares}$$
- Profit:
$$Profit = {NewShares\ast(SharePrice + NewSharePrice)}\ast{0.5}$$
- Profit is added to corporation's funds.
- `DefaultCooldown` is 4 hours.
- Cooldown: $$Cooldown = DefaultCooldown\ast\frac{TotalShares}{10^{9}}$$
- Part of the new shares are added to `InvestorShares`. The remaining ones are added to `IssuedShares`.
- `MaxPrivateShares`:
$$MaxPrivateShares = {NewShares}\ast{0.5}\ast\frac{InvestorShares}{TotalShares}$$
- `PrivateShares` is randomized between 0 and `MaxPrivateShares`, rounded to nearest 10 million.
- `InvestorShares`:
$$InvestorShares = InvestorShares + PrivateShares$$
- `IssuedShares`:
$$IssuedShares = IssuedShares + NewShares - PrivateShares$$
Sell shares:
- We cannot sell all our shares.
- We cannot sell more than $10^{14}$ shares at a time.
- Cooldown is 1 hour.
- Sold shares are added to `IssuedShares`.
Buy back shares:
- We can only buy back shares that were issued. The shares that were owned by government (when we use seed money) and investors cannot be bought back.
- Shares must be bought back at a 10% premium over the market price.
- We cannot use corporation's funds to buy back shares. They must be bought with our money.
- We cannot buy back more than $10^{14}$ shares at a time.
Sold/bought back shares are processed in multiple "iterations".
- Number of shares processed each iteration is shareSalesUntilPriceUpdate. Default value is $10^6$.
- Share price is recalculated each iteration.
$$OwnershipPercentage = \frac{OwnedShares - ProcessedShares}{TotalShares}$$
$$TargetSharePrice = \frac{CorporationValuation\ast\left(0.5 + \sqrt{OwnershipPercentage}\right)}{TotalShares}$$
$$SharePrice = \begin{cases} SharePrice\ast 1.005, SharePrice \leq TargetSharePrice \newline SharePrice\ast 0.995, SharePrice > TargetSharePrice\end{cases}$$

@ -0,0 +1,108 @@
# General advice
## Round 1
Create Agriculture division, expand to 6 cities and buy 6 warehouses. Set 3 employees to R&D, then switch to Operations (1) + Engineer (1) + Business (1) right before buying boost materials.
Make sure that your employees' energy and morale are at maximum value.
Write custom "Smart Supply" script. If you skip this step, buy "Smart Supply" feature. "Smart Supply" costs 25b, and 25b is huge in round 1.
There are not many things else to do in this round. The budget is too low, so you can skip all these things:
- Expand into other industries.
- Upgrade office.
- Buy corporation's upgrades (except Smart Storage).
Use remaining funds to buy these upgrades:
- Only focus on Smart Storage and warehouse upgrade.
- Only buy 1 or 2 Advert level(s).
After that, find the optimal quantities of boost materials and buy them. Do not use "Bulk Purchase", it requires paying upfront. Buying boost materials per second does not need funds, you can go into debt.
RP is at least ~30 after buying tea and throwing party. Waiting for RP is optional in this round. It's not mandatory and does not affect the offer significantly.
## Round 2
Buy "Export".
Upgrade Agriculture division:
- Find a good number for office size. 6 is the optimal size. In this round, office upgrade is still too expensive and has bad ROI. Instead, you should invest in upgrades that increase division production multiplier, e.g., Smart Storage and warehouse upgrade (more storage space for boost materials), Smart Factories.
- Buy a couple of Advert levels. Advert level 8 is enough for most cases.
Create Chemical division:
- Expanding into Chemical industry is mandatory. I tried skipping Chemical and focus budget to Agriculture. It's futile, low-quality materials cannot be sold well.
- Chemical division is a support division, so don't invest much funds on it. Don't waste funds on its Office/Advert upgrades.
- Chemical industry has low boost materials' coefficients, so you should only buy very small number of warehouse upgrade for it. On the other hand, you should not skip Chemical's warehouse upgrade entirely. You still need Chemical division produces an acceptable amount of high-quality Chemical; otherwise, the quality of Chemical used in PRODUCTION state of Agriculture division is reduced too much due to "dilution" in PURCHASE state. For this purpose, 1 warehouse upgrade is enough.
Focus on Smart Storage, Smart Factories, warehouse upgrade (Agriculture).
Waiting for RP is mandatory in this round. It serves 2 purposes:
- Raising RP in Chemical. High RP means high-quality Chemicals (material).
- Raising RP in Agriculture. High RP means high-quality Plants.
- High-quality Plants can be exported to Chemical to create a loop of quality-enhancing process.
- High-quality Plants can be sold easier (higher [MaxSalesVolume](./optimal-selling-price-market-ta2.md)). With your limited budget, you can only increase `MaxSalesVolume` by buying Advert and improving quality of output material. Buying Advert costs money, but waiting for RP is free (except your time).
Waiting for 460RP/300RP in Agriculture/Chemical division respectively is enough. You can wait for more if you want.
## Round 3+
Create Tobacco division in round 3 and set up export route for Plants from Agriculture to Tobacco. This is the optimal product division in this phase.
The basic game loop that you need to do in round 3+ is simple:
- Buy research.
- Buy Wilson and Advert.
- Continuously develop new product.
- Upgrade product division and buy corporation's upgrades.
- Upgrade support divisions.
These are the most important things that you need to focus on:
- Buy Wilson and Advert.
- Continuously develop new product.
- Get high-quality input materials from support division(s).
- Stock up on RP.
- Get Market-TA2 as soon as possible.
Wilson and Advert are extremely important, they are the main factors that make product extremely profitable.
- Check this [section](./wilson-analytics-advert.md) for details about the mechanism of Wilson and Advert.
- When you continuously improve the product division and buy the upgrades with small budget (profit of a few cycles):
- Buy Wilson if you can afford it.
- After that, use at least 20% of current funds to buy Advert.
- Stop buying Wilson and Advert when product division's awareness/popularity reaches max value.
[Product](./product.md) is the center of round 3+.
Product's rating is limited to product's effective rating by quality of input materials. You must make sure that support divisions produce enough high-quality materials for product division. For material divisions, increasing RP is the best way to improve the quality in early rounds. However, in round 3+, the most important factor is `EngineerProduction`, so you must prioritize the "Engineer" job over other jobs. It's especially true for Agriculture due to its mediocre `ScienceFactor`. Check this [section](./quality.md) for details. On the other hand, it's fine to invest minimally in support divisions, as long as they produce enough high-quality materials.
RP is important for product's rating. Do not deplete RP pool, especially right before completing new product.
Check this [section](./unlocks-upgrade-research.md) for more advices about researches.
Office setup is important to efficiently develop new product. There are multiple setups for different purposes:
- Raw production: This setup prioritizes production capability. For support divisions, you should "combine" the prioritization of "Engineer" job with this setup.
- Progress: This setup prioritizes development speed of new product. It's best for product division in round 3 and round 4. The product's development speed is very low in these 2 rounds (especially in round 3). By focusing on development speed and get better product sooner, you can reach the point that you can get an adequate offer faster.
- Profit. This setup prioritizes profit. It's best at the end of a round, before accepting offer.
- Profit-Progress. This setup provides good balance between current profit and development speed of new product. It's best after accepting last offer.
All the setups above can be calculated by an optimizer. However, that optimizer is hard to implement correctly and efficiently. If you are a newbie, you can skip this optimization and spread employees equally across non-R&D jobs. It's not optimal, but you can still reach the endgame after couple hours if you follow other important advices.
There are 2 types of office: main office and support office. Main office is where you develop new product. Support office is where you assign a large number of employees to R&D job to increase RP. The most common setup is 1 main office and 5 support offices. The most important office is the main office, its budget must be much higher than support offices' budget.
The purpose of investment offer is to get large funds and quickly grow the corporation. Better product brings more profit, and higher profit means higher offer. However, it takes a long time to develop early product(s). Sometimes, spending more time to develop better product before accepting offer can harm your overall growth. You must find a good number of products to develop before accepting offer.
Miscellaneous advices:
- Buy tea / throw party every cycle.
- `DesignInvestment` and `AdvertisingInvestment` scale very badly (the exponent is 0.1). It's fine to spend 1% of your current funds for them.
- Create [dummy divisions](./miscellany.md) to get higher offer.
- Prioritize Tobacco division over Chemical division when setting up export routes for Plants. [Export](./miscellany.md) route is FIFO.
You can visit the [official Discord server](https://discord.gg/TFc3hKD) to get access to more information.

@ -0,0 +1,64 @@
# Industry - Supply chain
## Basic term
Industry consumes input materials and produces output materials/products. Most industry produces either materials or products, some industries produce both of them.
- Material industries are simple to bootstrap. We use them as starting/support industries.
- Product industries are hard to bootstrap, but they generate massive profit.
Each industry has different values of "factor":
- `AICoreFactor`, `HardwareFactor`, `RealEstateFactor`, `RobotFactor`: boost material's coefficient. They are used for calculating [division production multiplier](./boost-material.md).
- `ScienceFactor`:
- With material industry: affect the output material's [quality](./quality.md).
- With product industry: affect the output product's [markup and rating](./product.md).
- `AdvertisingFactor`: affect the number of units that we can sell ([AdvertFactor](./optimal-selling-price-market-ta2.md)).
"Endgame" is the very late phase after the division passes the exponential growth. In this phase, new product is only marginally better than old products, and the profit increases slowly. For example, endgame of Tobacco is when profit reaches ~1e98/s.
## Criterion
In order to choose good industries for our supply chain, we must consider many criteria:
- Good synergy between industries. Industries must be able to support each other with "Export" feature. After rework in 2.3, quality of input material becomes crucial, importing high-quality materials from other industries is mandatory.
- Must have an easy to start material industry for early rounds. This industry must have good combinations of boost materials' coefficients and boost materials' sizes. This ensures high division product multiplier that is the most crucial factor in early rounds. In 4 boost materials, Real Estate (material, not industry) should be noticed because of its tiny size.
- Support industry is the industry that provide high-quality input material for other industries. High `ScienceFactor` is a bonus for support industry because its output material's quality can be boosted without much investment in early rounds.
- Must have a highly profitable product industry for late phases.
- It's preferred to require material from starting industry rather than to expand into another support industry.
- High `ScienceFactor`. This is necessary for high product's rating.
- High `AdvertisingFactor`. This means our product's selling price can be boosted greatly by Wilson and Advert. This is the most important factor for endgame. `AdvertisingFactor` scales tremendously good when you reach high advert bonuses (Awareness and Popularity). These bonuses are capped at ~1.7977e308.
- Any worthy industry must have at least 1 input material. Spring Water is the only industry that does not need any input material. It may look convenient at first glance, but it's trap for newbie. Having no input material means the quality/rating of output material/product will always be capped at square root of its maximum value.
## Agriculture + Chemical + Tobacco
Agriculture is inarguably the best starting industry. Its `realEstateFactor` is highest among all industries (0.72), and real estate's size is tiny (0.005). It means we can stock up on huge number of real estates and increase division product multiplier to extremely high value in early rounds.
Chemical is the best support industry if we choose Agriculture as starting industry. It has highest `ScienceFactor` among all material industries (0.75). As you can see in my round 2's strategy, I invest minimal budget to Chemical division and it can still adequately boost Agriculture (after waiting for some RP).
Agriculture and Chemical have great synergy. Agriculture needs Chemicals, produces Plants. Chemical needs Plants, produces Chemicals.
Tobacco is an excellent product industry:
- It requires only Plants, and Plants come from Agriculture. Therefore, quality of input material is not a problem with this industry if we choose Agriculture as starting industry.
- High `ScienceFactor` (0.75), only below Pharmaceutical.
- High `AdvertisingFactor` (0.2), only below Restaurant and Real Estate.
## Other product industries
All other product industries always have some kind of flaws in them and not be as versatile as Tobacco. For example:
- Pharmaceutical has highest `ScienceFactor` (0.8) but low `AdvertisingFactor` (0.16).
- Its products are potentially better than Tobacco's ones. This sounds good in theory, but not in practice. Pharmaceutical requires Chemicals. Chemicals can only be produced by Chemical industry, and that industry has bad production capability. The early products' effective rating will be low because the Chemical division cannot produce enough high-quality material units. In the end, the offers of round 3 and 4 are much lower than Tobacco's ones.
- It is worse than Tobacco in endgame.
- Healthcare has same `ScienceFactor` as Tobacco's one (0.75) but lowest `AdvertisingFactor` (0.11).
- It requires 4 input materials instead of 1 or 2. Those materials include Robots with coefficient of 10 (highest value of all industries), and size of Robots is biggest among boost materials. This means the required materials take up huge space in warehouse.
- This industry is the worst one in endgame because of its extremely low `AdvertisingFactor`.
- Restaurant has highest `AdvertisingFactor` (0.25) but very low `ScienceFactor` (0.12).
- Its products are significantly worse than Tobacco's ones. It takes too long to hit the exponential growth (due to worse products) and surpass Tobacco in profit.
- It's fine to expand into this industry after Tobacco to have better endgame.
- Real Estate has same `AdvertisingFactor` as Restaurant's one but lowest `ScienceFactor` (0.05). It requires 4 input materials instead of 1 or 2. This industry has same potential and problem like Restaurant with its high `AdvertisingFactor` and low `ScienceFactor`. The only difference is that its `ScienceFactor` is even lower than Restaurant's one.
## Conclusion
Agriculture + Chemical + Tobacco is the most balanced supply chain. It's very easy to bootstrap in early rounds and reaches high profit in late phases for practical purposes.

@ -0,0 +1,121 @@
# Miscellany
## Corporation's state
Corporation continuously transitions between 5 states: START → PURCHASE → PRODUCTION → EXPORT → SALE → START. 1 cycle of these transitions takes 10 seconds. If you have enough bonus time, it takes 1 second.
START:
- Division:
- Office:
- Calculate: energy, morale, total experience, salary, employee production by jobs.
- Set employees' jobs: copy data from `employeeNextJobs` to `employeeJobs`.
- Material market: update `demand`, `competition`, `marketPrice`.
- Product market: decrease `demand` and increase `competition`.
- Calculate corporation's financial statements: revenue, expenses, profit, dividend, total assets, valuation, share price.
PURCHASE:
- Buy input materials.
- If we have unlocked "Smart Supply", it's used for calculating optimal quantity of input material units.
PRODUCTION:
- Produce output materials and products.
- If new product's `developmentProgress` is greater than or equal to 100, it's finished. New finished product's stats are calculated in this state.
EXPORT: export output materials.
SALE:
- Sell output materials and product.
- If we have unlocked "Market-TA1" / "Market-TA2", it's used for calculating selling price.
Special cases: RP and salary expense are increased in 4 states: PURCHASE, PRODUCTION, EXPORT, SALE.
## Import and export
Importing/exporting material is done in EXPORT state.
EXPORT state is before SALE state. It means you sell the material units remained after being exported.
Export string can use "MAX", "EINV", "IINV", "EPROD" and "IPROD". Read the description in export popup for the meaning of these values.
The optimal export string is `(IPROD+IINV/10)*(-1)`. For example: export "Chemicals" from Chemical division to Agriculture division:
- Agriculture division needs 100 Chemicals/s and has 700 Chemicals in warehouse.
- IPROD = -100 ("Consumption is negative production")
- IINV = 700
- "Export" is expressed by number of units per second, so we want to export:
$$\left(100-\frac{700}{10}\right)=\left(-100+\frac{700}{10}\right)\ast(-1)=\left(IPROD+\frac{IINV}{10}\right)\ast(-1)$$
Export route is FIFO. You can remove an export route by using `cancelExportMaterial` NS API.
Export data is in `material.exports`.
## Use mathematical library
I use the JavaScript ports of these libraries. They don't support JavaScript.
I use the default setting without any tuning. If you tune their parameters properly, their accuracy and performance will be improved.
### Ceres Solver
Quote from <http://ceres-solver.org/>
> Ceres Solver is an open source C++ library for modeling and solving large, complicated optimization problems. It can be used to solve Non-linear Least Squares problems with bounds constraints and general unconstrained optimization problems.
JavaScript port: <https://github.com/Pterodactylus/Ceres.js>
We can use it to solve the non-linear systems in these cases:
- Case 1: Find the optimal quantities of boost materials.
- Case 2: Find `CreationJobFactors[JobName]` when calculating product markup.
The accuracy and performance are acceptable, so we employ it case 2. We don't use it in case 1 because there is another optimal solution, it's better in both accuracy and performance.
Quick test for case 2 shows that the accuracy is pretty good:
- `creationJobFactors`:
- Business: 420.103620358641
- Engineer: 29666.47672073447
- Management: 40466.091598191015
- Operations: 25760.399443536793
- Solver's result:
- Business: 420.08121628008024
- Engineer: 29664.894610028987
- Management: 40463.933544920386
- Operations: 25759.025643594476
## Noodles trick
There is a place called "Noodle Bar" in New Tokyo. After going there, there is a button that says "Eat noodles". Eating noodles gives you multiple benefits. Each benefit is miniscule, but you can press that button programmatically. With Corporation, the benefit is:
```typescript
Player.corporation.gainFunds(Player.corporation.revenue * 0.000001, "glitch in reality");
```
In previous versions, the revenue is multiplied by 0.01. If you do it fast enough, it'll raise the investment offer by a considerable amount. Nowadays, this trick is useless.
## "sudo.Assist" research
This research exists in ResearchMap.ts, but it's unused in BaseResearchTree.ts. If you use it in NS API, it will throw error.
```typescript
"sudo.Assist": new Research({
name: "sudo.Assist",
cost: 15e3,
desc: "Develop a virtual assistant AI to handle and manage administrative issues for your corporation.",
})
```
## Dummy division
Dummy division is the division that you create only to increase the [valuation](./financial-statement.md) and the [investment offer](./financial-statement.md).
Use Restaurant industry for dummy division. Its starting cost is only 10e9. Spring Water's starting cost is also 10e9, but it's a newbie trap, so it's best not to touch it.
For dummy division, you only need to expand to 6 cities and buy 6 warehouses. Don't invest in other things (Warehouse/Office/Advert upgrades).
If you start round 3 with large budget, you can create dummy divisions immediately to simplify the script's logic. It's not optimal but still acceptable. However, if the budget is tight, you should not create them at the start of round 3. After completing first product, profit will be high enough to get enough funds to create them without any problem.

@ -0,0 +1,185 @@
# Office
## Basic information
Employee's stats are kept track as average value. There are 6 average values: `AvgEnergy`, `AvgMorale`, `AvgIntelligence`, `AvgCharisma`, `AvgCreativity`, `AvgEfficiency`. Every time you hire a new employee, these average values are recalculated, they are modified by a randomized number from 50 to 100:
```typescript
this.avgMorale =
(this.avgMorale * this.numEmployees + getRandomInt(averageStat, averageStat)) / (this.numEmployees + 1);
```
Job assignment:
- Each office has 2 records: `employeeJobs` and `employeeNextJobs`. Data in `employeeJobs` (number of employees in each job) is used for calculations of other relevant data like `EmployeeProductionByJob`, `AvgEnergy`, `AvgMorale`, `TotalExperience`. When you call `setAutoJobAssignment`, its parameter is calculated and assigned to `employeeNextJobs`. In next cycle's START state, data in `employeeNextJobs` will be copied to `employeeJobs`.
- Behavior of `setAutoJobAssignment` may be confused at first glance. Let's say you call it like this: `ns.corporation.setAutoJobAssignment("Agriculture","Sector-12","Operations", 5)`
- If you have 5 \"Operations\" employees, it does nothing.
- If you have 7 \"Operations\" employees, it reduces number of \"Operations\" employees to 5, and set \"Unassigned\" employees to 2.
- If you have 2 \"Operations\" employees, it checks if you have at least 3 \"Unassigned\" employees. If yes, it changes \"Operations\" employees to 5 and reduces \"Unassigned\" employees by 3. If not, it throws error. Essentially, it tries to move employees from \"Unassigned\" to \"Operations\".
- This means the proper way to use `setAutoJobAssignment` is:
- Use it to set all jobs to 0.
- Use it to set all jobs to your requirements.
Total experience is increased in these cases:
- Hire new employee. Each new employee increases total experience by `getRandomInt(50, 100)`.
- In START state. Gain per cycle:
$$TotalExperienceGain = 0.0015\ast(TotalEmployees - UnassignedEmployees + InternEmployees\ast 9)$$
- If an office has 100 employees and all employees are assigned to non-intern positions, it gains 0.15 experience/cycle. It's 54 experience/hour without bonus time.
Salary per cycle:
$$Salary = 3\ast TotalEmployees\ast\left(AvgIntelligence+AvgCharisma+AvgCreativity+AvgEfficiency+\frac{TotalExperience}{TotalEmployees}\right)$$
## Upgrade
Upgrade cost:
$$UpgradeCost = BasePrice\ast{1.09}^{\frac{CurrentSize}{3}}$$
Upgrade cost from size 3 to size n:
$$UpgradeCost_{From\ 3\ to\ n} = \sum_{k = 0}^{\frac{n}{3} - 1}{BasePrice\ast 1.09^k}$$
$$UpgradeCost_{From\ 3\ to\ n} = BasePrice\ast\left( \frac{{1.09}^{\frac{n}{3}} - 1}{0.09} \right)$$
Upgrade cost size a to size b:
$$UpgradeCost_{From\ a\ to\ b} = BasePrice\ast\left( \frac{{1.09}^{\frac{b}{3}} - {1.09}^{\frac{a}{3}}}{0.09} \right)$$
Maximum size with a given `MaxCost`:
$$MaxUpgradeLevel = 3\ast\log_{1.09}\left( MaxCost\ast\frac{0.09}{BasePrice} + {1.09}^{\frac{CurrentSize}{3}} \right)$$
When we divide office's size by 3, we need to use `Math.ceil()` to round up the value. This means upgrading from size 3 to size 4 costs the same as upgrading from size 3 to size 6. The most cost-effective size is always a multiple of 3.
## Energy and morale
They are calculated in START state.
They start dropping when your office's number of employees is greater than or equal to 9. The minimum value is 10.
PerfMult is a multiplier that increases/decreases energy/morale.
$$InternMultiplier = 0.002\ast Min\left(\frac{1}{9},\frac{InternEmployees}{TotalEmployees}-\frac{1}{9}\right)\ast 9$$
$$PenaltyMultiplier = \begin{cases}0, & (CorpFunds > 0) \vee (DivisionLastCycleRevenue > DivisionLastCycleExpenses) \newline 0.001, & (CorpFunds < 0) \land (DivisionLastCycleRevenue < DivisionLastCycleExpenses)\end{cases}$$
$$PerfMult = \begin{cases}1.002, & TotalEmployees < 9 \newline 1 + InternMultiplier - PenaltyMultiplier, & TotalEmployees \geq 9\end{cases}$$
Buying tea gives a flat +2 to energy. It costs 500e3 per employee.
When throwing party, `PartyMult` is calculated. It's used in the calculation of morale in the next cycle.
$$PartyMult = 1 + \frac{PartyCostPerEmployee}{10^{7}}$$
`PartyMult` is not affected by number of employees. Therefore, you can throw a "big party" (high `PartyCostPerEmployee`) when you have 1 employee at low total cost (due to having only 1 employee), then hire the rest of employees later.
There is a flat randomized reduction of energy/morale per cycle. It's capped at 0.002 per cycle. This is tiny so it's not a problem.
There is a flat increase of morale if `PartyMult` is greater than 1. `PartyMult` is based on `PartyCostPerEmployee`, so this increase is based on `PartyCostPerEmployee`.
$$IncreaseOfMorale = (PartyMult - 1)\ast 10$$
$$IncreaseOfMorale = \frac{PartyCostPerEmployee}{10^{6}}$$
```typescript
const reduction = 0.002 * marketCycles;
const increase = this.partyMult > 1 ? (this.partyMult - 1) * 10 : 0;
this.avgEnergy = (this.avgEnergy - reduction * Math.random()) * perfMult + (this.teaPending ? 2 : 0);
this.avgMorale = ((this.avgMorale - reduction * Math.random()) * perfMult + increase) * this.partyMult;
```
There are 3 ways to counter the drop of energy/morale:
- Buy tea and throw party. You should always use this option. Writing script to automate them is very easy.
- Assign Intern. Many people throw around the ratio 1/9 as the way to counter the drop of energy/morale. You can only use that ratio when your corporation/division works fine. If it does not, there is a penalty multiplier (0.001). In this case, you need to use 1/6.
- Buy 2 researches: AutoBrew and AutoPartyManager. They keep energy/morale at maximum value. However, you should never buy them. It's always better to spend your RP on other useful researches or just stock up on RP.
`AvgEnergy` and `AvgMorale` are increased by a randomized amount when we hire new employee.
```typescript
this.avgMorale = (this.avgMorale * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1);
this.avgEnergy = (this.avgEnergy * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1);
```
Optimal `PartyCostPerEmployee`:
- The flat randomized reduction is tiny, so we can ignore it.
- We want to increase `AvgMorale` from `CurrentMorale` to `MaxMorale`:
$$\left( CurrentMorale\ast PerfMult + \frac{PartyCostPerEmployee}{10^{6}} \right)\ast\left( 1 + \frac{PartyCostPerEmployee}{10^{7}} \right) = MaxMorale$$
- Define:
$$a = CurrentMorale$$
$$b = MaxMorale$$
$$k = PerfMult$$
$$x = PartyCostPerEmployee$$
- We have equation:
$$\left( a\ast k + \frac{x}{10^{6}} \right)\ast\left( 1 + \frac{x}{10^{7}} \right) = b$$
$$x_{1} = - 500000\ast\left( \sqrt{(a\ast k - 10)^{2} + 40\ast b} + a\ast k + 10 \right)$$
$$x_{2} = 500000\ast\left( \sqrt{(a\ast k - 10)^{2} + 40\ast b} - a\ast k - 10 \right)$$
- $x_{1}$ is always negative. Therefore, $x_{2}$ is the only solution.
One big party is less cost-effective than multiple small parties. For example: throwing 1 big party for 70→100 morale costs more than throwing 3 small parties: 70→80, 80→90, 90→100.
Don't be a cheapskate when it comes to tea/party. Energy and morale are vital to efficient office. Check the next part for the formulas.
- It's fine to spend 500e3 per employee when throwing party. You can spend more if you want.
- Try to maintain maximum energy/morale at all times. Personally, I always buy tea / throw party when energy/morale decreases to 99.5 (109.5, if I bought the relevant researches).
- In round 1 and 2, the office's size is small, it's usually smaller than 9, so energy/morale is not a problem. In round 3+, you should buy tea / throw party every cycle.
## Employee production by job
In each cycle's START state, all stats are used for calculating "production" values, these values are saved in `office.employeeProductionByJob`. These values can be used later for calculating:
- RP.
- Material's quality.
- Product's stats.
- Division raw production.
- Material/product's MaxSalesVolume.
Formulas:
- Calculate multipliers of Intelligence, Charisma, Creativity, and Efficiency. They are the product of average value, upgrade benefit and research benefit.
- Production base:
$$ProductionBase = AvgMorale\ast AvgEnergy\ast 10^{-4}$$
- Experience:
$$Exp = \frac{TotalExperience}{TotalEmployees}$$
- Production multiplier:
- Operations: $$ProductionMultiplier = 0.6\ast IntelligenceMult + 0.1\ast CharismaMult + Exp + 0.5\ast CreativityMult + EfficiencyMult$$
- Engineer: $$ProductionMultiplier = IntelligenceMult + 0.1\ast CharismaMult + 1.5\ast Exp + EfficiencyMult$$
- Business: $$ProductionMultiplier = 0.4\ast IntelligenceMult + CharismaMult + 0.5\ast Exp$$
- Management: $$ProductionMultiplier = 2\ast CharismaMult + Exp + 0.2\ast CreativityMult + 0.7\ast EfficiencyMult$$
- Research and Development: $$ProductionMultiplier = 1.5\ast IntelligenceMult + 0.8\ast Exp + CreativityMult + 0.5\ast EfficiencyMult$$
- $EmployeesJobCount = office.employeeJobs[JobName]$
- Employee production by job:
$$EmployeeProductionByJob = EmployeesJobCount\ast ProductionMultiplier\ast ProductionBase$$
## Calculate employee's stat
4 stats `AvgIntelligence`, `AvgCharisma`, `AvgCreativity`, `AvgEfficiency` are inaccessible through NS API.
We can calculate them by using the formulas from previous part and [Ceres Solver](./miscellany.md). In 5 jobs Operations, Engineer, Business, Management, Research & Development, we need at least 4 jobs having 1 employee at the minimum to use this solution.

@ -0,0 +1,108 @@
# Optimal selling price - Market-TA2
## Market price and markup limit
Market price:
- Material: `material.marketPrice`.
- Product: `product.productionCost`. This value is based on `ProductMarketPriceMult`, input materials' `MarketPrice` and `Coefficient`.
- $n = {Number\ of\ input\ materials}$
- $ProductMarketPriceMult = 5$
$$ProductMarketPrice = ProductMarketPriceMult\ast\sum_{i = 1}^{n}{MaterialMarketPrice_i\ast MaterialCoefficient_i}$$
Markup limit:
- Material:
$$MaterialMarkupLimit = \frac{MaterialQuality}{MaterialMarkup}$$
- Product:
$$ProductMarkupLimit = \frac{Max(ProductEffectiveRating,0.001)}{ProductMarkup}$$
## Maximize sales volume
Define:
$$ExpectedSalesVolume = \frac{ProducedUnits}{10}$$
- This means we want to sell all produced units.
In cycle's SALE state, game calculates `MaxSalesVolume` of material/product. If we set price too high, `MaxSalesVolume` is penalized. In order to maximize profit, we have to set the highest possible price while `MaxSalesVolume` is still equals to `ExpectedSalesVolume`. This is what Market-TA2 does.
Calculation of material and product is pretty similar, so I'll call them "item" and use 1 formula.
`MaxSalesVolume` is the product of 7 multipliers:
- Item multiplier:
- Material:
$$ItemMultiplier = MaterialQuality + 0.001$$
- Product:
$$ItemMultiplier = 0.5\ast(ProductEffectiveRating)^{0.65}$$
- Business factor:
- `BusinessProduction = 1 + office.employeeProductionByJob["Business"]`
$${BusinessFactor = (BusinessProduction)}^{0.26} + \left({BusinessProduction}\ast{0.001}\right)$$
- Advert factor:
$$AwarenessFactor = (Awareness + 1)^{IndustryAdvertisingFactor}$$
$$PopularityFactor = (Popularity + 1)^{IndustryAdvertisingFactor}$$
$$RatioFactor = \begin{cases}Max(0.01,\frac{Popularity + 0.001}{Awareness}), & Awareness \neq 0 \newline 0.01, & Awareness = 0 \end{cases}$$
$$AdvertFactor = (AwarenessFactor\ast PopularityFactor\ast RatioFactor)^{0.85}$$
- Market factor:
$$MarketFactor = Max\left(0.1,{Demand\ast(100 - Competition)}\ast{0.01}\right)$$
- Corporation's upgrade bonus: `SalesBots` bonus.
- Division's research bonus: this is always 1. Currently there is not any research that increases the sales bonus.
- `MarkupMultiplier`: initialize with 1.
- `SellingPrice` is the selling price that you set.
- With materials, if we set `SellingPrice` to 0, `MarkupMultiplier` is $10^{12}$ (check the formula below). Extremely high `MarkupMultiplier` means that we can sell all units, regardless of other factors. This is the fastest way to discard materials.
- If `(SellingPrice > MarketPrice + MarkupLimit)`:
$$MarkupMultiplier = \left(\frac{MarkupLimit}{SellingPrice - MarketPrice}\right)^{2}$$
- If item is material and `SellingPrice` is less than `MarketPrice`:
$$MarkupMultiplier = \begin{cases}\frac{MarketPrice}{SellingPrice}, & SellingPrice > 0 \land SellingPrice < MarketPrice \newline 10^{12}, & SellingPrice \leq 0 \end{cases}$$
## Optimal selling price
As we can see with previous part, `MarkupMultiplier` is basically a penalty modifier if we set `SellingPrice` greater than `(MarketPrice + MarkupLimit)`, and we'll always do this. This means we need to find out highest possible `SellingPrice` while `MaxSalesVolume` is still equals to `ExpectedSalesVolume`.
This is the reason why we should not bother with Market-TA1. It simply sets `SellingPrice = MarketPrice + MarkupLimit`. This means Market-TA1 sets a "safe" `SellingPrice` for us, it guarantees that we won't be penalized due to too high price. However, this "safe" `SellingPrice` is too low, and we can find a much higher `SellingPrice`.
Formula:
- Define:
$$M = \ ItemMultiplier\ast BusinessFactor\ast AdvertFactor\ast MarketFactor\ast SaleBotsBonus\ast ResearchBonus$$
- We want `MaxSalesVolume` equals `ExpectedSalesVolume`:
$$MaxSalesVolume = ExpectedSalesVolume$$
$$M\ast\left(\frac{MarkupLimit}{SellingPrice - MarketPrice}\right)^{2} = ExpectedSalesVolume$$
$$\frac{MarkupLimit}{SellingPrice - MarketPrice} = \sqrt{\frac{ExpectedSalesVolume}{M}}$$
$$SellingPrice = \frac{MarkupLimit\ast\sqrt{M}}{\sqrt{ExpectedSalesVolume}} + MarketPrice$$
In order to use this formula, we need `MarkupLimit`. With product, we need `ProductMarkup` to calculate `MarkupLimit`, but `ProductMarkup` is inaccessible via NS API. We have two solutions:
- Calculate approximation value. Check previous section to see how to do this.
- Calculate `MarkupLimit` directly:
- Set `SellingPrice` to a very high value, it must be so high that we cannot sell all produced units (`MaxSalesVolume < ExpectedSalesVolume`). This forces the game applies the penalty modifier that contains `MarkupLimit`.
- Wait for 1 cycle to get `ActualSalesVolume`. It's `product.actualSellAmount` and `material.actualSellAmount`.
- Use `ActualSalesVolume` in place of `ExpectedSalesVolume` in previous formula: $MarkupLimit = (SellingPrice - MarketPrice)\ast\sqrt{\frac{ActualSalesVolume}{M}}$
- Calculate `ProductMarkup` from `MarkupLimit`, save `ProductMarkup` to reuse later. `ProductMarkup` never changes.

@ -0,0 +1,176 @@
# Product
## Overview
The product industry is much better than the material industry in the late phases because we can sell products at ridiculously high prices.
Market-TA2 automatically sets the optimal prices for products. Check this [section](./optimal-selling-price-market-ta2.md) to see how to implement a custom Market-TA2 script. Implementing a custom Market-TA2 script is the best optimization in round 3+.
Products need to be developed before being produced. We can put multiple products in the development queue, but only one product can be developed at a time.
There is a limit on how many products a division can have. The default limit is 3. There are 2 researches that increase that limit. However, those researches are nearly useless because, in most cases, there is no point in increasing the maximum number of products. Check this [section](./unlocks-upgrade-research.md) for details. When we reach the limit, we need to discontinue a product before developing a new one.
We need to continuously develop new products. New products are almost always better than the old ones and generate much more profit.
The product's markup and effective rating are extremely important because they are part of [MaxSalesVolume](./optimal-selling-price-market-ta2.md)'s calculation.
The product's effective rating is based on the product's rating and the input material's quality. Check this [section](./quality.md) to see how input material's quality affects the product's rating and effective rating. This is why we need a support division that produces high-quality material for the product division.
The product's markup and rating are based on:
- `CreationJobFactors[JobName]`. More about this in the next part.
- RP. This is why we should stock up on RP.
- `ResearchFactor`. It is the industry's `scienceFactor`.
- Design investment and advertising investment. Game UI shows them as "Design investment" and "Marketing investment". These two investments are not too important because their exponents in the formulas are very low. It's fine to use only 1% of current funds for them.
Office's upgrade and employee stats' upgrades are very important for products because they increase employee production. High employee production means high CreationJobFactors and RP. Those upgrades and products create a powerful loop: More upgrades → Better product → Higher profit → More upgrades.
Office setup is important to efficiently develop new products. Check this [section](./general-advice.md) for advice on how to set up the office.
## Formula
`CreationJobFactors[JobName]` are values accumulated over the time that product was developed. `DevelopmentProgress` starts at 0. In each cycle:
- Total employee production:
$$TotalEmployeeProd = OperationsProd + EngineerProd + ManagementProd$$
- Management factor:
$$ManagementFactor = 1 + \frac{ManagementProd}{1.2\ast TotalEmployeeProd}$$
- Product development multiplier:
$$ProductDevelopmentMultiplier = \left( (EngineerProd)^{0.34} + (OperationsProd)^{0.2} \right)\ast ManagementFactor$$
- Progress:
$$Progress = 0.01\ast ProductDevelopmentMultiplier$$
- Development progress:
$$DevelopmentProgress = DevelopmentProgress + Progress$$
- `CreationJobFactors[JobName]`:
$$CreationJobFactors\lbrack JobName\rbrack = CreationJobFactors\lbrack JobName\rbrack + {\lbrace EmployeeJob\rbrace Prod\ast Progress}\ast{0.01}$$
&nbsp;
When `DevelopmentProgress` reaches 100, product is finished.
- Define:
$$A = \ CreationJobFactors\lbrack Engineer\rbrack$$
$$B = \ CreationJobFactors\lbrack Management\rbrack$$
$$C = \ CreationJobFactors\lbrack RnD\rbrack$$
$$D = \ CreationJobFactors\lbrack Operations\rbrack$$
$$E = \ CreationJobFactors\lbrack Business\rbrack$$
$$TotalCreationJobFactors = A + B + C + D + E$$
- {JobName}Ratio:
$$EngineerRatio = \frac{A}{TotalCreationJobFactors}$$
$$ManagementRatio = \frac{B}{TotalCreationJobFactors}$$
$$RnDRatio = \frac{C}{TotalCreationJobFactors}$$
$$OperationsRatio = \frac{D}{TotalCreationJobFactors}$$
$$BusinessRatio = \frac{E}{TotalCreationJobFactors}$$
- Design investment multiplier:
$$DesignInvestMult = 1 + {(DesignInvestment)^{0.1}}\ast{0.01}$$
- Science multiplier:
$$ScienceMult = 1 + {(RP)^{ResearchFactor}}\ast{0.00125}$$
- Balance multiplier:
$$BalanceMult = 1.2\ast EngineerRatio + 0.9\ast ManagementRatio + 1.3\ast RnDRatio + 1.5\ast OperationsRatio + BusinessRatio$$
- Total multiplier:
$$TotalMult = BalanceMult\ast DesignInvestMult\ast ScienceMult$$
- Product's quality:
$$TotalMult\ast (0.1\ast A + 0.05\ast B + 0.05\ast C + 0.02\ast D + 0.02\ast E)$$
- Product's performance:
$$TotalMult\ast (0.15\ast A + 0.02\ast B + 0.02\ast C + 0.02\ast D + 0.02\ast E)$$
- Product's durability:
$$TotalMult\ast (0.05\ast A + 0.02\ast B + 0.08\ast C + 0.05\ast D + 0.05\ast E)$$
- Product's reliability:
$$TotalMult\ast (0.02\ast A + 0.08\ast B + 0.02\ast C + 0.05\ast D + 0.08\ast E)$$
- Product's aesthetics:
$$TotalMult\ast (0.08\ast B + 0.05\ast C + 0.02\ast D + 0.1\ast E)$$
- Product's features:
$$TotalMult\ast (0.08\ast A + 0.05\ast B + 0.02\ast C + 0.05\ast D + 0.05\ast E)$$
- Product's rating:
- If an industry produces product, it has its own `RatingWeights` for its product. `RatingWeights` contains coefficients of 6 stats: quality, performance, durability, reliability, aesthetics, features. For example: Tobacco's `RatingWeights`:
- Quality's coefficient: 0.7.
- Durability's coefficient: 0.1.
- Aesthetics' coefficient: 0.2.
- `RatingWeights` is `industryData.product.ratingWeights`.
- Formula:
$$ProductRating = \sum_{i = 1}^{6}{{ProductStat}_i\ast{StatCoefficient}_i}$$
- Advertising investment multiplier:
$$AdvertInvestMult = 1 + {(AdvertisingInvestment)^{0.1}}\ast{0.01}$$
- Business-Management ratio:
$$BusinessManagementRatio = Max\left( BusinessRatio + ManagementRatio,\ \left( \frac{1}{TotalCreationJobFactors} \right) \right)$$
- Product's markup:
$$ProductMarkup = \frac{100}{AdvertInvestMult\ast(ProductQuality + 0.001)^{0.65}\ast BusinessManagementRatio}$$
- Product's demand:
$$Demand = \begin{cases}Min(100,AdvertInvestMult\ast(100\ast(Popularity/Awareness))), & Awareness \neq 0 \newline 20, & Awareness = 0 \end{cases}$$
- Product's competition:
$$Competition = Random(0,70)$$
- Product's size:
- It's product.size.
- Formula:
$$ProductSize = \sum_{i = 1}^{NumberOfInputMaterials}{{InputMaterialSize}_i\ast{InputMaterialCoefficient}_i}$$
## Approximation value of product markup
In order to calculate product markup, we need:
- `CreationJobFactors[JobName]`
- `RP`
- `ResearchFactor`
- `DesignInvestment`
- `AdvertisingInvestment`
Product markup is calculated when product is finished. At that time, there is one thing that we cannot get: `CreationJobFactors[JobName]`, because there is not any NS API to query it. There are 2 approaches for this problem:
- Manually record them. This means that we simulate `product.creationJobFactors`. This approach is simple, but it has a big problem: if we miss any cycle, the data is invalid.
- Calculate them directly. Product's stats are public data, so with above formulas, we have a system of 6 functions with only 5 variables. We can use [Ceres Solver](./miscellany.md) to find its solution.

@ -0,0 +1,71 @@
# Quality
## Basic term
Let's define some terms:
- `AvgInputQuality`: average quality of input materials.
- `MaxOutputQuality`: maximum value of output material's quality.
- `OutputQuality`: final value of output material's quality. This value is always less than or equal to `MaxOutputQuality`.
- `MaxOutputRating`: maximum value of output product's rating.
- `OutputRating`: final value of output product's rating. This value is always less than or equal to `MaxOutputRating`.
Each industry has a set of input materials and their coefficients, for example, Agriculture needs Water and Chemicals, their coefficients are [0.5, 0.2] respectively. These coefficients do not affect `AvgInputQuality`. `AvgInputQuality` is the mean value of qualities of input materials in warehouse. For example, if Agriculture division's warehouse has Water (quality 1) and Chemicals (quality 11), `AvgInputQuality` is (1+11)/2.
Purchased material is low-quality. Its quality is always 1.
When you import/export your materials between different divisions, you can see quality of some input materials constantly change. Quality is high after EXPORT state, but it reduces after PURCHASE state. Qualities of the materials in warehouse are recalculated in these 2 states.
In PURCHASE state, material's quality is "diluted" by low-quality purchased material (quality 1).
$$Quality = \frac{Quality\ast CurrentQuantity + BuyAmount}{CurrentQuantity + BuyAmount}$$
In PRODUCTION state, the "diluted" quality value is used for calculating `AvgInputQuality`.
In EXPORT state:
$$Quality = \frac{Quality\ast CurrentQuantity + ImportQuality\ast ImportAmount}{CurrentQuantity + ImportAmount}$$
The production capability of support division should be balanced. The `ImportAmount` (the number of material units that the support division exports) does not need to equal the required number of input material units, but it should also not be too small.
## Material
`MaxOutputQuality` is sum of 3 values:
- Engineer summand:
- `EngineerProduction = office.employeeProductionByJob["Engineer"]`
$$EngineerSummand = \frac{EngineerProduction}{90}$$
- Research point summand:
$$ResearchPointSummand = (RP)^{IndustryScienceFactor}$$
- AI Cores summand (if there is AI Cores in the warehouse):
$$AICoresSummand = AICoresQuantity^{IndustryAICoreFactor}\ast{0.001}$$
Output quality:
$$OutputQuality = \sqrt{MaxOutputQuality}\ast AvgInputQuality$$
With formulas above, we have these conclusions:
- In early rounds, increasing RP is the best way to improve `MaxOutputQuality`. This is especially true for industry with high science factor like Chemical.
- In late rounds (round 3+), we have large funds to upgrade offices. In this case, the most important factor of `MaxOutputQuality` is `EngineerProduction`. "Engineer" is more significant than "Research & Development".
- `OutputQuality` starts at square root of `MaxOutputQuality`. `AvgInputQuality` increases it until it reaches `MaxOutputQuality`.
- This is a simple strategy for checking if we need to increase `AvgInputQuality`:
- If square of `AvgInputQuality` is greater than or equal to current output quality, it's fine.
- If not, you need to increase input material's quality. It usually means you need to improve the support division.
## Product
`MaxOutputRating` is product.rating.
Game UI shows `OutputRating` as "Effective rating"
Output rating:
$$OutputRating = \sqrt{MaxOutputRating}\ast AvgInputQuality$$
Use same strategy as material for checking `AvgInputQuality`.

@ -0,0 +1,51 @@
# Smart Supply
## Logic
Create a function called `getLimitedRawProduction`:
- Calculate `RawProduction`.
- Multiply `RawProduction` by 10.
- Calculate `RequiredStorageSpaceOfEachOutputUnit`. It is the net change in warehouse's storage space when producing an output unit.
- If `RequiredStorageSpaceOfEachOutputUnit` is greater than 0:
- Calculate `MaxNumberOfOutputUnits`. It is the maximum number of units that we can produce and store in warehouse's free space.
- Limit `RawProduction` to `MaxNumberOfOutputUnits` if needed.
Create a map called `SmartSupplyData`. Its key is `${divisionName}|${city}`. Its value is the `TotalRawProduction` that will be calculated later.
After PURCHASE state:
- Initialize `TotalRawProduction` with 0.
- If division produces materials: call `getLimitedRawProduction`, then add the returned value to `TotalRawProduction`.
- If division produces products:
- Loop through all products of the division.
- If the product is finished, call `getLimitedRawProduction`, then add the returned value to `TotalRawProduction`.
- Set `TotalRawProduction` to `SmartSupplyData`.
Before PURCHASE state:
- Check if warehouse is congested. If it is, alert player and try to mitigate the situation.
- Find required quantity of each input material to produce material/product:
- Get `TotalRawProduction` from `SmartSupplyData`.
- Multiply `TotalRawProduction` with input material's coefficient.
- Find which input material creates the least number of output units.
- Align all the input materials to the smallest amount.
- Calculate the total size of all input materials we are trying to buy.
- If there is not enough free space, we apply a multiplier to required quantity to not overfill warehouse.
- Deduct the number of stored input material units from the required quantity.
- Buy input materials.
## Detect warehouse congestion
Warehouse can be congested due to multiple reasons. One common case is when the logic of calculating required quantity of input materials does not take into account of free space and stored input materials units in warehouse. When it happens, warehouse is filled with excessive input materials and the production process is halted completely due to no free space for produced units.
For each case, we need to find way(s) to detect congestion and mitigate it. In the case above, we can use this simple heuristic:
- Create a map called `WarehouseCongestionData`. Its key is `${divisionName}|${city}`. Its value is the number of times that we suspect the warehouse is congested.
- In each cycle, check `material.productionAmount` and `product.productionAmount` of output material/product.
- If `productionAmount` is 0, increase the entry's value of this warehouse in the map by 1. If not, set the entry's value to 0.
- If the entry's value is greater than 5, the warehouse is very likely congested.
- This heuristic is based on the observation: when warehouse is filled with excessive input materials, the production process is halted completely, this means `productionAmount` is 0. We wait for 5 times to reduce false positives.
- When we start our Smart Supply script, the `productionAmount` of output material/product may be 0, because there is nothing controls the production process in previous cycles.
When there are excessive input materials, discarding all of them is the simplest mitigation measure. It's inefficient, but it's the fastest way to make our production line restart.

@ -0,0 +1,179 @@
# Unlocks - Upgrade - Research
## Unlocks
| **Name** | **Price** | **Description** |
| ------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Export | 20e9 | Allow exporting material between different divisions. Extremely important. Buy it at the start of round 2. |
| Smart Supply | 25e9 | Enable "Smart Supply" feature. Only buy it if you don't implement your custom [Smart Supply](./smart-supply.md) script. |
| Market Research - Demand | 5e9 | Grant access to [Demand](./demand-competition.md) data. You need it to implement a custom [Market-TA2](./optimal-selling-price-market-ta2.md) script. |
| Market Data - Competition | 5e9 | Grant access to [Competition](./demand-competition.md) data. You need it to implement a custom [Market-TA2](./optimal-selling-price-market-ta2.md) script. |
| VeChain | 10e9 | View more statistics about Corporation. Useless. |
| Shady Accounting | 500e12 | Reduce [DividendTax](./financial-statement.md) by 0.05 |
| Government Partnership | 2e15 | Reduce [DividendTax](./financial-statement.md) by 0.1 |
&nbsp;
## Upgrade
Each upgrade has different `BasePrice`, `PriceMult`, `Benefit.
Most upgrades affect all divisions.
There are 3 special upgrades. These upgrades only affect its division and have different formulas for cost/benefits.
- Warehouse. Check this [part](./warehouse.md).
- Office. Check this [part](./office.md).
- Advert. Check this [part](./wilson-analytics-advert.md).
Normal upgrade's formulas:
- Upgrade cost:
$$UpgradeCost = BasePrice\ast{PriceMult}^{UpgradeCurrentLevel}$$
- Upgrade cost from level 0 to level n:
$$UpgradeCost_{From\ 0\ to\ n} = \sum_{k = 0}^{n - 1}{BasePrice\ast {PriceMult}^k}$$
$$UpgradeCost_{From\ 0\ to\ n} = BasePrice\ast\left( \frac{1 - {PriceMult}^{n}}{1 - PriceMult} \right)$$
$$UpgradeCost_{From\ 0\ to\ n} = BasePrice\ast\left( \frac{{PriceMult}^{n} - 1}{PriceMult - 1} \right)$$
- Upgrade cost level a to level b:
$$UpgradeCost_{From\ a\ to\ b} = \sum_{k = 0}^{b - 1}{BasePrice\ast {PriceMult}^k} - \sum_{k = 0}^{a - 1}{BasePrice\ast {PriceMult}^k}$$
$$UpgradeCost_{From\ a\ to\ b} = BasePrice\ast\left( \frac{{PriceMult}^{b} - 1}{PriceMult - 1} \right) - BasePrice\ast\left( \frac{{PriceMult}^{a} - 1}{PriceMult - 1} \right)$$
$$UpgradeCost_{From\ a\ to\ b} = BasePrice\ast\left( \frac{{PriceMult}^{b} - {PriceMult}^{a}}{PriceMult - 1} \right)$$
- Maximum upgrade level with a given `MaxCost`:
$$MaxUpgradeLevel = \log_{PriceMult}\left( MaxCost\ast\frac{PriceMult - 1}{BasePrice} + (PriceMult)^{CurrentLevel} \right)$$
- Benefit:
- All benefits are multipliers. `BaseBenefit` is 1.
- The only exception is DreamSense. Its benefit is raw value, its `BaseBenefit` is 0.
$$Benefit = BaseBenefit + Benefit\ast CurrentLevel$$
&nbsp;
Normal upgrades:
| **Name** | **Base price** | **Price multiplier** | **Benefit** | **Type** |
| ---------------------------------- | -------------- | -------------------- | ----------- | ----------------------- |
| SmartFactories | 2e9 | 1.06 | 0.03 | Production |
| SmartStorage | 2e9 | 1.06 | 0.1 | Storage |
| DreamSense | 4e9 | 1.1 | 0.001 | Awareness/Popularity |
| WilsonAnalytics | 4e9 | 2 | 0.005 | Advert's benefits |
| NuoptimalNootropicInjectorImplants | 1e9 | 1.06 | 0.1 | Employee's creativity |
| SpeechProcessorImplants | 1e9 | 1.06 | 0.1 | Employee's charisma |
| NeuralAccelerators | 1e9 | 1.06 | 0.1 | Employee's intelligence |
| FocusWires | 1e9 | 1.06 | 0.1 | Employee's efficiency |
| ABCSalesBots | 1e9 | 1.07 | 0.01 | Sales |
| ProjectInsight | 5e9 | 1.07 | 0.05 | RP |
&nbsp;
Special upgrades:
| **Name** | **Base price** | **Price multiplier** | **Type** |
| --------- | -------------- | -------------------- | -------------------- |
| Warehouse | 1e9 | 1.07 | Storage |
| Office | 4e9 | 1.09 | Office's size |
| Advert | 1e9 | 1.06 | Awareness/Popularity |
&nbsp;
Advices:
- DreamSense is useless. Never buy it.
- Round 1:
- SmartStorage and Warehouse are the most important upgrades in this round.
- Only buy 1 or 2 Advert level(s).
- Round 2:
- SmartFactories, SmartStorage and Warehouse are the most important upgrades in this round.
- Only buy 1 Office level and a couple of Advert levels for Agriculture division.
- Do not buy Office/Advert for Chemical division.
- Check this [section](./general-advice.md) for more advices, especially for round 3+.
## Research
Each research has a set of multipliers. For example: `sciResearchMult`, `productionMult`, etc.
Benefit of research type is the product of all research's multiplier of the same type.
| **Type** | **Research** | **Multiplier** | **Effect** |
| --------------------- | --------------------------------------------- | -------------- | ----------------------- |
| advertisingMult | No research | 1 | Advert's benefits |
| employeeChaMult | CPH4 Injections | 1.1 | Employee's charisma |
| employeeCreMult | CPH4 Injections | 1.1 | Employee's creativity |
| employeeEffMult | CPH4 Injections, Overclock | 1.1\*1.25 | Employee's efficiency |
| employeeIntMult | CPH4 Injections, Overclock | 1.1\*1.25 | Employee's intelligence |
| productionMult | Drones -- Assembly Self-Correcting Assemblers | 1.2\*1.1 | Production |
| productProductionMult | uPgrade: Fulcrum | 1.05 | Product's production |
| salesMult | No research | 1 | Sales |
| sciResearchMult | Hi-Tech R&D Laboratory | 1.1 | RP |
| storageMult | Drones - Transport | 1.5 | Storage |
&nbsp;
Research list:
| **Name** | **Cost** | **Description** |
| ----------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| Hi-Tech R&D Laboratory | 5000 | Top priority. Increase RP gain rate. It is the prerequisite of all other researches. |
| Market-TA.I | 20000 | Useless. It is the prerequisite of Market-TA.II. |
| Market-TA.II | 50000 | Top priority if you don't write custom script. Check this [section](./optimal-selling-price-market-ta2.md) to see how to write custom script. |
| Automatic Drug Administration | 10000 | It is the prerequisite of Go-Juice and CPH4 Injections. |
| Go-Juice | 25000 | Useful. Increase maximum energy. |
| CPH4 Injections | 25000 | Useful. Increase employee's stats. |
| Overclock | 15000 | Useful. Increase employee's stats. It is the prerequisite of Sti.mu. |
| Sti.mu | 30000 | Useful. Increase maximum morale. |
| Drones | 5000 | It is the prerequisite of Drones - Assembly and Drones - Transport. |
| Drones - Assembly | 25000 | Useful. Increase all productions. |
| Drones - Transport | 30000 | Useful. Increase warehouse's storage space. |
| Self-Correcting Assemblers | 25000 | Useful. Increase all productions. |
| uPgrade: Fulcrum | 10000 | Useful. Increase product's production. |
| uPgrade: Capacity.I | 20000 | Not useful. The cost is too high for its mediocre benefit. Increase maximum number of products by 1 (from 3 to 4). |
| uPgrade: Capacity.II | 30000 | Not useful. The cost is too high for its mediocre benefit. Increase maximum number of products by 1 (from 4 to 5). |
| uPgrade: Dashboard | 5000 | Useless. |
| AutoBrew | 12000 | Useless. |
| AutoPartyManager | 15000 | Useless. |
| HRBuddy-Recruitment | 15000 | Useless. |
| HRBuddy-Training | 20000 | Useless. |
&nbsp;
Advices:
- Do not deplete entire RP pool to buy research. You should only buy research if it costs less than half of the RP pool. Personally, my conditions for buying researches are:
- For energy/morale and employee's stats: if it costs less than 20% of RP pool.
- For production: if it costs less than 10% of RP pool.
- If you don't have a custom Market-TA2 script, you must prioritize Market-TA1 and Market-TA2. Market-TA1 is useless, the only reason to buy it is because it's the prerequisite of Market-TA2. If you buy them, you should stock up on RP and buy them together. However, I recommend implementing a custom Market-TA2 ASAP. Market-TA1 and Market-TA2 cost 70000 RP, that's a huge number of RP at the start of round 3+. **Implementing a custom Market-TA2 script is the best optimization in round 3+.**
- After that, you should prioritize researches for higher maximum energy/morale and employee's stats over production. Researches for production are nice to have, but it's much less important than energy/morale/employee's stats.
- My research order for higher maximum energy/morale and employee's stats: Overclock → Sti.mu → Automatic Drug Administration → Go-Juice → CPH4 Injections.
- Do not buy these useless researches:
- uPgrade: Dashboard
- AutoBrew
- AutoPartyManager
- HRBuddy-Recruitment
- HRBuddy-Training
- In most cases, uPgrade: Capacity.I and uPgrade: Capacity.II are useless. New products are usually much better than the old ones, so there is no point in increasing the maximum number of products. The only exception is when you reach the endgame. In the endgame, new products are only marginally better than the old ones, so having more product slots may be beneficial. However, even in the endgame, those researches may do more harm than good. In the endgame, the warehouse's size and high-quality input materials are serious bottlenecks. Having more product slots means that you need more free space in the warehouse and more units of input materials. In some cases, increasing product slots actually reduces the overall profit. You need to fine-tune it per use case.
You can exchange hashes for RP if you have SF9. This number of RP is added to all divisions.
RP gain rate:
- RP is increased in 4 states: PURCHASE, PRODUCTION, EXPORT and SALE.
- RP gain per city per state:
- `RnDProduction = office.employeeProductionByJob["Research & Development"]`
$$RPGain = 0.004\ast(RnDProduction)^{0.5}\ast UpgradeMultiplier\ast ResearchMultiplier$$
- Industry's `ScienceFactor` does not affect RP gain rate.

@ -0,0 +1,24 @@
# Warehouse
Warehouse starts at level 1 after being bought. The initial price is 5e9.
`BasePrice` in the following formulas is the upgrade's base price (1e9), not the initial price above.
Warehouse upgrade cost: its formula is a bit different from other upgrades (The exponent is `UpgradeCurrentLevel+1` instead of `UpgradeCurrentLevel`):
$$UpgradeCost = BasePrice\ast{1.07}^{UpgradeCurrentLevel + 1}$$
Upgrade cost for buying from level 1 to level n:
$$UpgradeCost_{From\ 1\ to\ n} = BasePrice\ast\left( \frac{{1.07}^{n + 1} - 1}{0.07} \right)$$
Upgrade cost for buying from level a to level b:
$$UpgradeCost_{From\ a\ to\ b} = BasePrice\ast\left( \frac{{1.07}^{b + 1} - {1.07}^{a + 1}}{0.07} \right)$$
Warehouse size:
- Upgrade multiplier: multiplier from Smart Storage.
- Research multiplier: multiplier from researches.
$$WarehouseSize = WarehouseLevel\ast 100\ast UpgradeMultiplier\ast ResearchMultiplier$$

@ -0,0 +1,41 @@
# Wilson Analytics - Advert
## Awareness and popularity
Wilson and Advert increase 2 important stats: awareness and popularity. These 2 stats affect initial demand and maximum sellable number of product units. I'll show formulas for them in later sections.
Awareness and popularity are capped at `Number.MAX_VALUE` (~1.7976931348623157E+308).
Raw values of those stats are crucial, but their ratio is also important. We want to have high ratio of popularity/awareness, check this [section](./optimal-selling-price-market-ta2.md) for formulas.
DreamSense only increases popularity by 0.001/cycle/level and awareness by 0.004/cycle/level. Those benefits are minuscule. However, that's not its biggest problem, the biggest one is the ratio of popularity/awareness. We want that ratio to be as high as possible, but DreamSense constantly lowers it.
Popularity decreases by 0.0001 per cycle.
## Wilson Analytics
Wilson is a multiplier that is applied on Advert's benefits when we buy Advert, so it's not retroactive. Therefore, we need to buy it as soon as possible. However, there are cases that Wilson is too expensive and it does not bring much benefits. Round 1 and 2 are those cases.
Wilson has `priceMult` of 2, so its price is doubled every time we buy it. The exponentiation of Advert's cost function uses base of 1.06, so its price increases much slower.
## Advert
Advert is a special upgrade. It affects only the division that buys it.
Cost: use the formulas in this [section](./unlocks-upgrade-research.md) with `BasePrice` = 1e9 and `PriceMult` = 1.06.
Benefit:
$$AdvertMultiplier = WilsonUpgradeBenefit\ast ResearchAdvertisingMultiplier$$
$$Awareness = (Awareness + 3\ast AdvertMultiplier)\ast(1.005*AdvertMultiplier)$$
$$Popularity = (Popularity + AdvertMultiplier)\ast(1 + {Random(1,3)}\ast{0.005})\ast AdvertMultiplier$$
## Advice
Buying Advert without Wilson is a valid strategy in extremely low-budget situations like round 1 and 2.
Wilson becomes extremely important in big-budget situations. Wilson is usually good investment in late phases.
In round 3+, we usually improve the divisions continuously with relatively small budget (profit of a few cycles). In this case, we should buy Wilson as soon as we can afford it.

@ -1,3 +1,81 @@
# Corporations
# Corporation
PLACEHOLDER
Corporation is an interesting feature of Bitburner. It's the most profitable feature in the game, and it's so overpowered that many BitNodes tend to apply a penalty modifier. However, it's extremely complex and opaque for newbies. There are too many mechanisms that are intertwined with each other, so it's very easy to make a mistake, and that mistake can cripple your corporation. This documentation explains all the mechanisms that you need to know to build a successful corporation.
The documentation is a bit long and intimidating at first glance, but you don't need to read all the sections below at once. I recommend that you read the first 4 sections. They are the most important sections for newbies. After that, you can read the following sections at your leisure.
## Contents
- [Basic gameplay and term](./corporation/basic-gameplay-and-term.md)
- Basic gameplay
- Term
- [FAQ](./corporation/faq.md)
- [Industry - Supply chain](./corporation/industry-supply-chain.md)
- Basic term
- Criterion
- Agriculture + Chemical + Tobacco
- Other product industries
- Conclusion
- [General advice](./corporation/general-advice.md)
- Round 1
- Round 2
- Round 3+
- [Unlocks - Upgrade - Research](./corporation/unlocks-upgrade-research.md)
- Unlocks
- Upgrade
- Research
- [Warehouse](./corporation/warehouse.md)
- [Boost material](./corporation/boost-material.md)
- Division production multiplier
- Optimizer
- Solution
- Lagrange multiplier method
- Proof
- Handle low storage space
- [Division raw production](./corporation/division-raw-production.md)
- Definition
- Formula
- [Office](./corporation/office.md)
- Basic information
- Upgrade
- Energy and morale
- Employee production by job
- Calculate employee's stat
- [Quality](./corporation/quality.md)
- Basic term
- Material
- Product
- [Smart Supply](./corporation/smart-supply.md)
- Logic
- Detect warehouse congestion
- [Wilson Analytics Advert](./corporation/wilson-analytics-advert.md)
- Awareness and popularity
- Wilson Analytics
- Advert
- Advice
- [Demand Competition](./corporation/demand-competition.md)
- Usage
- Material
- Product
- [Product](./corporation/product.md)
- Overview
- Formula
- Approximation value of product markup
- [Optimal selling price Market-TA2](./corporation/optimal-selling-price-market-ta2.md)
- Market price and markup limit
- Maximize sales volume
- Optimal selling price
- [Financial statement](./corporation/financial-statement.md)
- Total assets
- Valuation
- Investment offer
- Dividend
- Shares
- [Miscellany](./corporation/miscellany.md)
- Corporation's states
- Import and export
- Use mathematical library
- Ceres Solver
- Noodles trick
- "sudo.Assist" research
- Dummy division

@ -1,46 +1,63 @@
// THIS FILE IS AUTOGENERATED
import file0 from "!!raw-loader!./doc/advanced/bitnodes.md";
import file1 from "!!raw-loader!./doc/advanced/bladeburners.md";
import file2 from "!!raw-loader!./doc/advanced/corporations.md";
import file3 from "!!raw-loader!./doc/advanced/faction_list.md";
import file4 from "!!raw-loader!./doc/advanced/gang.md";
import file5 from "!!raw-loader!./doc/advanced/grafting.md";
import file6 from "!!raw-loader!./doc/advanced/hacknetservers.md";
import file7 from "!!raw-loader!./doc/advanced/intelligence.md";
import file8 from "!!raw-loader!./doc/advanced/offlineandbonustime.md";
import file9 from "!!raw-loader!./doc/advanced/sleeves.md";
import file10 from "!!raw-loader!./doc/advanced/sourcefiles.md";
import file11 from "!!raw-loader!./doc/advanced/stanek.md";
import file12 from "!!raw-loader!./doc/basic/augmentations.md";
import file13 from "!!raw-loader!./doc/basic/codingcontracts.md";
import file14 from "!!raw-loader!./doc/basic/companies.md";
import file15 from "!!raw-loader!./doc/basic/crimes.md";
import file16 from "!!raw-loader!./doc/basic/factions.md";
import file17 from "!!raw-loader!./doc/basic/hacking.md";
import file18 from "!!raw-loader!./doc/basic/hacknet_nodes.md";
import file19 from "!!raw-loader!./doc/basic/infiltration.md";
import file20 from "!!raw-loader!./doc/basic/programs.md";
import file21 from "!!raw-loader!./doc/basic/ram.md";
import file22 from "!!raw-loader!./doc/basic/reputation.md";
import file23 from "!!raw-loader!./doc/basic/scripts.md";
import file24 from "!!raw-loader!./doc/basic/servers.md";
import file25 from "!!raw-loader!./doc/basic/stats.md";
import file26 from "!!raw-loader!./doc/basic/stockmarket.md";
import file27 from "!!raw-loader!./doc/basic/terminal.md";
import file28 from "!!raw-loader!./doc/basic/world.md";
import file29 from "!!raw-loader!./doc/changelog.md";
import file30 from "!!raw-loader!./doc/help/bitnode_order.md";
import file31 from "!!raw-loader!./doc/help/getting_started.md";
import file32 from "!!raw-loader!./doc/help/tools_and_resources.md";
import file33 from "!!raw-loader!./doc/index.md";
import file34 from "!!raw-loader!./doc/migrations/ns2.md";
import file35 from "!!raw-loader!./doc/migrations/v1.md";
import file36 from "!!raw-loader!./doc/migrations/v2.md";
import file37 from "!!raw-loader!./doc/programming/game_frozen.md";
import file38 from "!!raw-loader!./doc/programming/go_algorithms.md";
import file39 from "!!raw-loader!./doc/programming/hackingalgorithms.md";
import file40 from "!!raw-loader!./doc/programming/learn.md";
import file41 from "!!raw-loader!./doc/programming/remote_api.md";
import file2 from "!!raw-loader!./doc/advanced/corporation/basic-gameplay-and-term.md";
import file3 from "!!raw-loader!./doc/advanced/corporation/boost-material.md";
import file4 from "!!raw-loader!./doc/advanced/corporation/demand-competition.md";
import file5 from "!!raw-loader!./doc/advanced/corporation/division-raw-production.md";
import file6 from "!!raw-loader!./doc/advanced/corporation/faq.md";
import file7 from "!!raw-loader!./doc/advanced/corporation/financial-statement.md";
import file8 from "!!raw-loader!./doc/advanced/corporation/general-advice.md";
import file9 from "!!raw-loader!./doc/advanced/corporation/industry-supply-chain.md";
import file10 from "!!raw-loader!./doc/advanced/corporation/miscellany.md";
import file11 from "!!raw-loader!./doc/advanced/corporation/office.md";
import file12 from "!!raw-loader!./doc/advanced/corporation/optimal-selling-price-market-ta2.md";
import file13 from "!!raw-loader!./doc/advanced/corporation/product.md";
import file14 from "!!raw-loader!./doc/advanced/corporation/quality.md";
import file15 from "!!raw-loader!./doc/advanced/corporation/smart-supply.md";
import file16 from "!!raw-loader!./doc/advanced/corporation/unlocks-upgrade-research.md";
import file17 from "!!raw-loader!./doc/advanced/corporation/warehouse.md";
import file18 from "!!raw-loader!./doc/advanced/corporation/wilson-analytics-advert.md";
import file19 from "!!raw-loader!./doc/advanced/corporations.md";
import file20 from "!!raw-loader!./doc/advanced/faction_list.md";
import file21 from "!!raw-loader!./doc/advanced/gang.md";
import file22 from "!!raw-loader!./doc/advanced/grafting.md";
import file23 from "!!raw-loader!./doc/advanced/hacknetservers.md";
import file24 from "!!raw-loader!./doc/advanced/intelligence.md";
import file25 from "!!raw-loader!./doc/advanced/offlineandbonustime.md";
import file26 from "!!raw-loader!./doc/advanced/sleeves.md";
import file27 from "!!raw-loader!./doc/advanced/sourcefiles.md";
import file28 from "!!raw-loader!./doc/advanced/stanek.md";
import file29 from "!!raw-loader!./doc/basic/augmentations.md";
import file30 from "!!raw-loader!./doc/basic/codingcontracts.md";
import file31 from "!!raw-loader!./doc/basic/companies.md";
import file32 from "!!raw-loader!./doc/basic/crimes.md";
import file33 from "!!raw-loader!./doc/basic/factions.md";
import file34 from "!!raw-loader!./doc/basic/hacking.md";
import file35 from "!!raw-loader!./doc/basic/hacknet_nodes.md";
import file36 from "!!raw-loader!./doc/basic/infiltration.md";
import file37 from "!!raw-loader!./doc/basic/programs.md";
import file38 from "!!raw-loader!./doc/basic/ram.md";
import file39 from "!!raw-loader!./doc/basic/reputation.md";
import file40 from "!!raw-loader!./doc/basic/scripts.md";
import file41 from "!!raw-loader!./doc/basic/servers.md";
import file42 from "!!raw-loader!./doc/basic/stats.md";
import file43 from "!!raw-loader!./doc/basic/stockmarket.md";
import file44 from "!!raw-loader!./doc/basic/terminal.md";
import file45 from "!!raw-loader!./doc/basic/world.md";
import file46 from "!!raw-loader!./doc/changelog.md";
import file47 from "!!raw-loader!./doc/help/bitnode_order.md";
import file48 from "!!raw-loader!./doc/help/getting_started.md";
import file49 from "!!raw-loader!./doc/help/tools_and_resources.md";
import file50 from "!!raw-loader!./doc/index.md";
import file51 from "!!raw-loader!./doc/migrations/ns2.md";
import file52 from "!!raw-loader!./doc/migrations/v1.md";
import file53 from "!!raw-loader!./doc/migrations/v2.md";
import file54 from "!!raw-loader!./doc/programming/game_frozen.md";
import file55 from "!!raw-loader!./doc/programming/go_algorithms.md";
import file56 from "!!raw-loader!./doc/programming/hackingalgorithms.md";
import file57 from "!!raw-loader!./doc/programming/learn.md";
import file58 from "!!raw-loader!./doc/programming/remote_api.md";
interface Document {
default: string;
@ -48,43 +65,60 @@ interface Document {
export const AllPages: Record<string, Document> = {};
AllPages["advanced/bitnodes.md"] = file0;
AllPages["advanced/bladeburners.md"] = file1;
AllPages["advanced/corporations.md"] = file2;
AllPages["advanced/faction_list.md"] = file3;
AllPages["advanced/gang.md"] = file4;
AllPages["advanced/grafting.md"] = file5;
AllPages["advanced/hacknetservers.md"] = file6;
AllPages["advanced/intelligence.md"] = file7;
AllPages["advanced/offlineandbonustime.md"] = file8;
AllPages["advanced/sleeves.md"] = file9;
AllPages["advanced/sourcefiles.md"] = file10;
AllPages["advanced/stanek.md"] = file11;
AllPages["basic/augmentations.md"] = file12;
AllPages["basic/codingcontracts.md"] = file13;
AllPages["basic/companies.md"] = file14;
AllPages["basic/crimes.md"] = file15;
AllPages["basic/factions.md"] = file16;
AllPages["basic/hacking.md"] = file17;
AllPages["basic/hacknet_nodes.md"] = file18;
AllPages["basic/infiltration.md"] = file19;
AllPages["basic/programs.md"] = file20;
AllPages["basic/ram.md"] = file21;
AllPages["basic/reputation.md"] = file22;
AllPages["basic/scripts.md"] = file23;
AllPages["basic/servers.md"] = file24;
AllPages["basic/stats.md"] = file25;
AllPages["basic/stockmarket.md"] = file26;
AllPages["basic/terminal.md"] = file27;
AllPages["basic/world.md"] = file28;
AllPages["changelog.md"] = file29;
AllPages["help/bitnode_order.md"] = file30;
AllPages["help/getting_started.md"] = file31;
AllPages["help/tools_and_resources.md"] = file32;
AllPages["index.md"] = file33;
AllPages["migrations/ns2.md"] = file34;
AllPages["migrations/v1.md"] = file35;
AllPages["migrations/v2.md"] = file36;
AllPages["programming/game_frozen.md"] = file37;
AllPages["programming/go_algorithms.md"] = file38;
AllPages["programming/hackingalgorithms.md"] = file39;
AllPages["programming/learn.md"] = file40;
AllPages["programming/remote_api.md"] = file41;
AllPages["advanced/corporation/basic-gameplay-and-term.md"] = file2;
AllPages["advanced/corporation/boost-material.md"] = file3;
AllPages["advanced/corporation/demand-competition.md"] = file4;
AllPages["advanced/corporation/division-raw-production.md"] = file5;
AllPages["advanced/corporation/faq.md"] = file6;
AllPages["advanced/corporation/financial-statement.md"] = file7;
AllPages["advanced/corporation/general-advice.md"] = file8;
AllPages["advanced/corporation/industry-supply-chain.md"] = file9;
AllPages["advanced/corporation/miscellany.md"] = file10;
AllPages["advanced/corporation/office.md"] = file11;
AllPages["advanced/corporation/optimal-selling-price-market-ta2.md"] = file12;
AllPages["advanced/corporation/product.md"] = file13;
AllPages["advanced/corporation/quality.md"] = file14;
AllPages["advanced/corporation/smart-supply.md"] = file15;
AllPages["advanced/corporation/unlocks-upgrade-research.md"] = file16;
AllPages["advanced/corporation/warehouse.md"] = file17;
AllPages["advanced/corporation/wilson-analytics-advert.md"] = file18;
AllPages["advanced/corporations.md"] = file19;
AllPages["advanced/faction_list.md"] = file20;
AllPages["advanced/gang.md"] = file21;
AllPages["advanced/grafting.md"] = file22;
AllPages["advanced/hacknetservers.md"] = file23;
AllPages["advanced/intelligence.md"] = file24;
AllPages["advanced/offlineandbonustime.md"] = file25;
AllPages["advanced/sleeves.md"] = file26;
AllPages["advanced/sourcefiles.md"] = file27;
AllPages["advanced/stanek.md"] = file28;
AllPages["basic/augmentations.md"] = file29;
AllPages["basic/codingcontracts.md"] = file30;
AllPages["basic/companies.md"] = file31;
AllPages["basic/crimes.md"] = file32;
AllPages["basic/factions.md"] = file33;
AllPages["basic/hacking.md"] = file34;
AllPages["basic/hacknet_nodes.md"] = file35;
AllPages["basic/infiltration.md"] = file36;
AllPages["basic/programs.md"] = file37;
AllPages["basic/ram.md"] = file38;
AllPages["basic/reputation.md"] = file39;
AllPages["basic/scripts.md"] = file40;
AllPages["basic/servers.md"] = file41;
AllPages["basic/stats.md"] = file42;
AllPages["basic/stockmarket.md"] = file43;
AllPages["basic/terminal.md"] = file44;
AllPages["basic/world.md"] = file45;
AllPages["changelog.md"] = file46;
AllPages["help/bitnode_order.md"] = file47;
AllPages["help/getting_started.md"] = file48;
AllPages["help/tools_and_resources.md"] = file49;
AllPages["index.md"] = file50;
AllPages["migrations/ns2.md"] = file51;
AllPages["migrations/v1.md"] = file52;
AllPages["migrations/v2.md"] = file53;
AllPages["programming/game_frozen.md"] = file54;
AllPages["programming/go_algorithms.md"] = file55;
AllPages["programming/hackingalgorithms.md"] = file56;
AllPages["programming/learn.md"] = file57;
AllPages["programming/remote_api.md"] = file58;

@ -5,6 +5,8 @@ import remarkGfm from "remark-gfm";
import { h1, h2, h3, h4, h5, h6, li, Td, Th, table, tr, Blockquote, p } from "./components";
import { code, Pre } from "./code";
import { A } from "./a";
import remarkMath from "remark-math";
import rehypeMathjax from "rehype-mathjax/svg";
export function MD(props: { md: string }): React.ReactElement {
return (
@ -29,7 +31,8 @@ export function MD(props: { md: string }): React.ReactElement {
blockquote: Blockquote,
a: A,
}}
remarkPlugins={[remarkGfm]}
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[rehypeMathjax]}
>
{props.md}
</ReactMarkdown>