diff --git a/src/CodingContract/Contract.ts b/src/CodingContract/Contract.ts index 02e4f7a35..4fd525dad 100644 --- a/src/CodingContract/Contract.ts +++ b/src/CodingContract/Contract.ts @@ -82,6 +82,10 @@ export class CodingContract { this.reward = reward; } + getAnswer() { + return CodingContractTypes[this.type].getAnswer(this.state); + } + getData(): unknown { const func = CodingContractTypes[this.type].getData; return func ? func(this.state) : this.state; diff --git a/src/CodingContract/ContractTypes.ts b/src/CodingContract/ContractTypes.ts index 3103601db..443eeeb07 100644 --- a/src/CodingContract/ContractTypes.ts +++ b/src/CodingContract/ContractTypes.ts @@ -33,11 +33,13 @@ interface CodingContractType { 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 - * 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 * that can be copy-pasted to user code to solve the problem. */ diff --git a/src/CodingContract/contracts/AlgorithmicStockTrader.ts b/src/CodingContract/contracts/AlgorithmicStockTrader.ts index ea89737c6..b27688036 100644 --- a/src/CodingContract/contracts/AlgorithmicStockTrader.ts +++ b/src/CodingContract/contracts/AlgorithmicStockTrader.ts @@ -33,15 +33,17 @@ export const algorithmicStockTrader: Pick< return arr; }, numTries: 5, - solver: (data, answer) => { + getAnswer: (data) => { let maxCur = 0; let maxSoFar = 0; for (let i = 1; i < data.length; ++i) { maxCur = Math.max(0, (maxCur += data[i] - data[i - 1])); maxSoFar = Math.max(maxCur, maxSoFar); } - - return maxSoFar === answer; + return maxSoFar; + }, + solver: (data, answer) => { + return algorithmicStockTrader[CodingContractName.AlgorithmicStockTraderI].getAnswer(data) === answer; }, convertAnswer: (ans) => parseInt(ans, 10), validateAnswer: (ans): ans is number => typeof ans === "number", @@ -71,13 +73,16 @@ export const algorithmicStockTrader: Pick< return arr; }, - solver: (data, answer) => { + getAnswer: (data) => { let profit = 0; for (let p = 1; p < data.length; ++p) { 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), validateAnswer: (ans): ans is number => typeof ans === "number", @@ -107,7 +112,7 @@ export const algorithmicStockTrader: Pick< return arr; }, - solver: (data, answer) => { + getAnswer: (data) => { let hold1 = Number.MIN_SAFE_INTEGER; let hold2 = Number.MIN_SAFE_INTEGER; let release1 = 0; @@ -119,7 +124,10 @@ export const algorithmicStockTrader: Pick< 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), validateAnswer: (ans): ans is number => typeof ans === "number", @@ -154,13 +162,13 @@ export const algorithmicStockTrader: Pick< return [k, prices]; }, - solver: (data, answer) => { + getAnswer: (data) => { const k: number = data[0]; const prices: number[] = data[1]; const len = prices.length; if (len < 2) { - return answer === 0; + return 0; } if (k > len / 2) { let res = 0; @@ -168,7 +176,7 @@ export const algorithmicStockTrader: Pick< res += Math.max(prices[i] - prices[i - 1], 0); } - return res === answer; + return res; } 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), validateAnswer: (ans): ans is number => typeof ans === "number", diff --git a/src/CodingContract/contracts/ArrayJumpingGame.ts b/src/CodingContract/contracts/ArrayJumpingGame.ts index e9e33d97f..344e715b4 100644 --- a/src/CodingContract/contracts/ArrayJumpingGame.ts +++ b/src/CodingContract/contracts/ArrayJumpingGame.ts @@ -37,14 +37,17 @@ export const arrayJumpingGame: Pick< return arr; }, numTries: 1, - solver: (data, answer) => { + getAnswer: (data) => { const n: number = data.length; let i = 0; for (let reach = 0; i < n && i <= reach; ++i) { reach = Math.max(i + data[i], reach); } 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) => { const num = parseInt(ans); @@ -85,7 +88,7 @@ export const arrayJumpingGame: Pick< return arr; }, numTries: 3, - solver: (data, answer) => { + getAnswer: (data) => { const n: number = data.length; let reach = 0; let jumps = 0; @@ -105,7 +108,10 @@ export const arrayJumpingGame: Pick< lastJump = jumpedFrom; jumps++; } - return jumps === answer; + return jumps; + }, + solver: (data, answer) => { + return arrayJumpingGame[CodingContractName.ArrayJumpingGameII].getAnswer(data) === answer; }, convertAnswer: (ans) => parseInt(ans, 10), validateAnswer: (ans): ans is number => typeof ans === "number", diff --git a/src/CodingContract/contracts/Compression.ts b/src/CodingContract/contracts/Compression.ts index 1f2588895..dd2ec3d1a 100644 --- a/src/CodingContract/contracts/Compression.ts +++ b/src/CodingContract/contracts/Compression.ts @@ -1,4 +1,5 @@ import { CodingContractTypes } from "../ContractTypes"; +import { exceptionAlert } from "../../utils/helpers/exceptionAlert"; import { CodingContractName } from "@enums"; export const compression: Pick< @@ -49,8 +50,8 @@ export const compression: Pick< return plain.substring(0, length); }, - solver: (plain, answer) => { - if (plain.length === 0) return answer === ""; + getAnswer: (plain) => { + if (plain.length === 0) return ""; let out = ""; let count = 1; @@ -63,7 +64,10 @@ export const compression: Pick< count = 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, ""), validateAnswer: (ans): ans is string => typeof ans === "string", @@ -97,8 +101,11 @@ export const compression: Pick< generate: (): string => { return comprLZEncode(comprLZGenerate()); }, + getAnswer: (compr) => { + return comprLZDecode(compr) ?? ""; + }, solver: (compr, answer) => { - return (comprLZDecode(compr) ?? "") === answer; + return compression[CodingContractName.CompressionIILZDecompression].getAnswer(compr) === answer; }, convertAnswer: (ans) => ans.replace(/\s/g, ""), validateAnswer: (ans): ans is string => typeof ans === "string", @@ -135,8 +142,20 @@ export const compression: Pick< generate: (): string => { return comprLZGenerate(); }, + getAnswer: (plain) => { + return comprLZEncode(plain); + }, 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, ""), validateAnswer: (ans): ans is string => typeof ans === "string", diff --git a/src/CodingContract/contracts/Encryption.ts b/src/CodingContract/contracts/Encryption.ts index c71a9dc32..76574b7cf 100644 --- a/src/CodingContract/contracts/Encryption.ts +++ b/src/CodingContract/contracts/Encryption.ts @@ -57,13 +57,16 @@ export const encryption: Pick< Math.floor(Math.random() * 25 + 1), ]; }, - solver: (data, answer) => { + getAnswer: (data) => { // data = [plaintext, shift value] // build char array, shifting via map and join to final results const cipher = [...data[0]] .map((a) => (a === " " ? a : String.fromCharCode(((a.charCodeAt(0) - 65 - data[1] + 26) % 26) + 65))) .join(""); - return cipher === answer; + return cipher; + }, + solver: (data, answer) => { + return encryption[CodingContractName.EncryptionICaesarCipher].getAnswer(data) === answer; }, convertAnswer: (ans) => ans, validateAnswer: (ans): ans is string => typeof ans === "string", @@ -222,7 +225,7 @@ export const encryption: Pick< keys.sort(() => Math.random() - 0.5)[0], ]; }, - solver: (data, answer) => { + getAnswer: (data) => { // data = [plaintext, keyword] // build char array, shifting via map and corresponding keyword letter and join to final results 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); }) .join(""); - return cipher === answer; + return cipher; + }, + solver: (data, answer) => { + return encryption[CodingContractName.EncryptionIIVigenereCipher].getAnswer(data) === answer; }, convertAnswer: (ans) => ans, validateAnswer: (ans): ans is string => typeof ans === "string", diff --git a/src/CodingContract/contracts/FindAllValidMathExpressions.ts b/src/CodingContract/contracts/FindAllValidMathExpressions.ts index e544f7d94..5c4b3b60e 100644 --- a/src/CodingContract/contracts/FindAllValidMathExpressions.ts +++ b/src/CodingContract/contracts/FindAllValidMathExpressions.ts @@ -1,4 +1,5 @@ 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 { CodingContractName } from "@enums"; @@ -47,7 +48,7 @@ export const findAllValidMathExpressions: Pick { + getAnswer: (data) => { const num = data[0]; const target = data[1]; @@ -86,6 +87,20 @@ export const findAllValidMathExpressions: Pick { + 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; const solutions = new Set(answer); diff --git a/src/CodingContract/contracts/FindLargestPrimeFactor.ts b/src/CodingContract/contracts/FindLargestPrimeFactor.ts index a9926dbfd..4e7759e9b 100644 --- a/src/CodingContract/contracts/FindLargestPrimeFactor.ts +++ b/src/CodingContract/contracts/FindLargestPrimeFactor.ts @@ -13,7 +13,7 @@ export const findLargestPrimeFactor: Pick { return getRandomIntInclusive(500, 1e9); }, - solver: (data, answer) => { + getAnswer: (data) => { let fac = 2; let n: number = data; while (n > (fac - 1) * (fac - 1)) { @@ -23,7 +23,10 @@ export const findLargestPrimeFactor: Pick { + return findLargestPrimeFactor[CodingContractName.FindLargestPrimeFactor].getAnswer(data) === answer; }, convertAnswer: (ans) => parseInt(ans, 10), validateAnswer: (ans): ans is number => typeof ans === "number", diff --git a/src/CodingContract/contracts/GenerateIPAddresses.ts b/src/CodingContract/contracts/GenerateIPAddresses.ts index 55d07e7df..35a26e4e9 100644 --- a/src/CodingContract/contracts/GenerateIPAddresses.ts +++ b/src/CodingContract/contracts/GenerateIPAddresses.ts @@ -1,5 +1,6 @@ import { CodingContractName } from "@enums"; import { CodingContractTypes, removeBracketsFromArrayString } from "../ContractTypes"; +import { exceptionAlert } from "../../utils/helpers/exceptionAlert"; import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive"; export const generateIPAddresses: Pick = { @@ -28,7 +29,7 @@ export const generateIPAddresses: Pick { + getAnswer: (data) => { const ret: string[] = []; for (let a = 1; a <= 3; ++a) { for (let b = 1; b <= 3; ++b) { @@ -51,6 +52,18 @@ export const generateIPAddresses: Pick { + 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)); }, convertAnswer: (ans) => { diff --git a/src/CodingContract/contracts/HammingCode.ts b/src/CodingContract/contracts/HammingCode.ts index 1e04fb2e8..21983d3b2 100644 --- a/src/CodingContract/contracts/HammingCode.ts +++ b/src/CodingContract/contracts/HammingCode.ts @@ -36,8 +36,11 @@ export const hammingCode: Pick< const y = Math.pow(2, getRandomIntInclusive(1, 57)); return getRandomIntInclusive(Math.min(x, y), Math.max(x, y)); }, + getAnswer: (data) => { + return HammingEncode(data); + }, solver: (data, answer) => { - return HammingEncode(data) === answer; + return hammingCode[CodingContractName.HammingCodesIntegerToEncodedBinary].getAnswer(data) === answer; }, convertAnswer: (ans) => ans, validateAnswer: (ans): ans is string => typeof ans === "string", @@ -83,8 +86,11 @@ export const hammingCode: Pick< } return _buildArray.join(""); }, + getAnswer: (data) => { + return HammingDecode(data); + }, solver: (data, answer) => { - return HammingDecode(data) === answer; + return hammingCode[CodingContractName.HammingCodesEncodedBinaryToInteger].getAnswer(data) === answer; }, convertAnswer: (ans) => parseInt(ans, 10), validateAnswer: (ans): ans is number => typeof ans === "number", diff --git a/src/CodingContract/contracts/MergeOverlappingIntervals.ts b/src/CodingContract/contracts/MergeOverlappingIntervals.ts index 41647e750..97067a3d6 100644 --- a/src/CodingContract/contracts/MergeOverlappingIntervals.ts +++ b/src/CodingContract/contracts/MergeOverlappingIntervals.ts @@ -1,3 +1,4 @@ +import { exceptionAlert } from "../../utils/helpers/exceptionAlert"; import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive"; import { CodingContractTypes, convert2DArrayToString, removeBracketsFromArrayString } from "../ContractTypes"; import { CodingContractName } from "@enums"; @@ -30,7 +31,7 @@ export const mergeOverlappingIntervals: Pick { + getAnswer: (data) => { const intervals: [number, number][] = data.slice(); intervals.sort((a: [number, number], b: [number, number]) => { return a[0] - b[0]; @@ -50,6 +51,22 @@ export const mergeOverlappingIntervals: Pick { + 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]); }, convertAnswer: (ans) => { diff --git a/src/CodingContract/contracts/MinimumPathSumInATriangle.ts b/src/CodingContract/contracts/MinimumPathSumInATriangle.ts index 47d141906..20ef107b9 100644 --- a/src/CodingContract/contracts/MinimumPathSumInATriangle.ts +++ b/src/CodingContract/contracts/MinimumPathSumInATriangle.ts @@ -56,7 +56,7 @@ export const minimumPathSumInATriangle: Pick { + getAnswer: (data) => { const n: number = data.length; const dp: number[] = data[n - 1].slice(); for (let i = n - 2; i > -1; --i) { @@ -65,7 +65,10 @@ export const minimumPathSumInATriangle: Pick { + return minimumPathSumInATriangle[CodingContractName.MinimumPathSumInATriangle].getAnswer(data) === answer; }, convertAnswer: (ans) => parseInt(ans, 10), validateAnswer: (ans): ans is number => typeof ans === "number", diff --git a/src/CodingContract/contracts/Proper2ColoringOfAGraph.ts b/src/CodingContract/contracts/Proper2ColoringOfAGraph.ts index 6b8f5602a..54212d560 100644 --- a/src/CodingContract/contracts/Proper2ColoringOfAGraph.ts +++ b/src/CodingContract/contracts/Proper2ColoringOfAGraph.ts @@ -75,6 +75,9 @@ export const proper2ColoringOfAGraph: Pick { + return null; + }, solver: (data, answer) => { //Helper function to get neighbourhood of a vertex function neighbourhood(vertex: number): number[] { diff --git a/src/CodingContract/contracts/SanitizeParenthesesInExpression.ts b/src/CodingContract/contracts/SanitizeParenthesesInExpression.ts index 2a4d8d276..bd3b000cd 100644 --- a/src/CodingContract/contracts/SanitizeParenthesesInExpression.ts +++ b/src/CodingContract/contracts/SanitizeParenthesesInExpression.ts @@ -1,3 +1,4 @@ +import { exceptionAlert } from "../../utils/helpers/exceptionAlert"; import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive"; import { CodingContractTypes, removeBracketsFromArrayString, removeQuotesFromString } from "../ContractTypes"; import { CodingContractName } from "@enums"; @@ -45,7 +46,7 @@ export const sanitizeParenthesesInExpression: Pick< return chars.join(""); }, - solver: (data, answer) => { + getAnswer: (data) => { let left = 0; let right = 0; const res: string[] = []; @@ -94,6 +95,20 @@ export const sanitizeParenthesesInExpression: Pick< 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; return res.every((sol) => answer.includes(sol)); }, diff --git a/src/CodingContract/contracts/ShortestPathInAGrid.ts b/src/CodingContract/contracts/ShortestPathInAGrid.ts index c191aaf38..f69342c66 100644 --- a/src/CodingContract/contracts/ShortestPathInAGrid.ts +++ b/src/CodingContract/contracts/ShortestPathInAGrid.ts @@ -53,6 +53,9 @@ export const shortestPathInAGrid: Pick { + return null; + }, solver: (data, answer) => { const width = data[0].length; const height = data.length; diff --git a/src/CodingContract/contracts/SpiralizeMatrix.ts b/src/CodingContract/contracts/SpiralizeMatrix.ts index 96761c454..9aafdb1db 100644 --- a/src/CodingContract/contracts/SpiralizeMatrix.ts +++ b/src/CodingContract/contracts/SpiralizeMatrix.ts @@ -1,5 +1,6 @@ import { CodingContractName } from "@enums"; import { removeBracketsFromArrayString, type CodingContractTypes } from "../ContractTypes"; +import { exceptionAlert } from "../../utils/helpers/exceptionAlert"; import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive"; export const spiralizeMatrix: Pick = { @@ -55,7 +56,7 @@ export const spiralizeMatrix: Pick { + getAnswer: (data) => { const spiral: number[] = []; const m: number = data.length; const n: number = data[0].length; @@ -107,6 +108,22 @@ export const spiralizeMatrix: Pick { + 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]); }, convertAnswer: (ans) => { diff --git a/src/CodingContract/contracts/SquareRoot.ts b/src/CodingContract/contracts/SquareRoot.ts index c7d2ff743..6fd857885 100644 --- a/src/CodingContract/contracts/SquareRoot.ts +++ b/src/CodingContract/contracts/SquareRoot.ts @@ -34,6 +34,9 @@ ${data}`; const ans = BigInt(state[0]); return ans * ans + BigInt(state[1]); }, + getAnswer: () => { + return null; + }, solver: (state, answer) => { return state[0] === answer.toString(); }, diff --git a/src/CodingContract/contracts/SubarrayWithMaximumSum.ts b/src/CodingContract/contracts/SubarrayWithMaximumSum.ts index 23eec10a7..d38e7a558 100644 --- a/src/CodingContract/contracts/SubarrayWithMaximumSum.ts +++ b/src/CodingContract/contracts/SubarrayWithMaximumSum.ts @@ -23,13 +23,16 @@ export const subarrayWithMaximumSum: Pick { + getAnswer: (data) => { const nums: number[] = data.slice(); for (let i = 1; i < nums.length; i++) { 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), validateAnswer: (ans): ans is number => typeof ans === "number", diff --git a/src/CodingContract/contracts/TotalPrimesInRange.ts b/src/CodingContract/contracts/TotalPrimesInRange.ts index 562160dbe..52e919f3e 100644 --- a/src/CodingContract/contracts/TotalPrimesInRange.ts +++ b/src/CodingContract/contracts/TotalPrimesInRange.ts @@ -21,7 +21,7 @@ export const totalPrimesInRange: Pick { + getAnswer: (data) => { /** Simple implementation of Sieve of Eratosthenes * https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes*/ function simpleSieve(max: number): number[] { @@ -76,7 +76,11 @@ export const totalPrimesInRange: Pick { + return totalPrimesInRange[CodingContractName.TotalPrimesInRange].getAnswer(data) === answer; }, convertAnswer: (ans) => parseInt(ans, 10), validateAnswer: (ans): ans is number => typeof ans === "number", diff --git a/src/CodingContract/contracts/TotalWaysToSum.ts b/src/CodingContract/contracts/TotalWaysToSum.ts index 685c986ae..db70a2e0d 100644 --- a/src/CodingContract/contracts/TotalWaysToSum.ts +++ b/src/CodingContract/contracts/TotalWaysToSum.ts @@ -22,7 +22,7 @@ export const totalWaysToSum: Pick< generate: (): number => { return getRandomIntInclusive(8, 100); }, - solver: (data, answer) => { + getAnswer: (data) => { if (typeof data !== "number") throw new Error("solver expected number"); const ways: number[] = [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), validateAnswer: (ans): ans is number => typeof ans === "number", @@ -66,7 +69,7 @@ export const totalWaysToSum: Pick< } return [n, s]; }, - solver: (data, answer) => { + getAnswer: (data) => { // https://www.geeksforgeeks.org/coin-change-dp-7/?ref=lbp const n = data[0]; const s = data[1]; @@ -78,7 +81,10 @@ export const totalWaysToSum: Pick< 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), validateAnswer: (ans): ans is number => typeof ans === "number", diff --git a/src/CodingContract/contracts/UniquePathsInAGrid.ts b/src/CodingContract/contracts/UniquePathsInAGrid.ts index bb08e8ac9..4fd41593a 100644 --- a/src/CodingContract/contracts/UniquePathsInAGrid.ts +++ b/src/CodingContract/contracts/UniquePathsInAGrid.ts @@ -29,7 +29,7 @@ export const uniquePathsInAGrid: Pick< return [numRows, numColumns]; }, - solver: (data, answer) => { + getAnswer: (data) => { const n: number = data[0]; // Number of rows const m: number = data[1]; // Number of columns 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), validateAnswer: (ans): ans is number => typeof ans === "number", @@ -97,7 +100,7 @@ export const uniquePathsInAGrid: Pick< return grid; }, - solver: (data, answer) => { + getAnswer: (data) => { const obstacleGrid: number[][] = []; obstacleGrid.length = data.length; 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), validateAnswer: (ans): ans is number => typeof ans === "number", diff --git a/src/NetscriptFunctions/CodingContract.ts b/src/NetscriptFunctions/CodingContract.ts index e7c9f9558..af30acad3 100644 --- a/src/NetscriptFunctions/CodingContract.ts +++ b/src/NetscriptFunctions/CodingContract.ts @@ -55,6 +55,10 @@ export function NetscriptCodingContract(): InternalAPI { case CodingContractResult.Failure: { if (++contract.tries >= contract.getMaxNumTries()) { 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); } else { helpers.log( diff --git a/src/Terminal/Terminal.ts b/src/Terminal/Terminal.ts index 74aa2c91c..58ea5c82d 100644 --- a/src/Terminal/Terminal.ts +++ b/src/Terminal/Terminal.ts @@ -550,6 +550,10 @@ export class Terminal { ++contract.tries; if (contract.tries >= contract.getMaxNumTries()) { 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); } else { this.error(`Contract FAILED - ${contract.getMaxNumTries() - contract.tries} tries remaining`);