Dennis de Reus

4 minute read

I’m trying to keep up with the Advent of Code this year in Go.

Day 2 - part 1: finding IDs with duplicate or triplicate characters

And here we go, we’re given a set of IDs looking like srirtfyzlognvxwctqmphenbkd and have count the number of IDs that contain 1 or more characters exactly two or three times and then calculate the result of #duplicates * #triplicates.

Our approach will be to go ID by ID and iterate across each character in the ID and count how many times the ID contains this character. If any character is counted two or three times, we’ll increase our count and then return the ‘checksum’ requested. The solution below is not efficient - e.g. it will do searches that are known to be unnecessairy - but the solution space is small enough for this not to be an issue.

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	var duals, triples int
	input := readFile("input")

	// iterate over each ID, confirming for each if it has characters that are
	// included exactly two or three times

	for _, id := range input {
		has2, has3 := checkID(id)
		if has2 {
			duals++
		}
		if has3 {
			triples++
		}
	}

	// Display checksum
	fmt.Println(duals * triples)
}

// checkID returns if the ID has exactly 2 and/or 3 repeating characters
func checkID(id string) (bool, bool) {
	// Inefficient search
	var duplicate, triplicate bool
	for _, r1 := range id {
		var count int
		for _, r2 := range id {
			if r1 == r2 {
				count++
			}
		}
		if count == 2 {
			duplicate = true
		}
		if count == 3 {
			triplicate = true
		}
	}
	return duplicate, triplicate
}

func readFile(f string) []string {
	var r []string

	// Import inputs file
	file, err := os.Open(f)
	check(err)
	defer file.Close()

	// read file line by line into a slice
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		r = append(r, scanner.Text())
	}
	return r
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

Day 2 - part 2: finding IDs that differ by only 1 character with another ID

We’re now tasked to find 2 IDs that only differ by 1 character. My approach is to compare each ID with all IDs after it. Our comparison will compare IDs character by character and determine the amount of characters that differ. If only 1 character differs, we’ve found our match. For each comparison, we’re tracking the matching characters to return as the final answer.

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	input := readFile("input")

	// iterate over each ID, look for other IDs that are almost similar
	for i, id := range input {
		found, s := findSimilar(id, input[i+1:len(input)])
		if found {
			fmt.Println(s)
		}
	}
}

func findSimilar(id string, ids []string) (bool, string) {
	// iterate over all remaining IDs
	for _, id2 := range ids {
		var count int
		var s []rune
		// count the number of matching characters
		for i, r1 := range id {
			if r1 == rune(id2[i]) {
				count++
				s = append(s, r1)
			}
		}
		// if all but one character matches, return the match
		if count == len(id)-1 {
			return true, string(s)
		}
	}
	return false, ""
}

func readFile(f string) []string {
	var r []string

	// Import inputs file
	file, err := os.Open(f)
	check(err)
	defer file.Close()

	// read file line by line into a slice
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		r = append(r, scanner.Text())
	}
	return r
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

Writing code like this I realize there is an option for adversarial advent of code exercises where my inputs in this case can contain UTF-8 characters (which would mess up my simplistic conversion to runes for id2), or IDs with different sizes (which would break my character by character search if ID2 is shorter than ID1).