Advent of Code 2021 - Day 2

The advent of code is an annual coding challenge in the month of December. This article covers the second day's puzzle with a solution written in Go.

Advent of Code 2021 - Day 2
Photo by Elena Mozhvilo / Unsplash

Still with me? Great! Let's head on to solving day 2.

The story tells us, that we're still in the submarine and are trying to learn how to navigate it. The submarine can take a bunch of simple commands in the form of direction unit.

forward 5
down 5
forward 8
up 3
down 8
forward 2

Someone has already programmed a course for us and it's our task to figure out where we're going. We're to compute the final horizontal position and our final depth. An important note here is that down increases the depth while up decreases the depth of the submarine.

Part 1

Since I'm trying to learn some concepts of Go I decided to represent the commands as a struct:

type command struct {
    direction string
    steps int
}

This is easy enough. We have a direction and a number of steps in the defined direction.

We can now write go on to read our puzzle input.

func getInputs() []command {
	bytes, _ := ioutil.ReadFile(filename)

	lines := strings.Split(string(bytes), "\n")
	cmds := []command{}

	for _, line := range lines {
		if len(line) == 0 {
			continue
		}
		instr := strings.Split(line, " ")
		val, _ := strconv.Atoi(instr[1])
		cmds = append(cmds, command{instr[0], val})
	}
	return cmds
}

Once again, there's not too much magic going on here. We read the file, split it into lines, and then further split the lines into the two components representing our direction and a number. A more experienced Go programmer would probably use bufio.ReadLine to read the file line-by-line, but for now, I'm content that this will do the job just fine.

Computing our horizontal position and depth is now as easy as looping over all commands and tallying up the steps based on the direction:

var pos, depth int

for _, cmd := range cmds {
	switch cmd.direction {
	case "forward":
		pos += cmd.steps
	case "down":
		depth += cmd.steps
	case "up":
		depth -= cmd.steps
	}
}

Part 2

The second part of the story suggests that the final position we calculated doesn't make any sense, and so an alternative algorithm is proposed. The down and up commands don't directly affect our position but represent changes to the submarine's aim.

Assume for a moment our instructions were:

forward 5
down 1
forward 2

In Part 1 this would have resulted in a horizontal position of 7 and a depth of 1. In Part 2 the interpretation changes to:

  • Move 5 steps forward
  • Adjust aim to 1 (aim down!)
  • Move 2 more steps forward and 2 steps down in depth (since our aim is at 1 and we're stepping two times)

In other words, every movement forward by X steps will change our depth by X * aim.

We need to modify our code ever so slightly to keep track of our sub's aim:

var aim int
pos = 0
depth = 0

for _, cmd := range cmds {
    switch cmd.direction {
    case "forward":
        pos += cmd.steps
        depth += aim * cmd.steps
    case "down":
        aim += cmd.steps
    case "up":
        aim -= cmd.steps
    }
}

That's it. We're done with part 2.

Summary

Day 2 built really well on the previous day. It's good enough to practice I/O and processing text input. The task should be easy to solve even if the programming language you're using is relatively new to you.

💡
If you like articles like this one, please consider subscribing to my free newsletter where at least once a week I send out my latest work covering Julia, Python, Machine Learning, and other tech.

You can also follow me on Twitter.