Advent of Code 2020 in Go: Day 3 and 4

Here are my solutions for Day 3 and 4 of Advent of Code.

Day 3

On Day 3 I started to put my solutions for part one and to into a single file. Often part two builds onto what was needed for part one, so it makes sense to reuse it instead of copy and duplicate everything.

The task was to look at paths through a forest and calculate the number of collisions with trees.

In my solution my input is first read into a two-dimensional integer array:

file, _ := os.Open("input.txt")
defer file.Close()

treeMap := [][]int{}

scanner := bufio.NewScanner(file)
for scanner.Scan() {
treeRow := []int{}
for _, char := range scanner.Text() {
if char == '#' {
treeRow = append(treeRow, 1)
} else if char == '.' {
treeRow = append(treeRow, 0)
}
}
treeMap = append(treeMap, treeRow)
}

This makes it relatively easy to use a counter for the number of trees on the path:

rowLength := len(treeMap[0])

row := 0
pos := 0
trees := 0

for row < len(treeMap) {
trees += treeMap[row][pos]
row++
pos += 3
pos %= rowLength
}

fmt.Println("Part 1:", trees)

Because the tree pattern is repeating, there’s the trick with modulo 3, so that when the right end is reached, it starts again at the left.

In part 2 several slope patterns have to be calculated, so I introduce a slope type and calculate the required slopes in a loop:

trees = 1

type slope struct {
right, down int
}

for _, s := range []slope{{1, 1}, {3, 1}, {5, 1}, {7, 1}, {1, 2}} {
slopeTrees := 0
row = 0
pos = 0
for row < len(treeMap) {
slopeTrees += treeMap[row][pos]
row += s.down
pos += s.right
pos %= rowLength
}
trees *= slopeTrees
}

fmt.Println("Part 2:", trees)

All in all a rather simple task, in my opinion.

Day 4

Day 4 is a bit more complex and my code is much longer, so I only show excerpts here. My complete solution here.

Part 1 is still relatively simple, first the passports have to be read in and finally it has to be checked if certain fields are present:

file, _ := os.Open("input.txt")
defer file.Close()

passports := []map[string]string{}

passport := map[string]string{}

scanner := bufio.NewScanner(file)
for scanner.Scan() {
text := scanner.Text()
if len(text) == 0 {
// Empty row, new passport
passports = append(passports, passport)
passport = map[string]string{}
} else {
for _, part := range strings.Split(text, " ") {
keyValue := strings.Split(part, ":")
passport[keyValue[0]] = keyValue[1]
}
}
}
// Last passport
passports = append(passports, passport)

count := 0
ppLoop:
for _, pp := range passports {
for _, field := range []string{"byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"} {
if _, fieldPresent := pp[field]; !fieldPresent {
continue ppLoop
}
}
count++
}

fmt.Println("Part 1:", count)

In part 2, these fields must then also be validated. For this purpose I created several functions within the function to check the values, which is done in different ways for each value:

yrValid := func(byr string, min, max int) bool {
byrInt, err := strconv.Atoi(byr)
if err != nil || byrInt < min || byrInt > max {
return false
}
return true
}

hgtValid := func(hgt string) bool {
if strings.HasSuffix(hgt, "cm") {
if no, err := strconv.Atoi(strings.TrimSuffix(hgt, "cm")); err != nil || no < 150 || no > 193 {
return false
}
} else if strings.HasSuffix(hgt, "in") {
if no, err := strconv.Atoi(strings.TrimSuffix(hgt, "in")); err != nil || no < 59 || no > 76 {
return false
}
} else {
return false
}
return true
}

hclValid := func(hcl string) bool {
return regexp.MustCompile(`^#[a-f0-9]{6}$`).Match([]byte(hcl))
}

eclValid := func(ecl string) bool {
return ecl == "amb" || ecl == "blu" || ecl == "brn" || ecl == "gry" || ecl == "grn" || ecl == "hzl" || ecl == "oth"
}

pidValid := func(pid string) bool {
if len(pid) != 9 {
return false
}
if id, err := strconv.Atoi(pid); err != nil || id == 0 {
return false
}
return true
}

Not a particularly difficult task, but a more laborious one.

Interactions & Comments