mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 22:38:34 +02:00
173 lines
6.9 KiB
TypeScript
173 lines
6.9 KiB
TypeScript
import { CodingContractName } from "@enums";
|
|
import { CodingContractSignatures } from "@nsdefs";
|
|
|
|
import { algorithmicStockTrader } from "./contracts/AlgorithmicStockTrader";
|
|
import { arrayJumpingGame } from "./contracts/ArrayJumpingGame";
|
|
import { compression } from "./contracts/Compression";
|
|
import { encryption } from "./contracts/Encryption";
|
|
import { findAllValidMathExpressions } from "./contracts/FindAllValidMathExpressions";
|
|
import { findLargestPrimeFactor } from "./contracts/FindLargestPrimeFactor";
|
|
import { generateIPAddresses } from "./contracts/GenerateIPAddresses";
|
|
import { hammingCode } from "./contracts/HammingCode";
|
|
import { mergeOverlappingIntervals } from "./contracts/MergeOverlappingIntervals";
|
|
import { minimumPathSumInATriangle } from "./contracts/MinimumPathSumInATriangle";
|
|
import { proper2ColoringOfAGraph } from "./contracts/Proper2ColoringOfAGraph";
|
|
import { sanitizeParenthesesInExpression } from "./contracts/SanitizeParenthesesInExpression";
|
|
import { shortestPathInAGrid } from "./contracts/ShortestPathInAGrid";
|
|
import { spiralizeMatrix } from "./contracts/SpiralizeMatrix";
|
|
import { squareRoot } from "./contracts/SquareRoot";
|
|
import { subarrayWithMaximumSum } from "./contracts/SubarrayWithMaximumSum";
|
|
import { totalPrimesInRange } from "./contracts/TotalPrimesInRange";
|
|
import { totalWaysToSum } from "./contracts/TotalWaysToSum";
|
|
import { uniquePathsInAGrid } from "./contracts/UniquePathsInAGrid";
|
|
import { largestRectangle } from "./contracts/LargestRectangle";
|
|
|
|
// This is the base interface, but should not be used for
|
|
// typechecking individual entries. Use the two types below for that.
|
|
interface CodingContractType<Data, Answer, State = Data> {
|
|
/**
|
|
* Function that returns a string with the problem's description.
|
|
* Requires the 'data' of a Contract as input
|
|
*/
|
|
desc: (data: Data) => string;
|
|
/** Difficulty of the contract. Higher is harder. */
|
|
difficulty: number;
|
|
/** Function that generates a valid 'state' for a contract type */
|
|
generate: () => State;
|
|
/** Function that returns an answer, if possible, for a given contract */
|
|
getAnswer: (data: Data) => Answer | null;
|
|
/**
|
|
* Transforms the 'state' for a contract into its 'data'. The state is
|
|
* stored persistently as JSON, so it must be serializable. The data is what
|
|
* is given to the user and shown in the description. If this function is
|
|
* omitted, it will be the identity function (i.e. State == Data).
|
|
* You can use this to make problems where the "solver" is not a function
|
|
* that can be copy-pasted to user code to solve the problem.
|
|
*/
|
|
getData?: (state: State) => Data;
|
|
/** How many tries you get. Defaults to 10. */
|
|
numTries?: number;
|
|
/** Function that checks whether the players answer is correct. */
|
|
solver: (state: State, answer: Answer) => boolean;
|
|
/** Function that converts string answers to the expected answer format. */
|
|
convertAnswer: (answer: string) => Answer | null;
|
|
/** Function that validates the format of the provided answer. */
|
|
validateAnswer: (answer: unknown) => answer is Answer;
|
|
}
|
|
|
|
// This simple alias uses State == Data, and omits getData since it won't be used in this case.
|
|
type CodingContractSimpleType<Data, Answer> = Omit<CodingContractType<Data, Answer, Data>, "getData">;
|
|
|
|
// This alias has unique State and Data, and requires getData.
|
|
type CodingContractComplexType<Data, Answer, State> = Omit<CodingContractType<Data, Answer, State>, "getData"> & {
|
|
getData: (state: State) => Data;
|
|
};
|
|
|
|
type CodingContractDefinitions<Signatures extends Record<string, [unknown, unknown] | [unknown, unknown, unknown]>> = {
|
|
[T in keyof Signatures]: Signatures[T] extends [unknown, unknown, unknown]
|
|
? CodingContractComplexType<Signatures[T][0], Signatures[T][1], Signatures[T][2]>
|
|
: CodingContractSimpleType<Signatures[T][0], Signatures[T][1]>;
|
|
};
|
|
export type CodingContractTypes = CodingContractDefinitions<CodingContractSignatures>;
|
|
|
|
/* Helper functions for Coding Contract implementations */
|
|
export function removeBracketsFromArrayString(str: string): string {
|
|
let strCpy: string = str;
|
|
if (strCpy.startsWith("[")) {
|
|
strCpy = strCpy.slice(1);
|
|
}
|
|
if (strCpy.endsWith("]")) {
|
|
strCpy = strCpy.slice(0, -1);
|
|
}
|
|
|
|
return strCpy;
|
|
}
|
|
|
|
/**
|
|
* This function only performs very simple checks to add the outermost optional brackets. Callers must perform other
|
|
* preprocessing steps and postprocessing validations. For example:
|
|
* - "[ [0, 1]]" will be incorrectly wrapped and converted to "[[[0,1]]]". Callers need to remove redundant whitespace.
|
|
* - "[[1,2],3]" is not an array of arrays, but this function will return it as is. Callers need to call validateAnswer.
|
|
*
|
|
* Note:
|
|
* - "" will always be converted to an empty array ([]).
|
|
* - When parsing an array of arrays (isArrayOfArray = true), "[]" will be converted to an empty array ([]), not an
|
|
* array containing an empty array ([[]]).
|
|
*/
|
|
export function parseArrayString(answer: string, isArrayOfArray = false): unknown {
|
|
let modifiedAnswer = answer.trim();
|
|
|
|
if (isArrayOfArray && modifiedAnswer === "[]") {
|
|
return [];
|
|
}
|
|
|
|
// If it doesn't start with any bracket, it's definitely "naked".
|
|
if (!modifiedAnswer.startsWith("[")) {
|
|
modifiedAnswer = `[${modifiedAnswer}]`;
|
|
} else if (isArrayOfArray && !modifiedAnswer.startsWith("[[")) {
|
|
// If it's supposed to be an array of arrays but only has one "[".
|
|
modifiedAnswer = `[${modifiedAnswer}]`;
|
|
}
|
|
|
|
try {
|
|
return JSON.parse(modifiedAnswer);
|
|
} catch (error) {
|
|
console.error(`Invalid answer: ${answer}`);
|
|
console.error(error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function removeQuotesFromString(str: string): string {
|
|
let strCpy: string = str;
|
|
if (strCpy.startsWith('"') || strCpy.startsWith("'")) {
|
|
strCpy = strCpy.slice(1);
|
|
}
|
|
if (strCpy.endsWith('"') || strCpy.endsWith("'")) {
|
|
strCpy = strCpy.slice(0, -1);
|
|
}
|
|
|
|
return strCpy;
|
|
}
|
|
|
|
export function convert2DArrayToString(arr: number[][]): string {
|
|
const components: string[] = [];
|
|
for (const e of arr) {
|
|
let s = String(e);
|
|
s = ["[", s, "]"].join("");
|
|
components.push(s);
|
|
}
|
|
|
|
return components.join(",").replace(/\s/g, "");
|
|
}
|
|
|
|
export const CodingContractDefinitions: CodingContractTypes = {
|
|
...algorithmicStockTrader,
|
|
...arrayJumpingGame,
|
|
...compression,
|
|
...encryption,
|
|
...findAllValidMathExpressions,
|
|
...findLargestPrimeFactor,
|
|
...generateIPAddresses,
|
|
...hammingCode,
|
|
...largestRectangle,
|
|
...mergeOverlappingIntervals,
|
|
...minimumPathSumInATriangle,
|
|
...proper2ColoringOfAGraph,
|
|
...totalPrimesInRange,
|
|
...sanitizeParenthesesInExpression,
|
|
...shortestPathInAGrid,
|
|
...spiralizeMatrix,
|
|
...squareRoot,
|
|
...subarrayWithMaximumSum,
|
|
...totalWaysToSum,
|
|
...uniquePathsInAGrid,
|
|
};
|
|
|
|
// This untyped variant is easier to work with when the specific type is not known.
|
|
// The specific shape is already checked by the CodingContractDefinitions type, so it is safe to assert the type.
|
|
export const CodingContractTypes = CodingContractDefinitions as Record<
|
|
CodingContractName,
|
|
CodingContractType<unknown, unknown, unknown>
|
|
>;
|