Part 1
Read in a list of dialing instructions and use these to rotate a dial left or right, starting at 50. Count the number of times the dial is at 0 following a rotation.
As this is a dial, rotations can wrap around in either direction, i.e. from 99 to 0 (rotating right) and from 0 to 99 (rotating left).
Solution
First of all, we need a way of converting a dialling instruction into a data structure. We can parse the input string using a regular expression, and we represent the directions by constants for left (-1) and right (1). Defining the directions as +/- 1 means that we can later multiply the rotation by the direction to move the dial correctly (since multiplying by 1 will have no effect and -1 inverts the sign but leaves the number unchanged).
const (
DIRECTION_LEFT = -1
DIRECTION_RIGHT = 1
)
type Dial struct {
direction int
rotation int
}
func getDial(input string) (Dial, error) {
dial := Dial{}
input = strings.TrimSpace(input)
dialRegex := regexp.MustCompile(`(L|R){1}([0-9]+)`)
if dialRegex.MatchString(input) {
matches := dialRegex.FindStringSubmatch(input)
if matches[1] == "L" {
dial.direction = DIRECTION_LEFT
} else {
dial.direction = DIRECTION_RIGHT
}
dial.rotation, _ = strconv.Atoi(matches[2])
return dial, nil
}
return dial, errors.New("invalid dial input")
}
func getDials(input string) []Dial {
dials := []Dial{}
lines := strings.Split(input, "\n")
for lineIndex := range lines {
dial, err := getDial(lines[lineIndex])
if err == nil {
dials = append(dials, dial)
}
}
return dials
}We then need a function that takes a start position and rotates the dial by a given number of places and left / right. However, we also need to account for the fact that the numbers on the dial ‘wrap around’ in both directions, i.e. rotating left from 0 takes us to 99, and rotating right from 99 takes us to 0. This is effectively clock arithmetic (or modular arithmetic to use the generic mathematical term). Go already has a modulus operator, however it does not handle negative numbers the way we want:
func rotate(position int, direction int, rotation int) int {
return (position + (direction * rotation)) % (highestPosition + 1)
}If we rotate left from 0 one place, the above function will return -1 instead of 99. Not all programming languages work this way - the same code in Python would do what is required - but we can fix this with an extra step:
func rotate(position int, direction int, rotation int) int {
positionCount := highestPosition + 1
initialMod := (position + (direction * rotation)) % positionCount
return (initialMod + positionCount) % positionCount
}Now we need a function which rotates for a set of dials, and returns the final position:
func rotateDials(position int, dials []Dial) int {
for d := range dials {
position = rotate(position, dials[d].direction, dials[d].rotation)
}
return position
}This works but doesn’t give us what we need, which is the number of times the dial stops on zero. We can tweak the function slightly to take a target position and keep a count:
func rotateDialsWithTarget(target int, position int, dials []Dial) int {
targetCount := 0
for d := range dials {
position = rotate(position, dials[d].direction, dials[d].rotation)
if position == target {
targetCount++
}
}
return targetCount
}Part 2
Overall thoughts
As is to be expected from the first day, this was a very puzzle to solve.