diff --git a/2020/day-10/index.js b/2020/day-10/index.js new file mode 100644 index 0000000..af7e035 --- /dev/null +++ b/2020/day-10/index.js @@ -0,0 +1,3 @@ +// eslint-disable-next-line no-unused-vars +const console = require('../helpers') +require('./solution') diff --git a/2020/day-10/input.txt b/2020/day-10/input.txt new file mode 100644 index 0000000..dc30936 --- /dev/null +++ b/2020/day-10/input.txt @@ -0,0 +1,93 @@ +47 +99 +115 +65 +10 +55 +19 +73 +80 +100 +71 +110 +64 +135 +49 +3 +1 +98 +132 +2 +38 +118 +66 +116 +104 +87 +79 +114 +40 +37 +44 +97 +4 +140 +60 +86 +56 +133 +7 +146 +85 +111 +134 +53 +121 +77 +117 +21 +12 +81 +145 +129 +107 +93 +22 +48 +11 +54 +92 +78 +67 +20 +138 +125 +57 +96 +26 +147 +124 +34 +74 +143 +13 +28 +126 +50 +29 +70 +39 +63 +41 +91 +32 +84 +144 +27 +139 +33 +88 +72 +23 +103 +16 \ No newline at end of file diff --git a/2020/day-10/jolts.js b/2020/day-10/jolts.js new file mode 100644 index 0000000..bdb45fe --- /dev/null +++ b/2020/day-10/jolts.js @@ -0,0 +1,107 @@ +const countDifferences = (data) => { + const tallies = Array(4).fill(0) + // Always account for the outlet + data.push(0) + // Always add the native adapter at the end + tallies[3]++ + + // Iterate through the adapters + data.sort((a, b) => a - b) + .forEach((curr, idx) => { + if (!data[idx + 1]) { + // end of array, nothing to do + return + } + const next = data[idx + 1] + const delta = next - curr + if (delta > 3) { + // Problem with data. Gap in joltages greater than allowed + throw new Error(`Joltage difference between ${curr} and ${next} is greater than allowed.`) + } + + console.debug(`Joltage difference between ${curr} and ${next} is ${delta}.`) + tallies[delta]++ + }) + console.debug('Tallied voltage differences:', tallies) + return tallies +} + +const countCombinations = (data) => { + const tallies = Array(5).fill(0) + const delta = (idx) => { + return data[idx] - data[idx - 1] + } + + // Always account for the outlet + data.push(0) + data = data.sort((a, b) => a - b) + + const deltas = data.reduce((res, el, idx) => { + console.debug(idx, el, delta(idx)) + if (idx <= 0) { + return res + } + res.push(delta(idx)) + return res + }, []) + console.debug('joltage deltas', deltas) + + // I'm really not proud of this solution. It hardcodes too much logic with magic constants + // and only works because there are no joltage differences of 2, and the max allowed + // skip is 3. + // + // Since the rules say adapters can support 1, 2, or 3 jolt diferences, + // that means if the difference between n and n+2 is 3 or less, n+1 can be safely + // skipped. Potentially we can skip two. + // Every time we skip a number, the total amount of variations doubles + + // This logic would be a LOT messier if we had diffs of 2 in the data set + + // When we have 2 skips in a row, we need to leave one combo in case + // skipping both exceeds the max difference + // TODO: we aren't implementing this because our data set doesn't have + // any diffs of 2, which means we never have a 1 + 2 skip to worry about + + // When we have 3 skips in a row, we're definitely exceeding the max difference + // if the next is also a skip so we have to leave at least one in place + + // When we have 5 skips in a row.... etc.. + // TODO: we aren't implementing this because dataset doesn't have any examples + + deltas.forEach((d, idx, arr) => { + if (d === 1 && arr[idx + 1] === 1 && arr[idx + 2] === 1 && arr[idx + 3] === 1) { + console.debug('Found 4 in a row') + tallies[4]++ + deltas.splice(idx, 4) + } else if (d === 1 && arr[idx + 1] === 1 && arr[idx + 2] === 1) { + console.debug('Found 3 in a row') + tallies[3]++ + deltas.splice(idx, 3) + } else if (d === 1 && arr[idx + 1] === 1) { + console.debug('Found 2 in a row') + tallies[2]++ + deltas.splice(idx, 2) + } else if (d === 1) { + console.debug('Found 1 in a row') + tallies[1]++ + deltas.splice(idx, 1) + } + }) + + console.debug('skippable ranges', tallies) + console.debug([1, 1 ** tallies[1], 2 ** tallies[2], 4 ** tallies[3], 7 ** tallies[4]]) + return ( + 1 ** tallies[1] + ) * ( + 2 ** tallies[2] + ) * ( + 4 ** tallies[3] + ) * ( + 7 ** tallies[4] // 4 in a row is special case because we can't skip more than 3 + ) +} + +module.exports = { + countDifferences, + countCombinations +} diff --git a/2020/day-10/jolts.test.js b/2020/day-10/jolts.test.js new file mode 100644 index 0000000..c2d5bd7 --- /dev/null +++ b/2020/day-10/jolts.test.js @@ -0,0 +1,44 @@ +/* eslint-env mocha */ +const { expect } = require('chai') +const { countDifferences, countCombinations } = require('./jolts') + +const srcAdapters = [ + [16, 10, 15, 5, 1, 11, 7, 19, 6, 12, 4], + [28, 33, 18, 42, 31, 14, 46, 20, 48, 47, 24, 23, 49, 45, 19, 38, 39, 11, 1, 32, 25, 35, 8, 17, 7, 9, 4, 2, 34, 10, 3] +] + +describe('--- Day 10: Adapter Array ---', () => { + let adapters + beforeEach(() => { + // reset test data since arrays get mutated using a quick-and-dirty deep copy + adapters = JSON.parse(JSON.stringify(srcAdapters)) + }) + describe('Part 1', () => { + describe('countDifferences()', () => { + it('tabulates the amoount of joltage differences in the adapter set', () => { + const result = countDifferences(adapters[0]) + expect(result[1]).to.equal(7) + expect(result[3]).to.equal(5) + const result2 = countDifferences(adapters[1]) + expect(result2[1]).to.equal(22) + expect(result2[3]).to.equal(10) + }) + it('throws an error if any joltage differences exceed 3', () => { + expect(() => countDifferences([5, 40])).to.throw() + }) + it('throws an error if any joltage differences is less than 1', () => { + expect(() => countDifferences([5, 5])).to.throw() + }) + }) + }) + describe('Part 2', () => { + describe('countCombinations()', () => { + it('tabulates the amount of adapter combinations in the set', () => { + const result = countCombinations(adapters[0]) + expect(result).to.equal(8) + const result2 = countCombinations(adapters[1]) + expect(result2).to.equal(19208) + }) + }) + }) +}) diff --git a/2020/day-10/solution.js b/2020/day-10/solution.js new file mode 100644 index 0000000..1e53861 --- /dev/null +++ b/2020/day-10/solution.js @@ -0,0 +1,35 @@ +const fs = require('fs') +const path = require('path') +const filePath = path.join(__dirname, 'input.txt') +const { inputToArray } = require('../../2018/inputParser') +const { countDifferences, countCombinations } = require('./jolts') + +fs.readFile(filePath, { encoding: 'utf8' }, (err, initData) => { + if (err) throw err + + initData = inputToArray(initData.trim()).map(Number) + + const resetInput = () => { + // Deep copy to ensure we aren't mutating the original data + return JSON.parse(JSON.stringify(initData)) + } + + const part1 = () => { + const data = resetInput() + const differences = countDifferences(data) + return differences[1] * differences[3] + } + + const part2 = () => { + const data = resetInput() + return countCombinations(data) + } + const answers = [] + answers.push(part1()) + answers.push(part2()) + + answers.forEach((ans, idx) => { + console.info(`-- Part ${idx + 1} --`) + console.info(`Answer: ${ans}`) + }) +})