Skip to content

Commit ceb554b

Browse files
Merge pull request #242 from amclin/feat/2023-day-01
Feat/2023 day 01
2 parents 70f3286 + 45950a5 commit ceb554b

File tree

6 files changed

+1277
-1
lines changed

6 files changed

+1277
-1
lines changed

2023/day-01/checksum.js

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/**
2+
* Generates a checksum for a string by concatenating
3+
* the first and last digits found in the string
4+
* @param {string} data of a single line
5+
*/
6+
const checksumLine = (data) => {
7+
const parsed = data.replaceAll(/([^0-9])/g, '') // trim non-numeric characters
8+
let result = ''
9+
if (parsed.length === 1) { // some strings only have a single digit
10+
result = `${parsed}${parsed}`
11+
} else {
12+
result = `${parsed[0]}${parsed[parsed.length - 1]}`
13+
}
14+
return parseInt(result)
15+
}
16+
17+
const lazyNums = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']
18+
const lazyReg = new RegExp(lazyNums.join('|'))
19+
const lazyNumsReversed = lazyNums.map((num) => num.split('').reverse().join(''))
20+
const lazyReversReg = new RegExp(lazyNumsReversed.join('|'))
21+
22+
const lookupPosition = (match) => {
23+
const idx = lazyNums.indexOf(match)
24+
if (idx > 9) {
25+
return idx - 9
26+
}
27+
return idx
28+
}
29+
30+
const lookupPositionReversed = (match) => {
31+
const reverseMatch = match.split('').reverse().join('')
32+
const idx = lazyNums.indexOf(reverseMatch)
33+
if (idx > 9) {
34+
return idx - 9
35+
}
36+
return idx
37+
}
38+
39+
const lazyChecksumLine = (data) => {
40+
let first = ''
41+
data.replace(lazyReg, (match) => {
42+
first = lookupPosition(match)
43+
return match // reinsert so we don't bork the data string
44+
})
45+
// find last matching digit by reversing the string and searching backwards
46+
let last = ''
47+
data = data.split('').reverse().join('')
48+
data.replace(lazyReversReg, (match) => {
49+
last = lookupPositionReversed(match)
50+
return match // reinsert so we don't bork the data string
51+
})
52+
53+
return parseInt(`${first}${last}`)
54+
}
55+
56+
/**
57+
* Generates the checksum for an entire set
58+
* @param {array} set of lines containing data
59+
*/
60+
const checksumSet = (set, sanitize = false, lazy = false) => {
61+
let filter = (data) => data
62+
if (sanitize) {
63+
filter = sanitizeLine
64+
}
65+
66+
let checksum = checksumLine
67+
if (lazy) {
68+
checksum = lazyChecksumLine
69+
}
70+
71+
return set.reduce((total, current) => {
72+
return total + checksum(
73+
filter(current)
74+
)
75+
}, 0)
76+
}
77+
78+
const numbers = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']
79+
const numbersReversed = numbers.map((num) => num.split('').reverse().join(''))
80+
const reg = new RegExp(numbers.join('|'))
81+
const regGlobal = new RegExp(numbers.join('|'), 'g')
82+
const regReversed = new RegExp(numbersReversed.join('|'))
83+
84+
// Sanitizes using a single-pass regex replace all
85+
// Produces 53885 which is incorrect for part 2
86+
const byRegex = (data) => {
87+
return data.replaceAll(regGlobal, (matched) => numbers.indexOf(matched) + 1)
88+
}
89+
90+
// Sanitizes by replacing just the first and last text digits, in case shared letters is supposed to work
91+
// Produces 53853 which is too low for part 2
92+
const byFirstLast = (data) => {
93+
// sanitize first matching digit
94+
data = data.replace(reg, (matched) => numbers.indexOf(matched) + 1)
95+
// sanitize last matching digit by reversing the string and searching backwards
96+
data = data.split('').reverse().join('')
97+
data = data.replace(regReversed, (matched) => numbersReversed.indexOf(matched) + 1)
98+
99+
// return original order
100+
return data.split('').reverse().join('')
101+
}
102+
103+
/**
104+
* Sanitzizes a line by replacing spelled-out numbers with data
105+
* @param {string} data line of input to sanitize
106+
*/
107+
const sanitizeLine = (data, method = 'byFirstLast') => {
108+
const methods = {
109+
none: (data) => data,
110+
byFirstLast,
111+
byRegex
112+
}
113+
114+
return methods[method](data)
115+
}
116+
117+
module.exports = { checksumLine, checksumSet, sanitizeLine, lazyChecksumLine }

2023/day-01/checksum.test.js

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/* eslint-env mocha */
2+
const { expect } = require('chai')
3+
const { checksumSet, checksumLine, sanitizeLine, lazyChecksumLine } = require('./checksum')
4+
const fs = require('fs')
5+
const path = require('path')
6+
const filePath = path.join(__dirname, 'input.txt')
7+
const { inputToArray } = require('../../2018/inputParser')
8+
9+
describe('--- Day 1: Trebuchet?! ---', () => {
10+
describe('Part 1', () => {
11+
describe('checksum', () => {
12+
it('calculates the checksum for a string by concatentating the first and last number', () => {
13+
// provided
14+
expect(checksumLine('1abc2')).to.equal(12)
15+
expect(checksumLine('pqr3stu8vwx')).to.equal(38)
16+
expect(checksumLine('a1b2c3d4e5f')).to.equal(15)
17+
})
18+
it('handles the edge case of a line with only a single digit', () => {
19+
// provided
20+
expect(checksumLine('treb7uchet')).to.equal(77)
21+
})
22+
})
23+
describe('checksumSet', () => {
24+
it('calculates the checksum for a set of lines by summing the checksum of each line', () => {
25+
// provided
26+
const set = ['1abc2', 'pqr3stu8vwx', 'a1b2c3d4e5f', 'treb7uchet']
27+
expect(checksumSet(set)).to.equal(142)
28+
})
29+
})
30+
})
31+
describe('Part 2', () => {
32+
describe('sanitizeLine', () => {
33+
const data = [
34+
'two1nine',
35+
'eightwothree',
36+
'abcone2threexyz',
37+
'xtwone3four',
38+
'4nineeightseven2',
39+
'zoneight234',
40+
'7pqrstsixteen'
41+
]
42+
const result = [29, 83, 13, 24, 42, 14, 76]
43+
it('cleans up a string when digits are spelled out', () => {
44+
const set = JSON.parse(JSON.stringify(data))
45+
for (let x = 0; x < set.length; x++) {
46+
expect(checksumLine(sanitizeLine(set[x]))).to.equal(result[x])
47+
// expect(checksumLine(sanitizeLine(set[x], 'sanitizeByRegex'))).to.equal(result[x])
48+
// expect(checksumLine(sanitizeLine(set[x], 'sanitizeFirstLast'))).to.equal(result[x])
49+
}
50+
})
51+
it('allows for skipping sanitation', () => {
52+
const set = JSON.parse(JSON.stringify(data))
53+
for (let x = 0; x < set.length; x++) {
54+
expect(sanitizeLine(set[x], 'none')).to.equal(data[x])
55+
}
56+
})
57+
})
58+
describe('checksumSet', () => {
59+
const data = [
60+
'two1nine',
61+
'eightwothree',
62+
'abcone2threexyz',
63+
'xtwone3four',
64+
'4nineeightseven2',
65+
'zoneight234',
66+
'7pqrstsixteen'
67+
]
68+
it('can sanitize', () => {
69+
expect(checksumSet(data, true)).to.equal(281)
70+
})
71+
})
72+
describe('lazyChecksumLine', () => {
73+
const data = [
74+
'two1nine',
75+
'eightwothree',
76+
'abcone2threexyz',
77+
'xtwone3four',
78+
'4nineeightseven2',
79+
'zoneight234',
80+
'7pqrstsixteen'
81+
]
82+
const result = [29, 83, 13, 24, 42, 14, 76]
83+
it('can match text or numeric for checksum calcs', () => {
84+
const set = JSON.parse(JSON.stringify(data))
85+
for (let x = 0; x < set.length; x++) {
86+
expect(lazyChecksumLine(set[x])).to.equal(result[x])
87+
}
88+
})
89+
})
90+
91+
describe.skip('integeration', () => {
92+
let initData
93+
before((done) => {
94+
fs.readFile(filePath, { encoding: 'utf8' }, (err, rawData) => {
95+
if (err) throw err
96+
initData = inputToArray(rawData.trim())
97+
// Deep copy to ensure we aren't mutating the original data
98+
// data = JSON.parse(JSON.stringify(initData))
99+
done()
100+
})
101+
})
102+
103+
it('is not done without sanitation, since that matches part 1', () => {
104+
const data = JSON.parse(JSON.stringify(initData))
105+
const result = checksumSet(data, false, true)
106+
expect(result).to.not.equal(54953)
107+
})
108+
109+
it('is not done with a one-time regex', () => {
110+
const data = JSON.parse(JSON.stringify(initData))
111+
const result = checksumSet(data, false, true)
112+
expect(result).to.not.equal(53885) // result of one-time regex
113+
})
114+
115+
it('is not done by sanitizing just the first and last strings', () => {
116+
const data = JSON.parse(JSON.stringify(initData))
117+
const result = checksumSet(data, false, true)
118+
expect(result).to.not.equal(53853) // result of first/last substitution onlye
119+
})
120+
})
121+
})
122+
})

2023/day-01/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// eslint-disable-next-line no-unused-vars
2+
const console = require('../helpers')
3+
require('./solution')

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy