Advent of Code 2015 Day 1

Part 1

Read in a list of characters and use those to increment or decrement a number representing the current floor, starting at zero. Find the final floor.

The only characters in the input are:

There is no upper or lower limit on the current floor.

Test input: There is no test file for today, just a few examples.

Solution

This is straightforward, we just need to write a function which takes a start floor and input, and returns the final floor.

func finalFloor(startFloor int, input string) int {
	currentFloor := startFloor
	instructions := strings.Split(input, "")

	for i := range instructions {
		switch instructions[i] {
		case "(":
			currentFloor++
		case ")":
			currentFloor--
		}
	}

	return currentFloor
}

I originally an if / else if block, but the Go tooling suggested rewriting it as a switch statement. It was interesting that the tooling is intelligent enough to notice this, although I do not know if it there is any reason to prefer one over the other (switch is perhaps slightly more readable due to having fewer curly braces). case statements in Go do not fall-through by default and support expressions rather than just constants, so many if / else if blocks can be written as a switch.

Testing is straightforward, we call our finalFloor function with the examples from the puzzle page and assert that we end up at the right floor:

func TestFinalFloor(t *testing.T) {
	assert.Equal(t, 0, finalFloor(0, "(())"))
	assert.Equal(t, 0, finalFloor(0, "()()"))
	assert.Equal(t, 3, finalFloor(0, "((("))
	assert.Equal(t, 3, finalFloor(0, "(()(()("))
	assert.Equal(t, 3, finalFloor(0, "))((((("))
	assert.Equal(t, -1, finalFloor(0, "())"))
	assert.Equal(t, -1, finalFloor(0, "))("))
	assert.Equal(t, -3, finalFloor(0, ")))"))
	assert.Equal(t, -3, finalFloor(0, ")())())"))
}

Part 2

Given the same instructions, find the position of the first character that causes the floor to reach a certain value - in this case -1 (the basement).

Solution

This is only slightly more complex than part 1. As well as the start floor and the input, we also need the target floor, and we stop as soon as we have reached the target, returning the position (-1 will be returned if we never reach it, as that is not a valid position).

func targetFloorPosition(startFloor int, targetFloor int, input string) int {
	currentFloor := startFloor
	instructions := strings.Split(input, "")

	for i := range instructions {
		switch instructions[i] {
		case "(":
			currentFloor++
		case ")":
			currentFloor--
		}

		if currentFloor == targetFloor {
			// range is zero indexed but positions are one indexed
			return i + 1
		}
	}

	// Never reached the target floor
	return -1
}

Our test suite is fairly simple as there are only two examples on the puzzle page. We also add an additional assertion for the case where the target floor is never reached.

func TestTargetFloorPosition(t *testing.T) {
	assert.Equal(t, 1, targetFloorPosition(0, -1, ")"))
	assert.Equal(t, 5, targetFloorPosition(0, -1, "()())"))
	assert.Equal(t, -1, targetFloorPosition(0, -1, "((((("))
}

Overall thoughts

As is to be expected from the first day, this was a very puzzle to solve.