Skip to content

Commit

Permalink
2019/day04: Implement part2 solution
Browse files Browse the repository at this point in the history
  • Loading branch information
obalunenko committed Dec 22, 2019
1 parent 5c057f7 commit 82aeb16
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 20 deletions.
68 changes: 53 additions & 15 deletions puzzles/solutions/2019/day04/solution.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ type solution struct {
}

func (s solution) Part1(input io.Reader) (string, error) {
return run(input, isPasswordPart1)
}

func (s solution) Part2(input io.Reader) (string, error) {
return run(input, isPasswordPart2)
}

func (s solution) Name() string {
return s.name
}

func run(input io.Reader, criteria isPwdFunc) (string, error) {
buf := new(bytes.Buffer)
if _, err := buf.ReadFrom(input); err != nil {
return "", errors.Wrap(err, "failed to read")
Expand All @@ -37,23 +49,17 @@ func (s solution) Part1(input io.Reader) (string, error) {
return "", errors.New("invalid number of limits")
}

passwords, err := findPasswords(limits[0], limits[1])
passwords, err := findPasswords(limits[0], limits[1], criteria)
if err != nil {
return "", errors.Wrap(err, "failed to find passwords")
}

return strconv.Itoa(passwords), nil
}

func (s solution) Part2(input io.Reader) (string, error) {
return "", puzzles.ErrNotImplemented
}
type isPwdFunc func(n int) bool

func (s solution) Name() string {
return s.name
}

func findPasswords(low, high string) (int, error) {
func findPasswords(low, high string, criteria isPwdFunc) (int, error) {
lowd, err := strconv.Atoi(low)
if err != nil {
return -1, errors.Wrap(err, "failed to convert low to int")
Expand All @@ -67,7 +73,7 @@ func findPasswords(low, high string) (int, error) {
pwds := make([]int, 0, highd-lowd)

for i := lowd; i <= highd; i++ {
if isPassword(i) {
if criteria(i) {
pwds = append(pwds, i)
}
}
Expand All @@ -87,22 +93,54 @@ func isIncreasing(n int) bool {
return true
}

func hasDouble(n int) bool {
func hasRepeated(n int) bool {
nmbs := intToSlice(n)

var hasDouble bool
var hasRepeated bool

for i := 1; i <= len(nmbs)-1; i++ {
if nmbs[i] == nmbs[i-1] {
hasDouble = true
hasRepeated = true
}
}

return hasRepeated
}

func hasRepeatedWithDouble(n int) bool {
nmbs := intToSlice(n)

repeated := make(map[int]int)

for i := 1; i <= len(nmbs)-1; i++ {
if nmbs[i] == nmbs[i-1] {
repeated[nmbs[i]]++
}
}

if len(repeated) == 0 {
return false
}

var hasDouble bool

for i := 1; i < 10; i++ {
if n, ok := repeated[i]; ok {
if n == 1 {
hasDouble = true
}
}
}

return hasDouble
}

func isPassword(n int) bool {
return isIncreasing(n) && hasDouble(n)
func isPasswordPart1(n int) bool {
return isIncreasing(n) && hasRepeated(n)
}

func isPasswordPart2(n int) bool {
return isIncreasing(n) && hasRepeatedWithDouble(n)
}

func intToSlice(n int) [6]int {
Expand Down
101 changes: 97 additions & 4 deletions puzzles/solutions/2019/day04/solution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func Test_findPasswords(t *testing.T) {
tt := tt

t.Run(tt.name, func(t *testing.T) {
got, err := findPasswords(tt.args.low, tt.args.high)
got, err := findPasswords(tt.args.low, tt.args.high, isPasswordPart1)
if tt.wantErr {
assert.Error(t, err)
return
Expand Down Expand Up @@ -168,7 +168,7 @@ func Test_intToSlice(t *testing.T) {
}
}

func Test_hasDouble(t *testing.T) {
func Test_hasRepeated(t *testing.T) {
type args struct {
n int
}
Expand Down Expand Up @@ -198,7 +198,7 @@ func Test_hasDouble(t *testing.T) {
tt := tt

t.Run(tt.name, func(t *testing.T) {
got := hasDouble(tt.args.n)
got := hasRepeated(tt.args.n)
assert.Equal(t, tt.want, got)
})
}
Expand Down Expand Up @@ -241,7 +241,100 @@ func Test_isPassword(t *testing.T) {
tt := tt

t.Run(tt.name, func(t *testing.T) {
got := isPassword(tt.args.n)
got := isPasswordPart1(tt.args.n)
assert.Equal(t, tt.want, got)
})
}
}

func Test_isPasswordPart2(t *testing.T) {
type args struct {
n int
}

tests := []struct {
name string
args args
want bool
}{
{
name: "meets these criteria because the digits never decrease and all repeated digits are " +
"exactly two digits long.",
args: args{
n: 112233,
},
want: true,
},
{
name: "no longer meets the criteria (the repeated `44` is part of a larger group of `444`)",
args: args{
n: 123444,
},
want: false,
},
{
name: "meets the criteria (even though `1` is repeated more than twice, it still contains a double `22`).",
args: args{
n: 111122,
},
want: true,
},
}

for _, tt := range tests {
tt := tt

t.Run(tt.name, func(t *testing.T) {
got := isPasswordPart2(tt.args.n)
assert.Equal(t, tt.want, got)
})
}
}

func Test_solution_Part2(t *testing.T) {
type fields struct {
name string
}

type args struct {
input io.Reader
}

tests := []struct {
name string
fields fields
args args
want string
wantErr bool
}{
{
name: "",
fields: fields{
name: "",
},
args: args{
input: strings.NewReader("111000-111222"),
},
want: "46",
wantErr: false,
},
}

for _, tt := range tests {
tt := tt

t.Run(tt.name, func(t *testing.T) {
s := solution{
name: tt.fields.name,
}

got, err := s.Part2(tt.args.input)
if tt.wantErr {
assert.Error(t, err)
return
}

assert.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
Expand Down
21 changes: 20 additions & 1 deletion puzzles/solutions/2019/day04/spec.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# --- Day 4: Secure Container ---

## --- Part One ---

You arrive at the Venus fuel depot only to discover it's protected by a password. The Elves had written the password
on a sticky note, but someone threw it out.

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

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

Your puzzle input is `108457-562041`.
Your puzzle input is `108457-562041`.

## --- Part Two ---

An Elf just remembered one more important detail: the two adjacent matching digits are not part of a larger group
of matching digits.

Given this additional criterion, but still ignoring the range rule, the following are now true:

- `112233` meets these criteria because the digits never decrease and all repeated digits are exactly two digits long.
- `123444` no longer meets the criteria (the repeated `44` is part of a larger group of `444`).
- `111122` meets the criteria (even though `1` is repeated more than twice, it still contains a double `22`).


How many different passwords within the range given in your puzzle input meet all of the criteria?

Your puzzle input is still `108457-562041`.

0 comments on commit 82aeb16

Please sign in to comment.