Day 1: Chronal Calibration

Notice: If you have coding experience, I highly recommend to try and solve the puzzle in a language you already know. It's a lot of fun and you'll be able to focus on Go more later on.

Hi there, how nice of you to swing by! Do you know which day it is? Yes, the first day of Advent of Code 2018! As with the AoC puzzles, we’re starting the advent of Go introduction from scratch again as well.

Development setup

Alright, first things first before we dive into the first puzzle: your development setup. As a first step, you’ll need to install the Go compiler. The easiest way to do this, is to download it from https://golang.org/dl/ Choose your operating system and the latest version (1.11.2 as of today). If you have an older Go version installed, this may be a good moment to upgrade. You can check your install using the go version command as shown below.

thomas@local:~$ go version
go version go1.11.2 darwin/amd64

With Go installed, you can now create your project directory. Previous versions of Go had a concept called GOPATH where you had to centralize all your code. Since Go 1.11, you can now put your code anywhere you like using a Go Module. The command below will initialze the module in your current directory.

thomas@local:adventofcode/2018/$ go mod init github.com/blackskad/adventofcode/2018
go: creating new go.mod: module github.com/blackskad/adventofcode/2018

The name of the module should point to a location where you can make the module available, so usually it points to a git repository.

Getting started

So you’ve got your environment set up, good! Let’s get started with a minimal Go program to introduce you to the basics.

package main

import(
    "fmt"
)

func main(){
    fmt.Println("Hello world")
}
  • First of all, we’re declaring that this file is part of the package main. This is the package that always contains the entrypoint for the program.
  • Then we’re importing the fmt package. A package is reusable collection of code, in this case to print text to the commandline. In this case, the package name is short, which indicates it’s part of the standard library. Custom packages will include the repository name, just like module name.
  • Next we’re defining the main function. This is the entrypoint for every Go application.
  • And finally, we’re using the println function from the fmt package to print “Hello world” to standard output.

You can save the code above in a subdirectory of the module directory you created earlier as day01.go. To run the application, you can simply use the command below

thomas@local:adventofcode/2018/day01/$ go run day01.go
Hello world

Reading lines

We finally reached a point where we’re ready to solve todays puzzle. Go read the puzzle over here, it’ll help you understand the go solution a lot better. One thing we’ll need for (almost) every puzzle, is reading input from a file.

func main() {
	f, err := os.Open("input.txt")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	values := []int{}

	scanner := bufio.NewScanner(f)
	scanner.Split(bufio.ScanWords)
	for scanner.Scan() {
		v, err := strconv.Atoi(scanner.Text())
		if err != nil {
			panic(err)
		}
		values = append(values, v)
	}
    fmt.Printf("Read %d input values\n", len(values))
}

Let’s go over what’s happening here real quick:

  • First of all, we open the input file to be able to read from it. We’re using the Open function from the os package to do that. It returns a file pointer and an error if there was one.
  • Next, we call the panic function if there was an error opening the file. Panic is a buildin function that completely terminates your program and prints some debugging information.
  • The defer keyword executes the statement that follows when the function exits. So in this case, it will close the file reference whenever the main function exits.
  • Create an empty slice of integers. A slice is a datastructure similar to an array list in other languages.
  • The next block of code creates a Scanner object from the bufio package, using the file descriptor, and we tell it to split the input on whitespace. As long as Scan indicates there is more input, we’ll parse the Text input to an integer using the strconv.Atoi package and append it to the slice of values (or panic if the input was not an integer).
  • Finally, print the final lenght of the values slice

Solving part A

After the madness of reading a file, the solution for the fist part of the puzzle should look trivial: loop over the slice of values to sum all of them together.

func partA(vs []int) int {
	total := 0
	for _, v := range vs {
		total += v
	}
	return total
}
  • Define a function called partA that takes a slice of integer values as input variable and returns an int value as result.
  • Start of with a total of zero
  • Range over every element in vs. This would return two variables: an index and a value. In this case, we’ve given the value the name v. The index got the special name _, which is given to all variables that are not actually used in the code. The underscore tells the compiler to ignore that variable.
  • Add the value to the sum
  • Return the final total.

Solving part B

Now for the second part of the puzzle, we’re introducing two more new constructs:

  • A map datastructure with int keys and bool values. The values in the map can be accessed or set using square brackets. The nice thing about accessing the values, is that the map returns a second value which indicates whether the value was present in the map. So when we’re looking for a duplicate value, we put every value encountered in the map and check if the next value is already in the map.

  • An infinite for loop. We’ve already seen two different forms of the for loop for idx, v := range slice {} and for boolFunc() {}. A third form of the for loop is just for {} without any condition. This would be equal to a while(true) {} in Java. Since we don’t know in which iteration the repeated value occurs, we just loop infinitely and return when the value is found.

func partB(vs []int) int {
	freqs := map[int]bool{}

	total := 0
	for {
		for _, v := range vs {
			total += v
			if _, ok := freqs[total]; ok {
				return total
			}
			freqs[total] = true
		}
	}
}

Now you should have all the pieces to put your own Go solution together. Good luck and see you tomorrow!