CONTRACTS: Display contract answers on completely failed contracts (#2440)

This commit is contained in:
Adam Weeden
2026-01-08 16:21:52 -05:00
committed by GitHub
parent edf3d11b72
commit 7af9dca6bc
23 changed files with 221 additions and 48 deletions
+4
View File
@@ -82,6 +82,10 @@ export class CodingContract {
this.reward = reward; this.reward = reward;
} }
getAnswer() {
return CodingContractTypes[this.type].getAnswer(this.state);
}
getData(): unknown { getData(): unknown {
const func = CodingContractTypes[this.type].getData; const func = CodingContractTypes[this.type].getData;
return func ? func(this.state) : this.state; return func ? func(this.state) : this.state;
+3 -1
View File
@@ -33,11 +33,13 @@ interface CodingContractType<Data, Answer, State = Data> {
difficulty: number; difficulty: number;
/** Function that generates a valid 'state' for a contract type */ /** Function that generates a valid 'state' for a contract type */
generate: () => State; 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 * 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 * 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 * is given to the user and shown in the description. If this function is
* ommitted, it will be the identity function (i.e. State == Data). * 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 * 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. * that can be copy-pasted to user code to solve the problem.
*/ */
@@ -33,15 +33,17 @@ export const algorithmicStockTrader: Pick<
return arr; return arr;
}, },
numTries: 5, numTries: 5,
solver: (data, answer) => { getAnswer: (data) => {
let maxCur = 0; let maxCur = 0;
let maxSoFar = 0; let maxSoFar = 0;
for (let i = 1; i < data.length; ++i) { for (let i = 1; i < data.length; ++i) {
maxCur = Math.max(0, (maxCur += data[i] - data[i - 1])); maxCur = Math.max(0, (maxCur += data[i] - data[i - 1]));
maxSoFar = Math.max(maxCur, maxSoFar); maxSoFar = Math.max(maxCur, maxSoFar);
} }
return maxSoFar;
return maxSoFar === answer; },
solver: (data, answer) => {
return algorithmicStockTrader[CodingContractName.AlgorithmicStockTraderI].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => parseInt(ans, 10), convertAnswer: (ans) => parseInt(ans, 10),
validateAnswer: (ans): ans is number => typeof ans === "number", validateAnswer: (ans): ans is number => typeof ans === "number",
@@ -71,13 +73,16 @@ export const algorithmicStockTrader: Pick<
return arr; return arr;
}, },
solver: (data, answer) => { getAnswer: (data) => {
let profit = 0; let profit = 0;
for (let p = 1; p < data.length; ++p) { for (let p = 1; p < data.length; ++p) {
profit += Math.max(data[p] - data[p - 1], 0); profit += Math.max(data[p] - data[p - 1], 0);
} }
return profit === answer; return profit;
},
solver: (data, answer) => {
return algorithmicStockTrader[CodingContractName.AlgorithmicStockTraderII].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => parseInt(ans, 10), convertAnswer: (ans) => parseInt(ans, 10),
validateAnswer: (ans): ans is number => typeof ans === "number", validateAnswer: (ans): ans is number => typeof ans === "number",
@@ -107,7 +112,7 @@ export const algorithmicStockTrader: Pick<
return arr; return arr;
}, },
solver: (data, answer) => { getAnswer: (data) => {
let hold1 = Number.MIN_SAFE_INTEGER; let hold1 = Number.MIN_SAFE_INTEGER;
let hold2 = Number.MIN_SAFE_INTEGER; let hold2 = Number.MIN_SAFE_INTEGER;
let release1 = 0; let release1 = 0;
@@ -119,7 +124,10 @@ export const algorithmicStockTrader: Pick<
hold1 = Math.max(hold1, price * -1); hold1 = Math.max(hold1, price * -1);
} }
return release2 === answer; return release2;
},
solver: (data, answer) => {
return algorithmicStockTrader[CodingContractName.AlgorithmicStockTraderIII].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => parseInt(ans, 10), convertAnswer: (ans) => parseInt(ans, 10),
validateAnswer: (ans): ans is number => typeof ans === "number", validateAnswer: (ans): ans is number => typeof ans === "number",
@@ -154,13 +162,13 @@ export const algorithmicStockTrader: Pick<
return [k, prices]; return [k, prices];
}, },
solver: (data, answer) => { getAnswer: (data) => {
const k: number = data[0]; const k: number = data[0];
const prices: number[] = data[1]; const prices: number[] = data[1];
const len = prices.length; const len = prices.length;
if (len < 2) { if (len < 2) {
return answer === 0; return 0;
} }
if (k > len / 2) { if (k > len / 2) {
let res = 0; let res = 0;
@@ -168,7 +176,7 @@ export const algorithmicStockTrader: Pick<
res += Math.max(prices[i] - prices[i - 1], 0); res += Math.max(prices[i] - prices[i - 1], 0);
} }
return res === answer; return res;
} }
const hold: number[] = []; const hold: number[] = [];
@@ -189,7 +197,10 @@ export const algorithmicStockTrader: Pick<
} }
} }
return rele[k] === answer; return rele[k];
},
solver: (data, answer) => {
return algorithmicStockTrader[CodingContractName.AlgorithmicStockTraderIV].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => parseInt(ans, 10), convertAnswer: (ans) => parseInt(ans, 10),
validateAnswer: (ans): ans is number => typeof ans === "number", validateAnswer: (ans): ans is number => typeof ans === "number",
@@ -37,14 +37,17 @@ export const arrayJumpingGame: Pick<
return arr; return arr;
}, },
numTries: 1, numTries: 1,
solver: (data, answer) => { getAnswer: (data) => {
const n: number = data.length; const n: number = data.length;
let i = 0; let i = 0;
for (let reach = 0; i < n && i <= reach; ++i) { for (let reach = 0; i < n && i <= reach; ++i) {
reach = Math.max(i + data[i], reach); reach = Math.max(i + data[i], reach);
} }
const solution: boolean = i === n; const solution: boolean = i === n;
return (solution ? 1 : 0) === answer; return solution ? 1 : 0;
},
solver: (data, answer) => {
return arrayJumpingGame[CodingContractName.ArrayJumpingGame].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => { convertAnswer: (ans) => {
const num = parseInt(ans); const num = parseInt(ans);
@@ -85,7 +88,7 @@ export const arrayJumpingGame: Pick<
return arr; return arr;
}, },
numTries: 3, numTries: 3,
solver: (data, answer) => { getAnswer: (data) => {
const n: number = data.length; const n: number = data.length;
let reach = 0; let reach = 0;
let jumps = 0; let jumps = 0;
@@ -105,7 +108,10 @@ export const arrayJumpingGame: Pick<
lastJump = jumpedFrom; lastJump = jumpedFrom;
jumps++; jumps++;
} }
return jumps === answer; return jumps;
},
solver: (data, answer) => {
return arrayJumpingGame[CodingContractName.ArrayJumpingGameII].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => parseInt(ans, 10), convertAnswer: (ans) => parseInt(ans, 10),
validateAnswer: (ans): ans is number => typeof ans === "number", validateAnswer: (ans): ans is number => typeof ans === "number",
+24 -5
View File
@@ -1,4 +1,5 @@
import { CodingContractTypes } from "../ContractTypes"; import { CodingContractTypes } from "../ContractTypes";
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
import { CodingContractName } from "@enums"; import { CodingContractName } from "@enums";
export const compression: Pick< export const compression: Pick<
@@ -49,8 +50,8 @@ export const compression: Pick<
return plain.substring(0, length); return plain.substring(0, length);
}, },
solver: (plain, answer) => { getAnswer: (plain) => {
if (plain.length === 0) return answer === ""; if (plain.length === 0) return "";
let out = ""; let out = "";
let count = 1; let count = 1;
@@ -63,7 +64,10 @@ export const compression: Pick<
count = 1; count = 1;
} }
out += count + plain[plain.length - 1]; out += count + plain[plain.length - 1];
return out === answer; return out;
},
solver: (plain, answer) => {
return compression[CodingContractName.CompressionIRLECompression].getAnswer(plain) === answer;
}, },
convertAnswer: (ans) => ans.replace(/\s/g, ""), convertAnswer: (ans) => ans.replace(/\s/g, ""),
validateAnswer: (ans): ans is string => typeof ans === "string", validateAnswer: (ans): ans is string => typeof ans === "string",
@@ -97,8 +101,11 @@ export const compression: Pick<
generate: (): string => { generate: (): string => {
return comprLZEncode(comprLZGenerate()); return comprLZEncode(comprLZGenerate());
}, },
getAnswer: (compr) => {
return comprLZDecode(compr) ?? "";
},
solver: (compr, answer) => { solver: (compr, answer) => {
return (comprLZDecode(compr) ?? "") === answer; return compression[CodingContractName.CompressionIILZDecompression].getAnswer(compr) === answer;
}, },
convertAnswer: (ans) => ans.replace(/\s/g, ""), convertAnswer: (ans) => ans.replace(/\s/g, ""),
validateAnswer: (ans): ans is string => typeof ans === "string", validateAnswer: (ans): ans is string => typeof ans === "string",
@@ -135,8 +142,20 @@ export const compression: Pick<
generate: (): string => { generate: (): string => {
return comprLZGenerate(); return comprLZGenerate();
}, },
getAnswer: (plain) => {
return comprLZEncode(plain);
},
solver: (plain, answer) => { solver: (plain, answer) => {
return answer.length <= comprLZEncode(plain).length && comprLZDecode(answer) === plain; const encoded = compression[CodingContractName.CompressionIIILZCompression].getAnswer(plain);
if (encoded === null) {
exceptionAlert(
new Error(
`Unexpected null when calculating the answer for ${CodingContractName.CompressionIIILZCompression} contract. Data: ${plain}`,
),
);
return false;
}
return answer.length <= encoded.length && comprLZDecode(answer) === plain;
}, },
convertAnswer: (ans) => ans.replace(/\s/g, ""), convertAnswer: (ans) => ans.replace(/\s/g, ""),
validateAnswer: (ans): ans is string => typeof ans === "string", validateAnswer: (ans): ans is string => typeof ans === "string",
+10 -4
View File
@@ -57,13 +57,16 @@ export const encryption: Pick<
Math.floor(Math.random() * 25 + 1), Math.floor(Math.random() * 25 + 1),
]; ];
}, },
solver: (data, answer) => { getAnswer: (data) => {
// data = [plaintext, shift value] // data = [plaintext, shift value]
// build char array, shifting via map and join to final results // build char array, shifting via map and join to final results
const cipher = [...data[0]] const cipher = [...data[0]]
.map((a) => (a === " " ? a : String.fromCharCode(((a.charCodeAt(0) - 65 - data[1] + 26) % 26) + 65))) .map((a) => (a === " " ? a : String.fromCharCode(((a.charCodeAt(0) - 65 - data[1] + 26) % 26) + 65)))
.join(""); .join("");
return cipher === answer; return cipher;
},
solver: (data, answer) => {
return encryption[CodingContractName.EncryptionICaesarCipher].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => ans, convertAnswer: (ans) => ans,
validateAnswer: (ans): ans is string => typeof ans === "string", validateAnswer: (ans): ans is string => typeof ans === "string",
@@ -222,7 +225,7 @@ export const encryption: Pick<
keys.sort(() => Math.random() - 0.5)[0], keys.sort(() => Math.random() - 0.5)[0],
]; ];
}, },
solver: (data, answer) => { getAnswer: (data) => {
// data = [plaintext, keyword] // data = [plaintext, keyword]
// build char array, shifting via map and corresponding keyword letter and join to final results // build char array, shifting via map and corresponding keyword letter and join to final results
const cipher = [...data[0]] const cipher = [...data[0]]
@@ -232,7 +235,10 @@ export const encryption: Pick<
: String.fromCharCode(((a.charCodeAt(0) - 2 * 65 + data[1].charCodeAt(i % data[1].length)) % 26) + 65); : String.fromCharCode(((a.charCodeAt(0) - 2 * 65 + data[1].charCodeAt(i % data[1].length)) % 26) + 65);
}) })
.join(""); .join("");
return cipher === answer; return cipher;
},
solver: (data, answer) => {
return encryption[CodingContractName.EncryptionIIVigenereCipher].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => ans, convertAnswer: (ans) => ans,
validateAnswer: (ans): ans is string => typeof ans === "string", validateAnswer: (ans): ans is string => typeof ans === "string",
@@ -1,4 +1,5 @@
import { filterTruthy } from "../../utils/helpers/ArrayHelpers"; import { filterTruthy } from "../../utils/helpers/ArrayHelpers";
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive"; import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive";
import { CodingContractTypes, removeBracketsFromArrayString, removeQuotesFromString } from "../ContractTypes"; import { CodingContractTypes, removeBracketsFromArrayString, removeQuotesFromString } from "../ContractTypes";
import { CodingContractName } from "@enums"; import { CodingContractName } from "@enums";
@@ -47,7 +48,7 @@ export const findAllValidMathExpressions: Pick<CodingContractTypes, CodingContra
return [digits, target]; return [digits, target];
}, },
solver: (data, answer) => { getAnswer: (data) => {
const num = data[0]; const num = data[0];
const target = data[1]; const target = data[1];
@@ -86,6 +87,20 @@ export const findAllValidMathExpressions: Pick<CodingContractTypes, CodingContra
const result: string[] = []; const result: string[] = [];
helper(result, "", num, target, 0, 0, 0); helper(result, "", num, target, 0, 0, 0);
return result;
},
solver: (data, answer) => {
const result = findAllValidMathExpressions[CodingContractName.FindAllValidMathExpressions].getAnswer(data);
if (result === null) {
exceptionAlert(
new Error(
`Unexpected null when calculating the answer for ${CodingContractName.FindAllValidMathExpressions} contract. Data: ${data}`,
),
);
return false;
}
if (result.length !== answer.length) return false; if (result.length !== answer.length) return false;
const solutions = new Set(answer); const solutions = new Set(answer);
@@ -13,7 +13,7 @@ export const findLargestPrimeFactor: Pick<CodingContractTypes, CodingContractNam
generate: (): number => { generate: (): number => {
return getRandomIntInclusive(500, 1e9); return getRandomIntInclusive(500, 1e9);
}, },
solver: (data, answer) => { getAnswer: (data) => {
let fac = 2; let fac = 2;
let n: number = data; let n: number = data;
while (n > (fac - 1) * (fac - 1)) { while (n > (fac - 1) * (fac - 1)) {
@@ -23,7 +23,10 @@ export const findLargestPrimeFactor: Pick<CodingContractTypes, CodingContractNam
++fac; ++fac;
} }
return (n === 1 ? fac - 1 : n) === answer; return n === 1 ? fac - 1 : n;
},
solver: (data, answer) => {
return findLargestPrimeFactor[CodingContractName.FindLargestPrimeFactor].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => parseInt(ans, 10), convertAnswer: (ans) => parseInt(ans, 10),
validateAnswer: (ans): ans is number => typeof ans === "number", validateAnswer: (ans): ans is number => typeof ans === "number",
@@ -1,5 +1,6 @@
import { CodingContractName } from "@enums"; import { CodingContractName } from "@enums";
import { CodingContractTypes, removeBracketsFromArrayString } from "../ContractTypes"; import { CodingContractTypes, removeBracketsFromArrayString } from "../ContractTypes";
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive"; import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive";
export const generateIPAddresses: Pick<CodingContractTypes, CodingContractName.GenerateIPAddresses> = { export const generateIPAddresses: Pick<CodingContractTypes, CodingContractName.GenerateIPAddresses> = {
@@ -28,7 +29,7 @@ export const generateIPAddresses: Pick<CodingContractTypes, CodingContractName.G
return str; return str;
}, },
solver: (data, answer) => { getAnswer: (data) => {
const ret: string[] = []; const ret: string[] = [];
for (let a = 1; a <= 3; ++a) { for (let a = 1; a <= 3; ++a) {
for (let b = 1; b <= 3; ++b) { for (let b = 1; b <= 3; ++b) {
@@ -51,6 +52,18 @@ export const generateIPAddresses: Pick<CodingContractTypes, CodingContractName.G
} }
} }
return ret;
},
solver: (data, answer) => {
const ret = generateIPAddresses[CodingContractName.GenerateIPAddresses].getAnswer(data);
if (ret === null) {
exceptionAlert(
new Error(
`Unexpected null when calculating the answer for ${CodingContractName.GenerateIPAddresses} contract. Data: ${data}`,
),
);
return false;
}
return ret.length === answer.length && ret.every((ip) => answer.includes(ip)); return ret.length === answer.length && ret.every((ip) => answer.includes(ip));
}, },
convertAnswer: (ans) => { convertAnswer: (ans) => {
+8 -2
View File
@@ -36,8 +36,11 @@ export const hammingCode: Pick<
const y = Math.pow(2, getRandomIntInclusive(1, 57)); const y = Math.pow(2, getRandomIntInclusive(1, 57));
return getRandomIntInclusive(Math.min(x, y), Math.max(x, y)); return getRandomIntInclusive(Math.min(x, y), Math.max(x, y));
}, },
getAnswer: (data) => {
return HammingEncode(data);
},
solver: (data, answer) => { solver: (data, answer) => {
return HammingEncode(data) === answer; return hammingCode[CodingContractName.HammingCodesIntegerToEncodedBinary].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => ans, convertAnswer: (ans) => ans,
validateAnswer: (ans): ans is string => typeof ans === "string", validateAnswer: (ans): ans is string => typeof ans === "string",
@@ -83,8 +86,11 @@ export const hammingCode: Pick<
} }
return _buildArray.join(""); return _buildArray.join("");
}, },
getAnswer: (data) => {
return HammingDecode(data);
},
solver: (data, answer) => { solver: (data, answer) => {
return HammingDecode(data) === answer; return hammingCode[CodingContractName.HammingCodesEncodedBinaryToInteger].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => parseInt(ans, 10), convertAnswer: (ans) => parseInt(ans, 10),
validateAnswer: (ans): ans is number => typeof ans === "number", validateAnswer: (ans): ans is number => typeof ans === "number",
@@ -1,3 +1,4 @@
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive"; import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive";
import { CodingContractTypes, convert2DArrayToString, removeBracketsFromArrayString } from "../ContractTypes"; import { CodingContractTypes, convert2DArrayToString, removeBracketsFromArrayString } from "../ContractTypes";
import { CodingContractName } from "@enums"; import { CodingContractName } from "@enums";
@@ -30,7 +31,7 @@ export const mergeOverlappingIntervals: Pick<CodingContractTypes, CodingContract
return intervals; return intervals;
}, },
numTries: 15, numTries: 15,
solver: (data, answer) => { getAnswer: (data) => {
const intervals: [number, number][] = data.slice(); const intervals: [number, number][] = data.slice();
intervals.sort((a: [number, number], b: [number, number]) => { intervals.sort((a: [number, number], b: [number, number]) => {
return a[0] - b[0]; return a[0] - b[0];
@@ -50,6 +51,22 @@ export const mergeOverlappingIntervals: Pick<CodingContractTypes, CodingContract
} }
result.push([start, end]); result.push([start, end]);
return result;
},
solver: (data, answer) => {
const result = mergeOverlappingIntervals[CodingContractName.MergeOverlappingIntervals].getAnswer(data);
if (result === null) {
exceptionAlert(
new Error(
`Unexpected null when calculating the answer for ${
CodingContractName.MergeOverlappingIntervals
} contract. Data: ${JSON.stringify(data)}`,
),
);
return false;
}
return result.length === answer.length && result.every((a, i) => a[0] === answer[i][0] && a[1] === answer[i][1]); return result.length === answer.length && result.every((a, i) => a[0] === answer[i][0] && a[1] === answer[i][1]);
}, },
convertAnswer: (ans) => { convertAnswer: (ans) => {
@@ -56,7 +56,7 @@ export const minimumPathSumInATriangle: Pick<CodingContractTypes, CodingContract
return triangle; return triangle;
}, },
solver: (data, answer) => { getAnswer: (data) => {
const n: number = data.length; const n: number = data.length;
const dp: number[] = data[n - 1].slice(); const dp: number[] = data[n - 1].slice();
for (let i = n - 2; i > -1; --i) { for (let i = n - 2; i > -1; --i) {
@@ -65,7 +65,10 @@ export const minimumPathSumInATriangle: Pick<CodingContractTypes, CodingContract
} }
} }
return dp[0] === answer; return dp[0];
},
solver: (data, answer) => {
return minimumPathSumInATriangle[CodingContractName.MinimumPathSumInATriangle].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => parseInt(ans, 10), convertAnswer: (ans) => parseInt(ans, 10),
validateAnswer: (ans): ans is number => typeof ans === "number", validateAnswer: (ans): ans is number => typeof ans === "number",
@@ -75,6 +75,9 @@ export const proper2ColoringOfAGraph: Pick<CodingContractTypes, CodingContractNa
return [n + m, edges]; return [n + m, edges];
}, },
getAnswer: () => {
return null;
},
solver: (data, answer) => { solver: (data, answer) => {
//Helper function to get neighbourhood of a vertex //Helper function to get neighbourhood of a vertex
function neighbourhood(vertex: number): number[] { function neighbourhood(vertex: number): number[] {
@@ -1,3 +1,4 @@
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive"; import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive";
import { CodingContractTypes, removeBracketsFromArrayString, removeQuotesFromString } from "../ContractTypes"; import { CodingContractTypes, removeBracketsFromArrayString, removeQuotesFromString } from "../ContractTypes";
import { CodingContractName } from "@enums"; import { CodingContractName } from "@enums";
@@ -45,7 +46,7 @@ export const sanitizeParenthesesInExpression: Pick<
return chars.join(""); return chars.join("");
}, },
solver: (data, answer) => { getAnswer: (data) => {
let left = 0; let left = 0;
let right = 0; let right = 0;
const res: string[] = []; const res: string[] = [];
@@ -94,6 +95,20 @@ export const sanitizeParenthesesInExpression: Pick<
dfs(0, 0, left, right, data, "", res); dfs(0, 0, left, right, data, "", res);
return res;
},
solver: (data, answer) => {
const res = sanitizeParenthesesInExpression[CodingContractName.SanitizeParenthesesInExpression].getAnswer(data);
if (res === null) {
exceptionAlert(
new Error(
`Unexpected null when calculating the answer for ${CodingContractName.SanitizeParenthesesInExpression} contract. Data: ${data}`,
),
);
return false;
}
if (res.length !== answer.length) return false; if (res.length !== answer.length) return false;
return res.every((sol) => answer.includes(sol)); return res.every((sol) => answer.includes(sol));
}, },
@@ -53,6 +53,9 @@ export const shortestPathInAGrid: Pick<CodingContractTypes, CodingContractName.S
return grid; return grid;
}, },
getAnswer: () => {
return null;
},
solver: (data, answer) => { solver: (data, answer) => {
const width = data[0].length; const width = data[0].length;
const height = data.length; const height = data.length;
@@ -1,5 +1,6 @@
import { CodingContractName } from "@enums"; import { CodingContractName } from "@enums";
import { removeBracketsFromArrayString, type CodingContractTypes } from "../ContractTypes"; import { removeBracketsFromArrayString, type CodingContractTypes } from "../ContractTypes";
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive"; import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive";
export const spiralizeMatrix: Pick<CodingContractTypes, CodingContractName.SpiralizeMatrix> = { export const spiralizeMatrix: Pick<CodingContractTypes, CodingContractName.SpiralizeMatrix> = {
@@ -55,7 +56,7 @@ export const spiralizeMatrix: Pick<CodingContractTypes, CodingContractName.Spira
return matrix; return matrix;
}, },
solver: (data, answer) => { getAnswer: (data) => {
const spiral: number[] = []; const spiral: number[] = [];
const m: number = data.length; const m: number = data.length;
const n: number = data[0].length; const n: number = data[0].length;
@@ -107,6 +108,22 @@ export const spiralizeMatrix: Pick<CodingContractTypes, CodingContractName.Spira
} }
} }
return spiral;
},
solver: (data, answer) => {
const spiral = spiralizeMatrix[CodingContractName.SpiralizeMatrix].getAnswer(data);
if (spiral === null) {
exceptionAlert(
new Error(
`Unexpected null when calculating the answer for ${
CodingContractName.SpiralizeMatrix
} contract. Data: ${JSON.stringify(data)}`,
),
);
return false;
}
return spiral.length === answer.length && spiral.every((n, i) => n === answer[i]); return spiral.length === answer.length && spiral.every((n, i) => n === answer[i]);
}, },
convertAnswer: (ans) => { convertAnswer: (ans) => {
@@ -34,6 +34,9 @@ ${data}`;
const ans = BigInt(state[0]); const ans = BigInt(state[0]);
return ans * ans + BigInt(state[1]); return ans * ans + BigInt(state[1]);
}, },
getAnswer: () => {
return null;
},
solver: (state, answer) => { solver: (state, answer) => {
return state[0] === answer.toString(); return state[0] === answer.toString();
}, },
@@ -23,13 +23,16 @@ export const subarrayWithMaximumSum: Pick<CodingContractTypes, CodingContractNam
return arr; return arr;
}, },
solver: (data, answer) => { getAnswer: (data) => {
const nums: number[] = data.slice(); const nums: number[] = data.slice();
for (let i = 1; i < nums.length; i++) { for (let i = 1; i < nums.length; i++) {
nums[i] = Math.max(nums[i], nums[i] + nums[i - 1]); nums[i] = Math.max(nums[i], nums[i] + nums[i - 1]);
} }
return Math.max(...nums) === answer; return Math.max(...nums);
},
solver: (data, answer) => {
return subarrayWithMaximumSum[CodingContractName.SubarrayWithMaximumSum].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => parseInt(ans, 10), convertAnswer: (ans) => parseInt(ans, 10),
validateAnswer: (ans): ans is number => typeof ans === "number", validateAnswer: (ans): ans is number => typeof ans === "number",
@@ -21,7 +21,7 @@ export const totalPrimesInRange: Pick<CodingContractTypes, CodingContractName.To
const high = low + getRandomIntInclusive(1e5, 1e6); const high = low + getRandomIntInclusive(1e5, 1e6);
return [low, high]; return [low, high];
}, },
solver: (data, answer) => { getAnswer: (data) => {
/** Simple implementation of Sieve of Eratosthenes /** Simple implementation of Sieve of Eratosthenes
* https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes*/ * https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes*/
function simpleSieve(max: number): number[] { function simpleSieve(max: number): number[] {
@@ -76,7 +76,11 @@ export const totalPrimesInRange: Pick<CodingContractTypes, CodingContractName.To
//We trust the player generated the appropriate list of primes (or is more accurate at guessing primes than Gauss was at this range) and as such they deserve the reward. //We trust the player generated the appropriate list of primes (or is more accurate at guessing primes than Gauss was at this range) and as such they deserve the reward.
const primes = primeSieve(data[0], data[1]); const primes = primeSieve(data[0], data[1]);
return answer === primes;
return primes;
},
solver: (data, answer) => {
return totalPrimesInRange[CodingContractName.TotalPrimesInRange].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => parseInt(ans, 10), convertAnswer: (ans) => parseInt(ans, 10),
validateAnswer: (ans): ans is number => typeof ans === "number", validateAnswer: (ans): ans is number => typeof ans === "number",
+10 -4
View File
@@ -22,7 +22,7 @@ export const totalWaysToSum: Pick<
generate: (): number => { generate: (): number => {
return getRandomIntInclusive(8, 100); return getRandomIntInclusive(8, 100);
}, },
solver: (data, answer) => { getAnswer: (data) => {
if (typeof data !== "number") throw new Error("solver expected number"); if (typeof data !== "number") throw new Error("solver expected number");
const ways: number[] = [1]; const ways: number[] = [1];
ways.length = data + 1; ways.length = data + 1;
@@ -33,7 +33,10 @@ export const totalWaysToSum: Pick<
} }
} }
return ways[data] === answer; return ways[data];
},
solver: (data, answer) => {
return totalWaysToSum[CodingContractName.TotalWaysToSum].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => parseInt(ans, 10), convertAnswer: (ans) => parseInt(ans, 10),
validateAnswer: (ans): ans is number => typeof ans === "number", validateAnswer: (ans): ans is number => typeof ans === "number",
@@ -66,7 +69,7 @@ export const totalWaysToSum: Pick<
} }
return [n, s]; return [n, s];
}, },
solver: (data, answer) => { getAnswer: (data) => {
// https://www.geeksforgeeks.org/coin-change-dp-7/?ref=lbp // https://www.geeksforgeeks.org/coin-change-dp-7/?ref=lbp
const n = data[0]; const n = data[0];
const s = data[1]; const s = data[1];
@@ -78,7 +81,10 @@ export const totalWaysToSum: Pick<
ways[j] += ways[j - s[i]]; ways[j] += ways[j - s[i]];
} }
} }
return ways[n] === answer; return ways[n];
},
solver: (data, answer) => {
return totalWaysToSum[CodingContractName.TotalWaysToSumII].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => parseInt(ans, 10), convertAnswer: (ans) => parseInt(ans, 10),
validateAnswer: (ans): ans is number => typeof ans === "number", validateAnswer: (ans): ans is number => typeof ans === "number",
@@ -29,7 +29,7 @@ export const uniquePathsInAGrid: Pick<
return [numRows, numColumns]; return [numRows, numColumns];
}, },
solver: (data, answer) => { getAnswer: (data) => {
const n: number = data[0]; // Number of rows const n: number = data[0]; // Number of rows
const m: number = data[1]; // Number of columns const m: number = data[1]; // Number of columns
const currentRow: number[] = []; const currentRow: number[] = [];
@@ -44,7 +44,10 @@ export const uniquePathsInAGrid: Pick<
} }
} }
return currentRow[n - 1] === answer; return currentRow[n - 1];
},
solver: (data, answer) => {
return uniquePathsInAGrid[CodingContractName.UniquePathsInAGridI].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => parseInt(ans, 10), convertAnswer: (ans) => parseInt(ans, 10),
validateAnswer: (ans): ans is number => typeof ans === "number", validateAnswer: (ans): ans is number => typeof ans === "number",
@@ -97,7 +100,7 @@ export const uniquePathsInAGrid: Pick<
return grid; return grid;
}, },
solver: (data, answer) => { getAnswer: (data) => {
const obstacleGrid: number[][] = []; const obstacleGrid: number[][] = [];
obstacleGrid.length = data.length; obstacleGrid.length = data.length;
for (let i = 0; i < obstacleGrid.length; ++i) { for (let i = 0; i < obstacleGrid.length; ++i) {
@@ -116,7 +119,10 @@ export const uniquePathsInAGrid: Pick<
} }
} }
return obstacleGrid[obstacleGrid.length - 1][obstacleGrid[0].length - 1] === answer; return obstacleGrid[obstacleGrid.length - 1][obstacleGrid[0].length - 1];
},
solver: (data, answer) => {
return uniquePathsInAGrid[CodingContractName.UniquePathsInAGridII].getAnswer(data) === answer;
}, },
convertAnswer: (ans) => parseInt(ans, 10), convertAnswer: (ans) => parseInt(ans, 10),
validateAnswer: (ans): ans is number => typeof ans === "number", validateAnswer: (ans): ans is number => typeof ans === "number",
+4
View File
@@ -55,6 +55,10 @@ export function NetscriptCodingContract(): InternalAPI<ICodingContract> {
case CodingContractResult.Failure: { case CodingContractResult.Failure: {
if (++contract.tries >= contract.getMaxNumTries()) { if (++contract.tries >= contract.getMaxNumTries()) {
helpers.log(ctx, () => `Coding Contract attempt '${contract.fn}' failed. Contract is now self-destructing`); helpers.log(ctx, () => `Coding Contract attempt '${contract.fn}' failed. Contract is now self-destructing`);
const solution = contract.getAnswer();
if (solution !== null) {
helpers.log(ctx, () => `Coding Contract solution was: ${solution}`);
}
server.removeContract(contract.fn); server.removeContract(contract.fn);
} else { } else {
helpers.log( helpers.log(
+4
View File
@@ -550,6 +550,10 @@ export class Terminal {
++contract.tries; ++contract.tries;
if (contract.tries >= contract.getMaxNumTries()) { if (contract.tries >= contract.getMaxNumTries()) {
this.error("Contract FAILED - Contract is now self-destructing"); this.error("Contract FAILED - Contract is now self-destructing");
const solution = contract.getAnswer();
if (solution !== null) {
this.error(`Coding Contract solution was: ${solution}`);
}
server.removeContract(contract); server.removeContract(contract);
} else { } else {
this.error(`Contract FAILED - ${contract.getMaxNumTries() - contract.tries} tries remaining`); this.error(`Contract FAILED - ${contract.getMaxNumTries() - contract.tries} tries remaining`);