Added 6 new Coding Contracts. Added Coding Contract information to documentation.

This commit is contained in:
danielyxie
2019-02-01 00:27:24 -08:00
parent 2968445244
commit 0e8872fad1
9 changed files with 667 additions and 37 deletions

View File

@@ -10,9 +10,7 @@ import { getRandomInt } from "../utils/helpers/getRandomInt";
export function generateRandomContract() {
// First select a random problem type
const problemTypes = Object.keys(CodingContractTypes);
let randIndex = getRandomInt(0, problemTypes.length - 1);
let problemType = problemTypes[randIndex];
let problemType = getRandomProblemType();
// Then select a random reward type. 'Money' will always be the last reward type
const reward = getRandomReward();
@@ -26,6 +24,22 @@ export function generateRandomContract() {
randServer.addContract(contract);
}
export function generateRandomContractOnHome() {
// First select a random problem type
let problemType = getRandomProblemType();
// Then select a random reward type. 'Money' will always be the last reward type
const reward = getRandomReward();
// Choose random server
const serv = Player.getHomeComputer();
let contractFn = getRandomFilename(serv, reward);
let contract = new CodingContract(contractFn, problemType, reward);
serv.addContract(contract);
}
export function generateContract(params) {
// Problem Type
let problemType;
@@ -33,8 +47,7 @@ export function generateContract(params) {
if (params.problemType != null && problemTypes.includes(params.problemType)) {
problemType = params.problemType;
} else {
let randIndex = getRandomInt(0, problemTypes.length - 1);
problemType = problemTypes[randIndex];
problemType = getRandomProblemType();
}
// Reward Type - This is always random for now
@@ -91,6 +104,13 @@ function sanitizeRewardType(rewardType) {
return type;
}
function getRandomProblemType() {
const problemTypes = Object.keys(CodingContractTypes);
let randIndex = getRandomInt(0, problemTypes.length - 1);
return problemTypes[randIndex];
}
function getRandomReward() {
let reward = {};
reward.type = getRandomInt(0, CodingContractRewardType.Money);

View File

@@ -1,9 +1,17 @@
import { codingContractTypesMetadata,
DescriptionFunc,
GeneratorFunc,
SolverFunc } from "./data/codingcontracttypes";
import { IMap } from "./types";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
import { KEY } from "../utils/helpers/keyCodes";
import { createElement } from "../utils/uiHelpers/createElement";
import { createPopup } from "../utils/uiHelpers/createPopup";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
import { codingContractTypesMetadata, DescriptionFunc, GeneratorFunc, SolverFunc } from "./data/codingcontracttypes";
import { IMap } from "./types";
/* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */
@@ -171,18 +179,22 @@ export class CodingContract {
const contractType: CodingContractType = CodingContractTypes[this.type];
const popupId: string = `coding-contract-prompt-popup-${this.fn}`;
const txt: HTMLElement = createElement("p", {
innerText: ["You are attempting to solve a Coding Contract. You have",
innerHTML: ["You are attempting to solve a Coding Contract. You have",
`${this.getMaxNumTries() - this.tries} tries remaining,`,
"after which the contract will self-destruct.\n\n",
`${contractType.desc(this.data)}`].join(" "),
"after which the contract will self-destruct.<br><br>",
`${contractType.desc(this.data).replace(/\n/g, "<br>")}`].join(" "),
});
let answerInput: HTMLInputElement;
let solveBtn: HTMLElement;
let cancelBtn: HTMLElement;
answerInput = createElement("input", {
onkeydown: (e: any) => {
if (e.keyCode === 13 && answerInput.value !== "") {
if (e.keyCode === KEY.ENTER && answerInput.value !== "") {
e.preventDefault();
solveBtn.click();
} else if (e.keyCode === KEY.ESC) {
e.preventDefault();
cancelBtn.click();
}
},
placeholder: "Enter Solution here",
@@ -200,7 +212,7 @@ export class CodingContract {
},
innerText: "Solve",
});
const cancelBtn: HTMLElement = createElement("a", {
cancelBtn = createElement("a", {
class: "a-link-button",
clickListener: () => {
resolve(CodingContractResult.Cancelled);

View File

@@ -1,23 +1,26 @@
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import { generateRandomContract } from "./CodingContractGenerator";
import { Programs } from "./Programs/Programs";
import { Factions } from "./Faction/Factions";
import { Player } from "./Player";
import { AllServers } from "./Server";
import { hackWorldDaemon } from "./RedPill";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import { CodingContractTypes } from "./CodingContracts";
import { generateContract,
generateRandomContract,
generateRandomContractOnHome } from "./CodingContractGenerator";
import { Programs } from "./Programs/Programs";
import { Factions } from "./Faction/Factions";
import { Player } from "./Player";
import { AllServers } from "./Server";
import { hackWorldDaemon } from "./RedPill";
import { StockMarket,
SymbolToStockMap } from "./StockMarket/StockMarket";
import { Stock } from "./StockMarket/Stock";
import { Terminal } from "./Terminal";
SymbolToStockMap } from "./StockMarket/StockMarket";
import { Stock } from "./StockMarket/Stock";
import { Terminal } from "./Terminal";
import { numeralWrapper } from "./ui/numeralFormat";
import { numeralWrapper } from "./ui/numeralFormat";
import { dialogBoxCreate } from "../utils/DialogBox";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { createElement } from "../utils/uiHelpers/createElement";
import { createOptionElement } from "../utils/uiHelpers/createOptionElement";
import { getSelectText } from "../utils/uiHelpers/getSelectData";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
import { dialogBoxCreate } from "../utils/DialogBox";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { createElement } from "../utils/uiHelpers/createElement";
import { createOptionElement } from "../utils/uiHelpers/createOptionElement";
import { getSelectText } from "../utils/uiHelpers/getSelectData";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
const devMenuContainerId = "dev-menu-container";
@@ -429,6 +432,31 @@ export function createDevMenu() {
innerText: "Generate Random Contract",
});
const generateRandomContractOnHomeBtn = createElement("button", {
class: "std-button",
clickListener: () => {
generateRandomContractOnHome();
},
innerText: "Generate Random Contract on Home Comp",
});
const generateContractWithTypeSelector = createElement("select", { margin: "5px" });
const contractTypes = Object.keys(CodingContractTypes);
for (let i = 0; i < contractTypes.length; ++i) {
generateContractWithTypeSelector.add(createOptionElement(contractTypes[i]));
}
const generateContractWithTypeBtn = createElement("button", {
class: "std-button",
clickListener: () => {
generateContract({
problemType: getSelectText(generateContractWithTypeSelector),
server: "home",
});
},
innerText: "Generate Specified Contract Type on Home Comp",
});
// Stock Market
const stockmarketHeader = createElement("h2", {innerText: "Stock Market"});
@@ -563,6 +591,10 @@ export function createDevMenu() {
devMenuContainer.appendChild(createElement("br"));
devMenuContainer.appendChild(contractsHeader);
devMenuContainer.appendChild(generateRandomContractBtn);
devMenuContainer.appendChild(generateRandomContractOnHomeBtn);
devMenuContainer.appendChild(createElement("br"));
devMenuContainer.appendChild(generateContractWithTypeSelector);
devMenuContainer.appendChild(generateContractWithTypeBtn);
devMenuContainer.appendChild(stockmarketHeader);
devMenuContainer.appendChild(stockInput);
devMenuContainer.appendChild(stockPriceChangeInput);

View File

@@ -4702,8 +4702,17 @@ function NetscriptFunctions(workerScript) {
}
let data = contract.getData();
if (data.constructor === Array) {
// Pass a copy
return data.slice();
// For two dimensional arrays, we have to copy the internal arrays using
// slice() as well. As of right now, no contract has arrays that have
// more than two dimensions
const copy = data.slice();
for (let i = 0; i < copy.length; ++i) {
if (data[i].constructor === Array) {
copy[i] = data[i].slice();
}
}
return copy;
} else {
return data;
}

View File

@@ -30,6 +30,14 @@ function removeBracketsFromArrayString(str: string) {
return strCpy;
}
function removeQuotesFromString(str: 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;
}
function convert2DArrayToString(arr: any[][]) {
const components: string[] = [];
arr.forEach((e: any) => {
@@ -73,7 +81,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
desc: (n: number[]) => {
return ["Given the following integer array, find the contiguous subarray",
"(containing at least one number) which has the largest sum and return that sum.",
"'Sum' refers to the sum of all the numbers in the subarray.",
"'Sum' refers to the sum of all the numbers in the subarray.\n",
`${n.toString()}`].join(" ");
},
difficulty: 1,
@@ -152,8 +160,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
},
difficulty: 2,
gen: () => {
const m: number = getRandomInt(1, 10);
const n: number = getRandomInt(1, 10);
const m: number = getRandomInt(1, 15);
const n: number = getRandomInt(1, 15);
const matrix: number[][] = [];
matrix.length = m;
for (let i: number = 0; i < m; ++i) {
@@ -493,4 +501,422 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
return release2.toString() === ans;
},
},
{
desc: (data: any[]) => {
const k: number = (<number>data[0]);
const prices: number[] = (<number[]>data[1]);
return ["You are given the following array with two elements:\n\n",
`[${k}, [${prices}]]\n\n`,
"The first element is an integer k. The second element is an",
"array of stock prices (which are numbers) where the i-th element",
"represents the stock price on day i.\n\n",
"Determine the maximum possible profit you can earn using at most",
"k transactions. A transaction is defined as buying and then selling",
"one share of the stock. Note that you cannot engage in multiple",
"transactions at once. In other words, you must sell the stock before",
"you can buy it again.\n\n",
"If no profit can be made, then the answer should be 0."].join(" ");
},
difficulty: 8,
gen: () => {
const k: number = getRandomInt(2, 10);
const len: number = getRandomInt(1, 50);
const prices: number[] = [];
prices.length = len;
for (let i = 0; i < len; ++i) {
prices[i] = getRandomInt(1, 200);
}
return [k, prices];
},
name: "Algorithmic Stock Trader IV",
numTries: 10,
solver: (data: any[], ans: string) => {
const k: number = (<number>data[0]);
const prices: number[] = (<number[]>data[1]);
const len = prices.length;
if (len < 2) { return (parseInt(ans) === 0); }
if (k > len / 2) {
let res: number = 0;
for (let i = 1; i < len; ++i) {
res += Math.max(prices[i] - prices[i-1], 0);
}
return (parseInt(ans) === res);
}
const hold: number[] = [];
const rele: number[] = [];
hold.length = k + 1;
rele.length = k + 1;
for (let i = 0; i <= k; ++i) {
hold[i] = Number.MIN_SAFE_INTEGER;
rele[i] = 0;
}
let cur: number;
for (let i = 0; i < len; ++i) {
cur = prices[i];
for (let j = k; j > 0; --j) {
rele[j] = Math.max(rele[j], hold[j] + cur);
hold[j] = Math.max(hold[j], rele[j-1] - cur);
}
}
return (parseInt(ans) === rele[k]);
},
},
{
desc: (data: number[][]) => {
function createTriangleRecurse(data: number[][], level: number = 0): string {
const numLevels: number = data.length;
if (level >= numLevels) { return ""; }
const numSpaces = numLevels - level + 1;
let str: string = ["&nbsp;".repeat(numSpaces), "[", data[level].toString(), "]"].join("");
if (level < numLevels - 1) {
str += ",";
}
return str + "\n" + createTriangleRecurse(data, level+1);
}
function createTriangle(data: number[][]) {
return ["[\n", createTriangleRecurse(data), "]"].join("");
}
const triangle = createTriangle(data);
return ["Given a triangle, find the minimum path sum from top to bottom. In each step",
"of the path, you may only move to adjacent numbers in the row below.",
"The triangle is represented as a 2D array of numbers:\n\n",
`${triangle}\n\n`,
"Example: If you are given the following triangle:\n\n" +
"[\n",
"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[2],\n",
"&nbsp;&nbsp;&nbsp;&nbsp;[3,4],\n",
"&nbsp;&nbsp;&nbsp;[6,5,7],\n",
"&nbsp;&nbsp;[4,1,8,3]\n",
"]\n\n",
"The minimum path sum is 11 (2 -> 3 -> 5 -> 1)."].join(" ");
},
difficulty: 5,
gen: () => {
const triangle: number[][] = [];
const levels: number = getRandomInt(1, 12);
triangle.length = levels;
for (let row = 0; row < levels; ++row) {
triangle[row] = [];
triangle[row].length = row + 1;
for (let i = 0; i < triangle[row].length; ++i) {
triangle[row][i] = getRandomInt(1, 9);
}
}
return triangle;
},
name: "Minimum Path Sum in a Triangle",
numTries: 10,
solver: (data: number[][], ans: string) => {
let n: number = data.length;
let dp: number[] = data[n-1];
for (let i = n-2; i > -1; --i) {
for (let j = 0; j < data[i].length; ++j) {
dp[j] = Math.min(dp[j], dp[j + 1]) + data[i][j];
}
}
return dp[0] === parseInt(ans);
},
},
{
desc: (data: number[]) => {
const numRows = data[0];
const numColumns = data[1];
return ["You are in a grid with",
`${numRows} rows and ${numColumns} columns, and you are`,
"positioned in the top-left corner of that grid. You are trying to",
"reach the bottom-right corner of the grid, but you can only",
"move down or right on each step. Determine how many",
"unique paths there are from start to finish.\n\n",
"NOTE: The data returned for this contract is an array",
"with the number or rows and columns:\n\n",
`[${numRows}, ${numColumns}]`].join(" ");
},
difficulty: 3,
gen: () => {
const numRows: number = getRandomInt(1, 14);
const numColumns: number = getRandomInt(1, 14);
return [numRows, numColumns];
},
name: "Unique Paths in a Grid I",
numTries: 10,
solver: (data: number[], ans: string) => {
let n: number = data[0]; // Number of rows
let m: number = data[1]; // Number of columns
let currentRow: number[] = [];
currentRow.length = n;
for (let i = 0; i < n; i++) {
currentRow[i] = 1;
}
for (let row = 1; row < m; row++) {
for (let i = 1; i < n; i++) {
currentRow[i] += currentRow[i - 1];
}
}
return parseInt(ans) === currentRow[n - 1];
},
},
{
desc: (data: number[][]) => {
let gridString: string = "";
for (const line of data) {
gridString += `${line.toString()},\n`;
}
return ["You are located in the top-left corner of the following grid:\n\n",
`${gridString}\n`,
"You are trying reach the bottom-right corner of the grid, but you can only",
"move down or right on each step. Furthermore, 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 how many unique paths there are from start to finish.\n\n",
"NOTE: The data returned for this contract is an 2D array of numbers representing the grid."].join(" ");
},
difficulty: 5,
gen: () => {
const numRows: number = getRandomInt(1, 12);
const numColumns: number = getRandomInt(1, 12);
const grid: number[][] = [];
grid.length = numRows;
for (let i = 0; i < numRows; ++i) {
grid[i] = [];
grid[i].length = numColumns;
grid[i].fill(0);
}
for (let r = 0; r < numRows; ++r) {
for (let c = 0; c < numColumns; ++c) {
if (r === 0 && c === 0) { continue; }
if (r === numRows - 1 && c === numColumns - 1) { continue; }
// 15% chance of an element being an obstacle
if (Math.random() < 0.15) {
grid[r][c] = 1;
}
}
}
return grid;
},
name: "Unique Paths in a Grid II",
numTries: 10,
solver: (data: number[][], ans: string) => {
let obstacleGrid: number[][] = [];
obstacleGrid.length = data.length;
for (let i = 0; i < obstacleGrid.length; ++i) {
obstacleGrid[i] = data[i].slice();
}
for (let i = 0; i < obstacleGrid.length; i++) {
for (let j = 0; j < obstacleGrid[0].length; j++) {
if (obstacleGrid[i][j] == 1) {
obstacleGrid[i][j] = 0;
} else if (i==0 && j==0) {
obstacleGrid[0][0] = 1;
} else {
obstacleGrid[i][j] = (i > 0 ? obstacleGrid[i-1][j] : 0) + ( j > 0 ? obstacleGrid[i][j-1] : 0);
}
}
}
return (obstacleGrid[obstacleGrid.length -1][obstacleGrid[0].length-1] === parseInt(ans));
},
},
{
desc: (data: string) => {
return ["Given the following string:\n\n",
`${data}\n\n`,
"remove the minimum number of invalid parentheses in order to validate",
"the string. If there are multiple minimal ways to validate the string,",
"provide all of the possible results. The answer should be provided",
"as an array of strings. If it is impossible to validate the string",
"the result should be an array with only an empty string.\n\n",
"IMPORTANT: The string may contain letters, not just parentheses.",
`Examples:\n`,
`"()())()" -> ["()()()", "(())()"]\n`,
`"(a)())()" -> ["(a)()()", "(a())()"]\n`,
`")( -> [""]`].join(" ");
},
difficulty: 10,
gen: () => {
const len: number = getRandomInt(2, 20);
let chars: string[] = [];
chars.length = len;
// 80% chance of the first parenthesis being (
Math.random() < 0.8 ? chars[0] = "(" : chars[0] = ")";
for (let i = 1; i < len; ++i) {
const roll = Math.random();
if (roll < 0.4) {
chars[i] = "(";
} else if (roll < 0.8) {
chars[i] = ")";
} else {
chars[i] = "a";
}
}
return chars.join("");
},
name: "Sanitize Parentheses in Expression",
numTries: 10,
solver: (data: string, ans: string) => {
let left = 0;
let right = 0;
let res: string[] = [];
for (let i = 0; i < data.length; ++i) {
if (data[i] === '(') {
++left;
} else if (data[i] === ')') {
(left > 0) ? --left : ++right;
}
}
function dfs(pair: number, index: number, left: number, right: number, s: string, solution: string, res: string[]) {
if (s.length === index) {
if (left === 0 && right === 0 && pair === 0) {
for(var i = 0; i < res.length; i++) {
if(res[i] === solution) { return; }
}
res.push(solution);
}
return;
}
if (s[index] === '(') {
if (left > 0) {
dfs(pair, index + 1, left - 1, right, s, solution, res);
}
dfs(pair + 1, index + 1, left, right, s, solution + s[index], res);
} else if (s[index] === ')') {
if (right > 0) dfs(pair, index + 1, left, right - 1, s, solution, res);
if (pair > 0) dfs(pair - 1, index + 1, left, right, s, solution + s[index], res);
} else {
dfs(pair, index + 1, left, right, s, solution + s[index], res);
}
}
dfs(0, 0, left, right, data, "", res);
const sanitizedPlayerAns = removeBracketsFromArrayString(ans)
.replace(/\s/g, "");
const playerAnsArray: string[] = sanitizedPlayerAns.split(",");
if (playerAnsArray.length !== res.length) { return false; }
for (const resultInAnswer of res) {
if (!playerAnsArray.includes(resultInAnswer)) { return false; }
}
return true;
},
},
{
desc: (data: any[]) => {
const digits: string = data[0];
const target: number = data[1];
return ["You are given the following string which contains only digits between 0 and 9:\n\n",
`${digits}\n\n`,
`You are also given a target number of ${target}. Return all possible ways`,
"you can add the +, -, and * operators to the string such that it evaluates",
"to the target number.\n\n",
"The provided answer should be an array of strings containing the valid expressions.",
"The data provided by this problem is an array with two elements. The first element",
"is the string of digits, while the second element is the target number:\n\n",
`["${digits}", ${target}]\n\n`,
"Examples:\n\n",
`Input: digits = "123", target = 6\n`,
`Output: ["1+2+3", "1*2*3"]\n\n`,
`Input: digits = "105", target = 5\n`,
`Output: ["1*0+5", "10-5"]`].join(" ");
},
difficulty: 10,
gen: () => {
const numDigits = getRandomInt(4, 12);
const digitsArray: string[] = [];
digitsArray.length = numDigits;
for (let i = 0; i < digitsArray.length; ++i) {
if (i === 0) {
digitsArray[i] = String(getRandomInt(1, 9));
} else {
digitsArray[i] = String(getRandomInt(0, 9));
}
}
const target: number = getRandomInt(-100, 100);
const digits: string = digitsArray.join("");
return [digits, target];
},
name: "Find All Valid Math Expressions",
numTries: 10,
solver: (data: any[], ans: string) => {
const num: string = data[0];
const target: number = data[1];
function helper(res: string[], path: string, num: string, target: number, pos: number, evaluated: number, multed: number) {
if (pos === num.length) {
if (target === evaluated) {
res.push(path);
}
return;
}
for (let i = pos; i < num.length; ++i) {
if (i != pos && num[pos] == '0') { break; }
let cur = parseInt(num.substring(pos, i+1));
if (pos === 0) {
helper(res, path + cur, num, target, i + 1, cur, cur);
} else {
helper(res, path + "+" + cur, num, target, i + 1, evaluated + cur, cur);
helper(res, path + "-" + cur, num, target, i + 1, evaluated - cur, -cur);
helper(res, path + "*" + cur, num, target, i + 1, evaluated - multed + multed * cur, multed * cur);
}
}
}
const sanitizedPlayerAns: string = removeBracketsFromArrayString(ans);
const sanitizedPlayerAnsArr: string[] = sanitizedPlayerAns.split(",");
for (let i = 0; i < sanitizedPlayerAnsArr.length; ++i) {
sanitizedPlayerAnsArr[i] = removeQuotesFromString(sanitizedPlayerAnsArr[i]);
}
if (num == null || num.length === 0) {
if (sanitizedPlayerAnsArr.length === 0) { return true; }
if (sanitizedPlayerAnsArr.length === 1 && sanitizedPlayerAnsArr[0] === "") { return true; }
return false;
}
let result: string[] = [];
helper(result, "", num, target, 0, 0, 0);
for (const expr of result) {
if (!sanitizedPlayerAnsArr.includes(expr)) {
return false;
}
}
return true;
},
},
];