diff options
Diffstat (limited to 'challenge-146/iangoodnight/javascript/ch-2.js')
| -rwxr-xr-x | challenge-146/iangoodnight/javascript/ch-2.js | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/challenge-146/iangoodnight/javascript/ch-2.js b/challenge-146/iangoodnight/javascript/ch-2.js new file mode 100755 index 0000000000..a71c81fa85 --- /dev/null +++ b/challenge-146/iangoodnight/javascript/ch-2.js @@ -0,0 +1,257 @@ +#!/usr/bin/env node +// ch-2.js + +/******************************************************************************* + * https://theweeklychallenge.org/blog/perl-weekly-challenge-146/ + * + * ## Task 2 > Curious Fraction Tree + * ================================= + * + * Consider the following `Curious Fraction Tree`: + * + * ``` + * __________1/1__________ + * / \ + * ___1/2___ ___2/1___ + * / \ / \ + * 1/3 3/2 2/3 3/1 + * / \ / \ / \ / \ + * 1/4 4/3 3/5 5/2 2/5 5/3 3/4 4/1 + * ``` + * + * You are given a fraction, member of the tree created similar to the above + * sample. + * + * Write a script to find out the parent and grandparent of the given member. + * + * **Example 1:** + * + * ``` + * Input: $member = '3/5'; + * Output: parent = '3/2' and grandparent = '1/2' + * ``` + * + * **Example 2:** + * + * ``` + * Input: $member = '4/3'; + * Output: parent = '1/3' and grandparent = '1/2' + * ``` + * + ******************************************************************************/ + +'use strict'; + +/******************************************************************************* + * Dependencies **************************************************************** + ******************************************************************************/ + +const readline = require('readline'); + +/******************************************************************************* + * PWC Solution **************************************************************** + ******************************************************************************/ + +// Input guard +function isFractionString(string) { + const re = /^\d+\/\d+$/; + + return typeof string === 'string' && re.test(string.trim()); +} + +// Find immediate parent +function getCuriousParent(member = '1/1') { + if (!isFractionString(member)) { + const badArg = '`getCuriousParent` expects a string (i.e.: "3/4")'; + + throw new Error(badArg); + } + + const [num, den] = member.split('/').map((elem) => parseInt(elem, 10)); + + const value = num / den; + + if (value > 1) return `${num - den}/${den}`; + if (value < 1) return `${num}/${den - num}`; + return null; +} + +// Returns whole chain from member to root +function getCuriousTree(member = '1/1') { + if (!isFractionString(member)) { + throw new Error('`getCuriousTree` expects a string (i.e.: "3/2")'); + } + + const parents = []; + + let child = member; + + while (child !== null) { + parents.push(child); + child = getCuriousParent(child); + } + + return parents; +} + +// Not really necessarily to abstract this step, but it makes it easier to do +// other things with the whole chain, like figure out if it is rooted at 1/1 or +// not +function getCuriousGenerations(tree = [], generations = 2) { + return tree.slice(1, generations + 1); +} + +/******************************************************************************* + * Utilities ******************************************************************* + ******************************************************************************/ + +const colors = { + green: '\x1b[32m', + red: '\x1b[31m', + reset: '\x1b[0m', + yellow: '\x1b[33m', +}; + +function formatOutput(tree = [], termColors = {}) { + const { yellow: y = '', green: g = '', reset: rst = '' } = termColors; + + return tree + .map((member, idx, array) => { + const { length } = array; + + const conjunction = idx === length - 1 && length !== 1 ? 'and ' : ''; + + const last = idx === length - 1; + + let ordinal = idx === 0 ? 'parent' : 'grandparent'; + + if (idx > 1) ordinal = 'great-'.repeat(idx - 1) + ordinal; + + let separator = last ? '' : ', '; + + if (length === 2) separator = ' '; + + return `${conjunction}${y + ordinal + rst} = ${ + g + member + rst + }${separator}`; + }) + .join(''); +} + +function assertRoot(tree = [], termColors = {}) { + const last = [...tree].pop(); + + const { + green: g = '', + red: r = '', + reset: rst = '', + yellow: y = '', + } = termColors; + + if (last !== '1/1') { + return `${y}Curious fraction tree rooted at ${ + r + last + y + } and not at ${`${g}1/1${rst}`}`; + } + return null; +} + +function repl(termColors = {}) { + const { green: g = '', yellow: y = '', reset: rst = '' } = termColors; + + return new Promise((resolve) => { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + /* eslint-disable prefer-template */ + const mainPrompt = + 'Enter a fraction (i.e.: "' + + g + + '3/5' + + rst + + '") or type "' + + y + + 'exit' + + rst + + '" to quit /> '; + + const morePrompt = + 'Enter "' + + y + + 'more' + + rst + + '" to see more details, or enter a fraction (i.e.: "' + + g + + '3/5' + + rst + + '") to continue /> '; + /* eslint-enable prefer-template */ + + let currentTree; + let generations; + let assertedRoot; + + rl.setPrompt(mainPrompt); + + rl.prompt(); + + rl.on('line', (line) => { + const input = line.trim().toLowerCase(); + + if (input === 'exit' || input === 'quit' || input === 'q') { + return rl.close(); + } + + if (isFractionString(input)) { + currentTree = getCuriousTree(input); + generations = getCuriousGenerations(currentTree, 2); + assertedRoot = assertRoot(currentTree, termColors); + console.log(formatOutput(generations, termColors)); + rl.setPrompt(morePrompt); + return rl.prompt(); + } + + if (input === 'more') { + console.log(formatOutput(currentTree.slice(1), termColors)); + if (assertedRoot) console.log(assertedRoot); + currentTree = null; + generations = null; + assertedRoot = null; + rl.setPrompt(mainPrompt); + return rl.prompt(); + } + console.log(`Sorry, I don't understand ${input}.`); + return rl.prompt(); + }).on('close', () => { + console.log(`${y}Goodbye.${rst}`); + resolve(); + }); + }); +} + +function printBanner(termColors = {}) { + const { yellow: y = '', reset: rst = '' } = termColors; + + const message = 'A Curious Fraction Tree'; + + const underline = '='.repeat(message.length); + + console.log(`${y}${message}`); + console.log(`${underline}${rst}`); +} + +/******************************************************************************* + * Main ************************************************************************ + ******************************************************************************/ + +(async function main() { + try { + printBanner(colors); + await repl(colors); + } catch (error) { + console.log(error); + process.exit(1); + } +})(); |
