mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-27 03:25:44 +02:00
MISC: Make implicit string conversion consistent across all coding contracts (#2608)
This commit is contained in:
@@ -83,6 +83,41 @@ export function removeBracketsFromArrayString(str: string): string {
|
||||
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("'")) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { filterTruthy } from "../../utils/helpers/ArrayHelpers";
|
||||
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
|
||||
import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive";
|
||||
import { CodingContractTypes, removeBracketsFromArrayString, removeQuotesFromString } from "../ContractTypes";
|
||||
import { CodingContractTypes, parseArrayString } from "../ContractTypes";
|
||||
import { CodingContractName } from "@enums";
|
||||
|
||||
export const findAllValidMathExpressions: Pick<CodingContractTypes, CodingContractName.FindAllValidMathExpressions> = {
|
||||
@@ -107,10 +106,12 @@ export const findAllValidMathExpressions: Pick<CodingContractTypes, CodingContra
|
||||
return result.every((sol) => solutions.has(sol));
|
||||
},
|
||||
convertAnswer: (ans) => {
|
||||
const sanitized = removeBracketsFromArrayString(ans).split(",");
|
||||
return filterTruthy(sanitized).map((s) => removeQuotesFromString(s.replace(/\s/g, "")));
|
||||
const parsedAnswer = parseArrayString(ans);
|
||||
if (!findAllValidMathExpressions[CodingContractName.FindAllValidMathExpressions].validateAnswer(parsedAnswer)) {
|
||||
return null;
|
||||
}
|
||||
return parsedAnswer;
|
||||
},
|
||||
validateAnswer: (ans): ans is string[] =>
|
||||
typeof ans === "object" && Array.isArray(ans) && ans.every((s) => typeof s === "string"),
|
||||
validateAnswer: (ans): ans is string[] => Array.isArray(ans) && ans.every((s) => typeof s === "string"),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CodingContractName } from "@enums";
|
||||
import { CodingContractTypes, removeBracketsFromArrayString } from "../ContractTypes";
|
||||
import { CodingContractTypes, parseArrayString } from "../ContractTypes";
|
||||
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
|
||||
import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive";
|
||||
|
||||
@@ -67,10 +67,12 @@ export const generateIPAddresses: Pick<CodingContractTypes, CodingContractName.G
|
||||
return ret.length === answer.length && ret.every((ip) => answer.includes(ip));
|
||||
},
|
||||
convertAnswer: (ans) => {
|
||||
const sanitized = removeBracketsFromArrayString(ans).replace(/\s/g, "");
|
||||
return sanitized.split(",").map((ip) => ip.replace(/^(?<quote>['"])([\d.]*)\k<quote>$/g, "$2"));
|
||||
const parsedAnswer = parseArrayString(ans);
|
||||
if (!generateIPAddresses[CodingContractName.GenerateIPAddresses].validateAnswer(parsedAnswer)) {
|
||||
return null;
|
||||
}
|
||||
return parsedAnswer;
|
||||
},
|
||||
validateAnswer: (ans): ans is string[] =>
|
||||
typeof ans === "object" && Array.isArray(ans) && ans.every((s) => typeof s === "string"),
|
||||
validateAnswer: (ans): ans is string[] => Array.isArray(ans) && ans.every((s) => typeof s === "string"),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
|
||||
import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive";
|
||||
import { CodingContractTypes } from "../ContractTypes";
|
||||
import { parseArrayString, CodingContractTypes } from "../ContractTypes";
|
||||
import { CodingContractName } from "@enums";
|
||||
|
||||
export const largestRectangle: Pick<CodingContractTypes, CodingContractName.LargestRectangleInAMatrix> = {
|
||||
@@ -152,13 +152,7 @@ Answer: [[0,0],[3,1]]
|
||||
return userArea === (solution[1][0] - solution[0][0] + 1) * (solution[1][1] - solution[0][1] + 1);
|
||||
},
|
||||
convertAnswer: (ans) => {
|
||||
let parsedAnswer: unknown;
|
||||
try {
|
||||
parsedAnswer = JSON.parse(ans);
|
||||
} catch (error) {
|
||||
console.error("Invalid answer:", error);
|
||||
return null;
|
||||
}
|
||||
const parsedAnswer = parseArrayString(ans.replace(/\s/g, ""), true);
|
||||
if (!largestRectangle[CodingContractName.LargestRectangleInAMatrix].validateAnswer(parsedAnswer)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
|
||||
import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive";
|
||||
import { CodingContractTypes, convert2DArrayToString, removeBracketsFromArrayString } from "../ContractTypes";
|
||||
import { CodingContractTypes, convert2DArrayToString, parseArrayString } from "../ContractTypes";
|
||||
import { CodingContractName } from "@enums";
|
||||
|
||||
export const mergeOverlappingIntervals: Pick<CodingContractTypes, CodingContractName.MergeOverlappingIntervals> = {
|
||||
@@ -70,20 +70,13 @@ export const mergeOverlappingIntervals: Pick<CodingContractTypes, CodingContract
|
||||
return result.length === answer.length && result.every((a, i) => a[0] === answer[i][0] && a[1] === answer[i][1]);
|
||||
},
|
||||
convertAnswer: (ans) => {
|
||||
const arrayRegex = /\[\d+,\d+\]/g;
|
||||
const matches = ans.replace(/\s/g, "").match(arrayRegex);
|
||||
if (matches === null) return null;
|
||||
const arr = matches.map((a) =>
|
||||
removeBracketsFromArrayString(a)
|
||||
.split(",")
|
||||
.map((n) => parseInt(n)),
|
||||
);
|
||||
// An inline function is needed here, so that TS knows this returns true if it matches the type
|
||||
if (((a: number[][]): a is [number, number][] => a.every((n) => n.length === 2))(arr)) return arr;
|
||||
return null;
|
||||
const parsedAnswer = parseArrayString(ans.replace(/\s/g, ""), true);
|
||||
if (!mergeOverlappingIntervals[CodingContractName.MergeOverlappingIntervals].validateAnswer(parsedAnswer)) {
|
||||
return null;
|
||||
}
|
||||
return parsedAnswer;
|
||||
},
|
||||
validateAnswer: (ans): ans is [number, number][] =>
|
||||
typeof ans === "object" &&
|
||||
Array.isArray(ans) &&
|
||||
ans.every((a) => Array.isArray(a) && a.length === 2 && a.every((n) => typeof n === "number")),
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CodingContractTypes, removeBracketsFromArrayString } from "../ContractTypes";
|
||||
import { CodingContractTypes, parseArrayString } from "../ContractTypes";
|
||||
import { CodingContractName } from "@enums";
|
||||
|
||||
export const proper2ColoringOfAGraph: Pick<CodingContractTypes, CodingContractName.Proper2ColoringOfAGraph> = {
|
||||
@@ -121,14 +121,12 @@ export const proper2ColoringOfAGraph: Pick<CodingContractTypes, CodingContractNa
|
||||
return data[1].every(([a, b]) => answer[a] !== answer[b]);
|
||||
},
|
||||
convertAnswer: (ans) => {
|
||||
const sanitized = removeBracketsFromArrayString(ans).replace(/\s/g, "");
|
||||
if (sanitized === "") return [];
|
||||
const arr = sanitized.split(",").map((s) => parseInt(s, 10));
|
||||
// An inline function is needed here, so that TS knows this returns true if it matches the type
|
||||
if (((a): a is (1 | 0)[] => !a.some((n) => n !== 1 && n !== 0))(arr)) return arr;
|
||||
return null;
|
||||
const parsedAnswer = parseArrayString(ans.replace(/\s/g, ""));
|
||||
if (!proper2ColoringOfAGraph[CodingContractName.Proper2ColoringOfAGraph].validateAnswer(parsedAnswer)) {
|
||||
return null;
|
||||
}
|
||||
return parsedAnswer;
|
||||
},
|
||||
validateAnswer: (ans): ans is (1 | 0)[] =>
|
||||
typeof ans === "object" && Array.isArray(ans) && !ans.some((a) => a !== 1 && a !== 0),
|
||||
validateAnswer: (ans): ans is (1 | 0)[] => Array.isArray(ans) && !ans.some((a) => a !== 1 && a !== 0),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
|
||||
import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive";
|
||||
import { CodingContractTypes, removeBracketsFromArrayString, removeQuotesFromString } from "../ContractTypes";
|
||||
import { CodingContractTypes, parseArrayString } from "../ContractTypes";
|
||||
import { CodingContractName } from "@enums";
|
||||
|
||||
export const sanitizeParenthesesInExpression: Pick<
|
||||
@@ -113,10 +113,16 @@ export const sanitizeParenthesesInExpression: Pick<
|
||||
return res.every((sol) => answer.includes(sol));
|
||||
},
|
||||
convertAnswer: (ans) => {
|
||||
const sanitized = removeBracketsFromArrayString(ans).split(",");
|
||||
return sanitized.map((s) => removeQuotesFromString(s.replace(/\s/g, "")));
|
||||
const parsedAnswer = parseArrayString(ans);
|
||||
if (
|
||||
!sanitizeParenthesesInExpression[CodingContractName.SanitizeParenthesesInExpression].validateAnswer(
|
||||
parsedAnswer,
|
||||
)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return parsedAnswer;
|
||||
},
|
||||
validateAnswer: (ans): ans is string[] =>
|
||||
typeof ans === "object" && Array.isArray(ans) && ans.every((s) => typeof s === "string"),
|
||||
validateAnswer: (ans): ans is string[] => Array.isArray(ans) && ans.every((s) => typeof s === "string"),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@ export const shortestPathInAGrid: Pick<CodingContractTypes, CodingContractName.S
|
||||
" [[0,1],\n",
|
||||
" [1,0]]\n",
|
||||
"\n",
|
||||
"Answer: ''",
|
||||
`Answer: ""`,
|
||||
].join(" ");
|
||||
},
|
||||
difficulty: 7,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CodingContractName } from "@enums";
|
||||
import { removeBracketsFromArrayString, type CodingContractTypes } from "../ContractTypes";
|
||||
import { parseArrayString, type CodingContractTypes } from "../ContractTypes";
|
||||
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
|
||||
import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive";
|
||||
|
||||
@@ -127,10 +127,12 @@ export const spiralizeMatrix: Pick<CodingContractTypes, CodingContractName.Spira
|
||||
return spiral.length === answer.length && spiral.every((n, i) => n === answer[i]);
|
||||
},
|
||||
convertAnswer: (ans) => {
|
||||
const sanitized = removeBracketsFromArrayString(ans).replace(/\s/g, "").split(",");
|
||||
return sanitized.map((s) => parseInt(s));
|
||||
const parsedAnswer = parseArrayString(ans);
|
||||
if (!spiralizeMatrix[CodingContractName.SpiralizeMatrix].validateAnswer(parsedAnswer)) {
|
||||
return null;
|
||||
}
|
||||
return parsedAnswer;
|
||||
},
|
||||
validateAnswer: (ans): ans is number[] =>
|
||||
typeof ans === "object" && Array.isArray(ans) && ans.every((n) => typeof n === "number"),
|
||||
validateAnswer: (ans): ans is number[] => Array.isArray(ans) && ans.every((n) => typeof n === "number"),
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user