Skip to content

Commit 40c94fa

Browse files
Merge pull request #121 from amclin/feat/prep-2020
Solution for 2020 Day 1 parts 1 and 2
2 parents 24e9139 + e15993a commit 40c94fa

File tree

5 files changed

+379
-0
lines changed

5 files changed

+379
-0
lines changed

2020/day-01/expenseValidation.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
2+
/**
3+
* Validates a list of records by comparing every combination
4+
* to the checksum. Stops when the first match is found
5+
* @param {array} records List of records to check
6+
* @param {int} checksum The target sum that records should add up to
7+
* @param {int} goal The number of records we hope to find
8+
*/
9+
const validateRecords = (records, checksum = 2020, goal = 2) => {
10+
const results = []
11+
12+
// Intentionally using `function()` instead of `() =>` because
13+
// the thisArg won't get passed to the find callback otherwise
14+
// https://stackoverflow.com/questions/46639131/javascript-array-prototype-find-second-argument-thisarg-not-working
15+
function matcher (record) {
16+
this.depth = this.depth || 1 // depth tracking starts at level 1
17+
this.tracker = this.tracker || 0 // for basic sums, start counter at 0
18+
const subTotal = this.tracker + record
19+
// Found a match in the specified with desired qty of results, don't keep searching!
20+
if (subTotal === this.target && this.depth >= goal) {
21+
results.push(record)
22+
return true
23+
}
24+
// When subtotal exceeds target, return immediately and don't waste time
25+
// on more loops that won't get results
26+
if (subTotal > this.target) {
27+
return false
28+
}
29+
// If we're already at max depth, don't waste time on more loops
30+
if (this.depth >= this.maxDepth) {
31+
return false
32+
}
33+
// Check the next level down
34+
const res = records.find(matcher, {
35+
maxDepth: this.maxDepth,
36+
target: this.target,
37+
depth: this.depth + 1,
38+
tracker: this.tracker + record
39+
})
40+
// Propogate maches back up the recursion chain, capturing each
41+
if (res) {
42+
results.push(record)
43+
return true
44+
}
45+
// Nothing found with this combo, step to the next sibling
46+
return false
47+
}
48+
49+
// Parse the records to find results
50+
records.find(matcher, {
51+
maxDepth: goal,
52+
target: checksum
53+
})
54+
55+
if (results.length < 2) {
56+
throw new Error('Couldn\'t find a checksum match')
57+
}
58+
return results
59+
}
60+
61+
module.exports = {
62+
validateRecords
63+
}

2020/day-01/expenseValidation.test.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/* eslint-env mocha */
2+
const { expect } = require('chai')
3+
const { validateRecords } = require('./expenseValidation')
4+
5+
/**
6+
* Sum all the values in an array
7+
*/
8+
const arrSum = (arr) => arr.reduce((x, y) => x + y, 0)
9+
/**
10+
* Multiply all the values in an array
11+
*/
12+
const arrMult = (arr) => arr.reduce((x, y) => x * y, 1)
13+
const testData = [
14+
1721,
15+
979,
16+
366,
17+
299,
18+
675,
19+
1456
20+
]
21+
22+
describe('--- 2020 Day 1: Report Repair ---', () => {
23+
describe('Part 1', () => {
24+
describe('validateRecords()', () => {
25+
it('it finds the two records that sum to 2020', () => {
26+
const expected = [1721, 299]
27+
const results = validateRecords(testData, undefined, 2)
28+
// Should be 2 results
29+
expect(results.length).to.equal(2)
30+
// Result order is unnecessary, but all expected hould be in the result set
31+
expected.forEach(result => {
32+
expect(testData.indexOf(result)).to.be.greaterThan(-1)
33+
})
34+
// Results add up to the checksum
35+
expect(arrSum(results)).to.equal(2020)
36+
// Confirm the multiplied total
37+
expect(arrMult(results)).to.equal(514579)
38+
})
39+
it('it supports specifying an alternate checksum', () => {
40+
const expected = [testData[3], testData[5]]
41+
const checksum = arrSum(expected)
42+
const results = validateRecords(testData, checksum)
43+
// Should be 2 results
44+
expect(results.length).to.equal(2)
45+
// Result order is unnecessary, but all expected hould be in the result set
46+
expected.forEach(result => {
47+
expect(results.indexOf(result)).to.be.greaterThan(-1)
48+
})
49+
// Results add up to the checksum
50+
expect(arrSum(results)).to.equal(checksum)
51+
})
52+
it('Throws an error when no records provided', () => {
53+
expect(validateRecords).to.throw()
54+
})
55+
it('Throws an error when no records match checksum', () => {
56+
expect(() => validateRecords([1, 2, 3], 100)).to.throw('Couldn\'t find a checksum match')
57+
})
58+
})
59+
})
60+
describe('Part 2', () => {
61+
describe('validateRecords()', () => {
62+
it('it can find a specified number of records adding up to 2020', () => {
63+
const expected = [979, 366, 675]
64+
const results = validateRecords(testData, undefined, 3)
65+
// Should same number of results
66+
expect(results.length).to.equal(expected.length)
67+
// Result order is unnecessary, but all expected hould be in the result set
68+
expected.forEach(result => {
69+
expect(testData.indexOf(result)).to.be.greaterThan(-1)
70+
})
71+
// Results add up to the checksum
72+
expect(arrSum(results)).to.equal(2020)
73+
// Confirm the multiplied total
74+
expect(arrMult(results)).to.equal(241861950)
75+
})
76+
})
77+
})
78+
})

2020/day-01/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
require('./solution')

2020/day-01/input.txt

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
1539
2+
1914
3+
1866
4+
1407
5+
1706
6+
1423
7+
1834
8+
1700
9+
1573
10+
1486
11+
1743
12+
1394
13+
1693
14+
1705
15+
1530
16+
1811
17+
1626
18+
1473
19+
1901
20+
1481
21+
1527
22+
1841
23+
1891
24+
1750
25+
1343
26+
1899
27+
401
28+
1896
29+
1627
30+
1593
31+
1541
32+
874
33+
1484
34+
1210
35+
1692
36+
1963
37+
1964
38+
1780
39+
671
40+
1862
41+
1393
42+
1309
43+
1740
44+
1831
45+
1932
46+
1185
47+
1979
48+
1504
49+
1663
50+
1610
51+
1494
52+
1511
53+
1103
54+
1738
55+
1816
56+
1871
57+
1545
58+
1595
59+
1784
60+
1412
61+
1815
62+
1998
63+
1783
64+
1770
65+
1426
66+
1699
67+
1416
68+
1880
69+
1612
70+
1989
71+
1360
72+
1869
73+
1762
74+
1690
75+
1999
76+
1990
77+
1521
78+
1730
79+
703
80+
1463
81+
1670
82+
1472
83+
1413
84+
1669
85+
1502
86+
1548
87+
1475
88+
1694
89+
1314
90+
1980
91+
980
92+
1667
93+
890
94+
1569
95+
1456
96+
1406
97+
1924
98+
1973
99+
1965
100+
1533
101+
1827
102+
2000
103+
1847
104+
1520
105+
1729
106+
1512
107+
1555
108+
1566
109+
1505
110+
1672
111+
1169
112+
1835
113+
1850
114+
1493
115+
1861
116+
1288
117+
1675
118+
1676
119+
1556
120+
1320
121+
1757
122+
1870
123+
1642
124+
1903
125+
1372
126+
1967
127+
1894
128+
176
129+
1908
130+
1418
131+
1535
132+
1487
133+
1496
134+
1491
135+
1611
136+
1970
137+
1758
138+
1563
139+
1766
140+
1629
141+
1937
142+
1763
143+
1829
144+
1772
145+
1632
146+
1517
147+
1736
148+
1971
149+
1721
150+
1716
151+
1429
152+
1408
153+
1560
154+
1958
155+
1359
156+
1890
157+
1825
158+
1536
159+
1819
160+
1697
161+
1887
162+
1832
163+
2005
164+
892
165+
1471
166+
1425
167+
1677
168+
1673
169+
1128
170+
1878
171+
1062
172+
1470
173+
1875
174+
1854
175+
1518
176+
1568
177+
1919
178+
256
179+
1532
180+
1711
181+
1944
182+
1344
183+
1330
184+
1636
185+
1957
186+
1709
187+
1551
188+
1983
189+
1674
190+
1671
191+
1959
192+
1760
193+
1689
194+
1767
195+
1477
196+
1589
197+
1897
198+
1144
199+
1982
200+
1544

2020/day-01/solution.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const fs = require('fs')
2+
const path = require('path')
3+
const filePath = path.join(__dirname, 'input.txt')
4+
const { validateRecords } = require('./expenseValidation')
5+
const { inputToArray } = require('../../2018/inputParser')
6+
7+
fs.readFile(filePath, { encoding: 'utf8' }, (err, initData) => {
8+
if (err) throw err
9+
10+
initData = inputToArray(initData.trim())
11+
.map(el => Number(el))
12+
13+
const resetInput = () => {
14+
// Deep copy to ensure we aren't mutating the original data
15+
return JSON.parse(JSON.stringify(initData))
16+
}
17+
18+
const part1 = () => {
19+
const data = resetInput()
20+
return validateRecords(data) // Find 2 results for 2020
21+
.reduce((total, res) => total * res, 1)
22+
}
23+
24+
const part2 = () => {
25+
const data = resetInput()
26+
return validateRecords(data, undefined, 3) // Find 3 results for 2020
27+
.reduce((total, res) => total * res, 1)
28+
}
29+
const answers = []
30+
answers.push(part1())
31+
answers.push(part2())
32+
33+
answers.forEach((ans, idx) => {
34+
console.info(`-- Part ${idx + 1} --`)
35+
console.info(`Answer: ${ans}`)
36+
})
37+
})

0 commit comments

Comments
 (0)