Skip to content

Commit 82aeb16

Browse files
committed
2019/day04: Implement part2 solution
1 parent 5c057f7 commit 82aeb16

File tree

3 files changed

+170
-20
lines changed

3 files changed

+170
-20
lines changed

puzzles/solutions/2019/day04/solution.go

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@ type solution struct {
2727
}
2828

2929
func (s solution) Part1(input io.Reader) (string, error) {
30+
return run(input, isPasswordPart1)
31+
}
32+
33+
func (s solution) Part2(input io.Reader) (string, error) {
34+
return run(input, isPasswordPart2)
35+
}
36+
37+
func (s solution) Name() string {
38+
return s.name
39+
}
40+
41+
func run(input io.Reader, criteria isPwdFunc) (string, error) {
3042
buf := new(bytes.Buffer)
3143
if _, err := buf.ReadFrom(input); err != nil {
3244
return "", errors.Wrap(err, "failed to read")
@@ -37,23 +49,17 @@ func (s solution) Part1(input io.Reader) (string, error) {
3749
return "", errors.New("invalid number of limits")
3850
}
3951

40-
passwords, err := findPasswords(limits[0], limits[1])
52+
passwords, err := findPasswords(limits[0], limits[1], criteria)
4153
if err != nil {
4254
return "", errors.Wrap(err, "failed to find passwords")
4355
}
4456

4557
return strconv.Itoa(passwords), nil
4658
}
4759

48-
func (s solution) Part2(input io.Reader) (string, error) {
49-
return "", puzzles.ErrNotImplemented
50-
}
60+
type isPwdFunc func(n int) bool
5161

52-
func (s solution) Name() string {
53-
return s.name
54-
}
55-
56-
func findPasswords(low, high string) (int, error) {
62+
func findPasswords(low, high string, criteria isPwdFunc) (int, error) {
5763
lowd, err := strconv.Atoi(low)
5864
if err != nil {
5965
return -1, errors.Wrap(err, "failed to convert low to int")
@@ -67,7 +73,7 @@ func findPasswords(low, high string) (int, error) {
6773
pwds := make([]int, 0, highd-lowd)
6874

6975
for i := lowd; i <= highd; i++ {
70-
if isPassword(i) {
76+
if criteria(i) {
7177
pwds = append(pwds, i)
7278
}
7379
}
@@ -87,22 +93,54 @@ func isIncreasing(n int) bool {
8793
return true
8894
}
8995

90-
func hasDouble(n int) bool {
96+
func hasRepeated(n int) bool {
9197
nmbs := intToSlice(n)
9298

93-
var hasDouble bool
99+
var hasRepeated bool
94100

95101
for i := 1; i <= len(nmbs)-1; i++ {
96102
if nmbs[i] == nmbs[i-1] {
97-
hasDouble = true
103+
hasRepeated = true
104+
}
105+
}
106+
107+
return hasRepeated
108+
}
109+
110+
func hasRepeatedWithDouble(n int) bool {
111+
nmbs := intToSlice(n)
112+
113+
repeated := make(map[int]int)
114+
115+
for i := 1; i <= len(nmbs)-1; i++ {
116+
if nmbs[i] == nmbs[i-1] {
117+
repeated[nmbs[i]]++
118+
}
119+
}
120+
121+
if len(repeated) == 0 {
122+
return false
123+
}
124+
125+
var hasDouble bool
126+
127+
for i := 1; i < 10; i++ {
128+
if n, ok := repeated[i]; ok {
129+
if n == 1 {
130+
hasDouble = true
131+
}
98132
}
99133
}
100134

101135
return hasDouble
102136
}
103137

104-
func isPassword(n int) bool {
105-
return isIncreasing(n) && hasDouble(n)
138+
func isPasswordPart1(n int) bool {
139+
return isIncreasing(n) && hasRepeated(n)
140+
}
141+
142+
func isPasswordPart2(n int) bool {
143+
return isIncreasing(n) && hasRepeatedWithDouble(n)
106144
}
107145

108146
func intToSlice(n int) [6]int {

puzzles/solutions/2019/day04/solution_test.go

Lines changed: 97 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func Test_findPasswords(t *testing.T) {
8484
tt := tt
8585

8686
t.Run(tt.name, func(t *testing.T) {
87-
got, err := findPasswords(tt.args.low, tt.args.high)
87+
got, err := findPasswords(tt.args.low, tt.args.high, isPasswordPart1)
8888
if tt.wantErr {
8989
assert.Error(t, err)
9090
return
@@ -168,7 +168,7 @@ func Test_intToSlice(t *testing.T) {
168168
}
169169
}
170170

171-
func Test_hasDouble(t *testing.T) {
171+
func Test_hasRepeated(t *testing.T) {
172172
type args struct {
173173
n int
174174
}
@@ -198,7 +198,7 @@ func Test_hasDouble(t *testing.T) {
198198
tt := tt
199199

200200
t.Run(tt.name, func(t *testing.T) {
201-
got := hasDouble(tt.args.n)
201+
got := hasRepeated(tt.args.n)
202202
assert.Equal(t, tt.want, got)
203203
})
204204
}
@@ -241,7 +241,100 @@ func Test_isPassword(t *testing.T) {
241241
tt := tt
242242

243243
t.Run(tt.name, func(t *testing.T) {
244-
got := isPassword(tt.args.n)
244+
got := isPasswordPart1(tt.args.n)
245+
assert.Equal(t, tt.want, got)
246+
})
247+
}
248+
}
249+
250+
func Test_isPasswordPart2(t *testing.T) {
251+
type args struct {
252+
n int
253+
}
254+
255+
tests := []struct {
256+
name string
257+
args args
258+
want bool
259+
}{
260+
{
261+
name: "meets these criteria because the digits never decrease and all repeated digits are " +
262+
"exactly two digits long.",
263+
args: args{
264+
n: 112233,
265+
},
266+
want: true,
267+
},
268+
{
269+
name: "no longer meets the criteria (the repeated `44` is part of a larger group of `444`)",
270+
args: args{
271+
n: 123444,
272+
},
273+
want: false,
274+
},
275+
{
276+
name: "meets the criteria (even though `1` is repeated more than twice, it still contains a double `22`).",
277+
args: args{
278+
n: 111122,
279+
},
280+
want: true,
281+
},
282+
}
283+
284+
for _, tt := range tests {
285+
tt := tt
286+
287+
t.Run(tt.name, func(t *testing.T) {
288+
got := isPasswordPart2(tt.args.n)
289+
assert.Equal(t, tt.want, got)
290+
})
291+
}
292+
}
293+
294+
func Test_solution_Part2(t *testing.T) {
295+
type fields struct {
296+
name string
297+
}
298+
299+
type args struct {
300+
input io.Reader
301+
}
302+
303+
tests := []struct {
304+
name string
305+
fields fields
306+
args args
307+
want string
308+
wantErr bool
309+
}{
310+
{
311+
name: "",
312+
fields: fields{
313+
name: "",
314+
},
315+
args: args{
316+
input: strings.NewReader("111000-111222"),
317+
},
318+
want: "46",
319+
wantErr: false,
320+
},
321+
}
322+
323+
for _, tt := range tests {
324+
tt := tt
325+
326+
t.Run(tt.name, func(t *testing.T) {
327+
s := solution{
328+
name: tt.fields.name,
329+
}
330+
331+
got, err := s.Part2(tt.args.input)
332+
if tt.wantErr {
333+
assert.Error(t, err)
334+
return
335+
}
336+
337+
assert.NoError(t, err)
245338
assert.Equal(t, tt.want, got)
246339
})
247340
}

puzzles/solutions/2019/day04/spec.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
# --- Day 4: Secure Container ---
2+
3+
## --- Part One ---
4+
25
You arrive at the Venus fuel depot only to discover it's protected by a password. The Elves had written the password
36
on a sticky note, but someone threw it out.
47

@@ -18,4 +21,20 @@ Other than the range rule, the following are true:
1821

1922
How many different passwords within the range given in your puzzle input meet these criteria?
2023

21-
Your puzzle input is `108457-562041`.
24+
Your puzzle input is `108457-562041`.
25+
26+
## --- Part Two ---
27+
28+
An Elf just remembered one more important detail: the two adjacent matching digits are not part of a larger group
29+
of matching digits.
30+
31+
Given this additional criterion, but still ignoring the range rule, the following are now true:
32+
33+
- `112233` meets these criteria because the digits never decrease and all repeated digits are exactly two digits long.
34+
- `123444` no longer meets the criteria (the repeated `44` is part of a larger group of `444`).
35+
- `111122` meets the criteria (even though `1` is repeated more than twice, it still contains a double `22`).
36+
37+
38+
How many different passwords within the range given in your puzzle input meet all of the criteria?
39+
40+
Your puzzle input is still `108457-562041`.

0 commit comments

Comments
 (0)