diff --git a/challenges/08-coding-interview-questions-and-take-home-assignments/rosetta-code-problems.json b/challenges/08-coding-interview-questions-and-take-home-assignments/rosetta-code-problems.json new file mode 100644 index 0000000000..ad0a8d2cd2 --- /dev/null +++ b/challenges/08-coding-interview-questions-and-take-home-assignments/rosetta-code-problems.json @@ -0,0 +1,3389 @@ +{ + "name": "Rosetta code problems", + "order": "", + "time": "", + "helpRoom": "", + "challenges": [ + { + "title": "100 doors", + "type": "Waypoint", + "description": [ + "
There are 100 doors in a row that are all initially closed. You make 100 passes by the doors. The first time through, visit every door and 'toggle' the door (if the door is closed, open it; if it is open, close it). The second time, only visit every 2nd door (i.e., door #2, #4, #6, ...) and toggle it. The third time, visit every 3rd door (i.e., door #3, #6, #9, ...), etc., until you only visit the 100th door.
", + "Implement a function to determine the state of the doors after the last pass. Return the final result in an array, with only the door number included in the array if it is open.
" + ], + "challengeSeed": [ + "function getFinalOpenedDoors (numDoors) {", + " // Good luck!", + "}" + ], + "solutions": [ + "function getFinalOpenedDoors (numDoors) {\n // this is the final pattern (always squares).\n // thus, the most efficient solution simply returns an array of squares up to numDoors).\n const finalState = [];\n let i = 1;\n while (Math.pow(i, 2) <= numDoors) {\n finalState.push(Math.pow(i, 2));\n i++;\n }\n return finalState;\n}\n" + ], + "tail": [ + "const solution = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100];" + ], + "tests": [ + "assert(typeof getFinalOpenedDoors === 'function', 'message:getFinalOpenedDoors
is a function.');",
+ "assert(Array.isArray(getFinalOpenedDoors(100)), 'message: getFinalOpenedDoors
should return an array.');",
+ "assert.deepEqual(getFinalOpenedDoors(100), solution, 'message: getFinalOpenedDoors
did not produce the correct results.');"
+ ],
+ "id": "594810f028c0303b75339acb",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "24 game",
+ "type": "Waypoint",
+ "description": [
+ "Implement a function that takes a string of four digits as its argument, with each digit from 1 ──► 9 (inclusive) with repetitions allowed, and returns an arithmetic expression that evaluates to the number 24. If no such solution exists, return \"no solution exists.\"
", + "Rules:
", + " Only the following operators/functions are allowed: multiplication, division, addition, subtraction", + " Division should use floating point or rational arithmetic, etc, to preserve remainders.", + " Forming multiple digit numbers from the supplied digits is disallowed. (So an answer of 12+12 when given 1, 2, 2, and 1 is wrong).", + " The order of the digits when given does not have to be preserved.", + "Example inputs:
", + "solve24(\"4878\");
",
+ "solve24(\"1234\");
",
+ "solve24(\"6789\");
",
+ "solve24(\"1127\");
",
+ "Example outputs (strings):
", + "(7-8/8)*4
",
+ "3*1*4*2
",
+ "(6*8)/(9-7)
",
+ "(1+7)*(2+1)
"
+ ],
+ "challengeSeed": [
+ "function solve24 (numStr) {",
+ " // Good luck!",
+ " return true;",
+ "}"
+ ],
+ "solutions": [
+ "// noprotect\n\nfunction solve24 (numStr) {\n const digitsArr = numStr.split('');\n const answers = [];\n\n const digitPermutations = [];\n const operatorPermutations = [];\n\n function generateDigitPermutations (digits, permutations = []) {\n if (digits.length === 0) {\n digitPermutations.push(permutations);\n }\n else {\n for (let i = 0; i < digits.length; i++) {\n const curr = digits.slice();\n const next = curr.splice(i, 1);\n generateDigitPermutations(curr.slice(), permutations.concat(next));\n }\n }\n }\n\n function generateOperatorPermutations (permutations = []) {\n const operators = ['+', '-', '*', '/'];\n if (permutations.length === 3) {\n operatorPermutations.push(permutations);\n }\n else {\n for (let i = 0; i < operators.length; i++) {\n const curr = permutations.slice();\n curr.push(operators[i]);\n generateOperatorPermutations(curr);\n }\n }\n }\n\n generateDigitPermutations(digitsArr);\n generateOperatorPermutations();\n\n interleave();\n\n return answers[0];\n\n function interleave () {\n for (let i = 0; i < digitPermutations.length; i++) {\n for (let j = 0; j < operatorPermutations.length; j++) {\n const d = digitPermutations[i];\n const o = operatorPermutations[j];\n const perm = [\n `${d[0]}${o[0]}${d[1]}${o[1]}${d[2]}${o[2]}${d[3]}`,\n `(${d[0]}${o[0]}${d[1]})${o[1]}${d[2]}${o[2]}${d[3]}`,\n `${d[0]}${o[0]}(${d[1]}${o[1]}${d[2]})${o[2]}${d[3]}`,\n `${d[0]}${o[0]}${d[1]}${o[1]}(${d[2]}${o[2]}${d[3]})`,\n `${d[0]}${o[0]}(${d[1]}${o[1]}${d[2]}${o[2]}${d[3]})`,\n `(${d[0]}${o[0]}${d[1]}${o[1]}${d[2]})${o[2]}${d[3]}`,\n `(${d[0]}${o[0]}${d[1]})${o[1]}(${d[2]}${o[2]}${d[3]})`\n ];\n\n perm.forEach(combination => {\n const res = eval(combination);\n\n if (res === 24) {\n return answers.push(combination);\n }\n });\n }\n }\n }\n}\n"
+ ],
+ "tail": [
+ "const testCases = [",
+ " '4878',",
+ " '1234',",
+ " '6789',",
+ " '1127'",
+ "];",
+ "",
+ "const answers = [",
+ " ['(7-8/8)*4', '4*(7-8/8)', '(4-8+7)*8', '(4+7-8)*8', '(7+4-8)*8', '(7-8+4)*8', '8*(4-8+7)', '8*(4+7-8)', '8*(7+4-8)', '8*(7-8+4)'],",
+ " ['1*2*3*4', '1*2*4*3', '1*3*2*4', '1*3*4*2', '1*4*2*3', '1*4*3*2', '2*1*3*4', '2*1*4*3', '2*3*1*4', '2*3*4*1', '2*4*3*1', '2*4*1*3', '3*1*2*4', '3*1*4*2', '3*2*1*4', '3*2*4*1', '3*4*1*2', '3*4*2*1', '4*1*2*3', '4*1*3*2', '4*2*1*3', '4*2*3*1', '4*3*1*2', '4*3*2*1', '(1+2+3)*4', '(1+3+2)*4', '(2+1+3)*4', '(2+3+1)*4', '(3+1+2)*4', '(3+2+1)*4', '4*(1+2+3)', '4*(2+1+3)', '4*(2+3+1)', '4*(3+1+2)', '4*(3+2+1)'],",
+ " ['(6*8)/(9-7)', '(8*6)/(9-7)', '6*8/(9-7)', '8*6/(9-7)'],",
+ " ['(1+7)*(2+1)', '(1+7)*(1+2)', '(1+2)*(1+7)', '(1+2)*(7+1)', '(2+1)*(1+7)', '(7+1)*(2+1)']",
+ "];",
+ "",
+ "function include(ansArr, res) {",
+ " const index = ansArr.indexOf(res);",
+ " return index >= 0;",
+ "}"
+ ],
+ "tests": [
+ "assert(typeof solve24 === 'function', 'message: solve24
is a function.');",
+ "assert(include(answers[0], solve24(testCases[0])), 'message: solve24(\"4878\")
should return (7-8/8)*4
or 4*(7-8/8)
');",
+ "assert(include(answers[1], solve24(testCases[1])), 'message: solve24(\"1234\")
should return any arrangement of 1*2*3*4
');",
+ "assert(include(answers[2], solve24(testCases[2])), 'message: solve24(\"6789\")
should return (6*8)/(9-7)
or (8*6)/(9-7)
');",
+ "assert(include(answers[3], solve24(testCases[3])), 'message: solve24(\"1127\")
should return a permutation of (1+7)*(1*2)
');"
+ ],
+ "id": "5951e88f64ebf159166a1176",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "9 billion names of God the integer",
+ "type": "Waypoint",
+ "description": [
+ "This task is a variation of the short story by Arthur C. Clarke.
", + "(Solvers should be aware of the consequences of completing this task.)
", + "In detail, to specify what is meant by a “name”:
", + "The integer 1 has 1 name “1”.
", + "The integer 2 has 2 names “1+1”, and “2”.
", + "The integer 3 has 3 names “1+1+1”, “2+1”, and “3”.
", + "The integer 4 has 5 names “1+1+1+1”, “2+1+1”, “2+2”, “3+1”, “4”.
", + "The integer 5 has 7 names “1+1+1+1+1”, “2+1+1+1”, “2+2+1”, “3+1+1”, “3+2”, “4+1”, “5”.
", + "This can be visualized in the following form:
", + "", + " 1", + " 1 1", + " 1 1 1", + " 1 2 1 1", + " 1 2 2 1 1", + "1 3 3 2 1 1", + "", + "
Where row $n$ corresponds to integer $n$, and each column $C$ in row $m$ from left to right corresponds to the number of names beginning with $C$.
", + "Optionally note that the sum of the $n$-th row $P(n)$ is the integer partition function.
", + "Task", + "Implement a function that returns the sum of the $n$-th row.
" + ], + "challengeSeed": [ + "function numberOfNames (num) {", + " // Good luck!", + " return true;", + "}" + ], + "solutions": [ + "function numberOfNames (num) {\n const cache = [\n [1]\n ];\n for (let l = cache.length; l < num + 1; l++) {\n let Aa;\n let Mi;\n const r = [0];\n for (let x = 1; x < l + 1; x++) {\n r.push(r[r.length - 1] + (Aa = cache[l - x < 0 ? cache.length - (l - x) : l - x])[(Mi = Math.min(x, l - x)) < 0 ? Aa.length - Mi : Mi]);\n }\n cache.push(r);\n }\n return cache[num][cache[num].length - 1];\n}\n" + ], + "tests": [ + "assert(typeof numberOfNames === 'function', 'message:numberOfNames
is a function.');",
+ "assert.equal(numberOfNames(5), 7, 'message: numberOfNames(5)
should equal 7.');",
+ "assert.equal(numberOfNames(12), 77, 'message: numberOfNames(12)
should equal 77.');",
+ "assert.equal(numberOfNames(18), 385, 'message: numberOfNames(18)
should equal 385.');",
+ "assert.equal(numberOfNames(23), 1255, 'message: numberOfNames(23)
should equal 1255.');",
+ "assert.equal(numberOfNames(42), 53174, 'message: numberOfNames(42)
should equal 53174.');",
+ "assert.equal(numberOfNames(123), 2552338241, 'message: numberOfNames(123)
should equal 2552338241.');"
+ ],
+ "id": "5949b579404977fbaefcd736",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "ABC Problem",
+ "type": "Waypoint",
+ "description": [
+ "You are given a collection of ABC blocks (e.g., childhood alphabet blocks). There are 20 blocks with two letters on each block. A complete alphabet is guaranteed amongst all sides of the blocks. The sample collection of blocks:
", + "(B O)
", + "(X K)
", + "(D Q)
", + "(C P)
", + "(N A)
", + "(G T)
", + "(R E)
", + "(T G)
", + "(Q D)
", + "(F S)
", + "(J W)
", + "(H U)
", + "(V I)
", + "(A N)
", + "(O B)
", + "(E R)
", + "(F S)
", + "(L Y)
", + "(P C)
", + "(Z M)
", + "Some rules to keep in mind:
", + "Once a letter on a block is used, that block cannot be used again.", + "The function should be case-insensitive.", + "Implement a function that takes a string (word) and determines whether the word can be spelled with the given collection of blocks.
" + ], + "challengeSeed": [ + "function canMakeWord (word) {", + " // Good luck!", + "}" + ], + "solutions": [ + "function canMakeWord (word) {\n const characters = 'BO XK DQ CP NA GT RE TG QD FS JW HU VI AN OB ER FS LY PC ZM';\n const blocks = characters.split(' ').map(pair => pair.split(''));\n\n const letters = [...word.toUpperCase()];\n let length = letters.length;\n const copy = new Set(blocks);\n\n letters.forEach(letter => {\n for (let block of copy) {\n const index = block.indexOf(letter);\n\n if (index !== -1) {\n length--;\n copy.delete(block);\n break;\n }\n }\n });\n return !length;\n}\n" + ], + "tail": [ + "const words = ['bark', 'BooK', 'TReAT', 'COMMON', 'squAD', 'conFUSE'];" + ], + "tests": [ + "assert(typeof canMakeWord === 'function', 'message:canMakeWord
is a function.');",
+ "assert(typeof canMakeWord('hi') === 'boolean', 'message: canMakeWord
should return a boolean.');",
+ "assert(canMakeWord(words[0]), 'message: canMakeWord(\"bark\")
should return true.');",
+ "assert(!canMakeWord(words[1]), 'message: canMakeWord(\"BooK\")
should return false.');",
+ "assert(canMakeWord(words[2]), 'message: canMakeWord(\"TReAT\")
should return true.');",
+ "assert(!canMakeWord(words[3]), 'message: canMakeWord(\"COMMON\")
should return false.');",
+ "assert(canMakeWord(words[4]), 'message: canMakeWord(\"squAD\")
should return true.');",
+ "assert(canMakeWord(words[5]), 'message: canMakeWord(\"conFUSE\")
should return true.');"
+ ],
+ "id": "594810f028c0303b75339acc",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Abundant, deficient and perfect number classifications",
+ "type": "Waypoint",
+ "description": [
+ "These define three classifications of positive integers based on their proper divisors.
", + "Let $P(n)$ be the sum of the proper divisors of n where proper divisors are all positive integers n other than n itself.
", + "If P(n) < n
then n is classed as \"deficient\"
If P(n) === n
then n is classed as \"perfect\"
If P(n) > n
then n is classed as \"abundant\"
Example:
", + "6 has proper divisors of 1, 2, and 3.
", + "1 + 2 + 3 = 6, so 6 is classed as a perfect number.
", + "Implement a function that calculates how many of the integers from 1 to 20,000 (inclusive) are in each of the three classes. Output the result as an array in the following format [deficient, perfect, abundant]
.
getDPA
is a function.');",
+ "assert(Array.isArray(getDPA(100)), 'message: getDPA
should return an array.');",
+ "assert(getDPA(100).length === 3, 'message: getDPA
return value should have a length of 3.');",
+ "assert.deepEqual(getDPA(20000), solution, 'message: getDPA(20000)
should equal [15043, 4, 4953]');"
+ ],
+ "id": "594810f028c0303b75339acd",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Accumulator factory",
+ "type": "Waypoint",
+ "description": [
+ "Create a function that takes a single (numeric) argument and returns another function that is an accumulator. The returned accumulator function in turn also takes a single numeric argument, and returns the sum of all the numeric values passed in so far to that accumulator (including the initial value passed when the accumulator was created).
", + "Rules:
", + "Do not use global variables.
", + "Hint:
", + "Closures save outer state.
" + ], + "challengeSeed": [ + "function accumulator (sum) {", + " // Good luck!", + "}" + ], + "solutions": [ + "function accumulator (sum) {\n return function (n) {\n return sum += n;\n };\n}\n" + ], + "tail": [ + "const testFn = typeof accumulator(3) === 'function' && accumulator(3);", + "if (testFn) {", + " testFn(-4);", + " testFn(1.5);", + "}" + ], + "tests": [ + "assert(typeof accumulator === 'function', 'message:accumulator
is a function.');",
+ "assert(typeof accumulator(0) === 'function', 'message: accumulator(0)
should return a function.');",
+ "assert(typeof accumulator(0)(2) === 'number', 'message: accumulator(0)(2)
should return a number.');",
+ "assert(testFn(5) === 5.5, 'message: Passing in the values 3, -4, 1.5, and 5 should return 5.5.');"
+ ],
+ "id": "594810f028c0303b75339ace",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Ackermann function",
+ "type": "Waypoint",
+ "description": [
+ "The Ackermann function is a classic example of a recursive function, notable especially because it is not a primitive recursive function. It grows very quickly in value, as does the size of its call tree.
", + "The Ackermann function is usually defined as follows:
", + "$$A(m, n) =", + " \\begin{cases}", + " n+1 & \\mbox{if } m = 0 \\\\", + " A(m-1, 1) & \\mbox{if } m > 0 \\mbox{ and } n = 0 \\\\", + " A(m-1, A(m, n-1)) & \\mbox{if } m > 0 \\mbox{ and } n > 0.", + " \\end{cases}$$", + "Its arguments are never negative and it always terminates. Write a function which returns the value of $A(m, n)$. Arbitrary precision is preferred (since the function grows so quickly), but not required.
" + ], + "challengeSeed": [ + "function ack (m, n) {", + " // Good luck!", + "}" + ], + "solutions": [ + "function ack (m, n) {\n return m === 0 ? n + 1 : ack(m - 1, n === 0 ? 1 : ack(m, n - 1));\n}\n" + ], + "tests": [ + "assert(typeof ack === 'function', 'message:ack
is a function.');",
+ "assert(ack(0, 0) === 1, 'message: ack(0, 0)
should return 1.');",
+ "assert(ack(1, 1) === 3, 'message: ack(1, 1)
should return 3.');",
+ "assert(ack(2, 5) === 13, 'message: ack(2, 5)
should return 13.');",
+ "assert(ack(3, 3) === 61, 'message: ack(3, 3)
should return 61.');"
+ ],
+ "id": "594810f028c0303b75339acf",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Align columns",
+ "type": "Waypoint",
+ "description": [
+ "Given a text file of many lines, where fields within a line are delineated by a single $
character, write a program that aligns each column of fields by ensuring that words in each column are separated by at least one space. Further, allow for each word in a column to be either left justified, right justified, or center justified within its column.
Use the following text to test your programs:
", + "", + "Given$a$text$file$of$many$lines", + "where$fields$within$a$line$", + "are$delineated$by$a$single$'dollar'$character", + "write$a$program", + "that$aligns$each$column$of$fields", + "by$ensuring$that$words$in$each$", + "column$are$separated$by$at$least$one$space.", + "Further,$allow$for$each$word$in$a$column$to$be$either$left$", + "justified,$right$justified", + "or$center$justified$within$its$column.", + "", + "
Note that:
", + "The example input texts lines may, or may not, have trailing dollar characters.", + "All columns should share the same alignment.", + "Consecutive space characters produced adjacent to the end of lines are insignificant for the purposes of the task.", + "Output text will be viewed in a mono-spaced font on a plain text editor or basic terminal.", + "The minimum space between columns should be computed from the text and not hard-coded.", + "It is not a requirement to add separating characters between or around columns." + ], + "challengeSeed": [ + "const testArr = [", + " 'Given$a$text$file$of$many$lines',", + " 'where$fields$within$a$line$',", + " 'are$delineated$by$a$single$\"dollar\"$character',", + " 'write$a$program',", + " 'that$aligns$each$column$of$fields$',", + " 'by$ensuring$that$words$in$each$',", + " 'column$are$separated$by$at$least$one$space.',", + " 'Further,$allow$for$each$word$in$a$column$to$be$either$left$',", + " 'justified,$right$justified',", + " 'or$center$justified$within$its$column.'", + "];", + "", + "function formatText (input, justification) {", + " // Good luck!", + "}" + ], + "solutions": [ + "const testArr = [\n 'Given$a$text$file$of$many$lines',\n 'where$fields$within$a$line$',\n 'are$delineated$by$a$single$\"dollar\"$character',\n 'write$a$program',\n 'that$aligns$each$column$of$fields$',\n 'by$ensuring$that$words$in$each$',\n 'column$are$separated$by$at$least$one$space.',\n 'Further,$allow$for$each$word$in$a$column$to$be$either$left$',\n 'justified,$right$justified',\n 'or$center$justified$within$its$column.'\n];\n\nString.prototype.repeat = function (n) { return new Array(1 + parseInt(n)).join(this); };\n\nfunction formatText (input, justification) {\n let x, y, max, cols = 0, diff, left, right;\n for (x = 0; x < input.length; x++) {\n input[x] = input[x].split('$');\n if (input[x].length > cols) {\n cols = input[x].length;\n }\n }\n for (x = 0; x < cols; x++) {\n max = 0;\n for (y = 0; y < input.length; y++) {\n if (input[y][x] && max < input[y][x].length) {\n max = input[y][x].length;\n }\n }\n for (y = 0; y < input.length; y++) {\n if (input[y][x]) {\n diff = (max - input[y][x].length) / 2;\n left = ' '.repeat(Math.floor(diff));\n right = ' '.repeat(Math.ceil(diff));\n if (justification === 'left') {\n right += left; left = '';\n }\n if (justification === 'right') {\n left += right; right = '';\n }\n input[y][x] = left + input[y][x] + right;\n }\n }\n }\n for (x = 0; x < input.length; x++) {\n input[x] = input[x].join(' ');\n }\n input = input.join('\\n');\n return input;\n}\n" + ], + "tail": [ + "const testInput = [", + " 'Given$a$text$file$of$many$lines',", + " 'where$fields$within$a$line$',", + " 'are$delineated$by$a$single$\"dollar\"$character',", + " 'write$a$program',", + " 'that$aligns$each$column$of$fields$',", + " 'by$ensuring$that$words$in$each$',", + " 'column$are$separated$by$at$least$one$space.',", + " 'Further,$allow$for$each$word$in$a$column$to$be$either$left$',", + " 'justified,$right$justified',", + " 'or$center$justified$within$its$column.'", + "];", + "", + "const rightAligned = ` Given a text file of many lines", + " where fields within a line ", + " are delineated by a single \"dollar\" character", + " write a program", + " that aligns each column of fields ", + " by ensuring that words in each ", + " column are separated by at least one space.", + " Further, allow for each word in a column to be either left ", + "justified, right justified", + " or center justified within its column.`;", + "", + "const leftAligned = `Given a text file of many lines ", + "where fields within a line ", + "are delineated by a single \"dollar\" character", + "write a program ", + "that aligns each column of fields ", + "by ensuring that words in each ", + "column are separated by at least one space.", + "Further, allow for each word in a column to be either left ", + "justified, right justified", + "or center justified within its column. `;", + "", + "const centerAligned = ` Given a text file of many lines ", + " where fields within a line ", + " are delineated by a single \"dollar\" character", + " write a program ", + " that aligns each column of fields ", + " by ensuring that words in each ", + " column are separated by at least one space.", + " Further, allow for each word in a column to be either left ", + "justified, right justified", + " or center justified within its column. `;" + ], + "tests": [ + "assert(typeof formatText === 'function', 'message:formatText
is a function.');",
+ "assert.strictEqual(formatText(testInput, 'right'), rightAligned, 'message: formatText
with the above input and \"right\" justification should produce the following: ');",
+ "assert.strictEqual(formatText(testInput, 'left'), leftAligned, 'message: formatText
with the above input and \"left\" justification should produce the following: ');",
+ "assert.strictEqual(formatText(testInput, 'center'), centerAligned, 'message: formatText
with the above input and \"center\" justification should produce the following: ');"
+ ],
+ "id": "594810f028c0303b75339ad0",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Amicable pairs",
+ "type": "Waypoint",
+ "description": [
+ "Two integers $N$ and $M$ are said to be amicable pairs if $N \\neq M$ and the sum of the proper divisors of $N$ ($\\mathrm{sum}(\\mathrm{propDivs}(N))$) $= M$ as well as $\\mathrm{sum}(\\mathrm{propDivs}(M)) = N$.",
+ "Example:",
+ "1184 and 1210 are an amicable pair, with proper divisors:",
+ " 1, 2, 4, 8, 16, 32, 37, 74, 148, 296, 592 and ",
+ " 1, 2, 5, 10, 11, 22, 55, 110, 121, 242, 605 respectively.",
+ "Task:",
+ "Calculate and show here the Amicable pairs below 20,000 (there are eight).",
+ "Related tasks",
+ "Proper divisors",
+ "Abundant, deficient and perfect number classifications",
+ "Aliquot sequence classifications and its amicable classification."
+ ],
+ "challengeSeed": [
+ "function amicablePairsUpTo (maxNum) {",
+ " // Good luck!",
+ " return true;",
+ "}"
+ ],
+ "solutions": [
+ "// amicablePairsUpTo :: Int -> [(Int, Int)]\nfunction amicablePairsUpTo (maxNum) {\n return range(1, maxNum)\n .map(x => properDivisors(x)\n .reduce((a, b) => a + b, 0))\n .reduce((a, m, i, lst) => {\n const n = i + 1;\n\n return (m > n) && lst[m - 1] === n ?\n a.concat([\n [n, m]\n ]) : a;\n }, []);\n}\n\n// properDivisors :: Int -> [Int]\nfunction properDivisors (n) {\n if (n < 2) return [];\n\n const rRoot = Math.sqrt(n);\n const intRoot = Math.floor(rRoot);\n const blnPerfectSquare = rRoot === intRoot;\n const lows = range(1, intRoot)\n .filter(x => (n % x) === 0);\n\n return lows.concat(lows.slice(1)\n .map(x => n / x)\n .reverse()\n .slice(blnPerfectSquare | 0));\n}\n\n// Int -> Int -> Maybe Int -> [Int]\nfunction range (m, n, step) {\n const d = (step || 1) * (n >= m ? 1 : -1);\n\n return Array.from({\n length: Math.floor((n - m) / d) + 1\n }, (_, i) => m + (i * d));\n}\n"
+ ],
+ "tail": [
+ "const answer300 = [[220, 284]];",
+ "const answer3000 = [",
+ " [220, 284],",
+ " [1184, 1210],",
+ " [2620, 2924]",
+ "];",
+ "const answer20000 = [",
+ " [220, 284],",
+ " [1184, 1210],",
+ " [2620, 2924],",
+ " [5020, 5564],",
+ " [6232, 6368],",
+ " [10744, 10856],",
+ " [12285, 14595],",
+ " [17296, 18416]",
+ "];"
+ ],
+ "tests": [
+ "assert(typeof amicablePairsUpTo === 'function', 'message: amicablePairsUpTo
is a function.');",
+ "assert.deepEqual(amicablePairsUpTo(300), answer300, 'message: amicablePairsUpTo(300)
should return [[220,284]]
.');",
+ "assert.deepEqual(amicablePairsUpTo(3000), answer3000, 'message: amicablePairsUpTo(3000)
should return [[220,284],[1184,1210],[2620,2924]]
.');",
+ "assert.deepEqual(amicablePairsUpTo(20000), answer20000, 'message: amicablePairsUpTo(20000)
should return [[220,284],[1184,1210],[2620,2924],[5020,5564],[6232,6368],[10744,10856],[12285,14595],[17296,18416]]
.');"
+ ],
+ "id": "5949b579404977fbaefcd737",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Averages/Mode",
+ "type": "Waypoint",
+ "description": [
+ "Write a program to find the mode value of a collection.
The case where the collection is empty may be ignored. Care must be taken to handle the case where the mode is non-unique.
If it is not appropriate or possible to support a general collection, use a vector (array), if possible. If it is not appropriate or possible to support an unspecified value type, use integers.
" + ], + "challengeSeed": [ + "function mode (arr) {", + " // Good luck!", + " return true;", + "}" + ], + "solutions": [ + "function mode(arr) {\n const counter = {};\n let result = [];\n let max = 0;\n // for (const i in arr) {\n arr.forEach(el => {\n if (!(el in counter)) {\n counter[el] = 0;\n }\n counter[el]++;\n\n if (counter[el] === max) {\n result.push(el);\n }\n else if (counter[el] > max) {\n max = counter[el];\n result = [el];\n }\n });\n return result;\n}\n" + ], + "tail": [ + "const arr1 = [1, 3, 6, 6, 6, 6, 7, 7, 12, 12, 17];", + "const arr2 = [1, 2, 4, 4, 1];" + ], + "tests": [ + "assert(typeof mode === 'function', 'message:mode
is a function.');",
+ "assert.deepEqual(mode(arr1), [6], 'message: mode([1, 3, 6, 6, 6, 6, 7, 7, 12, 12, 17])
should equal [6]
');",
+ "assert.deepEqual(mode(arr2).sort(), [1, 4], 'message mode([1, 2, 4, 4, 1])
should equal [1, 4]
.');"
+ ],
+ "id": "594d8d0ab97724821379b1e6",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Averages/Pythagorean means",
+ "type": "Waypoint",
+ "description": [
+ "Compute all three of the Pythagorean means of the set of integers 1 through 10 (inclusive).
Show that $A(x_1,\\ldots,x_n) \\geq G(x_1,\\ldots,x_n) \\geq H(x_1,\\ldots,x_n)$ for this set of positive integers.
The most common of the three means, the arithmetic mean, is the sum of the list divided by its length: $ A(x_1, \\ldots, x_n) = \\frac{x_1 + \\cdots + x_n}{n}$The geometric mean is the $n$th root of the product of the list: $ G(x_1, \\ldots, x_n) = \\sqrt[n]{x_1 \\cdots x_n} $The harmonic mean is $n$ divided by the sum of the reciprocal of each item in the list: $ H(x_1, \\ldots, x_n) = \\frac{n}{\\frac{1}{x_1} + \\cdots + \\frac{1}{x_n}} $", + "Assume the input is an ordered array of all inclusive numbers.
", + "For the answer, please output an object in the following format:
", + "", + "{", + " values: {", + " Arithmetic: 5.5,", + " Geometric: 4.528728688116765,", + " Harmonic: 3.414171521474055", + " },", + " test: 'is A >= G >= H ? yes'", + "}", + "" + ], + "challengeSeed": [ + "function pythagoreanMeans (rangeArr) {", + " // Good luck!", + "}" + ], + "solutions": [ + "function pythagoreanMeans (rangeArr) {\n // arithmeticMean :: [Number] -> Number\n const arithmeticMean = xs =>\n foldl((sum, n) => sum + n, 0, xs) / length(xs);\n\n // geometricMean :: [Number] -> Number\n const geometricMean = xs =>\n raise(foldl((product, x) => product * x, 1, xs), 1 / length(xs));\n\n // harmonicMean :: [Number] -> Number\n const harmonicMean = xs =>\n length(xs) / foldl((invSum, n) => invSum + (1 / n), 0, xs);\n\n // GENERIC FUNCTIONS ------------------------------------------------------\n\n // A list of functions applied to a list of arguments\n // <*> :: [(a -> b)] -> [a] -> [b]\n const ap = (fs, xs) => //\n Array.prototype.concat(...fs.map(f => //\n Array.prototype.concat(...xs.map(x => [f(x)]))));\n\n // foldl :: (b -> a -> b) -> b -> [a] -> b\n const foldl = (f, a, xs) => xs.reduce(f, a);\n\n // length :: [a] -> Int\n const length = xs => xs.length;\n\n // mapFromList :: [(k, v)] -> Dictionary\n const mapFromList = kvs =>\n foldl((a, [k, v]) =>\n (a[(typeof k === 'string' && k)] = v, a), {}, kvs);\n\n // raise :: Num -> Int -> Num\n const raise = (n, e) => Math.pow(n, e);\n/*\n // show :: a -> String\n // show :: a -> Int -> String\n const show = (...x) =>\n JSON.stringify.apply(\n null, x.length > 1 ? [x[0], null, x[1]] : x\n );\n*/\n // zip :: [a] -> [b] -> [(a,b)]\n const zip = (xs, ys) =>\n xs.slice(0, Math.min(xs.length, ys.length))\n .map((x, i) => [x, ys[i]]);\n\n // TEST -------------------------------------------------------------------\n // mean :: Dictionary\n const mean = mapFromList(zip(\n ['Arithmetic', 'Geometric', 'Harmonic'],\n ap([arithmeticMean, geometricMean, harmonicMean], [\n rangeArr\n ])\n ));\n\n return {\n values: mean,\n test: `is A >= G >= H ? ${mean.Arithmetic >= mean.Geometric &&\n mean.Geometric >= mean.Harmonic ? 'yes' : 'no'}`\n };\n}\n" + ], + "tail": [ + "const range1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];", + "const answer1 = {", + " values: {", + " Arithmetic: 5.5,", + " Geometric: 4.528728688116765,", + " Harmonic: 3.414171521474055", + " },", + " test: 'is A >= G >= H ? yes'", + "};", + "" + ], + "tests": [ + "assert(typeof pythagoreanMeans === 'function', 'message:
pythagoreanMeans
is a function.');",
+ "assert.deepEqual(pythagoreanMeans(range1), answer1, 'message: pythagoreanMeans([1, 2, ..., 10])
should equal the same output above.');"
+ ],
+ "id": "594d966a1467eb84194f0086",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Averages/Root mean square",
+ "type": "Waypoint",
+ "description": [
+ "Compute the Root mean square of the numbers 1 through 10 inclusive.
", + "The root mean square is also known by its initials RMS (or rms), and as the quadratic mean.
The RMS is calculated as the mean of the squares of the numbers, square-rooted:
", + "$$x_{\\mathrm{rms}} = \\sqrt {{{x_1}^2 + {x_2}^2 + \\cdots + {x_n}^2} \\over n}. $$
" + ], + "challengeSeed": [ + "function rms (arr) {", + " // Good luck!", + "}" + ], + "solutions": [ + "function rms (arr) {\n const sumOfSquares = arr.reduce((s, x) => s + x * x, 0);\n return Math.sqrt(sumOfSquares / arr.length);\n}\n" + ], + "tail": [ + "const arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];", + "const answer1 = 6.2048368229954285;" + ], + "tests": [ + "assert(typeof rms === 'function', 'message:rms
is a function.');",
+ "assert.equal(rms(arr1), answer1, 'message: rms([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
should equal 6.2048368229954285
.');"
+ ],
+ "id": "594da033de4190850b893874",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Babbage problem",
+ "type": "Waypoint",
+ "description": [
+ "Charles Babbage, looking ahead to the sorts of problems his Analytical Engine would be able to solve, gave this example:
", + "What is the smallest positive integer whose square ends in the digits 269,696?", + "
- Babbage, letter to Lord Bowden, 1837; see Hollingdale and Tootill, Electronic Computers, second edition, 1970, p. 125.
", + "He thought the answer might be 99,736, whose square is 9,947,269,696; but he couldn't be certain.
", + "The task is to find out if Babbage had the right answer.
", + "Implement a function to return the lowest integer that satisfies the Babbage problem. If Babbage was right, return Babbage's number.
" + ], + "challengeSeed": [ + "function babbage (babbageNum, endDigits) {", + " // Good luck!", + " return true;", + "}" + ], + "solutions": [ + "function babbage (babbageAns, endDigits) {\n const babbageNum = Math.pow(babbageAns, 2);\n const babbageStartDigits = parseInt(babbageNum.toString().replace('269696', ''));\n let answer = 99736;\n\n // count down from this answer and save any sqrt int result. return lowest one\n for (let i = babbageStartDigits; i >= 0; i--) {\n const num = parseInt(i.toString().concat('269696'));\n const result = Math.sqrt(num);\n if (result === Math.floor(Math.sqrt(num))) {\n answer = result;\n }\n }\n\n return answer;\n}\n" + ], + "tail": [ + "const babbageAns = 99736;", + "const endDigits = 269696;", + "const answer = 25264;" + ], + "tests": [ + "assert(typeof babbage === 'function', 'message:babbage
is a function.');",
+ "assert.equal(babbage(babbageAns, endDigits), answer, 'message: babbage(99736, 269696)
should not return 99736 (there is a smaller answer).');"
+ ],
+ "id": "594db4d0dedb4c06a2a4cefd",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Balanced brackets",
+ "type": "Waypoint",
+ "description": [
+ "Determine whether a generated string of brackets is balanced; that is, whether it consists entirely of pairs of opening/closing brackets (in that order), none of which mis-nest.
", + "Examples:", + "(empty) true
", + "[]
true
][
false
[][]
true
][][
false
[]][[]
false
[[[[]]]]
true
isBalanced
is a function.');",
+ "assert(isBalanced(testCases[0]), 'message: isBalanced(\"[]\")
should return true.');",
+ "assert(!isBalanced(testCases[1]), 'message: isBalanced(\"]][[[][][][]][\")
should return false.');",
+ "assert(isBalanced(testCases[2]), 'message: isBalanced(\"[][[[[][][[[]]]]]]\")
should return true.');",
+ "assert(!isBalanced(testCases[3]), 'message: isBalanced(\"][\")
should return true.');",
+ "assert(!isBalanced(testCases[4]), 'message: isBalanced(\"[[[]]]][[]\")
should return true.');",
+ "assert(!isBalanced(testCases[5]), 'message: isBalanced(\"][[]\")
should return true.');",
+ "assert(!isBalanced(testCases[6]), 'message: isBalanced(\"][[][]][[[]]\")
should return true.');",
+ "assert(!isBalanced(testCases[7]), 'message: isBalanced(\"[[][]]][\")
should return true.');",
+ "assert(!isBalanced(testCases[8]), 'message: isBalanced(\"[[[]]][[]]]][][[\")
should return true.');",
+ "assert(!isBalanced(testCases[9]), 'message: isBalanced(\"[]][[]]][[[[][]]\")
should return true.');",
+ "assert(!isBalanced(testCases[10]), 'message: isBalanced(\"][]][[][\")
should return true.');",
+ "assert(isBalanced(testCases[11]), 'message: isBalanced(\"[[]][[][]]\")
should return true.');",
+ "assert(isBalanced(testCases[12]), 'message: isBalanced(\"[[]]\")
should return true.');",
+ "assert(!isBalanced(testCases[13]), 'message: isBalanced(\"]][]][[]][[[\")
should return true.');",
+ "assert(!isBalanced(testCases[14]), 'message: isBalanced(\"][]][][[\")
should return true.');",
+ "assert(!isBalanced(testCases[15]), 'message: isBalanced(\"][][\")
should return true.');",
+ "assert(!isBalanced(testCases[16]), 'message: isBalanced(\"[[]]][][][[]][\")
should return true.');",
+ "assert(isBalanced(testCases[17]), 'message: isBalanced(\"\")
should return true.');"
+ ],
+ "id": "594dc6c729e5700999302b45",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Circles of given radius through two points",
+ "type": "Waypoint",
+ "description": [
+ "Given two points on a plane and a radius, usually two circles of given radius can be drawn through the points.
", + "Exceptions:", + "A radius of zero should be treated as never describing circles (except in the case where the points are coincident).", + "If the points are coincident then an infinite number of circles with the point on their circumference can be drawn, unless the radius is equal to zero as well which then collapses the circles to a point.", + "If the points form a diameter then return a single circle.", + "If the points are too far apart then no circles can be drawn.Task:", + "Implement a function that takes two points and a radius and returns the two circles through those points. For each resulting circle, provide the coordinates for the center of each circle rounded to four decimal digits. Return each coordinate as an array, and coordinates as an array of arrays.", + "For edge cases, return the following:", + "If points are on the diameter, return one point. If the radius is also zero however, return\"Radius Zero\"
.",
+ "If points are coincident, return \"Coincident point. Infinite solutions\"
.",
+ "If points are farther apart than the diameter, return \"No intersection. Points further apart than circle diameter\"
.",
+ "Sample inputs:",
+ "", + " p1 p2 r", + "0.1234, 0.9876 0.8765, 0.2345 2.0", + "0.0000, 2.0000 0.0000, 0.0000 1.0", + "0.1234, 0.9876 0.1234, 0.9876 2.0", + "0.1234, 0.9876 0.8765, 0.2345 0.5", + "0.1234, 0.9876 0.1234, 0.9876 0.0", + "", + "Ref:", + "Finding the Center of a Circle from 2 Points and Radius from Math forum @ Drexel" + ], + "challengeSeed": [ + "function getCircles (...args) {", + " // Good luck!", + " return true;", + "}" + ], + "solutions": [ + "const hDist = (p1, p2) => Math.hypot(...p1.map((e, i) => e - p2[i])) / 2;\nconst pAng = (p1, p2) => Math.atan(p1.map((e, i) => e - p2[i]).reduce((p, c) => c / p, 1));\nconst solveF = (p, r) => t => [parseFloat((r * Math.cos(t) + p[0]).toFixed(4)), parseFloat((r * Math.sin(t) + p[1]).toFixed(4))];\nconst diamPoints = (p1, p2) => p1.map((e, i) => parseFloat((e + (p2[i] - e) / 2).toFixed(4)));\n\nfunction getCircles (...args) {\n const [p1, p2, s] = args;\n const solve = solveF(p1, s);\n const halfDist = hDist(p1, p2);\n\n let msg = [];\n switch (Math.sign(s - halfDist)) {\n case 0:\n msg = s ? diamPoints(p1, p2) :\n 'Radius Zero';\n break;\n case 1:\n if (!halfDist) {\n msg = 'Coincident point. Infinite solutions';\n }\n else {\n const theta = pAng(p1, p2);\n const theta2 = Math.acos(halfDist / s);\n [1, -1].map(e => solve(theta + e * theta2)).forEach(\n e => msg.push(e));\n }\n break;\n case -1:\n msg = 'No intersection. Points further apart than circle diameter';\n break;\n default:\n msg = 'Reached the default';\n }\n return msg;\n}\n" + ], + "tail": [ + "const testCases = [", + " [[0.1234, 0.9876], [0.8765, 0.2345], 2.0],", + " [[0.0000, 2.0000], [0.0000, 0.0000], 1.0],", + " [[0.1234, 0.9876], [0.1234, 0.9876], 2.0],", + " [[0.1234, 0.9876], [0.8765, 0.2345], 0.5],", + " [[0.1234, 0.9876], [0.1234, 0.9876], 0.0]", + "];", + "const answers = [", + " [[1.8631, 1.9742], [-0.8632, -0.7521]],", + " [0, 1],", + " 'Coincident point. Infinite solutions',", + " 'No intersection. Points further apart than circle diameter',", + " 'Radius Zero'", + "];" + ], + "tests": [ + "assert(typeof getCircles === 'function', 'message:
getCircles
is a function.');",
+ "assert.deepEqual(getCircles(...testCases[0]), answers[0], 'message: getCircles([0.1234, 0.9876], [0.8765, 0.2345], 2.0)
should return [[1.8631, 1.9742], [-0.8632, -0.7521]]
.');",
+ "assert.deepEqual(getCircles(...testCases[1]), answers[1], 'message: getCircles([0.0000, 2.0000], [0.0000, 0.0000], 1.0)
should return [0, 1]
');",
+ "assert.deepEqual(getCircles(...testCases[2]), answers[2], 'message: getCircles([0.1234, 0.9876], [0.1234, 0.9876], 2.0)
should return Coincident point. Infinite solutions
');",
+ "assert.deepEqual(getCircles(...testCases[3]), answers[3], 'message: getCircles([0.1234, 0.9876], [0.8765, 0.2345], 0.5)
should return No intersection. Points further apart than circle diameter
');",
+ "assert.deepEqual(getCircles(...testCases[4]), answers[4], 'message: getCircles([0.1234, 0.9876], [0.1234, 0.9876], 0.0)
should return Radius Zero
');"
+ ],
+ "id": "5951815dd895584b06884620",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Closest-pair problem",
+ "type": "Waypoint",
+ "description": [
+ "Task:",
+ "Provide a function to find the closest two points among a set of given points in two dimensions, i.e. to solve the Closest pair of points problem in the planar case.
The straightforward solution is a O(n2) algorithm (which we can call brute-force algorithm); the pseudo-code (using indexes) could be simply:
", + "", + "bruteForceClosestPair of P(1), P(2), ... P(N)", + "if N < 2 then", + " return ∞", + "else", + " minDistance ← |P(1) - P(2)|", + " minPoints ← { P(1), P(2) }", + " foreach i ∈ [1, N-1]", + " foreach j ∈ [i+1, N]", + " if |P(i) - P(j)| < minDistance then", + " minDistance ← |P(i) - P(j)|", + " minPoints ← { P(i), P(j) }", + " endif", + " endfor", + " endfor", + " return minDistance, minPoints", + "endif", + "", + "
A better algorithm is based on the recursive divide&conquer approach, as explained also at Wikipedia's Closest pair of points problem, which is O(n log n); a pseudo-code could be:
", + "", + "closestPair of (xP, yP)", + " where xP is P(1) .. P(N) sorted by x coordinate, and", + " yP is P(1) .. P(N) sorted by y coordinate (ascending order)", + "if N ≤ 3 then", + " return closest points of xP using brute-force algorithm", + "else", + " xL ← points of xP from 1 to ⌈N/2⌉", + " xR ← points of xP from ⌈N/2⌉+1 to N", + " xm ← xP(⌈N/2⌉)x", + " yL ← { p ∈ yP : px ≤ xm }", + " yR ← { p ∈ yP : px > xm }", + " (dL, pairL) ← closestPair of (xL, yL)", + " (dR, pairR) ← closestPair of (xR, yR)", + " (dmin, pairMin) ← (dR, pairR)", + " if dL < dR then", + " (dmin, pairMin) ← (dL, pairL)", + " endif", + " yS ← { p ∈ yP : |xm - px| < dmin }", + " nS ← number of points in yS", + " (closest, closestPair) ← (dmin, pairMin)", + " for i from 1 to nS - 1", + " k ← i + 1", + " while k ≤ nS and yS(k)y - yS(i)y < dmin", + " if |yS(k) - yS(i)| < closest then", + " (closest, closestPair) ← (|yS(k) - yS(i)|, {yS(k), yS(i)})", + " endif", + " k ← k + 1", + " endwhile", + " endfor", + " return closest, closestPair", + "endif", + "", + "References and further readings:", + " Closest pair of points problem", + " Closest Pair (McGill)", + " Closest Pair (UCSB)", + " Closest pair (WUStL)", + " Closest pair (IUPUI) ", + "
For the input, expect the argument to be an array of objects (points) with x
and y
members set to numbers. For the output, return an object containing the key:value pairs for distance
and pair
(i.e., the pair of two closest points).
getClosestPair
is a function.');",
+ "assert.equal(getClosestPair(points1).distance, answer1.distance, 'message: Distance should be the following.');",
+ "assert.deepEqual(JSON.parse(JSON.stringify(getClosestPair(points1))).pair, answer1.pair, 'message: Points should be the following.');",
+ "assert.equal(getClosestPair(points2).distance, answer2.distance, 'message: Distance should be the following.');",
+ "assert.deepEqual(JSON.parse(JSON.stringify(getClosestPair(points2))).pair, answer2.pair, 'message: Points should be the following.');"
+ ],
+ "id": "5951a53863c8a34f02bf1bdc",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Combinations",
+ "type": "Waypoint",
+ "description": [
+ "Task:",
+ "Given non-negative integers m and n, generate all size m combinations of the integers from 0 (zero) to n-1 in sorted order (each combination is sorted and the entire table is sorted).
", + "Example:", + "3 comb 5 is:
", + "", + "0 1 2", + "0 1 3", + "0 1 4", + "0 2 3", + "0 2 4", + "0 3 4", + "1 2 3", + "1 2 4", + "1 3 4", + "2 3 4", + "" + ], + "challengeSeed": [ + "function combinations (m, n) {", + " // Good luck!", + " return true;", + "}" + ], + "solutions": [ + "function combinations (m, n) {\n const nArr = [...Array(n).keys()];\n\n return (function generateCombinations (size, numArr) {\n const ret = [];\n\n for (let i = 0; i < numArr.length; i++) {\n if (size === 1) {\n ret.push([numArr[i]]);\n }\n else {\n const sub = generateCombinations(size - 1, numArr.slice(i + 1, numArr.length));\n for (let subI = 0; subI < sub.length; subI++) {\n const next = sub[subI];\n next.unshift(numArr[i]);\n ret.push(next);\n }\n }\n }\n return ret;\n }(m, nArr));\n}\n" + ], + "tail": [ + "const testInput1 = [3, 5];", + "const testOutput1 = [[0, 1, 2], [0, 1, 3], [0, 1, 4], [0, 2, 3], [0, 2, 4], [0, 3, 4], [1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]];", + "", + "const testInput2 = [4, 6];", + "const testOutput2 = [[0, 1, 2, 3], [0, 1, 2, 4], [0, 1, 2, 5], [0, 1, 3, 4], [0, 1, 3, 5], [0, 1, 4, 5], [0, 2, 3, 4], [0, 2, 3, 5], [0, 2, 4, 5], [0, 3, 4, 5], [1, 2, 3, 4], [1, 2, 3, 5], [1, 2, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5]];" + ], + "tests": [ + "assert(typeof combinations === 'function', 'message:
combinations
is a function.');",
+ "assert.deepEqual(combinations(testInput1[0], testInput1[1]), testOutput1, 'message: combinations(3, 5)
should return [[0, 1, 2], [0, 1, 3], [0, 1, 4], [0, 2, 3], [0, 2, 4], [0, 3, 4], [1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]
.');",
+ "assert.deepEqual(combinations(testInput2[0], testInput2[1]), testOutput2, 'message: combinations(4, 6)
should return [[0,1,2,3], [0,1,2,4], [0,1,2,5], [0,1,3,4], [0,1,3,5], [0,1,4,5], [0,2,3,4], [0,2,3,5], [0,2,4,5], [0,3,4,5], [1,2,3,4], [1,2,3,5], [1,2,4,5], [1,3,4,5], [2,3,4,5]]
');"
+ ],
+ "id": "5958469238c0d8d2632f46db",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Comma quibbling",
+ "type": "Waypoint",
+ "description": [
+ "Comma quibbling is a task originally set by Eric Lippert in his blog.
", + "Task:Write a function to generate a string output which is the concatenation of input words from a list/sequence where:
", + "An input of no words produces the output string of just the two brace characters \"{}\".", + "An input of just one word, e.g. [\"ABC\"], produces the output string of the word inside the two braces, e.g. \"{ABC}\".", + "An input of two words, e.g. [\"ABC\", \"DEF\"], produces the output string of the two words inside the two braces with the words separated by the string \" and \", e.g. \"{ABC and DEF}\".", + "An input of three or more words, e.g. [\"ABC\", \"DEF\", \"G\", \"H\"], produces the output string of all but the last word separated by \", \" with the last word separated by \" and \" and all within braces; e.g. \"{ABC, DEF, G and H}\".", + "Test your function with the following series of inputs showing your output here on this page:
", + "[] # (No input words).", + "[\"ABC\"]", + "[\"ABC\", \"DEF\"]", + "[\"ABC\", \"DEF\", \"G\", \"H\"]", + "Note: Assume words are non-empty strings of uppercase characters for this task.
" + ], + "challengeSeed": [ + "function quibble (words) {", + " // Good luck!", + " return true;", + "}" + ], + "solutions": [ + "function quibble (words) {\n return \"{\" +\n words.slice(0, words.length - 1).join(\",\") +\n (words.length > 1 ? \" and \" : \"\") +\n (words[words.length - 1] || '') +\n \"}\";\n}\n" + ], + "tail": [ + "const testCases = [[], [\"ABC\"], [\"ABC\", \"DEF\"], [\"ABC\", \"DEF\", \"G\", \"H\"]];", + "const results = [\"{}\", \"{ABC}\", \"{ABC and DEF}\", \"{ABC,DEF,G and H}\"];" + ], + "tests": [ + "assert(typeof quibble === 'function', 'message:quibble
is a function.');",
+ "assert(typeof quibble([\"ABC\"]) === 'string', 'message: quibble([\"ABC\"])
should return a string.');",
+ "assert.equal(quibble(testCases[0]), results[0], 'message: quibble([])
should return \"{}\".');",
+ "assert.equal(quibble(testCases[1]), results[1], 'message: quibble([\"ABC\"])
should return \"{ABC}\".');",
+ "assert.equal(quibble(testCases[2]), results[2], 'message: quibble([\"ABC\", \"DEF\"])
should return \"{ABC and DEF}\".');",
+ "assert.equal(quibble(testCases[3]), results[3], 'message: quibble([\"ABC\", \"DEF\", \"G\", \"H\"])
should return \"{ABC,DEF,G and H}\".');"
+ ],
+ "id": "596e414344c3b2872167f0fe",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Compare a list of strings",
+ "type": "Waypoint",
+ "description": [
+ "Given a list of arbitrarily many strings, implement a function for each of the following conditions:
test if they are all lexically equal", + " test if every string is lexically less than the one after it (i.e. whether the list is in strict ascending order)" + ], + "challengeSeed": [ + "function allEqual (arr) {", + " // Good luck!", + " return true;", + "}", + "", + "function azSorted (arr) {", + " // Good luck!", + " return true;", + "}" + ], + "solutions": [ + "function allEqual(a) {\n let out = true;\n let i = 0;\n while (++i < a.length) {\n out = out && (a[i - 1] === a[i]);\n } return out;\n}\n\nfunction azSorted(a) {\n let out = true;\n let i = 0;\n while (++i < a.length) {\n out = out && (a[i - 1] < a[i]);\n } return out;\n}\n" + ], + "tail": [ + "const testCases = [['AA', 'AA', 'AA', 'AA'], ['AA', 'ACB', 'BB', 'CC'], [], ['AA'], ['BB', 'AA']];" + ], + "tests": [ + "assert(typeof allEqual === 'function', 'message:allEqual
is a function.');",
+ "assert(typeof azSorted === 'function', 'message: azSorted
is a function.');",
+ "assert(allEqual(testCases[0]), 'message: allEqual([\"AA\", \"AA\", \"AA\", \"AA\"])
returns true.');",
+ "assert(!azSorted(testCases[0]), 'message: azSorted([\"AA\", \"AA\", \"AA\", \"AA\"])
returns false.');",
+ "assert(!allEqual(testCases[1]), 'message: allEqual([\"AA\", \"ACB\", \"BB\", \"CC\"])
returns false.');",
+ "assert(azSorted(testCases[1]), 'message: azSorted([\"AA\", \"ACB\", \"BB\", \"CC\"])
returns true.');",
+ "assert(allEqual(testCases[2]), 'message: allEqual([])
returns true.');",
+ "assert(azSorted(testCases[2]), 'message: azSorted([])
returns true.');",
+ "assert(allEqual(testCases[3]), 'message: allEqual([\"AA\"])
returns true.');",
+ "assert(azSorted(testCases[3]), 'message: azSorted([\"AA\"])
returns true.');",
+ "assert(!allEqual(testCases[4]), 'message: allEqual([\"BB\", \"AA\"])
returns false.');",
+ "assert(!azSorted(testCases[4]), 'message: azSorted([\"BB\", \"AA\"])
returns false.');"
+ ],
+ "id": "596e457071c35c882915b3e4",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Convert seconds to compound duration",
+ "type": "Waypoint",
+ "description": [
+ "Task:",
+ "Implement a function which:
", + "takes a positive integer representing a duration in seconds as input (e.g.,100
), and",
+ "returns a string which shows the same duration decomposed into weeks, days, hours, minutes, and seconds as detailed below (e.g., \"1 min, 40 sec
\").",
+ "Demonstrate that it passes the following three test-cases:
Test Cases
", + "input number | ", + "output number | ", + "
---|---|
7259 | ", + "2 hr, 59 sec | ",
+ "
86400 | ", + "1 d | ",
+ "
6000000 | ", + "9 wk, 6 d, 10 hr, 40 min | ",
+ "
Details
", + "The following five units should be used:", + "unit | ", + "suffix used in output | ", + "conversion | ", + "
---|---|---|
week | ", + "wk | ",
+ "1 week = 7 days | ", + "
day | ", + "d | ",
+ "1 day = 24 hours | ", + "
hour | ", + "hr | ",
+ "1 hour = 60 minutes | ", + "
minute | ", + "min | ",
+ "1 minute = 60 seconds | ", + "
second | ", + "sec | ",
+ "", + " |
1 d
\" and not \"0 wk, 1 d, 0 hr, 0 min, 0 sec
\").Give larger units precedence over smaller ones as much as possible (e.g., return 2 min, 10 sec
and not 1 min, 70 sec
or 130 sec
)Mimic the formatting shown in the test-cases (quantities sorted from largest unit to smallest and separated by comma+space; value and unit of each quantity separated by space).",
+ "convertSeconds
is a function.');",
+ "assert.equal(convertSeconds(testCases[0]), results[0], 'message: convertSeconds(7259)
should return 2 hr, 59 sec
.');",
+ "assert.equal(convertSeconds(testCases[1]), results[1], 'message: convertSeconds(86400)
should return 1 d
.');",
+ "assert.equal(convertSeconds(testCases[2]), results[2], 'message: convertSeconds(6000000)
should return 9 wk, 6 d, 10 hr, 40 min
.');"
+ ],
+ "id": "596fd036dc1ab896c5db98b1",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Count occurrences of a substring",
+ "type": "Waypoint",
+ "description": [
+ "Task:",
+ "Create a function, or show a built-in function, to count the number of non-overlapping occurrences of a substring inside a string.
The function should take two arguments:
", + "the first argument being the string to search, and", + "the second a substring to be searched for.", + "It should return an integer count.
", + "The matching should yield the highest number of non-overlapping matches.
In general, this essentially means matching from left-to-right or right-to-left.
" + ], + "challengeSeed": [ + "function countSubstring (str, subStr) {", + " // Good luck!", + " return true;", + "}" + ], + "solutions": [ + "function countSubstring(str, subStr) {\n const escapedSubStr = subStr.replace(/[.+*?^$[\\]{}()|/]/g, '\\\\$&');\n const matches = str.match(new RegExp(escapedSubStr, 'g'));\n return matches ? matches.length : 0;\n}\n" + ], + "tail": [ + "const testCases = ['the three truths', 'ababababab', 'abaabba*bbaba*bbab'];", + "const searchString = ['th', 'abab', 'a*b'];", + "const results = [3, 2, 2];" + ], + "tests": [ + "assert(typeof countSubstring === 'function', 'message:countSubstring
is a function.');",
+ "assert.equal(countSubstring(testCases[0], searchString[0]), results[0], 'message: countSubstring(\"the three truths\", \"th\")
should return 3
.');",
+ "assert.equal(countSubstring(testCases[1], searchString[1]), results[1], 'message: countSubstring(\"ababababab\", \"abab\")
should return 2
.');",
+ "assert.equal(countSubstring(testCases[2], searchString[2]), results[2], 'message: countSubstring(\"abaabba*bbaba*bbab\", \"a*b\")
should return 2
.');"
+ ],
+ "id": "596fda99c69f779975a1b67d",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Count the coins",
+ "type": "Waypoint",
+ "description": [
+ "There are four types of common coins in US currency:
", + "quarters (25 cents)", + "dimes (10 cents)", + "nickels (5 cents), and ", + "pennies (1 cent) ", + "There are six ways to make change for 15 cents:
", + "A dime and a nickel ", + "A dime and 5 pennies", + "3 nickels", + "2 nickels and 5 pennies", + "A nickel and 10 pennies", + "15 pennies", + "Task:", + "Implement a function to determine how many ways there are to make change for a dollar using these common coins? (1 dollar = 100 cents).
", + "Reference:", + " an algorithm from MIT Press. " + ], + "challengeSeed": [ + "function countCoins () {", + " // Good luck!", + " return true;", + "}" + ], + "solutions": [ + "function countCoins () {\n let t = 100;\n const operands = [1, 5, 10, 25];\n const targetsLength = t + 1;\n const operandsLength = operands.length;\n t = [1];\n\n for (let a = 0; a < operandsLength; a++) {\n for (let b = 1; b < targetsLength; b++) {\n // initialise undefined target\n t[b] = t[b] ? t[b] : 0;\n\n // accumulate target + operand ways\n t[b] += (b < operands[a]) ? 0 : t[b - operands[a]];\n }\n }\n\n return t[targetsLength - 1];\n}\n" + ], + "tests": [ + "assert(typeof countCoins === 'function', 'message:countCoins
is a function.');",
+ "assert.equal(countCoins(), 242, 'message: countCoints()
should return 242.');"
+ ],
+ "id": "59713bd26bdeb8a594fb9413",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Cramer's rule",
+ "type": "Waypoint",
+ "description": [
+ "In linear algebra, Cramer's rule is an explicit formula for the solution of a system of linear equations with as many equations as unknowns, valid whenever the system has a unique solution. It expresses the solution in terms of the determinants of the (square) coefficient matrix and of matrices obtained from it by replacing one column by the vector of right hand sides of the equations.
", + "Given
", + "", + "$\\left\\{\\begin{matrix}a_1x + b_1y + c_1z&= {\\color{red}d_1}\\\\a_2x + b_2y + c_2z&= {\\color{red}d_2}\\\\a_3x + b_3y + c_3z&= {\\color{red}d_3}\\end{matrix}\\right.$
", + "which in matrix format is
", + "$\\begin{bmatrix} a_1 & b_1 & c_1 \\\\ a_2 & b_2 & c_2 \\\\ a_3 & b_3 & c_3 \\end{bmatrix}\\begin{bmatrix} x \\\\ y \\\\ z \\end{bmatrix}=\\begin{bmatrix} {\\color{red}d_1} \\\\ {\\color{red}d_2} \\\\ {\\color{red}d_3} \\end{bmatrix}.$
", + "Then the values of $x, y$ and $z$ can be found as follows:
", + "$x = \\frac{\\begin{vmatrix} {\\color{red}d_1} & b_1 & c_1 \\\\ {\\color{red}d_2} & b_2 & c_2 \\\\ {\\color{red}d_3} & b_3 & c_3 \\end{vmatrix} } { \\begin{vmatrix} a_1 & b_1 & c_1 \\\\ a_2 & b_2 & c_2 \\\\ a_3 & b_3 & c_3 \\end{vmatrix}}, \\quad y = \\frac {\\begin{vmatrix} a_1 & {\\color{red}d_1} & c_1 \\\\ a_2 & {\\color{red}d_2} & c_2 \\\\ a_3 & {\\color{red}d_3} & c_3 \\end{vmatrix}} {\\begin{vmatrix} a_1 & b_1 & c_1 \\\\ a_2 & b_2 & c_2 \\\\ a_3 & b_3 & c_3 \\end{vmatrix}}, \\text{ and }z = \\frac { \\begin{vmatrix} a_1 & b_1 & {\\color{red}d_1} \\\\ a_2 & b_2 & {\\color{red}d_2} \\\\ a_3 & b_3 & {\\color{red}d_3} \\end{vmatrix}} {\\begin{vmatrix} a_1 & b_1 & c_1 \\\\ a_2 & b_2 & c_2 \\\\ a_3 & b_3 & c_3 \\end{vmatrix} }.$
", + "", + "Task", + "Given the following system of equations:
", + "$\\begin{cases}", + "2w-x+5y+z=-3 \\\\", + "3w+2x+2y-6z=-32 \\\\", + "w+3x+3y-z=-47 \\\\", + "5w-2x-3y+3z=49 \\\\", + "\\end{cases}$", + "
", + "solve for $w$, $x$, $y$ and $z$, using Cramer's rule.
" + ], + "challengeSeed": [ + "function cramersRule (matrix, freeTerms) {", + " // Good luck!", + " return true;", + "}" + ], + "solutions": [ + "/**\n * Compute Cramer's Rule\n * @param {array} matrix x,y,z, etc. terms\n * @param {array} freeTerms\n * @return {array} solution for x,y,z, etc.\n */\nfunction cramersRule(matrix, freeTerms) {\n const det = detr(matrix);\n const returnArray = [];\n let i;\n\n for (i = 0; i < matrix[0].length; i++) {\n const tmpMatrix = insertInTerms(matrix, freeTerms, i);\n returnArray.push(detr(tmpMatrix) / det);\n }\n return returnArray;\n}\n\n/**\n * Inserts single dimensional array into\n * @param {array} matrix multidimensional array to have ins inserted into\n * @param {array} ins single dimensional array to be inserted vertically into matrix\n * @param {array} at zero based offset for ins to be inserted into matrix\n * @return {array} New multidimensional array with ins replacing the at column in matrix\n */\nfunction insertInTerms(matrix, ins, at) {\n const tmpMatrix = clone(matrix);\n let i;\n for (i = 0; i < matrix.length; i++) {\n tmpMatrix[i][at] = ins[i];\n }\n return tmpMatrix;\n}\n/**\n * Compute the determinate of a matrix. No protection, assumes square matrix\n * function borrowed, and adapted from MIT Licensed numericjs library (www.numericjs.com)\n * @param {array} m Input Matrix (multidimensional array)\n * @return {number} result rounded to 2 decimal\n */\nfunction detr(m) {\n let ret = 1;\n let j;\n let k;\n const A = clone(m);\n const n = m[0].length;\n let alpha;\n\n for (j = 0; j < n - 1; j++) {\n k = j;\n for (let i = j + 1; i < n; i++) { if (Math.abs(A[i][j]) > Math.abs(A[k][j])) { k = i; } }\n if (k !== j) {\n const temp = A[k]; A[k] = A[j]; A[j] = temp;\n ret *= -1;\n }\n const Aj = A[j];\n for (let i = j + 1; i < n; i++) {\n const Ai = A[i];\n alpha = Ai[j] / Aj[j];\n for (k = j + 1; k < n - 1; k += 2) {\n const k1 = k + 1;\n Ai[k] -= Aj[k] * alpha;\n Ai[k1] -= Aj[k1] * alpha;\n }\n if (k !== n) { Ai[k] -= Aj[k] * alpha; }\n }\n if (Aj[j] === 0) { return 0; }\n ret *= Aj[j];\n }\n return Math.round(ret * A[j][j] * 100) / 100;\n}\n\n/**\n * Clone two dimensional Array using ECMAScript 5 map function and EcmaScript 3 slice\n * @param {array} m Input matrix (multidimensional array) to clone\n * @return {array} New matrix copy\n */\nfunction clone(m) {\n return m.map(a => a.slice());\n}\n" + ], + "tail": [ + "const matrices = [", + " [", + " [2, -1, 5, 1],", + " [3, 2, 2, -6],", + " [1, 3, 3, -1],", + " [5, -2, -3, 3]", + " ],", + " [", + " [3, 1, 1],", + " [2, 2, 5],", + " [1, -3, -4]", + " ]", + "];", + "const freeTerms = [[-3, -32, -47, 49], [3, -1, 2]];", + "", + "const answers = [[2, -12, -4, 1], [1, 1, -1]];" + ], + "tests": [ + "assert(typeof cramersRule === 'function', 'message:cramersRule
is a function.');",
+ "assert.deepEqual(cramersRule(matrices[0], freeTerms[0]), answers[0], 'message: cramersRule([[2, -1, 5, 1], [3, 2, 2, -6], [1, 3, 3, -1], [5, -2, -3, 3]], [-3, -32, -47, 49])
should return [2, -12, -4, 1]
.');",
+ "assert.deepEqual(cramersRule(matrices[1], freeTerms[1]), answers[1], 'message: cramersRule([[3, 1, 1], [2, 2, 5], [1, -3, -4]], [3, -1, 2])
should return [1, 1, -1]
.');"
+ ],
+ "id": "59713da0a428c1a62d7db430",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Date format",
+ "type": "Waypoint",
+ "description": [
+ "Task:",
+ "Return an array with the current date in the formats:
", + "- 2007-11-23 and
", + "- Sunday, November 23, 2007
", + "Example output: ['2007-11-23', 'Sunday, November 23, 2007']
getDataFormats()
should return [\"${dates[0]}\", \"${dates[1]}\"]
.`;"
+ ],
+ "tests": [
+ "assert(typeof getDateFormats === 'function', 'message: getDateFormats
is a function.');",
+ "assert(typeof getDateFormats() === 'object', 'message: Should return an object.');",
+ "assert(getDateFormats().length === 2, 'message: Should returned an array with 2 elements.');",
+ "assert.deepEqual(getDateFormats(), dates, equalsMessage);"
+ ],
+ "id": "59669d08d75b60482359409f",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Date manipulation",
+ "type": "Waypoint",
+ "description": [
+ "Task:",
+ "Given a date string in EST, output the given date as a string with 12 hours added to the time.
", + "Time zone should be preserved.
", + "Example input:
", + "\"March 7 2009 7:30pm EST\"
Example output:
", + "\"March 8 2009 7:30am EST\"
add12Hours
is a function.');",
+ "assert(typeof add12Hours(tests[0]) === 'string', 'message: add12Hours(dateString)
should return a string.');",
+ "assert(add12Hours(tests[0]) === answers[0], 'message: add12Hours(\"' + tests[0] + '\")
should return \"' + answers[0] + '\"
');",
+ "assert(add12Hours(tests[1]) === answers[1], 'message: Should handel day change. add12Hours(\"' + tests[1] + '\")
should return \"' + answers[1] + '\"
');",
+ "assert(add12Hours(tests[2]) === answers[2], 'message: Should handel month change in a leap years. add12Hours(\"' + tests[2] + '\")
should return \"' + answers[2] + '\"
');",
+ "assert(add12Hours(tests[3]) === answers[3], 'message: Should handel month change in a common years. add12Hours(\"' + tests[3] + '\")
should return \"' + answers[3] + '\"
');",
+ "assert(add12Hours(tests[4]) === answers[4], 'message: Should handel year change. add12Hours(\"' + tests[4] + '\")
should return \"' + answers[4] + '\"
');"
+ ],
+ "id": "5966c21cf732a95f1b67dd28",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Day of the week",
+ "type": "Waypoint",
+ "description": [
+ "A company decides that whenever Xmas falls on a Sunday they will give their workers all extra paid holidays so that, together with any public holidays, workers will not have to work the following week (between the 25th of December and the first of January).
", + "Task:
", + "Write a function that takes a start year and an end year and return an array of all the years where the 25th of December will be a Sunday.
" + ], + "challengeSeed": [ + "function findXmasSunday (start, end) {", + " // Good luck!", + " return true;", + "}" + ], + "solutions": [ + "function findXmasSunday (start, end) {\n const xmasSunday = [];\n for (let year = start; year <= end; year++) {\n const xmas = new Date(year, 11, 25);\n if (xmas.getDay() === 0) {\n xmasSunday.push(year);\n }\n }\n return xmasSunday;\n}\n" + ], + "tail": [ + "const firstSolution = [1977, 1983, 1988, 1994, 2005, 2011, 2016];", + "const secondSolution = [2011, 2016, 2022, 2033, 2039, 2044, 2050, 2061, 2067, 2072, 2078, 2089, 2095, 2101, 2107, 2112, 2118];" + ], + "tests": [ + "assert(typeof findXmasSunday === 'function', 'message:findXmasSunday
is a function.');",
+ "assert(typeof findXmasSunday(2000, 2100) === 'object', 'message: findChristmasSunday(2000, 2100)
should return an array.');",
+ "assert.deepEqual(findXmasSunday(1970, 2017), firstSolution, 'message: findChristmasSunday(2008, 2121
should return [1977, 1983, 1988, 1994, 2005, 2011, 2016]');",
+ "assert.deepEqual(findXmasSunday(2008, 2121), secondSolution, 'message: findChristmasSunday(2008, 2121
should return [2011, 2016, 2022, 2033, 2039, 2044, 2050, 2061, 2067, 2072, 2078, 2089, 2095, 2101, 2107, 2112, 2118]');"
+ ],
+ "id": "5966f99c45e8976909a85575",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Deal cards for FreeCell",
+ "type": "Waypoint",
+ "description": [
+ "Free Cell is the solitaire card game that Paul Alfille introduced to the PLATO system in 1978. Jim Horne, at Microsoft, changed the name to FreeCell and reimplemented the game for DOS, then Windows.
", + "This version introduced 32000 numbered deals. (The FreeCell FAQ tells this history.)
As the game became popular, Jim Horne disclosed the algorithm, and other implementations of FreeCell began to reproduce the Microsoft deals.
", + "These deals are numbered from 1 to 32000.
", + "Newer versions from Microsoft have 1 million deals, numbered from 1 to 1000000; some implementations allow numbers outside that range.
The algorithm uses this linear congruential generator from Microsoft C:
$state_{n + 1} \\equiv 214013 \\times state_n + 2531011 \\pmod{2^{31}}$", + "$rand_n = state_n \\div 2^{16}$", + "$rand_n$ is in range 0 to 32767.", + "The algorithm follows:
Seed the RNG with the number of the deal.", + "Create an array of 52 cards: Ace of Clubs, Ace of Diamonds, Ace of Hearts, Ace of Spades, 2 of Clubs, 2 of Diamonds, and so on through the ranks: Ace, 2, 3, 4, 5, 6, 7, 8, 9, 10, Jack, Queen, King. The array indexes are 0 to 51, with Ace of Clubs at 0, and King of Spades at 51.", + "Until the array is empty:", + " Choose a random card at index ≡ next random number (mod array length). ", + " Swap this random card with the last card of the array.", + " Remove this random card from the array. (Array length goes down by 1.)", + " Deal this random card.", + "Deal all 52 cards, face up, across 8 columns. The first 8 cards go in 8 columns, the next 8 cards go on the first 8 cards, and so on.", + "Example:", + "Order to deal cards
", + "1 2 3 4 5 6 7 8", + " 9 10 11 12 13 14 15 16", + "17 18 19 20 21 22 23 24", + "25 26 27 28 29 30 31 32", + "33 34 35 36 37 38 39 40", + "41 42 43 44 45 46 47 48", + "49 50 51 52", + "
Game #1
", + "[", + "['JD', '2D', '9H', 'JC', '5D', '7H', '7C', '5H'],", + "['KD', 'KC', '9S', '5S', 'AD', 'QC', 'KH', '3H'],", + "['2S', 'KS', '9D', 'QD', 'JS', 'AS', 'AH', '3C'],", + "['4C', '5C', 'TS', 'QH', '4H', 'AC', '4D', '7S'],", + "['3S', 'TD', '4S', 'TH', '8H', '2C', 'JH', '7D'],", + "['6D', '8S', '8D', 'QS', '6C', '3D', '8C', 'TC'],", + "['6S', '9C', '2H', '6H']", + "]", + "
Game #617
", + "[", + "['7D', 'AD', '5C', '3S', '5S', '8C', '2D', 'AH'],", + "['TD', '7S', 'QD', 'AC', '6D', '8H', 'AS', 'KH'],", + "['TH', 'QC', '3H', '9D', '6S', '8D', '3D', 'TC'],", + "['KD', '5H', '9S', '3C', '8S', '7H', '4D', 'JS'],", + "['4C', 'QS', '9C', '9H', '7C', '6H', '2C', '2S'],", + "['4S', 'TS', '2H', '5D', 'JC', '6C', 'JH', 'QH'],", + "['JD', 'KS', 'KC', '4H']", + "]", + "Task:", + "
Write a function to take a deal number and deal cards in the same order as this algorithm.
", + "The function must return a two dimentional array representing the FreeCell board.
", + "Deals can also be checked against FreeCell solutions to 1000000 games.
", + "(Summon a video solution, and it displays the initial deal.)
" + ], + "challengeSeed": [ + "function dealFreeCell (seed) {", + " // Good luck!", + " return true;", + "}" + ], + "solutions": [ + "// RNG\nfunction FreeCellRNG (seed) {\n return {\n lastNum: seed,\n next() {\n this.lastNum = ((214013 * this.lastNum) + 2531011) % (Math.pow(2, 31));\n return this.lastNum >> 16;\n }\n };\n}\n// Get cards\nfunction getDeck() {\n const ranks = ['A', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K'];\n const suits = ['C', 'D', 'H', 'S'];\n const cards = [];\n for (let i = 0; i < ranks.length; i += 1) {\n for (let j = 0; j < suits.length; j += 1) {\n cards.push(`${ranks[i]}${suits[j]}`);\n }\n }\n return cards;\n}\nfunction dealFreeCell(seed) {\n const rng = FreeCellRNG(seed);\n const deck = getDeck();\n\n const deltCards = [[], [], [], [], [], [], []];\n let currentColumn = 0;\n let currentRow = 0;\n\n let rand;\n let temp;\n let card;\n while (deck.length > 0) {\n // Choose a random card\n rand = rng.next() % deck.length;\n\n // Swap this random card with the last card in the array\n temp = deck[deck.length - 1];\n deck[deck.length - 1] = deck[rand];\n deck[rand] = temp;\n\n // Remove this card from the array\n card = deck.pop();\n\n // Deal this card\n deltCards[currentRow].push(card);\n currentColumn += 1;\n if (currentColumn === 8) {\n currentColumn = 0;\n currentRow += 1;\n }\n }\n\n return deltCards;\n}\n" + ], + "tail": [ + "const replaceThis = 3;", + "const game1 = [", + " ['JD', '2D', '9H', 'JC', '5D', '7H', '7C', '5H'],", + " ['KD', 'KC', '9S', '5S', 'AD', 'QC', 'KH', '3H'],", + " ['2S', 'KS', '9D', 'QD', 'JS', 'AS', 'AH', '3C'],", + " ['4C', '5C', 'TS', 'QH', '4H', 'AC', '4D', '7S'],", + " ['3S', 'TD', '4S', 'TH', '8H', '2C', 'JH', '7D'],", + " ['6D', '8S', '8D', 'QS', '6C', '3D', '8C', 'TC'],", + " ['6S', '9C', '2H', '6H']", + "];", + "const game617 = [", + " ['7D', 'AD', '5C', '3S', '5S', '8C', '2D', 'AH'],", + " ['TD', '7S', 'QD', 'AC', '6D', '8H', 'AS', 'KH'],", + " ['TH', 'QC', '3H', '9D', '6S', '8D', '3D', 'TC'],", + " ['KD', '5H', '9S', '3C', '8S', '7H', '4D', 'JS'],", + " ['4C', 'QS', '9C', '9H', '7C', '6H', '2C', '2S'],", + " ['4S', 'TS', '2H', '5D', 'JC', '6C', 'JH', 'QH'],", + " ['JD', 'KS', 'KC', '4H']", + "];" + ], + "tests": [ + "assert(typeof dealFreeCell === 'function', 'message:dealFreeCell
is a function.');",
+ "assert(typeof dealFreeCell(1) === 'object', 'message: dealFreeCell(seed)
should return an object.');",
+ "assert(dealFreeCell(1).length === 7, 'message: dealFreeCell(seed)
should return an array of length 7.');",
+ "assert.deepEqual(dealFreeCell(1), game1, 'message: dealFreeCell(1)
should return an array identical to example \"Game #1\"');",
+ "assert.deepEqual(dealFreeCell(617), game617, 'message: dealFreeCell(617)
should return an array identical to example \"Game #617\"');"
+ ],
+ "id": "59694356a6e7011f7f1c5f4e",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Deepcopy",
+ "type": "Waypoint",
+ "description": [
+ "Task:",
+ "Write a function that returns a deep copy of a given object.
", + "The copy must not be the same object that was given.
", + "This task will not test for:
", + "Objects with properties that are functions", + "Date objects or object with properties that are Date objects", + "RegEx or object with properties that are RegEx objects", + "Prototype copying" + ], + "null": [], + "challengeSeed": [ + "function deepcopy (obj) {", + " // Good luck!", + " return true;", + "}" + ], + "solutions": [ + "function deepcopy(obj) {\n return JSON.parse(JSON.stringify(obj));\n}\n\n" + ], + "tail": [ + "const obj1 = { test: 'test' };", + "const obj2 = {", + " t: 'test',", + " a: ['an', 'array']", + "};", + "const obj3 = {", + " t: 'try',", + " o: obj2", + "};" + ], + "tests": [ + "assert(typeof deepcopy === 'function', 'message:deepcopy
should be a function.');",
+ "assert(typeof deepcopy(obj1) === 'object', 'message: deepcopy({test: \"test\"})
should return an object.');",
+ "assert(deepcopy(obj2) != obj2, 'message: Should not return the same object that was provided.');",
+ "assert.deepEqual(deepcopy(obj2), obj2, 'message: When passed an object containing an array, should return a deep copy of the object.');",
+ "assert.deepEqual(deepcopy(obj3), obj3, 'message: When passed an object containing another object, should return a deep copy of the object.');"
+ ],
+ "id": "596a8888ab7c01048de257d5",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Define a primitive data type",
+ "type": "Waypoint",
+ "description": [
+ "Task:",
+ "Define a type that behaves like an integer but has a lowest valid value of 1 and a highest valid value of 10.
", + "Errors:", + "If you try to instantiate aNum
with a value outside of 1 - 10",
+ "it should throw a TypeError
with an error message of 'Out of range'
.",
+ "If you try to instantiate a Num
with a value that is not a number",
+ "it should throw a TypeError
with an error message of 'Not a Number'
."
+ ],
+ "challengeSeed": [
+ "function Num (n) {",
+ " // Good luck!",
+ " return n;",
+ "}"
+ ],
+ "solutions": [
+ "function Num(n) {\n const num = Math.floor(n);\n if (isNaN(num)) {\n throw new TypeError('Not a Number');\n }\n if (num < 1 || num > 10) {\n throw new TypeError('Out of range');\n }\n\n this._value = num;\n}\nNum.prototype.valueOf = function() { return this._value; };\nNum.prototype.toString = function () { return this._value.toString(); };\n\nfunction throws(func, errorType, msg) {\n let hasThrown = false;\n let errorMsg = '';\n let correctType = false;\n try {\n func();\n }\n catch (e) {\n hasThrown = true;\n errorMsg = e.message;\n if (e instanceof errorType) {\n correctType = true;\n }\n }\n return hasThrown && correctType && msg === errorMsg;\n}\n"
+ ],
+ "tail": [],
+ "tests": [
+ "assert(typeof Num === 'function', 'message: Num
should be a function.');",
+ "assert(typeof (new Num(4)) === 'object', 'message: new Num(4)
should return an object.');",
+ "assert(throws(() => new Num('test'), TypeError, 'Not a Number'), 'message: new Num(\\'test\\')
should throw a TypeError with message \\'Not a Number\\'.');",
+ "assert(throws(() => new Num(0), TypeError, 'Out of range'), 'message: new Num(0)
should throw a TypeError with message \\'Out of range\\'.');",
+ "assert(throws(() => new Num(-5), TypeError, 'Out of range'), 'message: new Num(-5)
should throw a TypeError with message \\'Out of range\\'.');",
+ "assert(throws(() => new Num(11), TypeError, 'Out of range'), 'message: new Num(10)
should throw a TypeError with message \\'Out of range\\'.');",
+ "assert(throws(() => new Num(20), TypeError, 'Out of range'), 'message: new Num(20)
should throw a TypeError with message \\'Out of range\\'.');",
+ "assert.equal(new Num(3) + new Num(4), 7, 'message: new Num(3) + new Num(4)
should equal 7.');",
+ "assert.equal(new Num(3) - new Num(4), -1, 'message: new Num(3) - new Num(4)
should equal -1.');",
+ "assert.equal(new Num(3) * new Num(4), 12, 'message: new Num(3) * new Num(4)
should equal 12.');",
+ "assert.equal(new Num(3) / new Num(4), 0.75, 'message: new Num(3) / new Num(4)
should equal 0.75.');",
+ "assert(new Num(3) < new Num(4), 'message: new Num(3) < new Num(4)
should be true.');",
+ "assert(!(new Num(3) > new Num(4)), 'message: new Num(3) > new Num(4)
should be false.');",
+ "assert.equal((new Num(5)).toString(), '5', 'message: (new Num(5)).toString()
should return \\'5\\'');"
+ ],
+ "id": "597089c87eec450c68aa1643",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Element-wise operations",
+ "type": "Waypoint",
+ "description": [
+ "Implement basic element-wise matrix-matrix and scalar-matrix operations.
Implement:
", + "::* addition
", + "::* subtraction
", + "::* multiplication
", + "::* division
", + "::* exponentiation
", + "The first parameter will be the operation to be performed, for example : \"m_add\" for matrix addition and \"s_add\" for scalar addition. The second and third parameters will be the matrices on which the operations are to be performed."
+ ],
+ "challengeSeed": [
+ "function operation (op, arr1, arr2) {",
+ " // Good luck!",
+ "}"
+ ],
+ "solutions": [
+ "function operation(op, arr1, arr2) {\n const ops = {\n add: ((a, b) => a + b),\n sub: ((a, b) => a - b),\n mult: ((a, b) => a * b),\n div: ((a, b) => a / b),\n exp: ((a, b) => Math.pow(a, b))\n };\n const ifm = op.startsWith('m');\n const doOp = ops[op.substring(2)];\n for (let i = 0; i < arr1.length; i++) {\n for (let j = 0; j < arr1[0].length; j++) {\n arr1[i][j] = doOp(arr1[i][j], (ifm) ? (arr2[i][j]) : (arr2));\n }\n }\n return arr1;\n}\n"
+ ],
+ "tests": [
+ "assert(typeof operation === 'function', 'message: operation
is a function.');",
+ "assert.deepEqual(operation('m_add', [[1, 2], [3, 4]], [[1, 2], [3, 4]]), [[2, 4], [6, 8]], 'message: operation(\"m_add\",[[1,2],[3,4]],[[1,2],[3,4]])
should return [[2,4],[6,8]]
.');",
+ "assert.deepEqual(operation('s_add', [[1, 2], [3, 4]], 2), [[3, 4], [5, 6]], 'message: operation(\"s_add\",[[1,2],[3,4]],[[1,2],[3,4]])
should return [[3,4],[5,6]]
.');",
+ "assert.deepEqual(operation('m_sub', [[1, 2], [3, 4]], [[1, 2], [3, 4]]), [[0, 0], [0, 0]], 'message: operation(\"m_sub\",[[1,2],[3,4]],[[1,2],[3,4]])
should return [[0,0],[0,0]]
.');",
+ "assert.deepEqual(operation('m_mult', [[1, 2], [3, 4]], [[1, 2], [3, 4]]), [[1, 4], [9, 16]], 'message: operation(\"m_mult\",[[1,2],[3,4]],[[1,2],[3,4]])
should return [[1,4],[9,16]]
.');",
+ "assert.deepEqual(operation('m_div', [[1, 2], [3, 4]], [[1, 2], [3, 4]]), [[1, 1], [1, 1]], 'message: operation(\"m_div\",[[1,2],[3,4]],[[1,2],[3,4]])
should return [[1,1],[1,1]]
.');",
+ "assert.deepEqual(operation('m_exp', [[1, 2], [3, 4]], [[1, 2], [3, 4]]), [[1, 4], [27, 256]], 'message: operation(\"m_exp\",[[1,2],[3,4]],[[1,2],[3,4]])
should return [[1,4],[27,256]]
.');",
+ "assert.deepEqual(operation('m_add', [[1, 2, 3, 4], [5, 6, 7, 8]], [[9, 10, 11, 12], [13, 14, 15, 16]]), [[10, 12, 14, 16], [18, 20, 22, 24]], 'message: operation(\"m_add\",[[1,2,3,4],[5,6,7,8]],[[9,10,11,12],[13,14,15,16]])
should return [[10,12,14,16],[18,20,22,24]]
.');"
+ ],
+ "id": "599c333915e0ea32d04d4bec",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Emirp primes",
+ "type": "Waypoint",
+ "description": [
+ "
An emirp (prime spelled backwards) are primes that when reversed (in their decimal representation) are a different prime.
", + "Write a function that should be able to : Show the first n eprimes numbers.Show the eprimes numbers in a range.Show the number of eprimes in a range.Show the nth eprimes number.
The function should have two paramters. The first will recieve n or the range as an array. The second will recieve a boolean, that specifies if the function returns the eprimes as an array or a single number(the number of primes in the range or the nth prime). According to the parameters the function should return an array or a number."
+ ],
+ "null": [],
+ "challengeSeed": [
+ "function emirps(n) {",
+ " // Good luck!",
+ "}"
+ ],
+ "solutions": [
+ "// noprotect\nfunction emirps(num, showEmirps)\n{\n const is_prime = function(n)\n\t{\n if (!(n % 2) || !(n % 3)) return false;\n let p = 1;\n while (p * p < n)\n\t\t\t { if (n % (p += 4) == 0 || n % (p += 2) == 0)\n\t\t\t { return false; } }\n return true;\n };\n const is_emirp = function(n) {\n const r = parseInt(n.toString().split('').reverse().join(''));\n return r != n && is_prime(n) && is_prime(r);\n };\n\n let i,\n arr = [];\n if (typeof num === 'number') {\n for (i = 0; arr.length < num; i++) if (is_emirp(i)) arr.push(i);\n // first x emirps\n if (showEmirps) return arr;\n // xth emirp\n return arr.pop();\n }\n\n if (Array.isArray(num)) {\n for (i = num[0]; i <= num[1]; i++) if (is_emirp(i)) arr.push(i);\n // emirps between x .. y\n if (showEmirps) return arr;\n // number of emirps between x .. y\n return arr.length;\n }\n}\n"
+ ],
+ "tests": [
+ "assert(typeof emirps === 'function', 'message: emirps
is a function.');",
+ "assert.deepEqual(emirps(20, true), [13, 17, 31, 37, 71, 73, 79, 97, 107, 113, 149, 157, 167, 179, 199, 311, 337, 347, 359, 389], 'message:emirps(20,true)
should return [13,17,31,37,71,73,79,97,107,113,149,157,167,179,199,311,337,347,359,389]
');",
+ "assert.deepEqual(emirps(10000), 948349, 'message:emirps(10000)
should return 948349
');",
+ "assert.deepEqual(emirps([7700, 8000], true), [7717, 7757, 7817, 7841, 7867, 7879, 7901, 7927, 7949, 7951, 7963], 'message:emirps([7700,8000],true)
should return [7717,7757,7817,7841,7867,7879,7901,7927,7949,7951,7963]
');",
+ "assert.deepEqual(emirps([7700, 8000], false), 11, 'message:emirps([7700,8000],true)
should return 11
');"
+ ],
+ "id": "599d0ba974141b0f508b37d5",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Entropy",
+ "type": "Waypoint",
+ "description": [
+ "Task:",
+ "
Calculate the Shannon entropy H of a given input string.
Given the discreet random variable $X$ that is a string of $N$ \"symbols\" (total characters) consisting of $n$ different characters (n=2 for binary), the Shannon entropy of X in bits/symbol is :
", + "$H_2(X) = -\\sum_{i=1}^n \\frac{count_i}{N} \\log_2 \\left(\\frac{count_i}{N}\\right)$
where $count_i$ is the count of character $n_i$.
" + ], + "challengeSeed": [ + "function entropy (s) {", + " // Good luck!", + "}" + ], + "solutions": [ + "function entropy(s) {\n\t// Create a dictionary of character frequencies and iterate over it.\n function process(s, evaluator) {\n let h = Object.create(null),\n k;\n s.split('').forEach(c => {\n h[c] && h[c]++ || (h[c] = 1); });\n if (evaluator) for (k in h) evaluator(k, h[k]);\n return h;\n }\n\t// Measure the entropy of a string in bits per symbol.\n\n let sum = 0,\n len = s.length;\n process(s, (k, f) => {\n const p = f / len;\n sum -= p * Math.log(p) / Math.log(2);\n });\n return sum;\n}\n" + ], + "tests": [ + "assert(typeof entropy === 'function', 'message:entropy
is a function.');",
+ "assert.equal(entropy('0'), 0, 'message: entropy(\"0\")
should return 0
');",
+ "assert.equal(entropy('01'), 1, 'message: entropy(\"01\")
should return 1
');",
+ "assert.equal(entropy('0123'), 2, 'message: entropy(\"0123\")
should return 2
');",
+ "assert.equal(entropy('01234567'), 3, 'message: entropy(\"01234567\")
should return 3
');",
+ "assert.equal(entropy('0123456789abcdef'), 4, 'message: entropy(\"0123456789abcdef\")
should return 4
');",
+ "assert.equal(entropy('1223334444'), 1.8464393446710154, 'message: entropy(\"1223334444\")
should return 1.8464393446710154
');"
+ ],
+ "id": "599d15309e88c813a40baf58",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Equilibrium index",
+ "type": "Waypoint",
+ "description": [
+ "An equilibrium index of a sequence is an index into the sequence such that the sum of elements at lower indices is equal to the sum of elements at higher indices.
", + "For example, in a sequence $A$:
:::: $A_0 = -7$
", + ":::: $A_1 = 1$
", + ":::: $A_2 = 5$
", + ":::: $A_3 = 2$
", + ":::: $A_4 = -4$
", + ":::: $A_5 = 3$
", + ":::: $A_6 = 0$
3 is an equilibrium index, because:
:::: $A_0 + A_1 + A_2 = A_4 + A_5 + A_6$
6 is also an equilibrium index, because:
:::: $A_0 + A_1 + A_2 + A_3 + A_4 + A_5 = 0$
(sum of zero elements is zero)
7 is not an equilibrium index, because it is not a valid index of sequence $A$.
", + "Write a function that, given a sequence, returns its equilibrium indices (if any).
Assume that the sequence may be very long.
" + ], + "challengeSeed": [ + "function equilibrium (a) {", + " // Good luck!", + "}" + ], + "solutions": [ + "function equilibrium(a) {\n let N = a.length,\n i,\n l = [],\n r = [],\n e = [];\n for (l[0] = a[0], r[N - 1] = a[N - 1], i = 1; i < N; i++)\n { l[i] = l[i - 1] + a[i], r[N - i - 1] = r[N - i] + a[N - i - 1]; }\n for (i = 0; i < N; i++)\n { if (l[i] === r[i]) e.push(i); }\n return e;\n}\n" + ], + "tail": [ + "const tests =", + " [[-7, 1, 5, 2, -4, 3, 0], // 3, 6", + " [2, 4, 6], // empty", + " [2, 9, 2], // 1", + " [1, -1, 1, -1, 1, -1, 1], // 0,1,2,3,4,5,6", + " [1], // 0", + " [] // empty", + " ];", + "const ans = [[3, 6], [], [1], [0, 1, 2, 3, 4, 5, 6], [0], []];" + ], + "tests": [ + "assert(typeof equilibrium === 'function', 'message:equilibrium
is a function.');",
+ "assert.deepEqual(equilibrium(tests[0]), ans[0], 'message: equilibrium([-7, 1, 5, 2, -4, 3, 0])
should return [3,6]
.');",
+ "assert.deepEqual(equilibrium(tests[1]), ans[1], 'message: equilibrium([2, 4, 6])
should return []
.');",
+ "assert.deepEqual(equilibrium(tests[2]), ans[2], 'message: equilibrium([2, 9, 2])
should return [1]
.');",
+ "assert.deepEqual(equilibrium(tests[3]), ans[3], 'message: equilibrium([1, -1, 1, -1, 1, -1, 1])
should return [0,1,2,3,4,5,6]
.');",
+ "assert.deepEqual(equilibrium(tests[4]), ans[4], 'message: equilibrium([1])
should return [0]
.');",
+ "assert.deepEqual(equilibrium(tests[5]), ans[5], 'message: equilibrium([])
should return []
.');"
+ ],
+ "id": "5987fd532b954e0f21b5d3f6",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Ethiopian multiplication",
+ "type": "Waypoint",
+ "description": [
+ "Ethiopian multiplication is a method of multiplying integers using only addition, doubling, and halving.
", + "Method:
", + "Take two numbers to be multiplied and write them down at the top of two columns.", + "In the left-hand column repeatedly halve the last number, discarding any remainders, and write the result below the last in the same column, until you write a value of 1.", + "In the right-hand column repeatedly double the last number and write the result below. stop when you add a result in the same row as where the left hand column shows 1.", + "Examine the table produced and discard any row where the value in the left column is even.", + "Sum the values in the right-hand column that remain to produce the result of multiplying the original two numbers together", + "For example: 17 × 34
", + "17 34
", + "Halving the first column:
", + "17 34
", + "8
", + "4
", + "2
", + "1
", + "Doubling the second column:
", + "17 34
", + "8 68
", + "4 136
", + "2 272
", + "1 544
", + "Strike-out rows whose first cell is even:
", + "17 34
", + "8 68
4 136
2 272
1 544
", + "Sum the remaining numbers in the right-hand column:
", + "17 34
", + "8 --
", + "4 ---
", + "2 ---
", + "1 544
", + "====
", + "578
", + "So 17 multiplied by 34, by the Ethiopian method is 578.
", + "Task:", + "The task is to define three named functions/methods/procedures/subroutines:
", + "one to halve an integer,", + "one to double an integer, and", + "one to state if an integer is even.", + "Use these functions to create a function that does Ethiopian multiplication.
" + ], + "challengeSeed": [ + "function eth_mult (a, b) {", + " // Good luck!", + "}" + ], + "solutions": [ + "function eth_mult(a, b) {\n let sum = 0; a = [a]; b = [b];\n\n let half = a => a / 2,\n double = a => a * 2,\n is_even = a => a % 2 == 0;\n\n while (a[0] !== 1) {\n a.unshift(Math.floor(half(a[0])));\n b.unshift(double(b[0]));\n }\n\n for (let i = a.length - 1; i > 0; i -= 1) {\n if (!is_even(a[i])) {\n sum += b[i];\n }\n }\n return sum + b[0];\n}" + ], + "tests": [ + "assert(typeof eth_mult === 'function', 'message:eth_mult
is a function.');",
+ "assert.equal(eth_mult(17, 34), 578, 'message: eth_mult(17,34)
should return 578
.');",
+ "assert.equal(eth_mult(23, 46), 1058, 'message: eth_mult(23,46)
should return 1058
.');",
+ "assert.equal(eth_mult(12, 27), 324, 'message: eth_mult(12,27)
should return 324
.');",
+ "assert.equal(eth_mult(56, 98), 5488, 'message: eth_mult(56,98)
should return 5488
.');",
+ "assert.equal(eth_mult(63, 74), 4662, 'message: eth_mult(63,74)
should return 4662
.');"
+ ],
+ "id": "599d1566a02b571412643b84",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Euler method",
+ "type": "Waypoint",
+ "description": [
+ "Euler's method numerically approximates solutions of first-order ordinary differential equations (ODEs) with a given initial value. It is an explicit method for solving initial value problems (IVPs), as described in the wikipedia page.
The ODE has to be provided in the following form:
:: $\\frac{dy(t)}{dt} = f(t,y(t))$
with an initial value
:: $y(t_0) = y_0$
To get a numeric solution, we replace the derivative on the LHS with a finite difference approximation:
:: $\\frac{dy(t)}{dt} \\approx \\frac{y(t+h)-y(t)}{h}$
then solve for $y(t+h)$:
:: $y(t+h) \\approx y(t) + h \\, \\frac{dy(t)}{dt}$
which is the same as
:: $y(t+h) \\approx y(t) + h \\, f(t,y(t))$
The iterative solution rule is then:
:: $y_{n+1} = y_n + h \\, f(t_n, y_n)$
where $h$ is the step size, the most relevant parameter for accuracy of the solution. A smaller step size increases accuracy but also the computation cost, so it has always has to be hand-picked according to the problem at hand.
", + "Example: Newton's Cooling Law
Newton's cooling law describes how an object of initial temperature $T(t_0) = T_0$ cools down in an environment of temperature $T_R$:
:: $\\frac{dT(t)}{dt} = -k \\, \\Delta T$
", + "or
", + ":: $\\frac{dT(t)}{dt} = -k \\, (T(t) - T_R)$
", + "It says that the cooling rate $\\frac{dT(t)}{dt}$ of the object is proportional to the current temperature difference $\\Delta T = (T(t) - T_R)$ to the surrounding environment.
The analytical solution, which we will compare to the numerical approximation, is
", + ":: $T(t) = T_R + (T_0 - T_R) \\; e^{-k t}$
", + "Task:", + "Implement a routine of Euler's method and then to use it to solve the given example of Newton's cooling law with it for three different step sizes of:
", + "::* 2 s
", + "::* 5 s and
", + "::* 10 s
", + "and to compare with the analytical solution.
", + "Initial values:", + "::* initial temperature $T_0$ shall be 100 °C
", + "::* room temperature $T_R$ shall be 20 °C
", + "::* cooling constant $k$ shall be 0.07
", + "::* time interval to calculate shall be from 0 s ──► 100 s
" + ], + "challengeSeed": [ + "function eulersMethod (x1, y1, x2, h) {", + " // Good luck!", + "}" + ], + "solutions": [ + "function eulersMethod(x1, y1, x2, h) {\n let x = x1;\n let y = y1;\n\n while ((x < x2 && x1 < x2) || (x > x2 && x1 > x2)) {\n y += h * (-0.07 * (y - 20));\n x += h;\n }\n\n return y;\n}\n" + ], + "tests": [ + "assert(typeof eulersMethod === 'function', 'message:eulersMethod
is a function.');",
+ "assert(typeof eulersMethod(0, 100, 100, 10) === 'number', 'message: eulersMethod(0, 100, 100, 10)
should return a number.');",
+ "assert.equal(eulersMethod(0, 100, 100, 2), 20.0424631833732, 'message: eulersMethod(0, 100, 100, 10)
should return 20.0424631833732.');",
+ "assert.equal(eulersMethod(0, 100, 100, 5), 20.01449963666907, 'message: eulersMethod(0, 100, 100, 10)
should return 20.01449963666907.');",
+ "assert.equal(eulersMethod(0, 100, 100, 10), 20.000472392, 'message: eulersMethod(0, 100, 100, 10)
should return 20.000472392.');"
+ ],
+ "id": "59880443fb36441083c6c20e",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Evaluate binomial coefficients",
+ "type": "Waypoint",
+ "description": [
+ "Write a function to calculate the binomial coefficient for the given value of n and k.
This formula is recommended:
", + "$\\binom{n}{k} = \\frac{n!}{(n-k)!k!} = \\frac{n(n-1)(n-2)\\ldots(n-k+1)}{k(k-1)(k-2)\\ldots 1}$" + ], + "challengeSeed": [ + "function binom (n, k) {", + " // Good luck!", + "}" + ], + "solutions": [ + "function binom(n, k) {\n let coeff = 1;\n for (let i = n - k + 1; i <= n; i++) coeff *= i;\n for (let i = 1; i <= k; i++) coeff /= i;\n return coeff;\n}\n" + ], + "tests": [ + "assert(typeof binom === 'function', 'message:binom
is a function.');",
+ "assert.equal(binom(5, 3), 10, 'message: binom(5,3)
should return 10.');",
+ "assert.equal(binom(7, 2), 21, 'message: binom(7,2)
should return 21.');",
+ "assert.equal(binom(10, 4), 210, 'message: binom(10,4)
should return 210.');",
+ "assert.equal(binom(6, 1), 6, 'message: binom(6,1)
should return 6.');",
+ "assert.equal(binom(12, 8), 495, 'message: binom(12,8)
should return 495.');"
+ ],
+ "id": "598de241872ef8353c58a7a2",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Extensible prime generator",
+ "type": "Waypoint",
+ "description": [
+ "Write a generator of prime numbers, in order, that will automatically adjust to accommodate the generation of any reasonably high prime.
The generator should be able to : Show the first n prime numbers.Show the prime numbers in a range.Show the number of primes in a range.Show the nth prime number.The function should have two paramters. The first will recieve n or the range as an array. The second will recieve a boolean, that specifies if the function returns the prime numbers as an array or a single number(the number of primes in the range or the nth prime). According to the parameters the function should return an array."
+ ],
+ "challengeSeed": [
+ "function primeGenerator (num, showPrimes) {",
+ " // Good luck!",
+ "}"
+ ],
+ "solutions": [
+ "// noprotect\nfunction primeGenerator(num, showPrimes) {\n let i,\n arr = [];\n\n function isPrime(num) {\n // try primes <= 16\n if (num <= 16) { return (\n num == 2 || num == 3 || num == 5 || num == 7 || num == 11 || num == 13\n ); }\n // cull multiples of 2, 3, 5 or 7\n if (num % 2 == 0 || num % 3 == 0 || num % 5 == 0 || num % 7 == 0)\n { return false; }\n // cull square numbers ending in 1, 3, 7 or 9\n for (let i = 10; i * i <= num; i += 10) {\n if (num % (i + 1) == 0) return false;\n if (num % (i + 3) == 0) return false;\n if (num % (i + 7) == 0) return false;\n if (num % (i + 9) == 0) return false;\n }\n return true;\n }\n\n if (typeof num === 'number') {\n for (i = 0; arr.length < num; i++) if (isPrime(i)) arr.push(i);\n // first x primes\n if (showPrimes) return arr;\n // xth prime\n return arr.pop();\n }\n\n if (Array.isArray(num)) {\n for (i = num[0]; i <= num[1]; i++) if (isPrime(i)) arr.push(i);\n // primes between x .. y\n if (showPrimes) return arr;\n // number of primes between x .. y\n return arr.length;\n }\n}\n"
+ ],
+ "tests": [
+ "assert(typeof primeGenerator === 'function', 'message: primeGenerator
is a function.');",
+ "assert.deepEqual(primeGenerator(20, true), [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71], 'message: primeGenerator
is a function.');",
+ "assert.deepEqual(primeGenerator([100, 150], true), [101, 103, 107, 109, 113, 127, 131, 137, 139, 149], 'message: primeGenerator
is a function.');",
+ "assert.equal(primeGenerator([7700, 8000], false), 30, 'message: primeGenerator
is a function.');",
+ "assert.equal(primeGenerator(10000, false), 104729, 'message: primeGenerator
is a function.');"
+ ],
+ "id": "598ee8b91b410510ae82efef",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Factorial",
+ "type": "Waypoint",
+ "description": [
+ "
Write a function to return the factorial of a number.
", + "Factorial of a number is given by :
", + "n! = n * (n-1) * (n-2) * ..... * 1", + "", + "For example :", + "3! = 3*2*1 = 6", + "4! = 4*3*2*1 = 24", + "
", + "Note : ", + "0! = 1 ", + "
" + ], + "challengeSeed": [ + "function factorial (n) {", + " // Good luck!", + "}" + ], + "solutions": [ + "function factorial(n) {\n let sum = 1;\n while (n > 1) {\n sum *= n;\n n--;\n }\n return sum;\n}\n\n" + ], + "tail": [ + "const results=[6,120,3628800];" + ], + "tests": [ + "assert(typeof factorial === 'function', 'message:factorial
is a function.');",
+ "assert(typeof factorial(2) === 'number', 'message: factorial(2)
should return a number.');",
+ "assert.equal(factorial(3),results[0],\"message: factorial(3)
should return 6.\");",
+ "assert.equal(factorial(5),results[1],\"message: factorial(3)
should return 120.\");",
+ "assert.equal(factorial(10),results[2],\"message: factorial(3)
should return 3,628,800.\");"
+ ],
+ "id": "597b2b2a2702b44414742771",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Factors of an integer",
+ "type": "Waypoint",
+ "description": [
+ "Write a function that returns the factors of a positive integer.
These factors are the positive integers by which the number being factored can be divided to yield a positive integer result.
", + "///" + ], + "challengeSeed": [ + "function factors (num) {", + " // Good luck!", + "}" + ], + "solutions": [ + "function factors(num)\n{\n let n_factors = [], i, sqr=Math.floor(Math.sqrt(num));\n\n for (i = 1; i <=sqr ; i += 1)\n if (num % i === 0)\n {\n n_factors.push(i);\n if (num / i !== i)\n n_factors.push(num / i);\n }\n n_factors.sort(function(a, b){return a - b;});\n return n_factors;\n}\n" + ], + "tail": [ + "const ans=[[1,3,5,9,15,45],[1,53],[1,2,4,8,16,32,64]];" + ], + "tests": [ + "assert(typeof factors === 'function', 'message:factors
is a function.');",
+ "assert.deepEqual(factors(45), ans[0], 'message: factors(45)
should return [1,3,5,9,15,45]
.');",
+ "assert.deepEqual(factors(53), ans[1], 'message: factors(53)
should return [1,53]
.');",
+ "assert.deepEqual(factors(64), ans[2], 'message: factors(64)
should return [1,2,4,8,16,32,64]
.');"
+ ],
+ "id": "597f1e7fbc206f0e9ba95dc4",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Fibonacci sequence",
+ "type": "Waypoint",
+ "description": [
+ "Write a function to generate the nth Fibonacci number.
", + "///The nth Fibonacci number is given by :", + "///
Fn = Fn-1 + Fn-2
", + "///The first two terms of the series are 0, 1.
", + "///Hence, the series is : 0, 1, 1, 2, 3, 5, 8, 13...
", + "///" + ], + "challengeSeed": [ + "function fibonacci(n) {", + " // Good luck!", + "}" + ], + "solutions": [ + "function fibonacci(n) {\n let a = 0, b = 1, t;\n while (--n > 0) {\n t = a;\n a = b;\n b += t;\n }\n return a;\n}\n" + ], + "tail": [], + "tests": [ + "assert(typeof fibonacci === 'function', 'message:fibonacci
is a function.');",
+ "assert(typeof fibonacci(2) == 'number', 'message: fibonacci(2)
should return a number.');",
+ "assert.equal(fibonacci(3),1,\"message: fibonacci(3)
should return 1.\");",
+ "assert.equal(fibonacci(5),3,\"message: fibonacci(5)
should return 3.\");",
+ "assert.equal(fibonacci(10),34,\"message: fibonacci(10)
should return 34.\");"
+ ],
+ "id": "597f24c1dda4e70f53c79c81",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Hailstone sequence",
+ "type": "Waypoint",
+ "description": [
+ "The Hailstone sequence of numbers can be generated from a starting positive integer, n by:
", + " If n is 1 then the sequence ends.", + " If n is even then the next n of the sequence = n/2
",
+ " If n is odd then the next n of the sequence = (3 * n) + 1
The (unproven) Collatz conjecture is that the hailstone sequence for any starting number always terminates.
", + "The hailstone sequence is also known as hailstone numbers (because the values are usually subject to multiple descents and ascents like hailstones in a cloud), or as the Collatz sequence.
", + "Task:", + "Create a routine to generate the hailstone sequence for a number.", + "Use the routine to show that the hailstone sequence for the number 27 has 112 elements starting with27, 82, 41, 124
and ending with 8, 4, 2, 1
",
+ "Show the number less than 100,000 which has the longest hailstone sequence together with that sequence's length. (But don't show the actual sequence!)See also:",
+ " xkcd (humourous)."
+ ],
+ "challengeSeed": [
+ "// noprotect",
+ "function hailstoneSequence () {",
+ " const res = [];",
+ " // Good luck!",
+ "",
+ " return res;",
+ "}"
+ ],
+ "solutions": [
+ "// noprotect\nfunction hailstoneSequence () {\n const res = [];\n\n function hailstone(n) {\n const seq = [n];\n while (n > 1) {\n n = n % 2 ? 3 * n + 1 : n / 2;\n seq.push(n);\n }\n return seq;\n }\n\n const h = hailstone(27);\n const hLen = h.length;\n res.push([...h.slice(0, 4), ...h.slice(hLen - 4, hLen)]);\n\n let n = 0;\n let max = 0;\n for (let i = 100000; --i;) {\n const seq = hailstone(i);\n const sLen = seq.length;\n\n if (sLen > max) {\n n = i;\n max = sLen;\n }\n }\n res.push([max, n]);\n\n return res;\n}\n"
+ ],
+ "tail": [
+ "const res = [[27, 82, 41, 124, 8, 4, 2, 1], [351, 77031]];"
+ ],
+ "tests": [
+ "assert(typeof hailstoneSequence === 'function', 'message: hailstoneSequence
is a function.');",
+ "assert.deepEqual(hailstoneSequence(), res, 'message: hailstoneSequence()
should return [[27,82,41,124,8,4,2,1], [351, 77031]]
');"
+ ],
+ "id": "595608ff8bcd7a50bd490181",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Happy numbers",
+ "type": "Waypoint",
+ "description": [
+ "A happy number is defined by the following process:
", + "Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those numbers for which this process ends in 1 are happy numbers, while those that do not end in 1 are unhappy numbers.
", + "Implement a function that returns true if the number is happy, or false if not.
" + ], + "challengeSeed": [ + "function happy (number) {", + " // Good luck!", + "}" + ], + "solutions": [ + "function happy (number) {\n let m;\n let digit;\n const cycle = [];\n\n while (number !== 1 && cycle[number] !== true) {\n cycle[number] = true;\n m = 0;\n while (number > 0) {\n digit = number % 10;\n m += Math.pow(digit, 2);\n number = (number - digit) / 10;\n }\n number = m;\n }\n return (number === 1);\n}\n" + ], + "tests": [ + "assert(typeof happy === 'function', 'message:happy
is a function.');",
+ "assert(typeof happy(1) === 'boolean', 'message: happy(1)
should return a boolean.');",
+ "assert(happy(1), 'message: happy(1)
should return true.');",
+ "assert(!happy(2), 'message: happy(2)
should return false.');",
+ "assert(happy(7), 'message: happy(7)
should return true.');",
+ "assert(happy(10), 'message: happy(10)
should return true.');",
+ "assert(happy(13), 'message: happy(13)
should return true.');",
+ "assert(happy(19), 'message: happy(19)
should return true.');",
+ "assert(happy(23), 'message: happy(23)
should return true.');",
+ "assert(happy(28), 'message: happy(28)
should return true.');",
+ "assert(happy(31), 'message: happy(31)
should return true.');",
+ "assert(happy(32), 'message: happy(32)
should return true:.');",
+ "assert(!happy(33), 'message: happy(33)
should return false.');"
+ ],
+ "id": "594810f028c0303b75339ad1",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Harshad or Niven series",
+ "type": "Waypoint",
+ "description": [
+ "The Harshad or Niven numbers are positive integers ≥ 1 that are divisible by the sum of their digits.
For example, 42 is a Harshad number as 42 is divisible by (4 + 2) without remainder.
", + "Assume that the series is defined as the numbers in increasing order.", + "Task:", + "Implement a function to generate successive members of the Harshad sequence.
Use it to list the first twenty members of the sequence and list the first Harshad number greater than 1000.
" + ], + "challengeSeed": [ + "function isHarshadOrNiven () {", + " const res = {", + " firstTwenty: [],", + " firstOver1000: undefined", + " };", + " // Change after this line", + "", + " return res;", + "}" + ], + "solutions": [ + "function isHarshadOrNiven() {\n const res = {\n firstTwenty: [],\n firstOver1000: undefined\n };\n\n function isHarshad(n) {\n let s = 0;\n const nStr = n.toString();\n for (let i = 0; i < nStr.length; ++i) {\n s += parseInt(nStr.charAt(i), 10);\n }\n return n % s === 0;\n }\n\n let count = 0;\n const harshads = [];\n\n for (let n = 1; count < 20; ++n) {\n if (isHarshad(n)) {\n count++;\n harshads.push(n);\n }\n }\n\n res.firstTwenty = harshads;\n\n let h = 1000;\n while (!isHarshad(++h));\n res.firstOver1000 = h;\n\n return res;\n}\n" + ], + "tail": [ + "const res = {", + " firstTwenty: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 18, 20, 21, 24, 27, 30, 36, 40, 42],", + " firstOver1000: 1002", + "};" + ], + "tests": [ + "assert(typeof isHarshadOrNiven === 'function', 'message:isHarshadOrNiven
is a function.');",
+ "assert.deepEqual(isHarshadOrNiven(), res, 'message: isHarshadOrNiven()
should return {\"firstTwenty\": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 18, 20, 21, 24, 27, 30, 36, 40, 42],\"firstOver1000\": 1002}
');"
+ ],
+ "id": "595668ca4cfe1af2fb9818d4",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Hash from two arrays",
+ "type": "Waypoint",
+ "description": [
+ "Task:",
+ "Using two Arrays of equal length, create a Hash object where the elements from one array (the keys) are linked to the elements of the other (the values)
", + "Related task:", + " Associative arrays/Creation" + ], + "challengeSeed": [ + "function arrToObj (keys, vals) {", + " // Good luck!", + " return true;", + "}" + ], + "solutions": [ + "function arrToObj (keys, vals) {\n return keys.reduce((map, key, index) => {\n map[key] = vals[index];\n return map;\n }, {});\n}" + ], + "tail": [ + "const testCases = [", + " [[1, 2, 3, 4, 5], ['a', 'b', 'c', 'd', 'e']],", + " [[1, 2, 3, 4, 5], ['a', 'b', 'c', 'd']],", + " [[1, 2, 3], ['a', 'b', 'c', 'd', 'e']],", + " [['a', 'b', 'c', 'd', 'e'], [1, 2, 3, 4, 5]],", + " [['a', 'b', 'c', 'd', 'e'], [1, 2, 3, 4]],", + " [['a', 'b', 'c'], [1, 2, 3, 4, 5]]", + "];", + "", + "const res = [", + " { 1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e' },", + " { 1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: undefined },", + " { 1: 'a', 2: 'b', 3: 'c' },", + " { a: 1, b: 2, c: 3, d: 4, e: 5 },", + " { a: 1, b: 2, c: 3, d: 4, e: undefined },", + " { a: 1, b: 2, c: 3 }", + "];" + ], + "tests": [ + "assert(typeof arrToObj === 'function', 'message:arrToObj
is a function.');",
+ "assert.deepEqual(arrToObj(...testCases[0]), res[0], 'message: arrToObj([1, 2, 3, 4, 5], [\"a\", \"b\", \"c\", \"d\", \"e\"])
should return { 1: \"a\", 2: \"b\", 3: \"c\", 4: \"d\", 5: \"e\" }
');",
+ "assert.deepEqual(arrToObj(...testCases[1]), res[1], 'message: arrToObj([1, 2, 3, 4, 5], [\"a\", \"b\", \"c\", \"d\"])
should return { 1: \"a\", 2: \"b\", 3: \"c\", 4: \"d\", 5: undefined }
');",
+ "assert.deepEqual(arrToObj(...testCases[2]), res[2], 'message: arrToObj([1, 2, 3], [\"a\", \"b\", \"c\", \"d\", \"e\"])
should return { 1: \"a\", 2: \"b\", 3: \"c\" }
');",
+ "assert.deepEqual(arrToObj(...testCases[3]), res[3], 'message: arrToObj([\"a\", \"b\", \"c\", \"d\", \"e\"], [1, 2, 3, 4, 5])
should return { \"a\": 1, \"b\": 2, \"c\": 3 , \"d\": 4, \"e\": 5 }
');",
+ "assert.deepEqual(arrToObj(...testCases[4]), res[4], 'message: arrToObj([\"a\", \"b\", \"c\", \"d\", \"e\"], [1, 2, 3, 4])
should return { \"a\": 1, \"b\": 2, \"c\": 3 , \"d\": 4, \"e\": undefined }
');",
+ "assert.deepEqual(arrToObj(...testCases[5]), res[5], 'message: arrToObj([\"a\", \"b\", \"c\"], [1, 2, 3, 4, 5])
should return { \"a\": 1, \"b\": 2, \"c\": 3 }
');"
+ ],
+ "id": "595671d4d2cdc305f0d5b36f",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Hash join",
+ "type": "Waypoint",
+ "description": [
+ "An inner join is an operation that combines two data tables into one table, based on matching column values. The simplest way of implementing this operation is the nested loop join algorithm, but a more scalable alternative is the hash join algorithm.
", + "Implement the \"hash join\" algorithm, and demonstrate that it passes the test-case listed below.
You should represent the tables as data structures that feel natural in your programming language.
", + "The \"hash join\" algorithm consists of two steps:
", + "Hash phase: Create a multimap from one of the two tables, mapping from each join column value to all the rows that contain it.", + " The multimap must support hash-based lookup which scales better than a simple linear search, because that's the whole point of this algorithm.", + " Ideally we should create the multimap for the smaller table, thus minimizing its creation time and memory size.", + "Join phase: Scan the other table, and find matching rows by looking in the multimap created before.", + "In pseudo-code, the algorithm could be expressed as follows:
", + "", + "let A = the first input table (or ideally, the larger one)", + "let B = the second input table (or ideally, the smaller one)", + "let jA = the join column ID of table A", + "let jB = the join column ID of table B", + "let MB = a multimap for mapping from single values to multiple rows of table B (starts out empty)", + "let C = the output table (starts out empty)", + "for each row b in table B:", + " place b in multimap MB under key b(jB)", + "for each row a in table A:", + " for each row b in multimap MB under key a(jA):", + " let c = the concatenation of row a and row b", + " place row c in table C", + "", + "Test-case", + "
Input
", + "",
+ "
| ",
+ "", + " |
Output
", + "A.Age | ", + "A.Name | ", + "B.Character | ", + "B.Nemesis", + " |
---|---|---|---|
27 | ", + "Jonah | ", + "Jonah | ", + "Whales", + " |
27 | ", + "Jonah | ", + "Jonah | ", + "Spiders", + " |
18 | ", + "Alan | ", + "Alan | ", + "Ghosts", + " |
18 | ", + "Alan | ", + "Alan | ", + "Zombies", + " |
28 | ", + "Glory | ", + "Glory | ", + "Buffy", + " |
28 | ", + "Alan | ", + "Alan | ", + "Ghosts", + " |
28 | ", + "Alan | ", + "Alan | ", + "Zombies", + " |
The order of the rows in the output table is not significant.
", + "If you're using numerically indexed arrays to represent table rows (rather than referring to columns by name), you could represent the output rows in the form [[27, \"Jonah\"], [\"Jonah\", \"Whales\"]]
.
hashJoin
is a function.');",
+ "assert.deepEqual(hashJoin(hash1, hash2), res, 'message: hashJoin([{ age: 27, name: \"Jonah\" }, { age: 18, name: \"Alan\" }, { age: 28, name: \"Glory\" }, { age: 18, name: \"Popeye\" }, { age: 28, name: \"Alan\" }], [{ character: \"Jonah\", nemesis: \"Whales\" }, { character: \"Jonah\", nemesis: \"Spiders\" }, { character: \"Alan\", nemesis: \"Ghosts\" }, { character:\"Alan\", nemesis: \"Zombies\" }, { character: \"Glory\", nemesis: \"Buffy\" }, { character: \"Bob\", nemesis: \"foo\" }])
should return [{\"A_age\": 27,\"A_name\": \"Jonah\", \"B_character\": \"Jonah\", \"B_nemesis\": \"Whales\"}, {\"A_age\": 27,\"A_name\": \"Jonah\", \"B_character\": \"Jonah\", \"B_nemesis\": \"Spiders\"}, {\"A_age\": 18,\"A_name\": \"Alan\", \"B_character\": \"Alan\", \"B_nemesis\": \"Ghosts\"}, {\"A_age\": 18,\"A_name\": \"Alan\", \"B_character\": \"Alan\", \"B_nemesis\": \"Zombies\"}, {\"A_age\": 28,\"A_name\": \"Glory\", \"B_character\": \"Glory\", \"B_nemesis\": \"Buffy\"}, {\"A_age\": 28,\"A_name\": \"Alan\", \"B_character\": \"Alan\", \"B_nemesis\": \"Ghosts\"}, {\"A_age\": 28,\"A_name\": \"Alan\", \"B_character\": \"Alan\", \"B_nemesis\": \"Zombies\"}]
');"
+ ],
+ "id": "5956795bc9e2c415eb244de1",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Heronian triangles",
+ "type": "Waypoint",
+ "description": [
+ "Hero's formula for the area of a triangle given the length of its three sides a, b, and c is given by:
$$A = \\sqrt{s(s-a)(s-b)(s-c)},$$
where s is half the perimeter of the triangle; that is,
$$s=\\frac{a+b+c}{2}.$$
", + "Heronian triangles are triangles whose sides and area are all integers.
", + "An example is the triangle with sides 3, 4, 5 whose area is 6 (and whose perimeter is 12).
", + "Note that any triangle whose sides are all an integer multiple of 3, 4, 5; such as 6, 8, 10, will also be a Heronian triangle.
Define a Primitive Heronian triangle as a Heronian triangle where the greatest common divisor
", + "of all three sides is 1 (unity).
This will exclude, for example, triangle 6, 8, 10.
", + "Task:", + "Implement a function based on Hero's formula that returns the first nth
ordered triangles in an array of arrays.
heronianTriangle
is a function.');",
+ "assert.deepEqual(heronianTriangle(testCases[0]), res[0], 'message: heronianTriangle()
should return [[3, 4, 5], [5, 5, 6], [5, 5, 8], [4, 13, 15], [5, 12, 13], [9, 10, 17], [3, 25, 26], [7, 15, 20], [10, 13, 13], [8, 15, 17]]
');",
+ "assert.deepEqual(heronianTriangle(testCases[1]), res[1], 'message: heronianTriangle()
should return [[3, 4, 5], [5, 5, 6], [5, 5, 8], [4, 13, 15], [5, 12, 13], [9, 10, 17], [3, 25, 26], [7, 15, 20], [10, 13, 13], [8, 15, 17], [13, 13, 24], [6, 25, 29], [11, 13, 20], [5, 29, 30], [13, 14, 15]],
');",
+ "assert.deepEqual(heronianTriangle(testCases[2]), res[2], 'message: heronianTriangle()
should return [[3, 4, 5], [5, 5, 6], [5, 5, 8], [4, 13, 15], [5, 12, 13], [9, 10, 17], [3, 25, 26], [7, 15, 20], [10, 13, 13], [8, 15, 17], [13, 13, 24], [6, 25, 29], [11, 13, 20], [5, 29, 30], [13, 14, 15], [10, 17, 21], [7, 24, 25], [8, 29, 35], [12, 17, 25], [4, 51, 53]],
');",
+ "assert.deepEqual(heronianTriangle(testCases[3]), res[3], 'message: heronianTriangle()
should return [[3, 4, 5], [5, 5, 6], [5, 5, 8], [4, 13, 15], [5, 12, 13], [9, 10, 17], [3, 25, 26], [7, 15, 20], [10, 13, 13], [8, 15, 17], [13, 13, 24], [6, 25, 29], [11, 13, 20], [5, 29, 30], [13, 14, 15], [10, 17, 21], [7, 24, 25], [8, 29, 35], [12, 17, 25], [4, 51, 53], [19, 20, 37],[16, 17, 17], [17, 17, 30], [16, 25, 39], [13, 20, 21]]
');"
+ ],
+ "id": "595b98f8b5a2245e243aa831",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Hofstadter Figure-Figure sequences",
+ "type": "Waypoint",
+ "description": [
+ "These two sequences of positive integers are defined as:
", + "$$R(1)=1\\ ;\\ S(1)=2 \\\\R(n)=R(n-1)+S(n-1), \\quad n>1.$$
", + "The sequence $S(n)$ is further defined as the sequence of positive integers not present in $R(n)$.
Sequence $R$ starts:
", + "1, 3, 7, 12, 18, ...
", + "Sequence $S$ starts:
", + "2, 4, 5, 6, 8, ...
", + "Task:", + "Create two functions named ffr and ffs that when given n return R(n) or S(n) respectively.(Note that R(1) = 1 and S(1) = 2 to avoid off-by-one errors).", + "No maximum value for n should be assumed.", + "Sloane's A005228 and A030124.", + "Wolfram MathWorld", + "Wikipedia: Hofstadter Figure-Figure sequences." + ], + "challengeSeed": [ + "// noprotect", + "function ffr(n) {", + " return n;", + "}", + "", + "function ffs(n) {", + " return n;", + "}" + ], + "solutions": [ + "// noprotect\nconst R = [null, 1];\nconst S = [null, 2];\n\nfunction extendSequences (n) {\n let current = Math.max(R[R.length - 1], S[S.length - 1]);\n let i;\n while (R.length <= n || S.length <= n) {\n i = Math.min(R.length, S.length) - 1;\n current += 1;\n if (current === R[i] + S[i]) {\n R.push(current);\n } else {\n S.push(current);\n }\n }\n}\n\nfunction ffr (n) {\n extendSequences(n);\n return R[n];\n}\n\nfunction ffs (n) {\n extendSequences(n);\n return S[n];\n}\n" + ], + "tail": [ + "const ffrParamRes = [[10, 69], [50, 1509], [100, 5764], [1000, 526334]];", + "const ffsParamRes = [[10, 14], [50, 59], [100, 112], [1000, 1041]];", + "" + ], + "tests": [ + "assert(typeof ffr === 'function', 'message:ffr
is a function.');",
+ "assert(typeof ffs === 'function', 'message: ffs
is a function.');",
+ "assert(Number.isInteger(ffr(1)), 'message: ffr
should return integer.');",
+ "assert(Number.isInteger(ffs(1)), 'message: ffs
should return integer.');",
+ "assert.equal(ffr(ffrParamRes[0][0]), ffrParamRes[0][1], 'message: ffr()
should return 69
');",
+ "assert.equal(ffr(ffrParamRes[1][0]), ffrParamRes[1][1], 'message: ffr()
should return 1509
');",
+ "assert.equal(ffr(ffrParamRes[2][0]), ffrParamRes[2][1], 'message: ffr()
should return 5764
');",
+ "assert.equal(ffr(ffrParamRes[3][0]), ffrParamRes[3][1], 'message: ffr()
should return 526334
');",
+ "assert.equal(ffs(ffsParamRes[0][0]), ffsParamRes[0][1], 'message: ffs()
should return 14
');",
+ "assert.equal(ffs(ffsParamRes[1][0]), ffsParamRes[1][1], 'message: ffs()
should return 59
');",
+ "assert.equal(ffs(ffsParamRes[2][0]), ffsParamRes[2][1], 'message: ffs()
should return 112
');",
+ "assert.equal(ffs(ffsParamRes[3][0]), ffsParamRes[3][1], 'message: ffs()
should return 1041
');"
+ ],
+ "id": "59622f89e4e137560018a40e",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Hofstadter Q sequence",
+ "type": "Waypoint",
+ "description": [
+ "The Hofstadter Q sequence is defined as:
", + "$Q(1)=Q(2)=1, \\\\ Q(n)=Q\\big(n-Q(n-1)\\big)+Q\\big(n-Q(n-2)), \\quad n>2.$
", + "It is defined like the Fibonacci sequence, but whereas the next term in the Fibonacci sequence is the sum of the previous two terms, in the Q sequence the previous two terms tell you how far to go back in the Q sequence to find the two numbers to sum to make the next term of the sequence.
", + "Task:", + "Implement the Hofstadter Q Sequence equation into JavaScript" + ], + "challengeSeed": [ + "function hofstadterQ (n) {", + " // Good luck!", + " return n;", + "}" + ], + "solutions": [ + "function hofstadterQ (n) {\n const memo = [1, 1, 1];\n const Q = function (i) {\n let result = memo[i];\n if (typeof result !== 'number') {\n result = Q(i - Q(i - 1)) + Q(i - Q(i - 2));\n memo[i] = result;\n }\n return result;\n };\n return Q(n);\n}\n" + ], + "tail": [ + "const testCase = [1000, 1500, 2000, 2500];", + "const res = [502, 755, 1005, 1261];" + ], + "tests": [ + "assert(typeof hofstadterQ === 'function', 'message:hofstadterQ
is a function.');",
+ "assert(Number.isInteger(hofstadterQ(1000)), 'message: hofstadterQ()
should return integer
');",
+ "assert.equal(hofstadterQ(testCase[0]), res[0], 'message: hofstadterQ(1000)
should return 502
');",
+ "assert.equal(hofstadterQ(testCase[1]), res[1], 'message: hofstadterQ(1500)
should return 755
');",
+ "assert.equal(hofstadterQ(testCase[2]), res[2], 'message: hofstadterQ(2000)
should return 1005
');",
+ "assert.equal(hofstadterQ(testCase[3]), res[3], 'message: hofstadterQ(2500)
should return 1261
');"
+ ],
+ "id": "59637c4d89f6786115efd814",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "S-Expressions",
+ "type": "Waypoint",
+ "description": [
+ "", + "S-Expressions are one convenient way to parse and store data.", + "
", + "Task:", + "", + " Write a simple reader/parser for S-Expressions that handles quoted and unquoted strings, integers and floats.", + "
", + "", + "The function should read a single but nested S-Expression from a string and", + "return it as a (nested) array.", + "
", + "", + " Newlines and other whitespace may be ignored unless contained within a quoted string.", + "
", + "“()” inside quoted strings are not interpreted, but treated as part of the string.", + "
", + "", + "Handling escaped quotes inside a string is optional; thus “(foo\"bar)” maybe treated as a string “foo\"bar”, or as an error.", + "
", + "", + "For this, the reader need not recognize “\\” for escaping, but should, in addition, recognize numbers if the language has appropriate datatypes.", + "
", + "", + "Note that with the exception of “()\"” (“\\” if escaping is supported) and whitespace there are no special characters. Anything else is allowed without quotes.", + "
", + "The reader should be able to read the following input
", + "", + "
", + " ((data \"quoted data\" 123 4.5)", + " (data (!@# (4.5) \"(more\" \"data)\")))", + "", + "", + "
", + "and turn it into a native datastructure. (see the", + "Pike, ", + "Python and", + "Ruby implementations", + "for examples of native data structures.)", + "
" + ], + "challengeSeed": [ + "function parseSexpr(str) {", + " // Good luck!", + " return true;", + "}" + ], + "solutions": [ + "function parseSexpr(str) {\n const t = str.match(/\\s*(\"[^\"]*\"|\\(|\\)|\"|[^\\s()\"]+)/g);\n for (var o, c = 0, i = t.length - 1; i >= 0; i--) {\n var n,\n ti = t[i].trim();\n if (ti == '\"') return;\n else if (ti == '(') t[i] = '[', c += 1;\n else if (ti == ')') t[i] = ']', c -= 1;\n else if ((n = +ti) == ti) t[i] = n;\n else t[i] = `'${ti.replace('\\'', '\\\\\\'')}'`;\n if (i > 0 && ti != ']' && t[i - 1].trim() != '(') t.splice(i, 0, ',');\n if (!c) if (!o) o = true; else return;\n }\n return c ? undefined : eval(t.join(''));\n}\n" + ], + "tail": [ + "const simpleSExpr = '(data1 data2 data3)';", + "const simpleSolution = ['data1', 'data2', 'data3'];", + "", + "const basicSExpr = '((data \"quoted data\" 123 4.5) (data (!@# (4.5) \"(more\" \"data)\")))';", + "const basicSolution = [[\"data\",\"\\\"quoted data\\\"\",123,4.5],[\"data\",[\"!@#\",[4.5],\"\\\"(more\\\"\",\"\\\"data)\\\"\"]]];" + ], + "tests": [ + "assert(typeof parseSexpr === 'function', 'message:parseSexpr
is a function.');",
+ "assert.deepEqual(parseSexpr(simpleSExpr), simpleSolution, \"message: parseSexpr('(data1 data2 data3)')
should return ['data1', 'data2', 'data3']\");",
+ "assert.deepEqual(parseSexpr(basicSExpr), basicSolution, \"message: parseSexpr('(data1 data2 data3)')
should return an array with 3 elements\");"
+ ],
+ "id": "59667989bf71cf555dd5d2ff",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Taxicab numbers",
+ "type": "Waypoint",
+ "description": [
+ "A taxicab number",
+ " (the definition that is being used here) is a positive integer that can be expressed as the sum of two positive cubes in more than one way.",
+ "The first taxicab number is 1729, which is:",
+ "13 + 123 and",
+ "93 + 103.",
+ "Taxicab numbers are also known as:",
+ " * taxi numbers",
+ " * taxi-cab numbers",
+ " * taxi cab numbers",
+ " * Hardy-Ramanujan numbers",
+ "Task:",
+ "Write a function that returns the lowest N taxicab numbers.",
+ "For each of the taxicab numbers, show the number as well as it's constituent cubes.",
+ "See also:",
+ "[http://oeis.org/A001235 A001235 taxicab numbers] on The On-Line Encyclopedia of Integer Sequences.",
+ " Hardy-Ramanujan Number on MathWorld.",
+ " taxicab number on MathWorld.",
+ " taxicab number on Wikipedia."
+ ],
+ "challengeSeed": [
+ "function taxicabNumbers (n) {",
+ " // Good luck!",
+ " return true;",
+ "}"
+ ],
+ "solutions": [
+ "function taxicabNumbers(nNumbers) {\n const cubeN = [];\n const s3s = {};\n\n const e = 100;\n for (let n = 1; n < e; n += 1) {\n cubeN[n] = n * n * n;\n }\n\n for (let a = 1; a < e - 1; a += 1) {\n const a3 = cubeN[a];\n for (let b = a; b < e; b += 1) {\n const b3 = cubeN[b];\n const s3 = a3 + b3;\n\n let abs = s3s[s3];\n if (!abs) {\n s3s[s3] = abs = [];\n }\n abs.push([a, b]);\n }\n }\n\n let i = 0;\n const res = [];\n Object.keys(s3s).forEach(s3 => {\n const abs = s3s[s3];\n if (abs.length >= 2) { // No two cube pairs found\n i += 1;\n if (i <= nNumbers) {\n res.push(s3);\n }\n }\n });\n return res.map(item => parseInt(item, 10));\n}\n"
+ ],
+ "tail": [
+ "const res4 = [1729, 4104, 13832, 20683];",
+ "const res25 = [",
+ " 1729, 4104, 13832, 20683, 32832, 39312, 40033, 46683, 64232, 65728, 110656,",
+ " 110808, 134379, 149389, 165464, 171288, 195841, 216027, 216125, 262656, 314496, 320264, 327763,",
+ " 373464, 402597",
+ "];",
+ "",
+ "const res39From20To29 = [314496, 320264, 327763, 373464, 402597, 439101, 443889, 513000, 513856];"
+ ],
+ "tests": [
+ "assert(typeof taxicabNumbers === 'function', 'message: taxicabNumbers
is a function.');",
+ "assert(typeof taxicabNumbers(2) === 'object', 'message: taxicabNumbers
should return an array.');",
+ "assert(typeof taxicabNumbers(100)[0] === 'number', 'message: taxicabNumbers
should return an array of numbers.');",
+ "assert.deepEqual(taxicabNumbers(4), res4, 'message: taxicabNumbers(4)
must return [1729, 4104, 13832, 20683].');",
+ "assert.deepEqual(taxicabNumbers(25), res25, 'message: taxicabNumbers(25) should return [1729, 4104, 13832, 20683, 32832, 39312, 40033, 46683, 64232, 65728, 110656, 110808, 134379, 149389, 165464, 171288, 195841, 216027, 216125, 262656, 314496, 320264, 327763, 373464, 402597]');",
+ "assert.deepEqual(taxicabNumbers(39).slice(20, 29), res39From20To29, 'message: taxicabNumbers(39) resulting numbers from 20 - 29 should be [314496,320264,327763,373464,402597,439101,443889,513000,513856].');"
+ ],
+ "id": "594ecc0d9a8cf816e3340187",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Tokenize a string with escaping",
+ "type": "Waypoint",
+ "description": [
+ "", + "Write a function or program that can split a string at each non-escaped occurrence of a separator character.", + "
", + "", + "It should accept three input parameters:", + "
", + " The string", + " The separator character", + " The escape character", + "It should output a list of strings.
", + "Rules for splitting:
", + " The fields that were separated by the separators, become the elements of the output list.", + " Empty fields should be preserved, even at the start and end.", + "Rules for escaping:
", + " \"Escaped\" means preceded by an occurrence of the escape character that is not already escaped itself.", + " When the escape character precedes a character that has no special meaning, it still counts as an escape (but does not do anything special).", + " Each occurrences of the escape character that was used to escape something, should not become part of the output.", + "Demonstrate that your function satisfies the following test-case:", + " Given string
one^|uno||three^^^^|four^^^|^cuatro|and using", + "
|as a separator and
^as escape character, your", + " function should output the following array:", + "", + "
", + " ['one|uno', '', 'three^^', 'four^|quatro', '']", + "" + ], + "challengeSeed": [ + "function tokenize(str, esc, sep) {", + " return true;", + "}" + ], + "solutions": [ + "// tokenize :: String -> Character -> Character -> [String]\nfunction tokenize(str, charDelim, charEsc) {\n const dctParse = str.split('')\n .reduce((a, x) => {\n const blnEsc = a.esc;\n const blnBreak = !blnEsc && x === charDelim;\n const blnEscChar = !blnEsc && x === charEsc;\n\n return {\n esc: blnEscChar,\n token: blnBreak ? '' : (\n a.token + (blnEscChar ? '' : x)\n ),\n list: a.list.concat(blnBreak ? a.token : [])\n };\n }, {\n esc: false,\n token: '',\n list: []\n });\n\n return dctParse.list.concat(\n dctParse.token\n );\n}\n" + ], + "tail": [ + "const testStr1 = 'one^|uno||three^^^^|four^^^|^cuatro|';", + "const res1 = ['one|uno', '', 'three^^', 'four^|cuatro', ''];", + "", + "// TODO add more tests", + "const testStr2 = 'a@&bcd&ef&&@@hi';", + "const res2 = ['a&bcd', 'ef', '', '@hi'];" + ], + "tests": [ + "assert(typeof tokenize === 'function', 'message:
tokenize
is a function.');",
+ "assert(typeof tokenize('a', 'b', 'c') === 'object', 'message: tokenize
should return an array.');",
+ "assert.deepEqual(tokenize(testStr1, '|', '^'), res1, \"message: tokenize('one^|uno||three^^^^|four^^^|^cuatro|', '|', '^')
should return ['one|uno', '', 'three^^', 'four^|cuatro', '']\");",
+ "assert.deepEqual(tokenize(testStr2, '&', '@'), res2, \"message: tokenize('a@&bcd&ef&&@@hi', '&', '@')
should return ['a&bcd', 'ef', '', '@hi']\");"
+ ],
+ "id": "594faaab4e2a8626833e9c3d",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Topological sort",
+ "type": "Waypoint",
+ "description": [
+ "", + "Given a mapping between items, and items they depend on, a ", + "topological sort orders ", + "items so that no item precedes an item it depends upon.", + "
", + "", + "The compiling of a library in the ", + "VHDL language", + "has the constraint that a library must be compiled after any library it depends on.", + "
", + "Task:", + "", + "Write a function that will return a valid compile order of VHDL libraries from their dependencies.", + "
", + " Assume library names are single words. ", + " Items mentioned as only dependents have no dependents of their own, but their order of compiling must be given.", + " Any self dependencies should be ignored. ", + " Any un-orderable dependencies should be ignored.", + "Use the following data as an example:
", + "", + "LIBRARY LIBRARY DEPENDENCIES", + "======= ====================", + "des_system_lib std synopsys std_cell_lib des_system_lib dw02 dw01 ramlib ieee", + "dw01 ieee dw01 dware gtech", + "dw02 ieee dw02 dware", + "dw03 std synopsys dware dw03 dw02 dw01 ieee gtech", + "dw04 dw04 ieee dw01 dware gtech", + "dw05 dw05 ieee dware", + "dw06 dw06 ieee dware", + "dw07 ieee dware", + "dware ieee dware", + "gtech ieee gtech", + "ramlib std ieee", + "std_cell_lib ieee std_cell_lib", + "synopsys", + "", + "
",
+ "Note: the above data would be un-orderable if, for example, dw04
is added to the list of dependencies of dw01
.",
+ "
There are two popular algorithms for topological sorting:
", + "", + " Kahn's 1962 topological sort, and depth-first search:", + " topological sort", + "
", + "", + " Jason Sachs:", + " ", + " \"Ten little algorithms, part 4: topological sort\"", + " .", + "
" + ], + "challengeSeed": [ + "function topologicalSort(libs) {", + " // Good luck!", + " return true;", + "}" + ], + "solutions": [ + "function topologicalSort(libs) {\n // A map of the input data, with the keys as the packages, and the values as\n // and array of packages on which it depends.\n const D = libs\n .split('\\n')\n .map(e => e.split(' ').filter(ep => ep !== ''))\n .reduce((p, c) =>\n p.set(c[0], c.filter((e, i) => (i > 0 && e !== c[0] ? e : null))), new Map());\n [].concat(...D.values()).forEach(e => {\n D.set(e, D.get(e) || []);\n });\n\n // The above map rotated so that it represents a DAG of the form\n // Map {\n // A => [ A, B, C],\n // B => [C],\n // C => []\n // }\n // where each key represents a node, and the array contains the edges.\n const G = [...D.keys()].reduce((p, c) =>\n p.set(\n c,\n [...D.keys()].filter(e => D.get(e).includes(c))),\n new Map()\n );\n\n // An array of leaf nodes; nodes with 0 in degrees.\n const Q = [...D.keys()].filter(e => D.get(e).length === 0);\n\n // The result array.\n const S = [];\n while (Q.length) {\n const u = Q.pop();\n S.push(u);\n G.get(u).forEach(v => {\n D.set(v, D.get(v).filter(e => e !== u));\n if (D.get(v).length === 0) {\n Q.push(v);\n }\n });\n }\n\n return S;\n}\n" + ], + "tail": [ + "const libsSimple =", + " `aaa bbb", + " bbb`;", + "", + "const libsVHDL =", + " `des_system_lib std synopsys std_cell_lib des_system_lib dw02 dw01 ramlib ieee", + " dw01 ieee dw01 dware gtech", + " dw02 ieee dw02 dware", + " dw03 std synopsys dware dw03 dw02 dw01 ieee gtech", + " dw04 dw04 ieee dw01 dware gtech", + " dw05 dw05 ieee dware", + " dw06 dw06 ieee dware", + " dw07 ieee dware", + " dware ieee dware", + " gtech ieee gtech", + " ramlib std ieee", + " std_cell_lib ieee std_cell_lib", + " synopsys`;", + "", + "const solutionVHDL = [", + " 'ieee', 'std_cell_lib', 'gtech', 'dware', 'dw07', 'dw06',", + " 'dw05', 'dw02', 'dw01', 'dw04', 'std', 'ramlib', 'synopsys',", + " 'dw03', 'des_system_lib'", + "];", + "", + "const libsCustom =", + " `a b c d", + " b c d", + " d c", + " c base", + " base`;", + "const solutionCustom = ['base', 'c', 'd', 'b', 'a'];", + "", + "const libsUnorderable =", + " `TestLib Base MainLib", + " MainLib TestLib", + " Base`;", + "", + "const solutionUnorderable = ['Base'];" + ], + "tests": [ + "assert(typeof topologicalSort === 'function', 'message:topologicalSort
is a function.');",
+ "assert.deepEqual(topologicalSort(libsSimple), ['bbb', 'aaa'], 'message: topologicalSort
must return correct library order..');",
+ "assert.deepEqual(topologicalSort(libsVHDL), solutionVHDL, 'message: topologicalSort
must return correct library order..');",
+ "assert.deepEqual(topologicalSort(libsCustom), solutionCustom, 'message: topologicalSort
must return correct library order..');",
+ "assert.deepEqual(topologicalSort(libsUnorderable), solutionUnorderable, 'message: topologicalSort
must ignore unorderable dependencies..');"
+ ],
+ "id": "594fa2746886f41f7d8bf225",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Top rank per group",
+ "type": "Waypoint",
+ "description": [
+ "Task:",
+ "Find the top N ranked data in each group, where N is provided as a parameter. Name of the rank and the group are also provided as parameter.
", + "Given the following data:", + "", + "[", + " { name: 'Tyler Bennett', id: 'E10297', salary: 32000, dept: 'D101' },", + " { name: 'John Rappl', id: 'E21437', salary: 47000, dept: 'D050' },", + " { name: 'George Woltman', id: 'E00127', salary: 53500, dept: 'D101' },", + " { name: 'Adam Smith', id: 'E63535', salary: 18000, dept: 'D202' },", + " { name: 'Claire Buckman', id: 'E39876', salary: 27800, dept: 'D202' },", + " { name: 'David McClellan', id: 'E04242', salary: 41500, dept: 'D101' },", + " { name: 'Rich Holcomb', id: 'E01234', salary: 49500, dept: 'D202' },", + " { name: 'Nathan Adams', id: 'E41298', salary: 21900, dept: 'D050' },", + " { name: 'Richard Potter', id: 'E43128', salary: 15900, dept: 'D101' },", + " { name: 'David Motsinger', id: 'E27002', salary: 19250, dept: 'D202' },", + " { name: 'Tim Sampair', id: 'E03033', salary: 27000, dept: 'D101' },", + " { name: 'Kim Arlich', id: 'E10001', salary: 57000, dept: 'D190' },", + " { name: 'Timothy Grove', id: 'E16398', salary: 29900, dept: 'D190' }", + "];", + "", + "one could rank top 10 employees in each department by calling", + "
topRankPerGroup(10, data, 'dept', 'salary')
",
+ "Given the following data:",
+ "", + "[", + " { name: 'Friday 13th', genre: 'horror', rating: 9.9 },", + " { name: \"Nightmare on Elm's Street\", genre: 'horror', rating: 5.7 },", + " { name: 'Titanic', genre: 'drama', rating: 7.3 },", + " { name: 'Maze Runner', genre: 'scifi', rating: 7.1 },", + " { name: 'Blade runner', genre: 'scifi', rating: 8.9 }", + "];", + "", + "one could rank the top-rated movie in each genre by calling", + "
topRankPerGroup(1, data, 'genre', 'rating')
"
+ ],
+ "challengeSeed": [
+ "function topRankPerGroup(n, data, groupName, rankName) {",
+ " // Good luck!",
+ " return true;",
+ "}"
+ ],
+ "solutions": [
+ "\nconst collectDept = function (arrOfObj, groupName) {\n const collect = arrOfObj.reduce((rtnObj, obj) => {\n if (rtnObj[obj[groupName]] === undefined) {\n rtnObj[obj[groupName]] = [];\n }\n rtnObj[obj[groupName]].push(obj);\n return rtnObj;\n }, {} // initial value to reduce\n );\n\n return Object.keys(collect).sort().map(key => collect[key]);\n};\n\nconst sortRank = function (arrOfRankArrs, rankName) {\n return arrOfRankArrs.map(item => item.sort((a, b) => {\n if (a[rankName] > b[rankName]) { return -1; }\n if (a[rankName] < b[rankName]) { return 1; }\n return 0;\n }));\n};\n\nfunction topRankPerGroup(n, data, groupName, rankName) {\n if (n < 0) { return; }\n return sortRank(collectDept(data, groupName),\n rankName).map(list => list.slice(0, n));\n}\n"
+ ],
+ "tail": [
+ "const testData1 = [",
+ " { name: 'Tyler Bennett', id: 'E10297', salary: 32000, dept: 'D101' },",
+ " { name: 'John Rappl', id: 'E21437', salary: 47000, dept: 'D050' },",
+ " { name: 'George Woltman', id: 'E00127', salary: 53500, dept: 'D101' },",
+ " { name: 'Adam Smith', id: 'E63535', salary: 18000, dept: 'D202' },",
+ " { name: 'Claire Buckman', id: 'E39876', salary: 27800, dept: 'D202' },",
+ " { name: 'David McClellan', id: 'E04242', salary: 41500, dept: 'D101' },",
+ " { name: 'Rich Holcomb', id: 'E01234', salary: 49500, dept: 'D202' },",
+ " { name: 'Nathan Adams', id: 'E41298', salary: 21900, dept: 'D050' },",
+ " { name: 'Richard Potter', id: 'E43128', salary: 15900, dept: 'D101' },",
+ " { name: 'David Motsinger', id: 'E27002', salary: 19250, dept: 'D202' },",
+ " { name: 'Tim Sampair', id: 'E03033', salary: 27000, dept: 'D101' },",
+ " { name: 'Kim Arlich', id: 'E10001', salary: 57000, dept: 'D190' },",
+ " { name: 'Timothy Grove', id: 'E16398', salary: 29900, dept: 'D190' }",
+ "];",
+ "",
+ "const res1 = topRankPerGroup(10, testData1, 'dept', 'salary');",
+ "",
+ "const testData2 = [",
+ " { name: 'Friday 13th', genre: 'horror', rating: 9.9 },",
+ " { name: \"Nightmare on Elm's Street\", genre: 'horror', rating: 5.7 },",
+ " { name: 'Titanic', genre: 'drama', rating: 7.3 },",
+ " { name: 'Maze Runner', genre: 'scifi', rating: 7.1 },",
+ " { name: 'Blade runner', genre: 'scifi', rating: 8.9 }",
+ "];",
+ "",
+ "const res2 = topRankPerGroup(1, testData2, 'genre', 'rating');",
+ "const res3 = topRankPerGroup(2, testData2, 'genre', 'rating');",
+ "",
+ "//console.log(JSON.stringify(topRankPerGroup(10, testData1)));"
+ ],
+ "tests": [
+ "assert(typeof topRankPerGroup === 'function', 'message: topRankPerGroup
is a function.');",
+ "assert(typeof topRankPerGroup(-1, []) === 'undefined', 'message: topRankPerGroup
returns undefined on negative n values.');",
+ "assert.equal(res1[0][0].dept, 'D050', 'message: First department must be D050');",
+ "assert.equal(res1[0][1].salary, 21900, 'message: First department must be D050');",
+ "assert.equal(res1[3][3].dept, 'D202', 'message: The last department must be D202');",
+ "assert.equal(res2[2].length, 1, 'message: topRankPerGroup(1, ...)
must return only top ranking result per group.');",
+ "assert.equal(res3[2][1].name, 'Maze Runner', 'message: topRankPerGroup(1, ...)
must return only top ranking result per group.');"
+ ],
+ "id": "595011cba5a81735713873bd",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Towers of Hanoi",
+ "type": "Waypoint",
+ "description": [
+ " Task:",
+ "Solve the Towers of Hanoi problem.
", + "",
+ "Your solution should accept the number of discs as the first parameters, and",
+ "three string used to identify each of the three stacks of discs, for example",
+ "towerOfHanoi(4, 'A', 'B', 'C')
. The function should return an",
+ "array of arrays containing the list of moves, source -> destination. For",
+ "example, the array [['A', 'C'], ['B', 'A']]
indicates that the",
+ "1st move was to move a disc from stack A to C, and the 2nd move was to move a",
+ "disc from stack B to A.",
+ "
towerOfHanoi
is a function.');",
+ "assert(res3.length === 7, 'message: towerOfHanoi(3, ...)
should return 7 moves.');",
+ "assert.deepEqual(towerOfHanoi(3, 'A', 'B', 'C'), res3Moves, \"message: towerOfHanoi(3, 'A', 'B', 'C')
should return [['A','B'],['A','C'],['B','C'],['A','B'],['C','A'],['C','B'],['A','B']].\");",
+ "assert.deepEqual(res5[9], ['Y', 'X'], 'message: towerOfHanoi(5, \"X\", \"Y\", \"Z\")
10th move should be Y -> X.');",
+ "assert.deepEqual(towerOfHanoi(7, 'A', 'B', 'C').slice(0, 10), res7First10Moves, \"message: towerOfHanoi(7, 'A', 'B', 'C')
first ten moves are [['A','B'],['A','C'],['B','C'],['A','B'],['C','A'],['C','B'],['A','B'],['A','C'],['B','C'],['B','A']].\");"
+ ],
+ "id": "5951ed8945deab770972ae56",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Vector cross product",
+ "type": "Waypoint",
+ "description": [
+ "A vector is defined as having three dimensions as being represented by an ordered collection of three numbers: (X, Y, Z).",
+ "",
+ "Task:",
+ " ",
+ " Write a function that takes two vectors (arrays) as input and computes their cross product.",
+ " ",
+ "Your function should return null
on",
+ "invalid inputs (ie vectors of different lengths).",
+ "
", + "A vector is defined as having three dimensions as being represented by an ordered collection of three numbers: (X, Y, Z).", + "
", + "",
+ "Task:",
+ " ",
+ " Write a function that takes any numbers of vectors (arrays) as input and computes their dot product.",
+ " ",
+ "Your function should return null
on",
+ "invalid inputs (ie vectors of different lengths).",
+ "
", + "Even today, with proportional fonts and complex layouts, there are still", + "cases where you need to wrap text at a specified", + "column. The basic task is to wrap a paragraph of text in a simple way.", + "Example text:", + "
", + "", + "Wrap text using a more sophisticated algorithm such as the Knuth and Plass TeX algorithm.", + "If your language provides this, you get easy extra credit,", + "but you ''must reference documentation'' indicating that the algorithm", + "is something better than a simple minimimum length algorithm.", + "", + "
", + "Task:", + " ", + " Write a function that can wrap this text to any number of characters.", + " ", + "As an example, the text wrapped to 80 characters should look like the following:", + "
", + "", + "Wrap text using a more sophisticated algorithm such as the Knuth and Plass TeX", + "algorithm. If your language provides this, you get easy extra credit, but you", + "must reference documentation indicating that the algorithm is something better", + "than a simple minimimum length algorithm.", + "" + ], + "challengeSeed": [ + "function wrap (text, limit) {", + " return text;", + "}" + ], + "solutions": [ + "function wrap (text, limit) {\n const noNewlines = text.replace('\\n', '');\n if (noNewlines.length > limit) {\n // find the last space within limit\n const edge = noNewlines.slice(0, limit).lastIndexOf(' ');\n if (edge > 0) {\n const line = noNewlines.slice(0, edge);\n const remainder = noNewlines.slice(edge + 1);\n return line + '\\n' + wrap(remainder, limit);\n }\n }\n return text;\n}\n" + ], + "tail": [ + "const text =", + "`Wrap text using a more sophisticated algorithm such as the Knuth and Plass TeX algorithm.", + "If your language provides this, you get easy extra credit,", + "but you ''must reference documentation'' indicating that the algorithm", + "is something better than a simple minimimum length algorithm.`;", + "", + "const wrapped80 = wrap(text, 80);", + "const wrapped42 = wrap(text, 42);", + "", + "const firstRow80 =", + " 'Wrap text using a more sophisticated algorithm such as the Knuth and Plass TeX';", + "", + "const firstRow42 = 'Wrap text using a more sophisticated';" + ], + "tests": [ + "assert.equal(typeof wrap, 'function', 'message: wrap must be a function.');", + "assert.equal(typeof wrap('abc', 10), 'string', 'message: wrap must return a string.');", + "assert(wrapped80.split('\\n').length === 4, 'message: wrap(80) must return 4 lines.');", + "assert.equal(wrapped80.split('\\n')[0], firstRow80);", + "assert(wrapped42.split('\\n').length === 7, 'message: wrap(42) must return 7 lines.');", + "assert.equal(wrapped42.split('\\n')[0], firstRow42);" + ], + "id": "594810f028c0303b75339ad4", + "challengeType": 5, + "releasedOn": "August 5, 2017" + }, + { + "title": "Y combinator", + "type": "Waypoint", + "description": [ + "
", + "In strict ", + "functional programming and", + "the lambda calculus, ", + "functions (lambda expressions) don't have state and are only allowed to refer to arguments of enclosing functions. ", + "This rules out the usual definition of a recursive function wherein a function is associated with the state of a variable and this variable's state is used in the body of the function.", + "
", + "", + "The Y combinator is itself a stateless function that,", + "when applied to another stateless function, returns a recursive version of the function. The Y combinator is", + "the simplest of the class of such functions, called ", + "fixed-point combinators.", + "
", + "Task:", + " ", + " Define the stateless Y combinator function and use it to compute", + " factorial.", + " ", + "factorial(N)
function is already given to you.",
+ "See also Jim Weirich: Adventures in Functional Programming."
+ ],
+ "challengeSeed": [
+ "function Y(f) {",
+ " return function() {",
+ " // Good luck!",
+ " };",
+ "}",
+ "",
+ "var factorial = Y(function(f) {",
+ " return function (n) {",
+ " return n > 1 ? n * f(n - 1) : 1;",
+ " };",
+ "});"
+ ],
+ "solutions": [
+ "var Y = f => (x => x(x))(y => f(x => y(y)(x)));\n"
+ ],
+ "tail": [
+ "var factorial = Y(f => n => (n > 1 ? n * f(n - 1) : 1));"
+ ],
+ "tests": [
+ "assert.equal(typeof Y(f => n => n), 'function', 'message: Y must return a function');",
+ "assert.equal(factorial(1), 1, 'message: factorial(1) must return 1.');",
+ "assert.equal(factorial(2), 2, 'message: factorial(2) must return 2.');",
+ "assert.equal(factorial(3), 6, 'message: factorial(3) must return 6.');",
+ "assert.equal(factorial(4), 24, 'message: factorial(4) must return 24.');",
+ "assert.equal(factorial(10), 3628800, 'message: factorial(10) must return 3628800.');"
+ ],
+ "id": "594810f028c0303b75339ad5",
+ "challengeType": 5,
+ "releasedOn": "August 5, 2017"
+ },
+ {
+ "title": "Zeckendorf number representation",
+ "type": "Waypoint",
+ "description": [
+ "", + "Just as numbers can be represented in a", + "positional notation as sums of multiples of the powers of ten (decimal)", + "or two (binary); all the positive integers can be represented as the sum", + "of one or zero times the distinct members of the Fibonacci series.", + "
", + "",
+ "Recall that the first six distinct Fibonacci",
+ "numbers are: 1, 2, 3, 5, 8, 13
. The decimal number eleven can",
+ "be written as 0*13 + 1*8 + 0*5 + 1*3 + 0*2 + 0*1
or",
+ "010100
in positional notation where the columns represent",
+ "multiplication by a particular member of the sequence. Leading zeroes are",
+ "dropped so that 11 decimal becomes 10100
.",
+ "
",
+ "10100 is not the only way to make 11 from the Fibonacci numbers however",
+ "0*13 + 1*8 + 0*5 + 0*3 + 1*2 + 1*1
or 010011 would also",
+ "represent decimal 11. For a true Zeckendorf number there is the added",
+ "restriction that ''no two consecutive Fibonacci numbers can be used''",
+ "which leads to the former unique solution.",
+ "
", + " Task:", + " Write a function that generates and returns an array of first N Zeckendorf numbers in order.", + "
" + ], + "challengeSeed": [ + "function zeckendorf(n) {", + " // good luck!", + "}" + ], + "solutions": [ + "// zeckendorf :: Int -> String\nfunction zeckendorf(n) {\n const f = (m, x) => (m < x ? [m, 0] : [m - x, 1]);\n return (n === 0 ? ([0]) :\n mapAccumL(f, n, reverse(\n tail(fibUntil(n))\n ))[1]).join('');\n}\n\n// fibUntil :: Int -> [Int]\nlet fibUntil = n => {\n const xs = [];\n until(\n ([a]) => a > n,\n ([a, b]) => (xs.push(a), [b, a + b]), [1, 1]\n );\n return xs;\n};\n\nlet mapAccumL = (f, acc, xs) => (\n xs.reduce((a, x) => {\n const pair = f(a[0], x);\n\n return [pair[0], a[1].concat(pair[1])];\n }, [acc, []])\n);\n\nlet until = (p, f, x) => {\n let v = x;\n while (!p(v)) v = f(v);\n return v;\n};\n\nconst tail = xs => (\n xs.length ? xs.slice(1) : undefined\n);\n\nconst reverse = xs => xs.slice(0).reverse();\n" + ], + "tail": [ + "const range = (m, n) => (", + " Array.from({", + " length: Math.floor(n - m) + 1", + " }, (_, i) => m + i)", + ");", + "", + "const solution20 = [", + " '1', '10', '100', '101', '1000', '1001', '1010', '10000', '10001',", + " '10010', '10100', '10101', '100000', '100001', '100010', '100100', '100101',", + " '101000', '101001', '101010'", + "];", + "", + "const answer = range(1, 20).map(zeckendorf);" + ], + "tests": [ + "assert.equal(typeof zeckendorf, 'function', 'message: zeckendorf must be function');", + "assert.deepEqual(answer, solution20);" + ], + "id": "594810f028c0303b75339ad6", + "challengeType": 5, + "releasedOn": "August 5, 2017" + }, + { + "title": "Zhang-Suen thinning algorithm", + "type": "Waypoint", + "description": [ + "This is an algorithm used to thin a black and white i.e. one bit per pixel images.", + "For example, with an input image of:", + "", + " ################# #############", + " ################## ################", + " ################### ##################", + " ######## ####### ###################", + " ###### ####### ####### ######", + " ###### ####### #######", + " ################# #######", + " ################ #######", + " ################# #######", + " ###### ####### #######", + " ###### ####### #######", + " ###### ####### ####### ######", + " ######## ####### ###################", + " ######## ####### ###### ################## ######", + " ######## ####### ###### ################ ######", + " ######## ####### ###### ############# ######", + "", + "It produces the thinned output:", + "
", + " ", + " # ########## #######", + " ## # #### #", + " # # ##", + " # # #", + " # # #", + " # # #", + " ############ #", + " # # #", + " # # #", + " # # #", + " # # #", + " # ##", + " # ############", + " ### ###", + " ", + "", + "
P9 | P2 | P3 |
P8 | P1 | P4 |
P7 | P6 | P5 |
", + "Task:", + "Write a routine to perform Zhang-Suen thinning on an image matrix of ones and zeroes.", + "
" + ], + "challengeSeed": [ + "const testImage = [", + " ' ',", + " ' ################# ############# ',", + " ' ################## ################ ',", + " ' ################### ################## ',", + " ' ######## ####### ################### ',", + " ' ###### ####### ####### ###### ',", + " ' ###### ####### ####### ',", + " ' ################# ####### ',", + " ' ################ ####### ',", + " ' ################# ####### ',", + " ' ###### ####### ####### ',", + " ' ###### ####### ####### ',", + " ' ###### ####### ####### ###### ',", + " ' ######## ####### ################### ',", + " ' ######## ####### ###### ################## ###### ',", + " ' ######## ####### ###### ################ ###### ',", + " ' ######## ####### ###### ############# ###### ',", + " ' '];", + "", + "function thinImage(image) {", + " // Good luck!", + "}" + ], + "solutions": [ + "function Point(x, y) {\n this.x = x;\n this.y = y;\n}\n\nconst ZhangSuen = (function () {\n function ZhangSuen() {\n }\n\n ZhangSuen.nbrs = [[0, -1], [1, -1], [1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1]];\n\n ZhangSuen.nbrGroups = [[[0, 2, 4], [2, 4, 6]], [[0, 2, 6], [0, 4, 6]]];\n\n ZhangSuen.toWhite = [];\n\n ZhangSuen.main = function (image) {\n ZhangSuen.grid = new Array(image);\n for (let r = 0; r < image.length; r++) {\n ZhangSuen.grid[r] = image[r].split('');\n }\n ZhangSuen.thinImage();\n return ZhangSuen.getResult();\n };\n\n ZhangSuen.thinImage = function () {\n let firstStep = false;\n let hasChanged;\n do {\n hasChanged = false;\n firstStep = !firstStep;\n for (let r = 1; r < ZhangSuen.grid.length - 1; r++) {\n for (let c = 1; c < ZhangSuen.grid[0].length - 1; c++) {\n if (ZhangSuen.grid[r][c] !== '#') {\n continue;\n }\n const nn = ZhangSuen.numNeighbors(r, c);\n if (nn < 2 || nn > 6) {\n continue;\n }\n if (ZhangSuen.numTransitions(r, c) !== 1) {\n continue;\n }\n if (!ZhangSuen.atLeastOneIsWhite(r, c, firstStep ? 0 : 1)) {\n continue;\n }\n ZhangSuen.toWhite.push(new Point(c, r));\n hasChanged = true;\n }\n }\n for (let i = 0; i < ZhangSuen.toWhite.length; i++) {\n const p = ZhangSuen.toWhite[i];\n ZhangSuen.grid[p.y][p.x] = ' ';\n }\n ZhangSuen.toWhite = [];\n } while ((firstStep || hasChanged));\n };\n\n ZhangSuen.numNeighbors = function (r, c) {\n let count = 0;\n for (let i = 0; i < ZhangSuen.nbrs.length - 1; i++) {\n if (ZhangSuen.grid[r + ZhangSuen.nbrs[i][1]][c + ZhangSuen.nbrs[i][0]] === '#') {\n count++;\n }\n }\n return count;\n };\n\n ZhangSuen.numTransitions = function (r, c) {\n let count = 0;\n for (let i = 0; i < ZhangSuen.nbrs.length - 1; i++) {\n if (ZhangSuen.grid[r + ZhangSuen.nbrs[i][1]][c + ZhangSuen.nbrs[i][0]] === ' ') {\n if (ZhangSuen.grid[r + ZhangSuen.nbrs[i + 1][1]][c + ZhangSuen.nbrs[i + 1][0]] === '#') {\n count++;\n }\n }\n }\n return count;\n };\n\n ZhangSuen.atLeastOneIsWhite = function (r, c, step) {\n let count = 0;\n const group = ZhangSuen.nbrGroups[step];\n for (let i = 0; i < 2; i++) {\n for (let j = 0; j < group[i].length; j++) {\n const nbr = ZhangSuen.nbrs[group[i][j]];\n if (ZhangSuen.grid[r + nbr[1]][c + nbr[0]] === ' ') {\n count++;\n break;\n }\n }\n }\n return count > 1;\n };\n\n ZhangSuen.getResult = function () {\n const result = [];\n for (let i = 0; i < ZhangSuen.grid.length; i++) {\n const row = ZhangSuen.grid[i].join('');\n result.push(row);\n }\n return result;\n };\n return ZhangSuen;\n}());\n\nfunction thinImage(image) {\n return ZhangSuen.main(image);\n}\n" + ], + "tail": [ + "", + "const imageForTests = [", + " ' ',", + " ' ################# ############# ',", + " ' ################## ################ ',", + " ' ################### ################## ',", + " ' ######## ####### ################### ',", + " ' ###### ####### ####### ###### ',", + " ' ###### ####### ####### ',", + " ' ################# ####### ',", + " ' ################ ####### ',", + " ' ################# ####### ',", + " ' ###### ####### ####### ',", + " ' ###### ####### ####### ',", + " ' ###### ####### ####### ###### ',", + " ' ######## ####### ################### ',", + " ' ######## ####### ###### ################## ###### ',", + " ' ######## ####### ###### ################ ###### ',", + " ' ######## ####### ###### ############# ###### ',", + " ' '];", + "const expected = [", + " ' ',", + " ' ',", + " ' # ########## ####### ',", + " ' ## # #### # ',", + " ' # # ## ',", + " ' # # # ',", + " ' # # # ',", + " ' # # # ',", + " ' ############ # ',", + " ' # # # ',", + " ' # # # ',", + " ' # # # ',", + " ' # # # ',", + " ' # ## ',", + " ' # ############ ',", + " ' ### ### ',", + " ' ',", + " ' '", + "];", + "const result = thinImage(imageForTests);" + ], + "tests": [ + "assert.equal(typeof thinImage, 'function', 'thinImage must be a function');", + "assert.equal(typeof result, 'object', 'thinImage must return an array of strings');", + "assert.equal(typeof result[0], 'string', 'thinImage must return an array of strings');", + "assert.deepEqual(result, expected, 'thinImage must return an array of strings');" + ], + "id": "594810f028c0303b75339ad7", + "challengeType": 5, + "releasedOn": "August 5, 2017" + }, + { + "title": "Zig-zag matrix", + "type": "Waypoint", + "description": [ + "A ''zig-zag'' array is a square arrangement of the first ", + "$N^2$ integers, where the", + "numbers increase sequentially as you zig-zag along the array's ", + "anti-diagonals.", + "For example, given '''5''', produce this array:", + "", + " 0 1 5 6 14", + " 2 4 7 13 15", + " 3 8 12 16 21", + " 9 11 17 20 22", + "10 18 19 23 24", + "", + "Write a function that takes the size of the zig-zag matrix, and returns the", + "corresponding matrix as two-dimensional array." + ], + "challengeSeed": [ + "function ZigZagMatrix(n) {", + " // Good luck!", + " return [[], []];", + "}" + ], + "solutions": [ + "function ZigZagMatrix(n) {\n const mtx = [];\n for (let i = 0; i < n; i++) {\n mtx[i] = [];\n }\n\n let i = 1;\n let j = 1;\n for (let e = 0; e < n * n; e++) {\n mtx[i - 1][j - 1] = e;\n if ((i + j) % 2 === 0) {\n // Even stripes\n if (j < n) j++;\n else i += 2;\n if (i > 1) i--;\n } else {\n // Odd stripes\n if (i < n) i++;\n else j += 2;\n if (j > 1) j--;\n }\n }\n return mtx;\n}\n" + ], + "tail": [ + "const zm1 = [[0]];", + "const zm2 = [[0, 1], [2, 3]];", + "const zm5 = [", + " [0, 1, 5, 6, 14],", + " [2, 4, 7, 13, 15],", + " [3, 8, 12, 16, 21],", + " [9, 11, 17, 20, 22],", + " [10, 18, 19, 23, 24]", + "];" + ], + "tests": [ + "assert.equal(typeof ZigZagMatrix, 'function', 'message: ZigZagMatrix must be a function');", + "assert.equal(typeof ZigZagMatrix(1), 'object', 'message: ZigZagMatrix should return array');", + "assert.equal(typeof ZigZagMatrix(1)[0], 'object', 'message: ZigZagMatrix should return an array of nestes arrays');", + "assert.deepEqual(ZigZagMatrix(1), zm1, 'message: ZigZagMatrix(1) should return [[0]]');", + "assert.deepEqual(ZigZagMatrix(2), zm2, 'message: ZigZagMatrix(2) should return [[0, 1], [2, 3]]');", + "assert.deepEqual(ZigZagMatrix(5), zm5, 'message: ZigZagMatrix(5) must return specified matrix');" + ], + "id": "594810f028c0303b75339ad8", + "challengeType": 5, + "releasedOn": "August 5, 2017" + } + ] +}