jlelse's Blog

Thoughts, stories and ideas

Advent of Code 2020 in Go: Day 1 and 2

Published on in šŸ‘Øā€šŸ’» Dev
Short link: https://b.jlel.se/s/56
Share 

I decided to publish my Advent of Code solutions regularly (probably every other day) here on this blog. So there are a few more posts with code to read. After all, code is a big part of my life, so it should be more present here on the blog.

The full (and possibly improved) code is available either on my Gitea instance or a GitHub mirror.

Day 1

Day 1 started with the task of correcting expense reports. The input is a list of numbers. In part 1 two numbers were searched for, which add up to 2020. These should be multiplied and finally result in the answer.

My approach was simple. Run through the list in two nested loops and see when the respective numbers add up to 2020. If this is the case, the product of both numbers is returned.

func main() {
	// Open the file
	file, _ := os.Open("input.txt")
	defer file.Close()

	numbers := []int{}

	// Read every line from the input and convert it to an integer
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		n, _ := strconv.Atoi(scanner.Text())
		numbers = append(numbers, n)
	}

	for _, i := range numbers {
		for _, j := range numbers {
			if i+j == 2020 {
				// i and j add up to 2020, return the product
				fmt.Println(i * j)
				return
			}
		}
	}
}

The second task was then that three numbers together make 2020. Therefore I simply introduced a third nested loop:

// First loop
for _, i := range numbers {
	// Second loop
	for _, j := range numbers {
		// Third loop
		for _, k := range numbers {
			if i+j+k == 2020 {
				fmt.Println(i * j * k)
				return
			}
		}
	}
}

This is certainly not the most efficient solution, but in the end the right result was achieved. Better done than perfect.

Day 2

The task on the Day 2 was already a bit more difficult. Now it was about validating passwords. There were certain rules for that:

Each line gives the password policy and then the password. The password policy indicates the lowest and highest number of times a given letter must appear for the password to be valid. For example, 1-3 a means that the password must contain a at least 1 time and at most 3 times.

The following example was given, of which only password 1 and 3 were valid:

1-3 a: abcde
1-3 b: cdefg
2-9 c: ccccccccc

To parse the input I had the idea to use regular expressions:

// file := ...

passwords := []*password{}

inputRegex := regexp.MustCompile(`^(\d+)-(\d+) (\w): (\w+)$`)

scanner := bufio.NewScanner(file)
for scanner.Scan() {
	text := scanner.Text()
	min, _ := strconv.Atoi(inputRegex.ReplaceAllString(text, "$1"))
	max, _ := strconv.Atoi(inputRegex.ReplaceAllString(text, "$2"))
	passwords = append(passwords, &password{
		min:      min,
		max:      max,
		letter:   inputRegex.ReplaceAllString(text, "$3"),
		password: inputRegex.ReplaceAllString(text, "$4"),
	})
}

Counting the occurrences of the character for every password is then quite simple using strings.Count():

correct := 0
for _, pw := range passwords {
	if count := strings.Count(pw.password, pw.letter); count >= pw.min && count <= pw.max {
		correct++
	}
}
fmt.Println(correct)

In part 2 the rules were a bit different and the numbers now give two positions of the password, where the counting of the positions starts at 1 and not at 0!

Exactly one of these positions must contain the given letter. Other occurrences of the letter are irrelevant for the purposes of policy enforcement.

So I renamed the fields min and max in the type password to first and second and replaced the counting by an XOR comparison of the letters at the positions:

for _, pw := range passwords {
	if (string(pw.password[pw.first-1]) == pw.letter) != (string(pw.password[pw.second-1]) == pw.letter) {
		correct++
	}
}

Those were the first two days! I’m curious about what is coming soon and if I can really keep it up until Christmas. šŸŽ…

Tags:

Jan-Lukas Else
Interactions & Comments