mirror of
https://github.com/Luzifer/aoc2019.git
synced 2024-12-21 21:41:16 +00:00
Add solution for Day 4
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
34a363cce1
commit
588a27e37d
3 changed files with 182 additions and 0 deletions
123
day04.go
Normal file
123
day04.go
Normal file
|
@ -0,0 +1,123 @@
|
|||
package aoc2019
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func day4NumberToDigitSlice(n int64) []int {
|
||||
var out []int
|
||||
sn := strconv.FormatInt(n, 10)
|
||||
for _, c := range sn {
|
||||
d, err := strconv.Atoi(string(c))
|
||||
if err != nil {
|
||||
// This should not happen as the input is a guaranteed base-10 number
|
||||
panic(err)
|
||||
}
|
||||
out = append(out, d)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func day4IsValidPassword(n, min, max int64) bool {
|
||||
sn := day4NumberToDigitSlice(n)
|
||||
|
||||
// It is a six-digit number.
|
||||
if len(sn) != 6 {
|
||||
return false
|
||||
}
|
||||
|
||||
// The value is within the range given in your puzzle input.
|
||||
if n < min || n > max {
|
||||
return false
|
||||
}
|
||||
|
||||
// Two adjacent digits are the same (like 22 in 122345).
|
||||
var foundAdjacentMatch bool
|
||||
for i := 1; i < len(sn); i++ {
|
||||
if sn[i] == sn[i-1] {
|
||||
foundAdjacentMatch = true
|
||||
}
|
||||
}
|
||||
if !foundAdjacentMatch {
|
||||
return false
|
||||
}
|
||||
|
||||
// Going from left to right, the digits never decrease; they only ever increase or stay the same (like 111123 or 135679).
|
||||
for i := 1; i < len(sn); i++ {
|
||||
if sn[i] < sn[i-1] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func day4IsValidPasswordPart2(in, min, max int64) bool {
|
||||
// Previous rules still apply
|
||||
if !day4IsValidPassword(in, min, max) {
|
||||
return false
|
||||
}
|
||||
|
||||
sn := day4NumberToDigitSlice(in)
|
||||
|
||||
var (
|
||||
last = sn[0]
|
||||
count = 1
|
||||
)
|
||||
|
||||
for i := 1; i < len(sn); i++ {
|
||||
if sn[i] != last && count == 2 {
|
||||
return true
|
||||
}
|
||||
|
||||
if sn[i] == last {
|
||||
count++
|
||||
continue
|
||||
}
|
||||
|
||||
last = sn[i]
|
||||
count = 1
|
||||
}
|
||||
|
||||
return count == 2
|
||||
}
|
||||
|
||||
func solveDay4WithFunction(inFile string, vf func(int64, int64, int64) bool) (int, error) {
|
||||
raw, err := ioutil.ReadFile(inFile)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "Unable to read input file")
|
||||
}
|
||||
|
||||
pts := strings.Split(strings.TrimSpace(string(raw)), "-")
|
||||
min, err := strconv.ParseInt(pts[0], 10, 64)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "Unable to parse minimum")
|
||||
}
|
||||
|
||||
max, err := strconv.ParseInt(pts[1], 10, 64)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "Unable to parse maximum")
|
||||
}
|
||||
|
||||
var count int
|
||||
for i := min; i <= max; i++ {
|
||||
if vf(i, min, max) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func solveDay4Part1(inFile string) (int, error) {
|
||||
return solveDay4WithFunction(inFile, day4IsValidPassword)
|
||||
}
|
||||
|
||||
func solveDay4Part2(inFile string) (int, error) {
|
||||
return solveDay4WithFunction(inFile, day4IsValidPasswordPart2)
|
||||
}
|
1
day04_input.txt
Normal file
1
day04_input.txt
Normal file
|
@ -0,0 +1 @@
|
|||
125730-579381
|
58
day04_test.go
Normal file
58
day04_test.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package aoc2019
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDay4NumberToDigitSlice(t *testing.T) {
|
||||
for n, expSlice := range map[int64][]int{
|
||||
1234567: {1, 2, 3, 4, 5, 6, 7},
|
||||
43626145: {4, 3, 6, 2, 6, 1, 4, 5},
|
||||
} {
|
||||
if s := day4NumberToDigitSlice(n); !reflect.DeepEqual(expSlice, s) {
|
||||
t.Errorf("Number to slice for number %d yield unexpected result: exp=%+v got=%+v", n, expSlice, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDay4ValidPassword(t *testing.T) {
|
||||
for n, expValid := range map[int64]bool{
|
||||
111111: true,
|
||||
223450: false,
|
||||
123789: false,
|
||||
} {
|
||||
if v := day4IsValidPassword(n, 0, math.MaxInt64); v != expValid {
|
||||
t.Errorf("Number %d did not have expected validity: exp=%v got=%v", n, expValid, v)
|
||||
}
|
||||
}
|
||||
|
||||
for n, expValid := range map[int64]bool{
|
||||
112233: true,
|
||||
123444: false,
|
||||
111122: true,
|
||||
} {
|
||||
if v := day4IsValidPasswordPart2(n, 0, math.MaxInt64); v != expValid {
|
||||
t.Errorf("Number %d did not have expected validity for part 2: exp=%v got=%v", n, expValid, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateDay4_Part1(t *testing.T) {
|
||||
count, err := solveDay4Part1("day04_input.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Day 4 solver failed: %s", err)
|
||||
}
|
||||
|
||||
t.Logf("Solution Day 4 Part 1: %d", count)
|
||||
}
|
||||
|
||||
func TestCalculateDay4_Part2(t *testing.T) {
|
||||
count, err := solveDay4Part2("day04_input.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Day 4 solver failed: %s", err)
|
||||
}
|
||||
|
||||
t.Logf("Solution Day 4 Part 2: %d", count)
|
||||
}
|
Loading…
Reference in a new issue