mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-18 13:43:49 +01:00
commit
34b68f6722
42
dist/vendor.bundle.js
vendored
42
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
2
dist/vendor.bundle.js.map
vendored
2
dist/vendor.bundle.js.map
vendored
File diff suppressed because one or more lines are too long
@ -5,10 +5,10 @@ Grafting
|
||||
Grafting is an experimental process through which you can obtain the benefits of
|
||||
Augmentations, without needing to reboot your body.
|
||||
|
||||
In order to graft, you must first purchase a blueprint for and craft the Augmentation.
|
||||
This can be done at VitaLife in New Tokyo, where you'll find a shady researcher with
|
||||
questionable connections. Once you purchase a blueprint, you will start crafting the
|
||||
Augmentation, and it will be grafted to your body once complete.
|
||||
Grafting can be done at VitaLife in New Tokyo, where you'll find a shady researcher with
|
||||
questionable connections. From there, you can spend a sum of money to begin grafting
|
||||
Augmentations. This will take some time. When done, the Augmentation will be applied to
|
||||
your character without needing to install.
|
||||
|
||||
Be warned, some who have tested grafting have reported an unidentified malware. Dubbed
|
||||
"Entropy", this virus seems to grow in potency as more Augmentations are grafted,
|
||||
|
@ -93,9 +93,15 @@ The list contains the name of (i.e. the value returned by
|
||||
| Subarray with Maximum Sum | | Given an array of integers, find the contiguous subarray (containing |
|
||||
| | | at least one number) which has the largest sum and return that sum. |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Total Ways to Sum | | Given a number, how many different ways can that number be written as |
|
||||
| Total Ways to Sum | | Given a number, how many different distinct ways can that number be written as |
|
||||
| | | a sum of at least two positive integers? |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Total Ways to Sum II | | You are given an array with two elements. The first element is an integer n. |
|
||||
| | | The second element is an array of numbers representing the set of available integers. |
|
||||
| | | How many different distinct ways can that number n be written as |
|
||||
| | | a sum of integers contained in the given set? |
|
||||
| | | You may use each integer in the set zero or more times. |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Spiralize Matrix | | Given an array of array of numbers representing a 2D matrix, return the |
|
||||
| | | elements of that matrix in clockwise spiral order. |
|
||||
| | | |
|
||||
@ -115,6 +121,16 @@ The list contains the name of (i.e. the value returned by
|
||||
| | | Assuming you are initially positioned at the start of the array, determine |
|
||||
| | | whether you are able to reach the last index of the array. |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Array Jumping Game II | | You are given an array of integers where each element represents the |
|
||||
| | | maximum possible jump distance from that position. For example, if you |
|
||||
| | | are at position i and your maximum jump length is n, then you can jump |
|
||||
| | | to any position from i to i+n. |
|
||||
| | | |
|
||||
| | | Assuming you are initially positioned at the start of the array, determine |
|
||||
| | | the minimum number of jumps to reach the end of the array. |
|
||||
| | | |
|
||||
| | | If it's impossible to reach the end, then the answer should be 0. |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Merge Overlapping Intervals | | Given an array of intervals, merge all overlapping intervals. An interval |
|
||||
| | | is an array with two numbers, where the first number is always less than |
|
||||
| | | the second (e.g. [1, 5]). |
|
||||
@ -196,6 +212,23 @@ The list contains the name of (i.e. the value returned by
|
||||
| | | |
|
||||
| | | Determine how many unique paths there are from start to finish. |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Shortest Path in a Grid | | You are given a 2D array of numbers (array of array of numbers) representing |
|
||||
| | | a grid. The 2D array contains 1's and 0's, where 1 represents an obstacle and |
|
||||
| | | 0 represents a free space. |
|
||||
| | | |
|
||||
| | | Assume you are initially positioned in top-left corner of that grid and that you |
|
||||
| | | are trying to reach the bottom-right corner. In each step, you may move to the up, |
|
||||
| | | down, left or right. Furthermore, you cannot move onto spaces which have obstacles. |
|
||||
| | | |
|
||||
| | | Determine if paths exist from start to destination, and find the shortest one. |
|
||||
| | | |
|
||||
| | | Examples: |
|
||||
| | | [[0,1,0,0,0], |
|
||||
| | | [0,0,0,1,0]] -> "DRRURRD" |
|
||||
| | | [[0,1], |
|
||||
| | | [1,0]] -> "" |
|
||||
| | | |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Sanitize Parentheses in Expression | | Given a string with parentheses and letters, remove the minimum number of invalid |
|
||||
| | | parentheses in order to validate the string. If there are multiple minimal ways |
|
||||
| | | to validate the string, provide all of the possible results. |
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
42
package-lock.json
generated
42
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "bitburner",
|
||||
"version": "1.5.0",
|
||||
"version": "1.6.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "bitburner",
|
||||
"version": "1.5.0",
|
||||
"version": "1.6.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "SEE LICENSE IN license.txt",
|
||||
"dependencies": {
|
||||
@ -69,7 +69,7 @@
|
||||
"babel-jest": "^27.0.6",
|
||||
"babel-loader": "^8.0.5",
|
||||
"cypress": "^8.3.1",
|
||||
"electron": "^14.0.2",
|
||||
"electron": "^14.2.4",
|
||||
"electron-packager": "^15.4.0",
|
||||
"eslint": "^7.24.0",
|
||||
"file-loader": "^6.2.0",
|
||||
@ -7821,9 +7821,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/electron": {
|
||||
"version": "14.0.2",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-14.0.2.tgz",
|
||||
"integrity": "sha512-LIJj795cfggUtLHIM84lseE7LC0kAs/HNVXoDFPTjtYzQikPX9XAIMI1BTJcod3j+U1ZXsayk9N4M3M890WD3w==",
|
||||
"version": "14.2.4",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-14.2.4.tgz",
|
||||
"integrity": "sha512-uskCIp+fpohqVYtM2Q28rbXLqGjZ6sWYylXcX6N+K8jR8kR2eHuDMIkO8DzWrTsqA6t4UNAzn+bJnA3VfIIjQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
@ -15190,9 +15190,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"node_modules/mixin-deep": {
|
||||
"version": "1.3.2",
|
||||
@ -16484,9 +16484,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/plist": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/plist/-/plist-3.0.4.tgz",
|
||||
"integrity": "sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/plist/-/plist-3.0.5.tgz",
|
||||
"integrity": "sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"base64-js": "^1.5.1",
|
||||
@ -28515,9 +28515,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"electron": {
|
||||
"version": "14.0.2",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-14.0.2.tgz",
|
||||
"integrity": "sha512-LIJj795cfggUtLHIM84lseE7LC0kAs/HNVXoDFPTjtYzQikPX9XAIMI1BTJcod3j+U1ZXsayk9N4M3M890WD3w==",
|
||||
"version": "14.2.4",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-14.2.4.tgz",
|
||||
"integrity": "sha512-uskCIp+fpohqVYtM2Q28rbXLqGjZ6sWYylXcX6N+K8jR8kR2eHuDMIkO8DzWrTsqA6t4UNAzn+bJnA3VfIIjQw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@electron/get": "^1.0.1",
|
||||
@ -34244,9 +34244,9 @@
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"mixin-deep": {
|
||||
"version": "1.3.2",
|
||||
@ -35264,9 +35264,9 @@
|
||||
}
|
||||
},
|
||||
"plist": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/plist/-/plist-3.0.4.tgz",
|
||||
"integrity": "sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/plist/-/plist-3.0.5.tgz",
|
||||
"integrity": "sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"base64-js": "^1.5.1",
|
||||
|
@ -70,7 +70,7 @@
|
||||
"babel-jest": "^27.0.6",
|
||||
"babel-loader": "^8.0.5",
|
||||
"cypress": "^8.3.1",
|
||||
"electron": "^14.0.2",
|
||||
"electron": "^14.2.4",
|
||||
"electron-packager": "^15.4.0",
|
||||
"eslint": "^7.24.0",
|
||||
"file-loader": "^6.2.0",
|
||||
|
@ -2,7 +2,6 @@
|
||||
import * as React from "react";
|
||||
import { IMap } from "../types";
|
||||
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { Faction } from "../Faction/Faction";
|
||||
import { Factions } from "../Faction/Factions";
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
@ -134,7 +133,7 @@ function generateStatsDescription(mults: IMap<number>, programs?: string[], star
|
||||
desc = (
|
||||
<>
|
||||
{desc}
|
||||
<br />+{f(mults.charisma_mult - 1)} Charisma skill
|
||||
<br />+{f(mults.charisma_mult - 1)} charisma skill
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1484,6 +1484,25 @@ export const initGeneralAugmentations = (): Augmentation[] => [
|
||||
),
|
||||
factions: [FactionNames.TianDiHui],
|
||||
}),
|
||||
|
||||
// Grafting-exclusive Augmentation
|
||||
new Augmentation({
|
||||
name: AugmentationNames.CongruityImplant,
|
||||
repCost: Infinity,
|
||||
moneyCost: 50e12,
|
||||
info: (
|
||||
<>
|
||||
Developed by a pioneer in Grafting research, this implant generates pulses of stability which seem to have a
|
||||
nullifying effect versus the Entropy virus.
|
||||
<br />
|
||||
<br />
|
||||
<b>Note:</b> For unknown reasons, the lowercase <code>n</code> appears to be an integral component to its
|
||||
functionality.
|
||||
</>
|
||||
),
|
||||
stats: <>This Augmentation removes the Entropy virus, and prevents it from affecting you again.</>,
|
||||
factions: [],
|
||||
}),
|
||||
];
|
||||
|
||||
export const initBladeburnerAugmentations = (): Augmentation[] => [
|
||||
|
@ -141,6 +141,12 @@ function applyAugmentation(aug: IPlayerOwnedAugmentation, reapply = false): void
|
||||
}
|
||||
}
|
||||
|
||||
// Special logic for Congruity Implant
|
||||
if (aug.name === AugmentationNames.CongruityImplant && !reapply) {
|
||||
Player.entropy = 0;
|
||||
Player.applyEntropy(Player.entropy);
|
||||
}
|
||||
|
||||
// Push onto Player's Augmentation list
|
||||
if (!reapply) {
|
||||
const ownedAug = new PlayerOwnedAugmentation(aug.name);
|
||||
@ -148,8 +154,8 @@ function applyAugmentation(aug: IPlayerOwnedAugmentation, reapply = false): void
|
||||
}
|
||||
}
|
||||
|
||||
function installAugmentations(): boolean {
|
||||
if (Player.queuedAugmentations.length == 0) {
|
||||
function installAugmentations(force?: boolean): boolean {
|
||||
if (Player.queuedAugmentations.length == 0 && !force) {
|
||||
dialogBoxCreate("You have not purchased any Augmentations to install!");
|
||||
return false;
|
||||
}
|
||||
@ -179,12 +185,14 @@ function installAugmentations(): boolean {
|
||||
augmentationList += aug.name + level + "<br>";
|
||||
}
|
||||
Player.queuedAugmentations = [];
|
||||
dialogBoxCreate(
|
||||
"You slowly drift to sleep as scientists put you under in order " +
|
||||
"to install the following Augmentations:<br>" +
|
||||
augmentationList +
|
||||
"<br>You wake up in your home...you feel different...",
|
||||
);
|
||||
if (!force) {
|
||||
dialogBoxCreate(
|
||||
"You slowly drift to sleep as scientists put you under in order " +
|
||||
"to install the following Augmentations:<br>" +
|
||||
augmentationList +
|
||||
"<br>You wake up in your home...you feel different...",
|
||||
);
|
||||
}
|
||||
prestigeAugmentation();
|
||||
return true;
|
||||
}
|
||||
|
@ -91,6 +91,7 @@ export enum AugmentationNames {
|
||||
BionicArms = "Bionic Arms",
|
||||
SNA = "Social Negotiation Assistant (S.N.A)",
|
||||
HydroflameLeftArm = "Hydroflame Left Arm",
|
||||
CongruityImplant = "nickofolas Congruity Implant",
|
||||
EsperEyewear = "EsperTech Bladeburner Eyewear",
|
||||
EMS4Recombination = "EMS-4 Recombination",
|
||||
OrionShoulder = "ORION-MKIV Shoulder",
|
||||
|
@ -276,7 +276,7 @@ export const CONSTANTS: {
|
||||
CodingContractBaseCompanyRepGain: 4000,
|
||||
CodingContractBaseMoneyGain: 75e6,
|
||||
|
||||
// Augmentation crafting multipliers
|
||||
// Augmentation grafting multipliers
|
||||
AugmentationGraftingCostMult: 3,
|
||||
AugmentationGraftingTimeBase: 3600000,
|
||||
|
||||
|
@ -257,7 +257,7 @@ export function BulkPurchase(corp: ICorporation, warehouse: Warehouse, material:
|
||||
if (isNaN(amt) || amt < 0) {
|
||||
throw new Error(`Invalid input amount`);
|
||||
}
|
||||
if (amt * matSize <= maxAmount) {
|
||||
if (amt > maxAmount) {
|
||||
throw new Error(`You do not have enough warehouse size to fit this purchase`);
|
||||
}
|
||||
const cost = amt * material.bCost;
|
||||
|
@ -30,7 +30,7 @@ export function CityTabs(props: IProps): React.ReactElement {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Tabs variant="fullWidth" value={city} onChange={handleChange} sx={{ maxWidth: "65%" }}>
|
||||
<Tabs variant="fullWidth" value={city} onChange={handleChange} sx={{ maxWidth: "65vw" }}>
|
||||
{Object.values(division.offices).map(
|
||||
(office: OfficeSpace | 0) => office !== 0 && <Tab key={office.loc} label={office.loc} value={office.loc} />,
|
||||
)}
|
||||
|
@ -38,7 +38,7 @@ export function CorporationRoot(): React.ReactElement {
|
||||
|
||||
return (
|
||||
<Context.Corporation.Provider value={corporation}>
|
||||
<Tabs variant="scrollable" value={divisionName} onChange={handleChange} sx={{ maxWidth: "65%" }} scrollButtons>
|
||||
<Tabs variant="scrollable" value={divisionName} onChange={handleChange} sx={{ maxWidth: "65vw" }} scrollButtons>
|
||||
<Tab label={corporation.name} value={"Overview"} />
|
||||
{corporation.divisions.map((div) => (
|
||||
<Tab key={div.name} label={div.name} value={div.name} />
|
||||
|
@ -203,7 +203,7 @@ export const getFactionAugmentationsFiltered = (player: IPlayer, faction: Factio
|
||||
// Remove special augs
|
||||
augs = augs.filter((a) => !a.isSpecial);
|
||||
|
||||
const blacklist: string[] = [AugmentationNames.NeuroFluxGovernor];
|
||||
const blacklist: string[] = [AugmentationNames.NeuroFluxGovernor, AugmentationNames.CongruityImplant];
|
||||
|
||||
if (player.bitNodeN !== 2) {
|
||||
// TRP is not available outside of BN2 for Gangs
|
||||
|
@ -225,7 +225,6 @@ export class Gang implements IGang {
|
||||
if (AllGangs[otherGang].territory <= 0) return;
|
||||
const territoryGain = calculateTerritoryGain(thisGang, otherGang);
|
||||
AllGangs[thisGang].territory += territoryGain;
|
||||
if (AllGangs[thisGang].territory > 0.999) AllGangs[thisGang].territory = 1;
|
||||
AllGangs[otherGang].territory -= territoryGain;
|
||||
if (thisGang === gangName) {
|
||||
this.clash(true); // Player won
|
||||
@ -239,9 +238,7 @@ export class Gang implements IGang {
|
||||
if (AllGangs[thisGang].territory <= 0) return;
|
||||
const territoryGain = calculateTerritoryGain(otherGang, thisGang);
|
||||
AllGangs[thisGang].territory -= territoryGain;
|
||||
if (AllGangs[otherGang].territory < 0.001) AllGangs[otherGang].territory = 0;
|
||||
AllGangs[otherGang].territory += territoryGain;
|
||||
if (AllGangs[otherGang].territory > 0.999) AllGangs[otherGang].territory = 1;
|
||||
if (thisGang === gangName) {
|
||||
this.clash(false); // Player lost
|
||||
} else if (otherGang === gangName) {
|
||||
@ -251,6 +248,11 @@ export class Gang implements IGang {
|
||||
AllGangs[thisGang].power *= 1 / 1.01;
|
||||
}
|
||||
}
|
||||
|
||||
const total = Object.values(AllGangs)
|
||||
.map((g) => g.territory)
|
||||
.reduce((p, c) => p + c, 0);
|
||||
Object.values(AllGangs).forEach((g) => (g.territory /= total));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import { Money } from "../../ui/React/Money";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
import { MathJaxWrapper } from "../../MathJaxWrapper";
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
|
||||
type IProps = {
|
||||
p: IPlayer;
|
||||
@ -30,9 +31,13 @@ export function RamButton(props: IProps): React.ReactElement {
|
||||
props.rerender();
|
||||
}
|
||||
|
||||
const bnMult = BitNodeMultipliers.HomeComputerRamCost === 1 ? "" : `\\cdot ${BitNodeMultipliers.HomeComputerRamCost}`;
|
||||
console.log(BitNodeMultipliers.HomeComputerRamCost);
|
||||
return (
|
||||
<Tooltip
|
||||
title={<MathJaxWrapper>{`\\(\\large{cost = 3.2 \\cdot 10^3 \\cdot 1.58^{log_2{(ram)}}}\\)`}</MathJaxWrapper>}
|
||||
title={
|
||||
<MathJaxWrapper>{`\\(\\large{cost = ram \\cdot 3.2 \\cdot 10^4 \\cdot 1.58^{log_2{(ram)}}} ${bnMult}\\)`}</MathJaxWrapper>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<br />
|
||||
|
153
src/Netscript/APIWrapper.ts
Normal file
153
src/Netscript/APIWrapper.ts
Normal file
@ -0,0 +1,153 @@
|
||||
import { getRamCost } from "./RamCostGenerator";
|
||||
import type { IPort } from "../NetscriptPort";
|
||||
import type { BaseServer } from "../Server/BaseServer";
|
||||
import type { WorkerScript } from "./WorkerScript";
|
||||
import { makeRuntimeRejectMsg } from "../NetscriptEvaluator";
|
||||
import { Player } from "../Player";
|
||||
|
||||
type ExternalFunction = (...args: any[]) => any;
|
||||
type ExternalAPI = {
|
||||
[string: string]: ExternalAPI | ExternalFunction;
|
||||
};
|
||||
|
||||
type InternalFunction<F extends (...args: unknown[]) => unknown> = (ctx: NetscriptContext) => F;
|
||||
export type InternalAPI<API> = {
|
||||
[Property in keyof API]: API[Property] extends ExternalFunction
|
||||
? InternalFunction<API[Property]>
|
||||
: API[Property] extends ExternalAPI
|
||||
? InternalAPI<API[Property]>
|
||||
: never;
|
||||
};
|
||||
|
||||
type WrappedNetscriptFunction = (...args: unknown[]) => unknown;
|
||||
type WrappedNetscriptAPI = {
|
||||
readonly [string: string]: WrappedNetscriptAPI | WrappedNetscriptFunction;
|
||||
};
|
||||
|
||||
export type NetscriptContext = {
|
||||
makeRuntimeErrorMsg: (message: string) => string;
|
||||
log: (message: () => string) => void;
|
||||
workerScript: WorkerScript;
|
||||
function: string;
|
||||
helper: WrappedNetscriptHelpers;
|
||||
};
|
||||
|
||||
type NetscriptHelpers = {
|
||||
updateDynamicRam: (fnName: string, ramCost: number) => void;
|
||||
makeRuntimeErrorMsg: (caller: string, msg: string) => string;
|
||||
string: (funcName: string, argName: string, v: unknown) => string;
|
||||
number: (funcName: string, argName: string, v: unknown) => number;
|
||||
boolean: (v: unknown) => boolean;
|
||||
getServer: (hostname: string, callingFnName: string) => BaseServer;
|
||||
checkSingularityAccess: (func: string) => void;
|
||||
hack: (hostname: any, manual: any, { threads: requestedThreads, stock }?: any) => Promise<number>;
|
||||
getValidPort: (funcName: string, port: any) => IPort;
|
||||
};
|
||||
|
||||
type WrappedNetscriptHelpers = {
|
||||
makeRuntimeErrorMsg: (msg: string) => string;
|
||||
string: (argName: string, v: unknown) => string;
|
||||
number: (argName: string, v: unknown) => number;
|
||||
boolean: (v: unknown) => boolean;
|
||||
getServer: (hostname: string) => BaseServer;
|
||||
checkSingularityAccess: () => void;
|
||||
hack: (hostname: any, manual: any, { threads: requestedThreads, stock }?: any) => Promise<number>;
|
||||
getValidPort: (port: any) => IPort;
|
||||
};
|
||||
|
||||
function wrapFunction(
|
||||
helpers: NetscriptHelpers,
|
||||
wrappedAPI: any,
|
||||
workerScript: WorkerScript,
|
||||
func: (_ctx: NetscriptContext) => (...args: unknown[]) => unknown,
|
||||
...tree: string[]
|
||||
): void {
|
||||
const functionPath = tree.join(".");
|
||||
const functionName = tree.pop();
|
||||
if (typeof functionName !== "string") {
|
||||
throw makeRuntimeRejectMsg(workerScript, "Failure occured while wrapping netscript api");
|
||||
}
|
||||
const ctx = {
|
||||
makeRuntimeErrorMsg: (message: string) => {
|
||||
return helpers.makeRuntimeErrorMsg(functionPath, message);
|
||||
},
|
||||
log: (message: () => string) => {
|
||||
workerScript.log(functionPath, message);
|
||||
},
|
||||
workerScript,
|
||||
function: functionName,
|
||||
helper: {
|
||||
makeRuntimeErrorMsg: (msg: string) => helpers.makeRuntimeErrorMsg(functionPath, msg),
|
||||
string: (argName: string, v: unknown) => helpers.string(functionPath, argName, v),
|
||||
number: (argName: string, v: unknown) => helpers.number(functionPath, argName, v),
|
||||
boolean: helpers.boolean,
|
||||
getServer: (hostname: string) => helpers.getServer(hostname, functionPath),
|
||||
checkSingularityAccess: () => helpers.checkSingularityAccess(functionName),
|
||||
hack: helpers.hack,
|
||||
getValidPort: (port: any) => helpers.getValidPort(functionPath, port),
|
||||
},
|
||||
};
|
||||
function wrappedFunction(...args: unknown[]): unknown {
|
||||
helpers.updateDynamicRam(ctx.function, getRamCost(Player, ...tree, ctx.function));
|
||||
return func(ctx)(...args);
|
||||
}
|
||||
const parent = getNestedProperty(wrappedAPI, ...tree);
|
||||
Object.defineProperty(parent, functionName, {
|
||||
value: wrappedFunction,
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function wrapAPI(
|
||||
helpers: NetscriptHelpers,
|
||||
wrappedAPI: ExternalAPI,
|
||||
workerScript: WorkerScript,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
namespace: any,
|
||||
...tree: string[]
|
||||
): WrappedNetscriptAPI {
|
||||
if (typeof namespace !== "object") throw new Error("Invalid namespace?");
|
||||
for (const property of Object.getOwnPropertyNames(namespace)) {
|
||||
switch (typeof namespace[property]) {
|
||||
case "function": {
|
||||
wrapFunction(helpers, wrappedAPI, workerScript, namespace[property], ...tree, property);
|
||||
break;
|
||||
}
|
||||
case "object": {
|
||||
wrapAPI(helpers, wrappedAPI, workerScript, namespace[property], ...tree, property);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
setNestedProperty(wrappedAPI, namespace[property], ...tree, property);
|
||||
}
|
||||
}
|
||||
}
|
||||
return wrappedAPI;
|
||||
}
|
||||
|
||||
function setNestedProperty(root: any, value: any, ...tree: string[]): any {
|
||||
let target = root;
|
||||
const key = tree.pop();
|
||||
if (typeof key !== "string") {
|
||||
throw new Error("Failure occured while wrapping netscript api (setNestedProperty)");
|
||||
}
|
||||
for (const branch of tree) {
|
||||
if (target[branch] === undefined) {
|
||||
target[branch] = {};
|
||||
}
|
||||
target = target[branch];
|
||||
}
|
||||
target[key] = value;
|
||||
}
|
||||
|
||||
function getNestedProperty(root: any, ...tree: string[]): any {
|
||||
let target = root;
|
||||
for (const branch of tree) {
|
||||
if (target[branch] === undefined) {
|
||||
target[branch] = {};
|
||||
}
|
||||
target = target[branch];
|
||||
}
|
||||
return target;
|
||||
}
|
@ -99,6 +99,7 @@ import { Flags } from "./NetscriptFunctions/Flags";
|
||||
import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence";
|
||||
import { CalculateShareMult, StartSharing } from "./NetworkShare/Share";
|
||||
import { CityName } from "./Locations/data/CityNames";
|
||||
import { wrapAPI } from "./Netscript/APIWrapper";
|
||||
|
||||
interface NS extends INS {
|
||||
[key: string]: any;
|
||||
@ -491,7 +492,8 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
const sleeve = NetscriptSleeve(Player, workerScript, helper);
|
||||
const extra = NetscriptExtra(Player, workerScript, helper);
|
||||
const hacknet = NetscriptHacknet(Player, workerScript, helper);
|
||||
const stanek = NetscriptStanek(Player, workerScript, helper);
|
||||
const stanek = wrapAPI(helper, {}, workerScript, NetscriptStanek(Player, workerScript, helper), "stanek")
|
||||
.stanek as unknown as IStanek;
|
||||
const bladeburner = NetscriptBladeburner(Player, workerScript, helper);
|
||||
const codingcontract = NetscriptCodingContract(Player, workerScript, helper);
|
||||
const corporation = NetscriptCorporation(Player, workerScript, helper);
|
||||
|
@ -54,7 +54,7 @@ export function NetscriptGrafting(player: IPlayer, workerScript: WorkerScript, h
|
||||
if (player.city !== CityName.NewTokyo) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
"grafting.graftAugmentation",
|
||||
"You must be in New Tokyo to begin crafting an Augmentation.",
|
||||
"You must be in New Tokyo to begin grafting an Augmentation.",
|
||||
);
|
||||
}
|
||||
if (!getAvailableAugs(player).includes(augName)) {
|
||||
@ -90,7 +90,7 @@ export function NetscriptGrafting(player: IPlayer, workerScript: WorkerScript, h
|
||||
Router.toTerminal();
|
||||
}
|
||||
|
||||
workerScript.log("grafting.graftAugmentation", () => `Began crafting Augmentation ${augName}.`);
|
||||
workerScript.log("grafting.graftAugmentation", () => `Began grafting Augmentation ${augName}.`);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
@ -6,7 +6,6 @@ import { startWorkerScript } from "../NetscriptWorker";
|
||||
import { Augmentation } from "../Augmentation/Augmentation";
|
||||
import { Augmentations } from "../Augmentation/Augmentations";
|
||||
import { augmentationExists, installAugmentations } from "../Augmentation/AugmentationHelpers";
|
||||
import { prestigeAugmentation } from "../Prestige";
|
||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||
import { killWorkerScript } from "../Netscript/killWorkerScript";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
@ -212,7 +211,7 @@ export function NetscriptSingularity(
|
||||
|
||||
workerScript.log("softReset", () => "Soft resetting. This will cause this script to be killed");
|
||||
setTimeout(() => {
|
||||
prestigeAugmentation();
|
||||
installAugmentations(true);
|
||||
runAfterReset(cbScript);
|
||||
}, 0);
|
||||
|
||||
|
@ -97,7 +97,7 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
|
||||
travel: function (_sleeveNumber: unknown, _cityName: unknown): boolean {
|
||||
updateRam("travel");
|
||||
const sleeveNumber = helper.number("travel", "sleeveNumber", _sleeveNumber);
|
||||
const cityName = helper.string("setToUniversityCourse", "cityName", _cityName);
|
||||
const cityName = helper.string("travel", "cityName", _cityName);
|
||||
checkSleeveAPIAccess("travel");
|
||||
checkSleeveNumber("travel", sleeveNumber);
|
||||
return player.sleeves[sleeveNumber].travel(player, cityName as CityName);
|
||||
@ -105,7 +105,7 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
|
||||
setToCompanyWork: function (_sleeveNumber: unknown, acompanyName: unknown): boolean {
|
||||
updateRam("setToCompanyWork");
|
||||
const sleeveNumber = helper.number("setToCompanyWork", "sleeveNumber", _sleeveNumber);
|
||||
const companyName = helper.string("setToUniversityCourse", "companyName", acompanyName);
|
||||
const companyName = helper.string("setToCompanyWork", "companyName", acompanyName);
|
||||
checkSleeveAPIAccess("setToCompanyWork");
|
||||
checkSleeveNumber("setToCompanyWork", sleeveNumber);
|
||||
|
||||
@ -117,7 +117,7 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
|
||||
const other = player.sleeves[i];
|
||||
if (other.currentTask === SleeveTaskType.Company && other.currentTaskLocation === companyName) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
"sleeve.setToFactionWork",
|
||||
"sleeve.setToCompanyWork",
|
||||
`Sleeve ${sleeveNumber} cannot work for company ${companyName} because Sleeve ${i} is already working for them.`,
|
||||
);
|
||||
}
|
||||
@ -132,8 +132,8 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
|
||||
): boolean | undefined {
|
||||
updateRam("setToFactionWork");
|
||||
const sleeveNumber = helper.number("setToFactionWork", "sleeveNumber", _sleeveNumber);
|
||||
const factionName = helper.string("setToUniversityCourse", "factionName", _factionName);
|
||||
const workType = helper.string("setToUniversityCourse", "workType", _workType);
|
||||
const factionName = helper.string("setToFactionWork", "factionName", _factionName);
|
||||
const workType = helper.string("setToFactionWork", "workType", _workType);
|
||||
checkSleeveAPIAccess("setToFactionWork");
|
||||
checkSleeveNumber("setToFactionWork", sleeveNumber);
|
||||
|
||||
@ -163,8 +163,8 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
|
||||
setToGymWorkout: function (_sleeveNumber: unknown, _gymName: unknown, _stat: unknown): boolean {
|
||||
updateRam("setToGymWorkout");
|
||||
const sleeveNumber = helper.number("setToGymWorkout", "sleeveNumber", _sleeveNumber);
|
||||
const gymName = helper.string("setToUniversityCourse", "gymName", _gymName);
|
||||
const stat = helper.string("setToUniversityCourse", "stat", _stat);
|
||||
const gymName = helper.string("setToGymWorkout", "gymName", _gymName);
|
||||
const stat = helper.string("setToGymWorkout", "stat", _stat);
|
||||
checkSleeveAPIAccess("setToGymWorkout");
|
||||
checkSleeveNumber("setToGymWorkout", sleeveNumber);
|
||||
|
||||
|
@ -2,112 +2,112 @@ import { INetscriptHelper } from "./INetscriptHelper";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { netscriptDelay } from "../NetscriptEvaluator";
|
||||
import { getRamCost } from "../Netscript/RamCostGenerator";
|
||||
|
||||
import { staneksGift } from "../CotMG/Helper";
|
||||
import { Fragments, FragmentById } from "../CotMG/Fragment";
|
||||
|
||||
import {
|
||||
Stanek as IStanek,
|
||||
Fragment as IFragment,
|
||||
ActiveFragment as IActiveFragment,
|
||||
Stanek as IStanek,
|
||||
} from "../ScriptEditor/NetscriptDefinitions";
|
||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||
import { NetscriptContext, InternalAPI } from "src/Netscript/APIWrapper";
|
||||
|
||||
export function NetscriptStanek(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): IStanek {
|
||||
export function NetscriptStanek(
|
||||
player: IPlayer,
|
||||
workerScript: WorkerScript,
|
||||
helper: INetscriptHelper,
|
||||
): InternalAPI<IStanek> {
|
||||
function checkStanekAPIAccess(func: string): void {
|
||||
if (!player.hasAugmentation(AugmentationNames.StaneksGift1, true)) {
|
||||
helper.makeRuntimeErrorMsg(func, "Requires Stanek's Gift installed.");
|
||||
}
|
||||
}
|
||||
|
||||
const updateRam = (funcName: string): void =>
|
||||
helper.updateDynamicRam(funcName, getRamCost(player, "stanek", funcName));
|
||||
|
||||
return {
|
||||
giftWidth: function (): number {
|
||||
updateRam("giftWidth");
|
||||
checkStanekAPIAccess("giftWidth");
|
||||
return staneksGift.width();
|
||||
},
|
||||
giftHeight: function (): number {
|
||||
updateRam("giftHeight");
|
||||
checkStanekAPIAccess("giftHeight");
|
||||
return staneksGift.height();
|
||||
},
|
||||
chargeFragment: function (_rootX: unknown, _rootY: unknown): Promise<void> {
|
||||
updateRam("chargeFragment");
|
||||
const rootX = helper.number("stanek.chargeFragment", "rootX", _rootX);
|
||||
const rootY = helper.number("stanek.chargeFragment", "rootY", _rootY);
|
||||
checkStanekAPIAccess("chargeFragment");
|
||||
const fragment = staneksGift.findFragment(rootX, rootY);
|
||||
if (!fragment)
|
||||
throw helper.makeRuntimeErrorMsg("stanek.chargeFragment", `No fragment with root (${rootX}, ${rootY}).`);
|
||||
const time = staneksGift.inBonus() ? 200 : 1000;
|
||||
return netscriptDelay(time, workerScript).then(function () {
|
||||
const charge = staneksGift.charge(player, fragment, workerScript.scriptRef.threads);
|
||||
workerScript.log("stanek.chargeFragment", () => `Charged fragment for ${charge} charge.`);
|
||||
return Promise.resolve();
|
||||
});
|
||||
},
|
||||
fragmentDefinitions: function (): IFragment[] {
|
||||
updateRam("fragmentDefinitions");
|
||||
checkStanekAPIAccess("fragmentDefinitions");
|
||||
workerScript.log("stanek.fragmentDefinitions", () => `Returned ${Fragments.length} fragments`);
|
||||
return Fragments.map((f) => f.copy());
|
||||
},
|
||||
activeFragments: function (): IActiveFragment[] {
|
||||
updateRam("activeFragments");
|
||||
checkStanekAPIAccess("activeFragments");
|
||||
workerScript.log("stanek.activeFragments", () => `Returned ${staneksGift.fragments.length} fragments`);
|
||||
return staneksGift.fragments.map((af) => {
|
||||
return { ...af.copy(), ...af.fragment().copy() };
|
||||
});
|
||||
},
|
||||
clearGift: function (): void {
|
||||
updateRam("clearGift");
|
||||
checkStanekAPIAccess("clearGift");
|
||||
workerScript.log("stanek.clearGift", () => `Cleared Stanek's Gift.`);
|
||||
staneksGift.clear();
|
||||
},
|
||||
canPlaceFragment: function (_rootX: unknown, _rootY: unknown, _rotation: unknown, _fragmentId: unknown): boolean {
|
||||
updateRam("canPlaceFragment");
|
||||
const rootX = helper.number("stanek.canPlaceFragment", "rootX", _rootX);
|
||||
const rootY = helper.number("stanek.canPlaceFragment", "rootY", _rootY);
|
||||
const rotation = helper.number("stanek.canPlaceFragment", "rotation", _rotation);
|
||||
const fragmentId = helper.number("stanek.canPlaceFragment", "fragmentId", _fragmentId);
|
||||
checkStanekAPIAccess("canPlaceFragment");
|
||||
const fragment = FragmentById(fragmentId);
|
||||
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.canPlaceFragment", `Invalid fragment id: ${fragmentId}`);
|
||||
const can = staneksGift.canPlace(rootX, rootY, rotation, fragment);
|
||||
return can;
|
||||
},
|
||||
placeFragment: function (_rootX: unknown, _rootY: unknown, _rotation: unknown, _fragmentId: unknown): boolean {
|
||||
updateRam("placeFragment");
|
||||
const rootX = helper.number("stanek.placeFragment", "rootX", _rootX);
|
||||
const rootY = helper.number("stanek.placeFragment", "rootY", _rootY);
|
||||
const rotation = helper.number("stanek.placeFragment", "rotation", _rotation);
|
||||
const fragmentId = helper.number("stanek.placeFragment", "fragmentId", _fragmentId);
|
||||
checkStanekAPIAccess("placeFragment");
|
||||
const fragment = FragmentById(fragmentId);
|
||||
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.placeFragment", `Invalid fragment id: ${fragmentId}`);
|
||||
return staneksGift.place(rootX, rootY, rotation, fragment);
|
||||
},
|
||||
getFragment: function (_rootX: unknown, _rootY: unknown): IActiveFragment | undefined {
|
||||
updateRam("getFragment");
|
||||
const rootX = helper.number("stanek.getFragment", "rootX", _rootX);
|
||||
const rootY = helper.number("stanek.getFragment", "rootY", _rootY);
|
||||
checkStanekAPIAccess("getFragment");
|
||||
const fragment = staneksGift.findFragment(rootX, rootY);
|
||||
if (fragment !== undefined) return fragment.copy();
|
||||
return undefined;
|
||||
},
|
||||
removeFragment: function (_rootX: unknown, _rootY: unknown): boolean {
|
||||
updateRam("removeFragment");
|
||||
const rootX = helper.number("stanek.removeFragment", "rootX", _rootX);
|
||||
const rootY = helper.number("stanek.removeFragment", "rootY", _rootY);
|
||||
checkStanekAPIAccess("removeFragment");
|
||||
return staneksGift.delete(rootX, rootY);
|
||||
},
|
||||
giftWidth: (_ctx: NetscriptContext) =>
|
||||
function (): number {
|
||||
checkStanekAPIAccess("giftWidth");
|
||||
return staneksGift.width();
|
||||
},
|
||||
giftHeight: (_ctx: NetscriptContext) =>
|
||||
function (): number {
|
||||
checkStanekAPIAccess("giftHeight");
|
||||
return staneksGift.height();
|
||||
},
|
||||
chargeFragment: (_ctx: NetscriptContext) =>
|
||||
function (_rootX: unknown, _rootY: unknown): Promise<void> {
|
||||
const rootX = _ctx.helper.number("rootX", _rootX);
|
||||
const rootY = _ctx.helper.number("rootY", _rootY);
|
||||
checkStanekAPIAccess("chargeFragment");
|
||||
const fragment = staneksGift.findFragment(rootX, rootY);
|
||||
if (!fragment) throw _ctx.makeRuntimeErrorMsg(`No fragment with root (${rootX}, ${rootY}).`);
|
||||
const time = staneksGift.inBonus() ? 200 : 1000;
|
||||
return netscriptDelay(time, workerScript).then(function () {
|
||||
const charge = staneksGift.charge(player, fragment, workerScript.scriptRef.threads);
|
||||
_ctx.log(() => `Charged fragment for ${charge} charge.`);
|
||||
return Promise.resolve();
|
||||
});
|
||||
},
|
||||
fragmentDefinitions: (_ctx: NetscriptContext) =>
|
||||
function (): IFragment[] {
|
||||
checkStanekAPIAccess("fragmentDefinitions");
|
||||
_ctx.log(() => `Returned ${Fragments.length} fragments`);
|
||||
return Fragments.map((f) => f.copy());
|
||||
},
|
||||
activeFragments: (_ctx: NetscriptContext) =>
|
||||
function (): IActiveFragment[] {
|
||||
checkStanekAPIAccess("activeFragments");
|
||||
_ctx.log(() => `Returned ${staneksGift.fragments.length} fragments`);
|
||||
return staneksGift.fragments.map((af) => {
|
||||
return { ...af.copy(), ...af.fragment().copy() };
|
||||
});
|
||||
},
|
||||
clearGift: (_ctx: NetscriptContext) =>
|
||||
function (): void {
|
||||
checkStanekAPIAccess("clearGift");
|
||||
_ctx.log(() => `Cleared Stanek's Gift.`);
|
||||
staneksGift.clear();
|
||||
},
|
||||
canPlaceFragment: (_ctx: NetscriptContext) =>
|
||||
function (_rootX: unknown, _rootY: unknown, _rotation: unknown, _fragmentId: unknown): boolean {
|
||||
const rootX = _ctx.helper.number("rootX", _rootX);
|
||||
const rootY = _ctx.helper.number("rootY", _rootY);
|
||||
const rotation = _ctx.helper.number("rotation", _rotation);
|
||||
const fragmentId = _ctx.helper.number("fragmentId", _fragmentId);
|
||||
checkStanekAPIAccess("canPlaceFragment");
|
||||
const fragment = FragmentById(fragmentId);
|
||||
if (!fragment) throw _ctx.makeRuntimeErrorMsg(`Invalid fragment id: ${fragmentId}`);
|
||||
const can = staneksGift.canPlace(rootX, rootY, rotation, fragment);
|
||||
return can;
|
||||
},
|
||||
placeFragment: (_ctx: NetscriptContext) =>
|
||||
function (_rootX: unknown, _rootY: unknown, _rotation: unknown, _fragmentId: unknown): boolean {
|
||||
const rootX = _ctx.helper.number("rootX", _rootX);
|
||||
const rootY = _ctx.helper.number("rootY", _rootY);
|
||||
const rotation = _ctx.helper.number("rotation", _rotation);
|
||||
const fragmentId = _ctx.helper.number("fragmentId", _fragmentId);
|
||||
checkStanekAPIAccess("placeFragment");
|
||||
const fragment = FragmentById(fragmentId);
|
||||
if (!fragment) throw _ctx.makeRuntimeErrorMsg(`Invalid fragment id: ${fragmentId}`);
|
||||
return staneksGift.place(rootX, rootY, rotation, fragment);
|
||||
},
|
||||
getFragment: (_ctx: NetscriptContext) =>
|
||||
function (_rootX: unknown, _rootY: unknown): IActiveFragment | undefined {
|
||||
const rootX = _ctx.helper.number("rootX", _rootX);
|
||||
const rootY = _ctx.helper.number("rootY", _rootY);
|
||||
checkStanekAPIAccess("getFragment");
|
||||
const fragment = staneksGift.findFragment(rootX, rootY);
|
||||
if (fragment !== undefined) return fragment.copy();
|
||||
return undefined;
|
||||
},
|
||||
removeFragment: (_ctx: NetscriptContext) =>
|
||||
function (_rootX: unknown, _rootY: unknown): boolean {
|
||||
const rootX = _ctx.helper.number("rootX", _rootX);
|
||||
const rootY = _ctx.helper.number("rootY", _rootY);
|
||||
checkStanekAPIAccess("removeFragment");
|
||||
return staneksGift.delete(rootX, rootY);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { getRamCost } from "../Netscript/RamCostGenerator";
|
||||
import { buyStock, sellStock, shortStock, sellShort } from "../StockMarket/BuyingAndSelling";
|
||||
import { StockMarket, SymbolToStockMap, placeOrder, cancelOrder } from "../StockMarket/StockMarket";
|
||||
import { StockMarket, SymbolToStockMap, placeOrder, cancelOrder, initStockMarketFn } from "../StockMarket/StockMarket";
|
||||
import { getBuyTransactionCost, getSellTransactionGain } from "../StockMarket/StockMarketHelpers";
|
||||
import { OrderTypes } from "../StockMarket/data/OrderTypes";
|
||||
import { PositionTypes } from "../StockMarket/data/PositionTypes";
|
||||
@ -411,6 +411,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
|
||||
}
|
||||
|
||||
player.hasWseAccount = true;
|
||||
initStockMarketFn();
|
||||
player.loseMoney(getStockMarketWseCost(), "stock");
|
||||
workerScript.log("stock.purchaseWseAccount", () => "Purchased WSE Account Access");
|
||||
return true;
|
||||
|
@ -130,9 +130,13 @@ export const GraftingRoot = (): React.ReactElement => {
|
||||
<>
|
||||
Cancelling grafting will <b>not</b> save grafting progress, and the money you spend will <b>not</b>{" "}
|
||||
be returned.
|
||||
<br />
|
||||
<br />
|
||||
Additionally, grafting an Augmentation will increase the potency of the Entropy virus.
|
||||
{!player.hasAugmentation(AugmentationNames.CongruityImplant) && (
|
||||
<>
|
||||
<br />
|
||||
<br />
|
||||
Additionally, grafting an Augmentation will increase the potency of the Entropy virus.
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
@ -1366,15 +1366,19 @@ export function craftAugmentationWork(this: IPlayer, numCycles: number): boolean
|
||||
export function finishGraftAugmentationWork(this: IPlayer, cancelled: boolean): string {
|
||||
const augName = this.graftAugmentationName;
|
||||
if (cancelled === false) {
|
||||
dialogBoxCreate(
|
||||
`You've finished crafting ${augName}.<br>The augmentation has been grafted to your body, but you feel a bit off.`,
|
||||
);
|
||||
|
||||
applyAugmentation(Augmentations[augName]);
|
||||
this.entropy += 1;
|
||||
this.applyEntropy(this.entropy);
|
||||
|
||||
if (!this.hasAugmentation(AugmentationNames.CongruityImplant)) {
|
||||
this.entropy += 1;
|
||||
this.applyEntropy(this.entropy);
|
||||
}
|
||||
|
||||
dialogBoxCreate(
|
||||
`You've finished grafting ${augName}.<br>The augmentation has been applied to your body` +
|
||||
(this.hasAugmentation(AugmentationNames.CongruityImplant) ? "." : ", but you feel a bit off."),
|
||||
);
|
||||
} else {
|
||||
dialogBoxCreate(`You cancelled the crafting of ${augName}.<br>Your money was not returned to you.`);
|
||||
dialogBoxCreate(`You cancelled the grafting of ${augName}.<br>Your money was not returned to you.`);
|
||||
}
|
||||
|
||||
// Intelligence gain
|
||||
|
@ -306,7 +306,7 @@ export function processStockPrices(numCycles = 1): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function initStockMarketFnForReact(): void {
|
||||
export function initStockMarketFn(): void {
|
||||
initStockMarket();
|
||||
initSymbolToStockMap();
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { MinHeap } from "../utils/Heap";
|
||||
|
||||
import { HammingEncode, HammingDecode } from "../utils/HammingCodeTools";
|
||||
/* tslint:disable:completed-docs no-magic-numbers arrow-return-shorthand */
|
||||
|
||||
/* Function that generates a valid 'data' for a contract type */
|
||||
@ -122,7 +124,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
" 2 + 2\n",
|
||||
" 2 + 1 + 1\n",
|
||||
" 1 + 1 + 1 + 1\n\n",
|
||||
`How many different ways can the number ${n} be written as a sum of at least`,
|
||||
`How many different distinct ways can the number ${n} be written as a sum of at least`,
|
||||
"two positive integers?",
|
||||
].join(" ");
|
||||
},
|
||||
@ -145,6 +147,51 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
return ways[data] === parseInt(ans, 10);
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: (data: [number, number[]]): string => {
|
||||
const n: number = data[0];
|
||||
const s: number[] = data[1];
|
||||
return [
|
||||
`How many different distinct ways can the number ${n} be written`,
|
||||
"as a sum of integers contained in the set:\n\n",
|
||||
`[${s}]?\n\n`,
|
||||
"You may use each integer in the set zero or more times.",
|
||||
].join(" ");
|
||||
},
|
||||
difficulty: 2,
|
||||
gen: (): [number, number[]] => {
|
||||
const n: number = getRandomInt(12, 200);
|
||||
const maxLen: number = getRandomInt(8, 12);
|
||||
const s: number[] = [];
|
||||
// Bias towards small numbers is intentional to have much bigger answers in general
|
||||
// to force people better optimize their solutions
|
||||
for (let i = 1; i <= n; i++) {
|
||||
if (s.length == maxLen) {
|
||||
break;
|
||||
}
|
||||
if (Math.random() < 0.6 || n - i < maxLen - s.length) {
|
||||
s.push(i);
|
||||
}
|
||||
}
|
||||
return [n, s];
|
||||
},
|
||||
name: "Total Ways to Sum II",
|
||||
numTries: 10,
|
||||
solver: (data: [number, number[]], ans: string): boolean => {
|
||||
// https://www.geeksforgeeks.org/coin-change-dp-7/?ref=lbp
|
||||
const n = data[0];
|
||||
const s = data[1];
|
||||
const ways: number[] = [1];
|
||||
ways.length = n + 1;
|
||||
ways.fill(0, 1);
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
for (let j = s[i]; j <= n; j++) {
|
||||
ways[j] += ways[j - s[i]];
|
||||
}
|
||||
}
|
||||
return ways[n] === parseInt(ans, 10);
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: (n: number[][]): string => {
|
||||
let d: string = [
|
||||
@ -310,6 +357,62 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
return (ans === "1" && solution) || (ans === "0" && !solution);
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: (arr: number[]): string => {
|
||||
return [
|
||||
"You are given the following array of integers:\n\n",
|
||||
`${arr}\n\n`,
|
||||
"Each element in the array represents your MAXIMUM jump length",
|
||||
"at that position. This means that if you are at position i and your",
|
||||
"maximum jump length is n, you can jump to any position from",
|
||||
"i to i+n.",
|
||||
"\n\nAssuming you are initially positioned",
|
||||
"at the start of the array, determine the minimum number of",
|
||||
"jumps to reach the end of the array.\n\n",
|
||||
"If it's impossible to reach the end, then the answer should be 0.",
|
||||
].join(" ");
|
||||
},
|
||||
difficulty: 3,
|
||||
gen: (): number[] => {
|
||||
const len: number = getRandomInt(3, 25);
|
||||
const arr: number[] = [];
|
||||
arr.length = len;
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
for (let j = 0; j < 10; j++) {
|
||||
if (Math.random() <= j / 10 + 0.1) {
|
||||
arr[i] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arr;
|
||||
},
|
||||
name: "Array Jumping Game II",
|
||||
numTries: 3,
|
||||
solver: (data: number[], ans: string): boolean => {
|
||||
const n: number = data.length;
|
||||
let reach = 0;
|
||||
let jumps = 0;
|
||||
let lastJump = -1;
|
||||
while (reach < n - 1) {
|
||||
let jumpedFrom = -1;
|
||||
for (let i = reach; i > lastJump; i--) {
|
||||
if (i + data[i] > reach) {
|
||||
reach = i + data[i];
|
||||
jumpedFrom = i;
|
||||
}
|
||||
}
|
||||
if (jumpedFrom === -1) {
|
||||
jumps = 0;
|
||||
break;
|
||||
}
|
||||
lastJump = jumpedFrom;
|
||||
jumps++;
|
||||
}
|
||||
return jumps === parseInt(ans, 10);
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: (arr: number[][]): string => {
|
||||
return [
|
||||
@ -794,6 +897,140 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
return obstacleGrid[obstacleGrid.length - 1][obstacleGrid[0].length - 1] === parseInt(ans);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Shortest Path in a Grid",
|
||||
desc: (data: number[][]): string => {
|
||||
return [
|
||||
"You are located in the top-left corner of the following grid:\n\n",
|
||||
` [${data.map((line) => "[" + line + "]").join(",\n ")}]\n\n`,
|
||||
"You are trying to find the shortest path to the bottom-right corner of the grid,",
|
||||
"but there are obstacles on the grid that you cannot move onto.",
|
||||
"These obstacles are denoted by '1', while empty spaces are denoted by 0.\n\n",
|
||||
"Determine the shortest path from start to finish, if one exists.",
|
||||
"The answer should be given as a string of UDLR characters, indicating the moves along the path\n\n",
|
||||
"NOTE: If there are multiple equally short paths, any of them is accepted as answer.",
|
||||
"If there is no path, the answer should be an empty string.\n",
|
||||
"NOTE: The data returned for this contract is an 2D array of numbers representing the grid.\n\n",
|
||||
"Examples:\n\n",
|
||||
" [[0,1,0,0,0],\n",
|
||||
" [0,0,0,1,0]]\n",
|
||||
"\n",
|
||||
"Answer: 'DRRURRD'\n\n",
|
||||
" [[0,1],\n",
|
||||
" [1,0]]\n",
|
||||
"\n",
|
||||
"Answer: ''\n\n",
|
||||
].join(" ");
|
||||
},
|
||||
difficulty: 7,
|
||||
numTries: 10,
|
||||
gen: (): number[][] => {
|
||||
const height = getRandomInt(6, 12);
|
||||
const width = getRandomInt(6, 12);
|
||||
const dstY = height - 1;
|
||||
const dstX = width - 1;
|
||||
const minPathLength = dstY + dstX; // Math.abs(dstY - srcY) + Math.abs(dstX - srcX)
|
||||
|
||||
const grid: number[][] = new Array(height);
|
||||
for (let y = 0; y < height; y++) grid[y] = new Array(width).fill(0);
|
||||
|
||||
for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
if (y == 0 && x == 0) continue; // Don't block start
|
||||
if (y == dstY && x == dstX) continue; // Don't block destination
|
||||
|
||||
// Generate more obstacles the farther a position is from start and destination.
|
||||
// Raw distance factor peaks at 50% at half-way mark. Rescale to 40% max.
|
||||
// Obstacle chance range of [15%, 40%] produces ~78% solvable puzzles
|
||||
const distanceFactor = (Math.min(y + x, dstY - y + dstX - x) / minPathLength) * 0.8;
|
||||
if (Math.random() < Math.max(0.15, distanceFactor)) grid[y][x] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return grid;
|
||||
},
|
||||
solver: (data: number[][], ans: string): boolean => {
|
||||
const width = data[0].length;
|
||||
const height = data.length;
|
||||
const dstY = height - 1;
|
||||
const dstX = width - 1;
|
||||
|
||||
const distance: [number][] = new Array(height);
|
||||
//const prev: [[number, number] | undefined][] = new Array(height);
|
||||
const queue = new MinHeap<[number, number]>();
|
||||
|
||||
for (let y = 0; y < height; y++) {
|
||||
distance[y] = new Array(width).fill(Infinity) as [number];
|
||||
//prev[y] = new Array(width).fill(undefined) as [undefined];
|
||||
}
|
||||
|
||||
function validPosition(y: number, x: number): boolean {
|
||||
return y >= 0 && y < height && x >= 0 && x < width && data[y][x] == 0;
|
||||
}
|
||||
|
||||
// List in-bounds and passable neighbors
|
||||
function* neighbors(y: number, x: number): Generator<[number, number]> {
|
||||
if (validPosition(y - 1, x)) yield [y - 1, x]; // Up
|
||||
if (validPosition(y + 1, x)) yield [y + 1, x]; // Down
|
||||
if (validPosition(y, x - 1)) yield [y, x - 1]; // Left
|
||||
if (validPosition(y, x + 1)) yield [y, x + 1]; // Right
|
||||
}
|
||||
|
||||
// Prepare starting point
|
||||
distance[0][0] = 0;
|
||||
queue.push([0, 0], 0);
|
||||
|
||||
// Take next-nearest position and expand potential paths from there
|
||||
while (queue.size > 0) {
|
||||
const [y, x] = queue.pop() as [number, number];
|
||||
for (const [yN, xN] of neighbors(y, x)) {
|
||||
const d = distance[y][x] + 1;
|
||||
if (d < distance[yN][xN]) {
|
||||
if (distance[yN][xN] == Infinity)
|
||||
// Not reached previously
|
||||
queue.push([yN, xN], d);
|
||||
// Found a shorter path
|
||||
else queue.changeWeight(([yQ, xQ]) => yQ == yN && xQ == xN, d);
|
||||
//prev[yN][xN] = [y, x];
|
||||
distance[yN][xN] = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No path at all?
|
||||
if (distance[dstY][dstX] == Infinity) return ans == "";
|
||||
|
||||
// There is a solution, require that the answer path is as short as the shortest
|
||||
// path we found
|
||||
if (ans.length > distance[dstY][dstX]) return false;
|
||||
|
||||
// Further verify that the answer path is a valid path
|
||||
let ansX = 0;
|
||||
let ansY = 0;
|
||||
for (const direction of ans) {
|
||||
switch (direction) {
|
||||
case "U":
|
||||
ansY -= 1;
|
||||
break;
|
||||
case "D":
|
||||
ansY += 1;
|
||||
break;
|
||||
case "L":
|
||||
ansX -= 1;
|
||||
break;
|
||||
case "R":
|
||||
ansX += 1;
|
||||
break;
|
||||
default:
|
||||
return false; // Invalid character
|
||||
}
|
||||
if (!validPosition(ansY, ansX)) return false;
|
||||
}
|
||||
|
||||
// Path was valid, finally verify that the answer path brought us to the end coordinates
|
||||
return ansY == dstY && ansX == dstX;
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: (data: string): string => {
|
||||
return [
|
||||
@ -1008,4 +1245,62 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
return true;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "HammingCodes: Integer to encoded Binary",
|
||||
numTries: 10,
|
||||
difficulty: 5,
|
||||
desc: (n: number): string => {
|
||||
return [
|
||||
"You are given the following decimal Value: \n",
|
||||
`${n} \n`,
|
||||
"Convert it into a binary string and encode it as a 'Hamming-Code'. eg:\n ",
|
||||
"Value 8 will result into binary '1000', which will be encoded",
|
||||
"with the pattern 'pppdpddd', where p is a paritybit and d a databit,\n",
|
||||
"or '10101' (Value 21) will result into (pppdpdddpd) '1111101011'.\n\n",
|
||||
"NOTE: You need an parity Bit on Index 0 as an 'overall'-paritybit. \n",
|
||||
"NOTE 2: You should watch the HammingCode-video from 3Blue1Brown, which explains the 'rule' of encoding,",
|
||||
"including the first Index parity-bit mentioned on the first note.\n\n",
|
||||
"Now the only one rule for this encoding:\n",
|
||||
" It's not allowed to add additional leading '0's to the binary value\n",
|
||||
"That means, the binary value has to be encoded as it is",
|
||||
].join(" ");
|
||||
},
|
||||
gen: (): number => {
|
||||
return getRandomInt(Math.pow(2, 4), Math.pow(2, getRandomInt(1, 57)));
|
||||
},
|
||||
solver: (data: number, ans: string): boolean => {
|
||||
return ans === HammingEncode(data);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "HammingCodes: Encoded Binary to Integer",
|
||||
difficulty: 8,
|
||||
numTries: 10,
|
||||
desc: (n: string): string => {
|
||||
return [
|
||||
"You are given the following encoded binary String: \n",
|
||||
`'${n}' \n`,
|
||||
"Treat it as a Hammingcode with 1 'possible' error on an random Index.\n",
|
||||
"Find the 'possible' wrong bit, fix it and extract the decimal value, which is hidden inside the string.\n\n",
|
||||
"Note: The length of the binary string is dynamic, but it's encoding/decoding is following Hammings 'rule'\n",
|
||||
"Note 2: Index 0 is an 'overall' parity bit. Watch the Hammingcode-video from 3Blue1Brown for more information\n",
|
||||
"Note 3: There's a ~55% chance for an altered Bit. So... MAYBE there is an altered Bit 😉\n",
|
||||
"Extranote for automation: return the decimal value as a string",
|
||||
].join(" ");
|
||||
},
|
||||
gen: (): string => {
|
||||
const _alteredBit = Math.round(Math.random());
|
||||
const _buildArray: Array<string> = HammingEncode(
|
||||
getRandomInt(Math.pow(2, 4), Math.pow(2, getRandomInt(1, 57))),
|
||||
).split("");
|
||||
if (_alteredBit) {
|
||||
const _randomIndex: number = getRandomInt(0, _buildArray.length - 1);
|
||||
_buildArray[_randomIndex] = _buildArray[_randomIndex] == "0" ? "1" : "0";
|
||||
}
|
||||
return _buildArray.join("");
|
||||
},
|
||||
solver: (data: string, ans: string): boolean => {
|
||||
return parseInt(ans, 10) === HammingDecode(data);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@ -22,7 +22,7 @@ import { buyStock, sellStock, shortStock, sellShort } from "../StockMarket/Buyin
|
||||
import {
|
||||
cancelOrder,
|
||||
eventEmitterForUiReset,
|
||||
initStockMarketFnForReact,
|
||||
initStockMarketFn,
|
||||
placeOrder,
|
||||
StockMarket,
|
||||
} from "../StockMarket/StockMarket";
|
||||
@ -310,7 +310,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
|
||||
function softReset(): void {
|
||||
dialogBoxCreate("Soft Reset!");
|
||||
prestigeAugmentation();
|
||||
installAugmentations(true);
|
||||
resetErrorBoundary();
|
||||
Router.toTerminal();
|
||||
}
|
||||
@ -445,7 +445,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
buyStockShort={shortStock}
|
||||
cancelOrder={cancelOrder}
|
||||
eventEmitterForReset={eventEmitterForUiReset}
|
||||
initStockMarket={initStockMarketFnForReact}
|
||||
initStockMarket={initStockMarketFn}
|
||||
p={player}
|
||||
placeOrder={placeOrder}
|
||||
sellStockLong={sellStock}
|
||||
|
@ -504,7 +504,7 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}>
|
||||
<Grid item>
|
||||
<Typography>
|
||||
You are currently working on crafting {player.graftAugmentationName}.
|
||||
You are currently working on grafting {player.graftAugmentationName}.
|
||||
<br />
|
||||
<br />
|
||||
You have been working for {convertTimeMsToTimeElapsedString(player.timeWorked)}
|
||||
@ -519,7 +519,7 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button sx={{ mx: 2 }} onClick={cancel}>
|
||||
Cancel work on crafting Augmentation
|
||||
Cancel work on grafting Augmentation
|
||||
</Button>
|
||||
<Button onClick={unfocus}>Do something else simultaneously</Button>
|
||||
</Grid>
|
||||
|
97
src/utils/HammingCodeTools.ts
Normal file
97
src/utils/HammingCodeTools.ts
Normal file
@ -0,0 +1,97 @@
|
||||
// by Discord: H3draut3r#6722, feel free to ask me any questions. i probably don't know the answer 🤣
|
||||
export function HammingEncode(value: number): string {
|
||||
// encoding following Hammings rule
|
||||
function HammingSumOfParity(_lengthOfDBits: number): number {
|
||||
// will calculate the needed amount of parityBits 'without' the "overall"-Parity (that math took me 4 Days to get it working)
|
||||
return _lengthOfDBits < 3 || _lengthOfDBits == 0 // oh and of course using ternary operators, it's a pretty neat function
|
||||
? _lengthOfDBits == 0
|
||||
? 0
|
||||
: _lengthOfDBits + 1
|
||||
: // the following math will only work, if the length is greater equal 3, otherwise it's "kind of" broken :D
|
||||
Math.ceil(Math.log2(_lengthOfDBits * 2)) <=
|
||||
Math.ceil(Math.log2(1 + _lengthOfDBits + Math.ceil(Math.log2(_lengthOfDBits))))
|
||||
? Math.ceil(Math.log2(_lengthOfDBits) + 1)
|
||||
: Math.ceil(Math.log2(_lengthOfDBits));
|
||||
}
|
||||
const _data = value.toString(2).split(""); // first, change into binary string, then create array with 1 bit per index
|
||||
const _sumParity: number = HammingSumOfParity(_data.length); // get the sum of needed parity bits (for later use in encoding)
|
||||
const count = (arr: Array<string>, val: string): number =>
|
||||
arr.reduce((a: number, v: string) => (v === val ? a + 1 : a), 0);
|
||||
// function count for specific entries in the array, for later use
|
||||
|
||||
const _build = ["x", "x", ..._data.splice(0, 1)]; // init the "pre-build"
|
||||
for (let i = 2; i < _sumParity; i++) {
|
||||
// add new paritybits and the corresponding data bits (pre-building array)
|
||||
_build.push("x", ..._data.splice(0, Math.pow(2, i) - 1));
|
||||
}
|
||||
// now the "calculation"... get the paritybits ('x') working
|
||||
for (const index of _build.reduce(function (a: Array<number>, e: string, i: number) {
|
||||
if (e == "x") a.push(i);
|
||||
return a;
|
||||
}, [])) {
|
||||
// that reduce will result in an array of index numbers where the "x" is placed
|
||||
const _tempcount = index + 1; // set the "stepsize" for the parityBit
|
||||
const _temparray = []; // temporary array to store the extracted bits
|
||||
const _tempdata = [..._build]; // only work with a copy of the _build
|
||||
while (_tempdata[index] !== undefined) {
|
||||
// as long as there are bits on the starting index, do "cut"
|
||||
const _temp: Array<string> = _tempdata.splice(index, _tempcount * 2); // cut stepsize*2 bits, then...
|
||||
_temparray.push(..._temp.splice(0, _tempcount)); // ... cut the result again and keep the first half
|
||||
}
|
||||
_temparray.splice(0, 1); // remove first bit, which is the parity one
|
||||
_build[index] = (count(_temparray, "1") % 2).toString(); // count with remainder of 2 and"toString" to store the parityBit
|
||||
} // parity done, now the "overall"-parity is set
|
||||
_build.unshift((count(_build, "1") % 2).toString()); // has to be done as last element
|
||||
return _build.join(""); // return the _build as string
|
||||
}
|
||||
|
||||
export function HammingDecode(_data: string): number {
|
||||
//check for altered bit and decode
|
||||
const _build = _data.split(""); // ye, an array for working, again
|
||||
const _testArray = []; //for the "truthtable". if any is false, the data has an altered bit, will check for and fix it
|
||||
const _sumParity = Math.ceil(Math.log2(_data.length)); // sum of parity for later use
|
||||
const count = (arr: Array<string>, val: string): number =>
|
||||
arr.reduce((a: number, v: string) => (v === val ? a + 1 : a), 0);
|
||||
// the count.... again ;)
|
||||
|
||||
let _overallParity = _build.splice(0, 1).join(""); // store first index, for checking in next step and fix the _build properly later on
|
||||
_testArray.push(_overallParity == (count(_build, "1") % 2).toString() ? true : false); // first check with the overall parity bit
|
||||
for (let i = 0; i < _sumParity; i++) {
|
||||
// for the rest of the remaining parity bits we also "check"
|
||||
const _tempIndex = Math.pow(2, i) - 1; // get the parityBits Index
|
||||
const _tempStep = _tempIndex + 1; // set the stepsize
|
||||
const _tempData = [..._build]; // get a "copy" of the build-data for working
|
||||
const _tempArray = []; // init empty array for "testing"
|
||||
while (_tempData[_tempIndex] != undefined) {
|
||||
// extract from the copied data until the "starting" index is undefined
|
||||
const _temp = [..._tempData.splice(_tempIndex, _tempStep * 2)]; // extract 2*stepsize
|
||||
_tempArray.push(..._temp.splice(0, _tempStep)); // and cut again for keeping first half
|
||||
}
|
||||
const _tempParity = _tempArray.shift(); // and again save the first index separated for checking with the rest of the data
|
||||
_testArray.push(_tempParity == (count(_tempArray, "1") % 2).toString() ? true : false);
|
||||
// is the _tempParity the calculated data? push answer into the 'truthtable'
|
||||
}
|
||||
let _fixIndex = 0; // init the "fixing" index and start with 0
|
||||
for (let i = 1; i < _sumParity + 1; i++) {
|
||||
// simple binary adding for every boolean in the _testArray, starting from 2nd index of it
|
||||
_fixIndex += _testArray[i] ? 0 : Math.pow(2, i) / 2;
|
||||
}
|
||||
_build.unshift(_overallParity); // now we need the "overall" parity back in it's place
|
||||
// try fix the actual encoded binary string if there is an error
|
||||
if (_fixIndex > 0 && _testArray[0] == false) {
|
||||
// if the overall is false and the sum of calculated values is greater equal 0, fix the corresponding hamming-bit
|
||||
_build[_fixIndex] = _build[_fixIndex] == "0" ? "1" : "0";
|
||||
} else if (_testArray[0] == false) {
|
||||
// otherwise, if the the overall_parity is the only wrong, fix that one
|
||||
_overallParity = _overallParity == "0" ? "1" : "0";
|
||||
} else if (_testArray[0] == true && _testArray.some((truth) => truth == false)) {
|
||||
return 0; // uhm, there's some strange going on... 2 bits are altered? How? This should not happen 👀
|
||||
}
|
||||
// oof.. halfway through... we fixed an possible altered bit, now "extract" the parity-bits from the _build
|
||||
for (let i = _sumParity; i >= 0; i--) {
|
||||
// start from the last parity down the 2nd index one
|
||||
_build.splice(Math.pow(2, i), 1);
|
||||
}
|
||||
_build.splice(0, 1); // remove the overall parity bit and we have our binary value
|
||||
return parseInt(_build.join(""), 2); // parse the integer with redux 2 and we're done!
|
||||
}
|
133
src/utils/Heap.ts
Normal file
133
src/utils/Heap.ts
Normal file
@ -0,0 +1,133 @@
|
||||
/** Binary heap. */
|
||||
abstract class BinHeap<T> {
|
||||
/**
|
||||
* Heap data array consisting of [weight, payload] pairs, arranged by weight
|
||||
* to satisfy heap condition.
|
||||
*
|
||||
* Encodes the binary tree by storing tree root at index 0 and
|
||||
* left child of element i at `i * 2 + 1` and
|
||||
* right child of element i at `i * 2 + 2`.
|
||||
*/
|
||||
protected data: [number, T][];
|
||||
|
||||
constructor() {
|
||||
this.data = [];
|
||||
}
|
||||
|
||||
/** Get number of elements in the heap. */
|
||||
public get size(): number {
|
||||
return this.data.length;
|
||||
}
|
||||
|
||||
/** Add a new element to the heap. */
|
||||
public push(value: T, weight: number): void {
|
||||
const i = this.data.length;
|
||||
this.data[i] = [weight, value];
|
||||
this.heapifyUp(i);
|
||||
}
|
||||
|
||||
/** Get the value of the root-most element of the heap, without changing the heap. */
|
||||
public peek(): T | undefined {
|
||||
if (this.data.length == 0) return undefined;
|
||||
|
||||
return this.data[0][1];
|
||||
}
|
||||
|
||||
/** Remove the root-most element of the heap and return the removed element's value. */
|
||||
public pop(): T | undefined {
|
||||
if (this.data.length == 0) return undefined;
|
||||
|
||||
const value = this.data[0][1];
|
||||
|
||||
this.data[0] = this.data[this.data.length - 1];
|
||||
this.data.length = this.data.length - 1;
|
||||
|
||||
this.heapifyDown(0);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/** Change the weight of an element in the heap. */
|
||||
public changeWeight(predicate: (value: T) => boolean, weight: number): void {
|
||||
// Find first element with matching value, if any
|
||||
const i = this.data.findIndex((e) => predicate(e[1]));
|
||||
if (i == -1) return;
|
||||
|
||||
// Update that element's weight
|
||||
this.data[i][0] = weight;
|
||||
|
||||
// And re-heapify if needed
|
||||
const p = Math.floor((i - 1) / 2);
|
||||
|
||||
if (!this.heapOrderABeforeB(this.data[p][0], this.data[i][0]))
|
||||
// Needs to shift root-wards?
|
||||
this.heapifyUp(i);
|
||||
// Try shifting deeper
|
||||
else this.heapifyDown(i);
|
||||
}
|
||||
|
||||
/** Restore heap condition, starting at index i and traveling towards root. */
|
||||
protected heapifyUp(i: number): void {
|
||||
// Swap the new element up towards root until it reaches root position or
|
||||
// settles under under a suitable parent
|
||||
while (i > 0) {
|
||||
const p = Math.floor((i - 1) / 2);
|
||||
|
||||
// Reached heap-ordered state already?
|
||||
if (this.heapOrderABeforeB(this.data[p][0], this.data[i][0])) break;
|
||||
|
||||
// Swap
|
||||
const tmp = this.data[p];
|
||||
this.data[p] = this.data[i];
|
||||
this.data[i] = tmp;
|
||||
|
||||
// And repeat at parent index
|
||||
i = p;
|
||||
}
|
||||
}
|
||||
|
||||
/** Restore heap condition, starting at index i and traveling away from root. */
|
||||
protected heapifyDown(i: number): void {
|
||||
// Swap the shifted element down in the heap until it either reaches the
|
||||
// bottom layer or is in correct order relative to it's children
|
||||
while (i < this.data.length) {
|
||||
const l = i * 2 + 1;
|
||||
const r = i * 2 + 2;
|
||||
let toSwap = i;
|
||||
|
||||
// Find which one of element i and it's children should be closest to root
|
||||
if (l < this.data.length && this.heapOrderABeforeB(this.data[l][0], this.data[toSwap][0])) toSwap = l;
|
||||
if (r < this.data.length && this.heapOrderABeforeB(this.data[r][0], this.data[toSwap][0])) toSwap = r;
|
||||
|
||||
// Already in order?
|
||||
if (i == toSwap) break;
|
||||
|
||||
// Not in order. Swap child that should be closest to root up to 'i' and repeat
|
||||
const tmp = this.data[toSwap];
|
||||
this.data[toSwap] = this.data[i];
|
||||
this.data[i] = tmp;
|
||||
|
||||
i = toSwap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should element with weight `weightA` be closer to root than element with
|
||||
* weight `weightB`?
|
||||
*/
|
||||
protected abstract heapOrderABeforeB(weightA: number, weightB: number): boolean;
|
||||
}
|
||||
|
||||
/** Binary max-heap. */
|
||||
export class MaxHeap<T> extends BinHeap<T> {
|
||||
heapOrderABeforeB(weightA: number, weightB: number): boolean {
|
||||
return weightA > weightB;
|
||||
}
|
||||
}
|
||||
|
||||
/** Binary min-heap. */
|
||||
export class MinHeap<T> extends BinHeap<T> {
|
||||
heapOrderABeforeB(weightA: number, weightB: number): boolean {
|
||||
return weightA < weightB;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user