mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-30 09:57:32 +01:00
Merge branch 'dev' of github.com:danielyxie/bitburner into improvement/stats-augs-ui
This commit is contained in:
commit
fbf9bc521f
28
dist/vendor.bundle.js
vendored
28
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
@ -196,6 +196,23 @@ The list contains the name of (i.e. the value returned by
|
|||||||
| | | |
|
| | | |
|
||||||
| | | Determine how many unique paths there are from start to finish. |
|
| | | 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 |
|
| 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 |
|
| | | parentheses in order to validate the string. If there are multiple minimal ways |
|
||||||
| | | to validate the string, provide all of the possible results. |
|
| | | 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
@ -2,7 +2,6 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { IMap } from "../types";
|
import { IMap } from "../types";
|
||||||
|
|
||||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
|
||||||
import { Faction } from "../Faction/Faction";
|
import { Faction } from "../Faction/Faction";
|
||||||
import { Factions } from "../Faction/Factions";
|
import { Factions } from "../Faction/Factions";
|
||||||
import { numeralWrapper } from "../ui/numeralFormat";
|
import { numeralWrapper } from "../ui/numeralFormat";
|
||||||
|
@ -154,8 +154,8 @@ function applyAugmentation(aug: IPlayerOwnedAugmentation, reapply = false): void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function installAugmentations(): boolean {
|
function installAugmentations(force?: boolean): boolean {
|
||||||
if (Player.queuedAugmentations.length == 0) {
|
if (Player.queuedAugmentations.length == 0 && !force) {
|
||||||
dialogBoxCreate("You have not purchased any Augmentations to install!");
|
dialogBoxCreate("You have not purchased any Augmentations to install!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -185,12 +185,14 @@ function installAugmentations(): boolean {
|
|||||||
augmentationList += aug.name + level + "<br>";
|
augmentationList += aug.name + level + "<br>";
|
||||||
}
|
}
|
||||||
Player.queuedAugmentations = [];
|
Player.queuedAugmentations = [];
|
||||||
|
if (!force) {
|
||||||
dialogBoxCreate(
|
dialogBoxCreate(
|
||||||
"You slowly drift to sleep as scientists put you under in order " +
|
"You slowly drift to sleep as scientists put you under in order " +
|
||||||
"to install the following Augmentations:<br>" +
|
"to install the following Augmentations:<br>" +
|
||||||
augmentationList +
|
augmentationList +
|
||||||
"<br>You wake up in your home...you feel different...",
|
"<br>You wake up in your home...you feel different...",
|
||||||
);
|
);
|
||||||
|
}
|
||||||
prestigeAugmentation();
|
prestigeAugmentation();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -203,7 +203,7 @@ export const getFactionAugmentationsFiltered = (player: IPlayer, faction: Factio
|
|||||||
// Remove special augs
|
// Remove special augs
|
||||||
augs = augs.filter((a) => !a.isSpecial);
|
augs = augs.filter((a) => !a.isSpecial);
|
||||||
|
|
||||||
const blacklist: string[] = [AugmentationNames.NeuroFluxGovernor];
|
const blacklist: string[] = [AugmentationNames.NeuroFluxGovernor, AugmentationNames.CongruityImplant];
|
||||||
|
|
||||||
if (player.bitNodeN !== 2) {
|
if (player.bitNodeN !== 2) {
|
||||||
// TRP is not available outside of BN2 for Gangs
|
// TRP is not available outside of BN2 for Gangs
|
||||||
|
@ -225,7 +225,6 @@ export class Gang implements IGang {
|
|||||||
if (AllGangs[otherGang].territory <= 0) return;
|
if (AllGangs[otherGang].territory <= 0) return;
|
||||||
const territoryGain = calculateTerritoryGain(thisGang, otherGang);
|
const territoryGain = calculateTerritoryGain(thisGang, otherGang);
|
||||||
AllGangs[thisGang].territory += territoryGain;
|
AllGangs[thisGang].territory += territoryGain;
|
||||||
if (AllGangs[thisGang].territory > 0.999) AllGangs[thisGang].territory = 1;
|
|
||||||
AllGangs[otherGang].territory -= territoryGain;
|
AllGangs[otherGang].territory -= territoryGain;
|
||||||
if (thisGang === gangName) {
|
if (thisGang === gangName) {
|
||||||
this.clash(true); // Player won
|
this.clash(true); // Player won
|
||||||
@ -239,9 +238,7 @@ export class Gang implements IGang {
|
|||||||
if (AllGangs[thisGang].territory <= 0) return;
|
if (AllGangs[thisGang].territory <= 0) return;
|
||||||
const territoryGain = calculateTerritoryGain(otherGang, thisGang);
|
const territoryGain = calculateTerritoryGain(otherGang, thisGang);
|
||||||
AllGangs[thisGang].territory -= territoryGain;
|
AllGangs[thisGang].territory -= territoryGain;
|
||||||
if (AllGangs[otherGang].territory < 0.001) AllGangs[otherGang].territory = 0;
|
|
||||||
AllGangs[otherGang].territory += territoryGain;
|
AllGangs[otherGang].territory += territoryGain;
|
||||||
if (AllGangs[otherGang].territory > 0.999) AllGangs[otherGang].territory = 1;
|
|
||||||
if (thisGang === gangName) {
|
if (thisGang === gangName) {
|
||||||
this.clash(false); // Player lost
|
this.clash(false); // Player lost
|
||||||
} else if (otherGang === gangName) {
|
} else if (otherGang === gangName) {
|
||||||
@ -251,6 +248,11 @@ export class Gang implements IGang {
|
|||||||
AllGangs[thisGang].power *= 1 / 1.01;
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import { startWorkerScript } from "../NetscriptWorker";
|
|||||||
import { Augmentation } from "../Augmentation/Augmentation";
|
import { Augmentation } from "../Augmentation/Augmentation";
|
||||||
import { Augmentations } from "../Augmentation/Augmentations";
|
import { Augmentations } from "../Augmentation/Augmentations";
|
||||||
import { augmentationExists, installAugmentations } from "../Augmentation/AugmentationHelpers";
|
import { augmentationExists, installAugmentations } from "../Augmentation/AugmentationHelpers";
|
||||||
import { prestigeAugmentation } from "../Prestige";
|
|
||||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||||
import { killWorkerScript } from "../Netscript/killWorkerScript";
|
import { killWorkerScript } from "../Netscript/killWorkerScript";
|
||||||
import { CONSTANTS } from "../Constants";
|
import { CONSTANTS } from "../Constants";
|
||||||
@ -212,7 +211,7 @@ export function NetscriptSingularity(
|
|||||||
|
|
||||||
workerScript.log("softReset", () => "Soft resetting. This will cause this script to be killed");
|
workerScript.log("softReset", () => "Soft resetting. This will cause this script to be killed");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
prestigeAugmentation();
|
installAugmentations(true);
|
||||||
runAfterReset(cbScript);
|
runAfterReset(cbScript);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
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 */
|
/* tslint:disable:completed-docs no-magic-numbers arrow-return-shorthand */
|
||||||
|
|
||||||
/* Function that generates a valid 'data' for a contract type */
|
/* Function that generates a valid 'data' for a contract type */
|
||||||
@ -794,6 +796,140 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
|||||||
return obstacleGrid[obstacleGrid.length - 1][obstacleGrid[0].length - 1] === parseInt(ans);
|
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 => {
|
desc: (data: string): string => {
|
||||||
return [
|
return [
|
||||||
@ -1008,4 +1144,62 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
|||||||
return true;
|
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);
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
@ -310,7 +310,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
|||||||
|
|
||||||
function softReset(): void {
|
function softReset(): void {
|
||||||
dialogBoxCreate("Soft Reset!");
|
dialogBoxCreate("Soft Reset!");
|
||||||
prestigeAugmentation();
|
installAugmentations(true);
|
||||||
resetErrorBoundary();
|
resetErrorBoundary();
|
||||||
Router.toTerminal();
|
Router.toTerminal();
|
||||||
}
|
}
|
||||||
|
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