Skip to content

Commit 072038c

Browse files
Merge pull request #123 from amclin/feat/2020-day-02
Feat/2020 day 02
2 parents 2b39e17 + 47c46fa commit 072038c

File tree

5 files changed

+1225
-0
lines changed

5 files changed

+1225
-0
lines changed

2020/day-02/cleanupPasswords.js

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/**
2+
* Splits a password record into its component parts
3+
*/
4+
const splitRecord = (row) => {
5+
const record = row.split(': ')
6+
.map((el) => el.trim())
7+
8+
return {
9+
rule: record[0],
10+
password: record[1]
11+
}
12+
}
13+
14+
/**
15+
* Splits a password validation rule into its component parts
16+
* using the original rules
17+
*/
18+
const oldSplitRule = (rule) => {
19+
const splitRow = rule.split(/-| /)
20+
21+
return {
22+
min: Number(splitRow[0]),
23+
max: Number(splitRow[1]),
24+
char: String(splitRow[2])
25+
}
26+
}
27+
28+
/**
29+
* Splits a password validation rule into its component parts
30+
* using the new rules
31+
*/
32+
const newSplitRule = (rule) => {
33+
const splitRow = rule.split(/-| /)
34+
35+
return {
36+
positions: [
37+
Number(splitRow[0]),
38+
Number(splitRow[1])
39+
],
40+
char: String(splitRow[2])
41+
}
42+
}
43+
44+
/**
45+
* Validates a password against the specified rule
46+
* using the original rules
47+
*/
48+
const oldIsValidPassword = (rule, password) => {
49+
// count how many times `rule.char` exists in `password`
50+
const count = (
51+
password.match(
52+
new RegExp(rule.char, 'g')
53+
) || []
54+
).length
55+
// check the conditions
56+
if (count < rule.min) return false
57+
if (count > rule.max) return false
58+
return true
59+
}
60+
61+
/**
62+
* Validates a password against the specified rule
63+
* using the new rules
64+
*/
65+
const newIsValidPassword = (rule, password) => {
66+
let matches = 0
67+
rule.positions.forEach((pos) => {
68+
// index starts with 1
69+
if (password[pos - 1] === rule.char) {
70+
matches++
71+
}
72+
})
73+
// Only one match allowed, not 2, not 0
74+
return (matches === 1)
75+
}
76+
77+
const oldIsValidRecord = (record) => {
78+
const { rule, password } = splitRecord(record)
79+
const parsedRule = oldSplitRule(rule)
80+
return oldIsValidPassword(parsedRule, password)
81+
}
82+
83+
const newIsValidRecord = (record) => {
84+
const { rule, password } = splitRecord(record)
85+
const parsedRule = newSplitRule(rule)
86+
return newIsValidPassword(parsedRule, password)
87+
}
88+
89+
module.exports = {
90+
old: {
91+
splitRule: oldSplitRule,
92+
isValidPassword: oldIsValidPassword,
93+
isValidRecord: oldIsValidRecord
94+
},
95+
cur: {
96+
splitRule: newSplitRule,
97+
isValidPassword: newIsValidPassword,
98+
isValidRecord: newIsValidRecord
99+
},
100+
splitRecord
101+
}

2020/day-02/cleanupPasswords.test.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/* eslint-env mocha */
2+
const { expect } = require('chai')
3+
const { splitRecord, old, cur } = require('./cleanupPasswords')
4+
5+
const testData = [
6+
'1-3 a: abcde',
7+
'1-3 b: cdefg',
8+
'2-9 c: ccccccccc'
9+
]
10+
11+
describe('--- Day 2: Password Philosophy ---', () => {
12+
describe('Part 1', () => {
13+
describe('splitRecord()', () => {
14+
it('splits a password record into components parts', () => {
15+
testData.forEach((row, idx) => {
16+
const { rule, password } = splitRecord(row)
17+
expect(`${rule}: ${password}`).to.equal(testData[idx])
18+
})
19+
})
20+
})
21+
describe('splitRule()', () => {
22+
it('splits a password formatting rule into component parts', () => {
23+
testData.forEach((row, idx) => {
24+
const { rule, password } = splitRecord(row)
25+
const { min, max, char } = old.splitRule(rule)
26+
expect(`${min}-${max} ${char}: ${password}`).to.equal(testData[idx])
27+
})
28+
})
29+
})
30+
describe('isValidPassword()', () => {
31+
it('checks if a specified password matches the specified rule', () => {
32+
const expectedResults = [true, false, true]
33+
testData.forEach((row, idx) => {
34+
const { rule, password } = splitRecord(row)
35+
const { min, max, char } = old.splitRule(rule)
36+
expect(old.isValidPassword({ min, max, char }, password))
37+
.to.equal(expectedResults[idx])
38+
})
39+
})
40+
it('won\'t allow more than the specified character count', () => {
41+
const badPass = 'abcabcabcabc'
42+
expect(old.isValidPassword({ min: 2, max: 3, char: 'a' }, badPass))
43+
.to.equal(false)
44+
})
45+
})
46+
describe('isValidRecord()', () => {
47+
it('checks if a specified record contains valid rule and password', () => {
48+
const expectedResults = [true, false, true]
49+
testData.forEach((row, idx) => {
50+
expect(old.isValidRecord(row))
51+
.to.equal(expectedResults[idx])
52+
})
53+
})
54+
})
55+
})
56+
describe('Part 2', () => {
57+
describe('splitRule()', () => {
58+
it('splits a password formatting rule into component parts', () => {
59+
testData.forEach((row, idx) => {
60+
const { rule, password } = splitRecord(row)
61+
const { positions, char } = cur.splitRule(rule)
62+
expect(`${positions.join('-')} ${char}: ${password}`).to.equal(testData[idx])
63+
})
64+
})
65+
})
66+
describe('isValidPassword()', () => {
67+
it('checks if a specified password matches the specified rule', () => {
68+
const expectedResults = [true, false, false]
69+
testData.forEach((row, idx) => {
70+
const { rule, password } = splitRecord(row)
71+
const ruleObj = cur.splitRule(rule)
72+
expect(cur.isValidPassword(ruleObj, password))
73+
.to.equal(expectedResults[idx])
74+
})
75+
})
76+
})
77+
describe('isValidRecord()', () => {
78+
it('checks if a specified record contains valid rule and password', () => {
79+
const expectedResults = [true, false, false]
80+
testData.forEach((row, idx) => {
81+
expect(cur.isValidRecord(row))
82+
.to.equal(expectedResults[idx])
83+
})
84+
})
85+
})
86+
})
87+
})

2020/day-02/index.js

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

0 commit comments

Comments
 (0)