Skip to content

Commit

Permalink
Merge pull request #121 from amclin/feat/prep-2020
Browse files Browse the repository at this point in the history
Solution for 2020 Day 1 parts 1 and 2
  • Loading branch information
amclin authored Dec 6, 2020
2 parents 24e9139 + e15993a commit 40c94fa
Show file tree
Hide file tree
Showing 5 changed files with 379 additions and 0 deletions.
63 changes: 63 additions & 0 deletions 2020/day-01/expenseValidation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@

/**
* Validates a list of records by comparing every combination
* to the checksum. Stops when the first match is found
* @param {array} records List of records to check
* @param {int} checksum The target sum that records should add up to
* @param {int} goal The number of records we hope to find
*/
const validateRecords = (records, checksum = 2020, goal = 2) => {
const results = []

// Intentionally using `function()` instead of `() =>` because
// the thisArg won't get passed to the find callback otherwise
// https://stackoverflow.com/questions/46639131/javascript-array-prototype-find-second-argument-thisarg-not-working
function matcher (record) {
this.depth = this.depth || 1 // depth tracking starts at level 1
this.tracker = this.tracker || 0 // for basic sums, start counter at 0
const subTotal = this.tracker + record
// Found a match in the specified with desired qty of results, don't keep searching!
if (subTotal === this.target && this.depth >= goal) {
results.push(record)
return true
}
// When subtotal exceeds target, return immediately and don't waste time
// on more loops that won't get results
if (subTotal > this.target) {
return false
}
// If we're already at max depth, don't waste time on more loops
if (this.depth >= this.maxDepth) {
return false
}
// Check the next level down
const res = records.find(matcher, {
maxDepth: this.maxDepth,
target: this.target,
depth: this.depth + 1,
tracker: this.tracker + record
})
// Propogate maches back up the recursion chain, capturing each
if (res) {
results.push(record)
return true
}
// Nothing found with this combo, step to the next sibling
return false
}

// Parse the records to find results
records.find(matcher, {
maxDepth: goal,
target: checksum
})

if (results.length < 2) {
throw new Error('Couldn\'t find a checksum match')
}
return results
}

module.exports = {
validateRecords
}
78 changes: 78 additions & 0 deletions 2020/day-01/expenseValidation.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/* eslint-env mocha */
const { expect } = require('chai')
const { validateRecords } = require('./expenseValidation')

/**
* Sum all the values in an array
*/
const arrSum = (arr) => arr.reduce((x, y) => x + y, 0)
/**
* Multiply all the values in an array
*/
const arrMult = (arr) => arr.reduce((x, y) => x * y, 1)
const testData = [
1721,
979,
366,
299,
675,
1456
]

describe('--- 2020 Day 1: Report Repair ---', () => {
describe('Part 1', () => {
describe('validateRecords()', () => {
it('it finds the two records that sum to 2020', () => {
const expected = [1721, 299]
const results = validateRecords(testData, undefined, 2)
// Should be 2 results
expect(results.length).to.equal(2)
// Result order is unnecessary, but all expected hould be in the result set
expected.forEach(result => {
expect(testData.indexOf(result)).to.be.greaterThan(-1)
})
// Results add up to the checksum
expect(arrSum(results)).to.equal(2020)
// Confirm the multiplied total
expect(arrMult(results)).to.equal(514579)
})
it('it supports specifying an alternate checksum', () => {
const expected = [testData[3], testData[5]]
const checksum = arrSum(expected)
const results = validateRecords(testData, checksum)
// Should be 2 results
expect(results.length).to.equal(2)
// Result order is unnecessary, but all expected hould be in the result set
expected.forEach(result => {
expect(results.indexOf(result)).to.be.greaterThan(-1)
})
// Results add up to the checksum
expect(arrSum(results)).to.equal(checksum)
})
it('Throws an error when no records provided', () => {
expect(validateRecords).to.throw()
})
it('Throws an error when no records match checksum', () => {
expect(() => validateRecords([1, 2, 3], 100)).to.throw('Couldn\'t find a checksum match')
})
})
})
describe('Part 2', () => {
describe('validateRecords()', () => {
it('it can find a specified number of records adding up to 2020', () => {
const expected = [979, 366, 675]
const results = validateRecords(testData, undefined, 3)
// Should same number of results
expect(results.length).to.equal(expected.length)
// Result order is unnecessary, but all expected hould be in the result set
expected.forEach(result => {
expect(testData.indexOf(result)).to.be.greaterThan(-1)
})
// Results add up to the checksum
expect(arrSum(results)).to.equal(2020)
// Confirm the multiplied total
expect(arrMult(results)).to.equal(241861950)
})
})
})
})
1 change: 1 addition & 0 deletions 2020/day-01/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require('./solution')
200 changes: 200 additions & 0 deletions 2020/day-01/input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
1539
1914
1866
1407
1706
1423
1834
1700
1573
1486
1743
1394
1693
1705
1530
1811
1626
1473
1901
1481
1527
1841
1891
1750
1343
1899
401
1896
1627
1593
1541
874
1484
1210
1692
1963
1964
1780
671
1862
1393
1309
1740
1831
1932
1185
1979
1504
1663
1610
1494
1511
1103
1738
1816
1871
1545
1595
1784
1412
1815
1998
1783
1770
1426
1699
1416
1880
1612
1989
1360
1869
1762
1690
1999
1990
1521
1730
703
1463
1670
1472
1413
1669
1502
1548
1475
1694
1314
1980
980
1667
890
1569
1456
1406
1924
1973
1965
1533
1827
2000
1847
1520
1729
1512
1555
1566
1505
1672
1169
1835
1850
1493
1861
1288
1675
1676
1556
1320
1757
1870
1642
1903
1372
1967
1894
176
1908
1418
1535
1487
1496
1491
1611
1970
1758
1563
1766
1629
1937
1763
1829
1772
1632
1517
1736
1971
1721
1716
1429
1408
1560
1958
1359
1890
1825
1536
1819
1697
1887
1832
2005
892
1471
1425
1677
1673
1128
1878
1062
1470
1875
1854
1518
1568
1919
256
1532
1711
1944
1344
1330
1636
1957
1709
1551
1983
1674
1671
1959
1760
1689
1767
1477
1589
1897
1144
1982
1544
37 changes: 37 additions & 0 deletions 2020/day-01/solution.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const fs = require('fs')
const path = require('path')
const filePath = path.join(__dirname, 'input.txt')
const { validateRecords } = require('./expenseValidation')
const { inputToArray } = require('../../2018/inputParser')

fs.readFile(filePath, { encoding: 'utf8' }, (err, initData) => {
if (err) throw err

initData = inputToArray(initData.trim())
.map(el => Number(el))

const resetInput = () => {
// Deep copy to ensure we aren't mutating the original data
return JSON.parse(JSON.stringify(initData))
}

const part1 = () => {
const data = resetInput()
return validateRecords(data) // Find 2 results for 2020
.reduce((total, res) => total * res, 1)
}

const part2 = () => {
const data = resetInput()
return validateRecords(data, undefined, 3) // Find 3 results for 2020
.reduce((total, res) => total * res, 1)
}
const answers = []
answers.push(part1())
answers.push(part2())

answers.forEach((ans, idx) => {
console.info(`-- Part ${idx + 1} --`)
console.info(`Answer: ${ans}`)
})
})

0 comments on commit 40c94fa

Please sign in to comment.