Dazbo's Advent of Code solutions, written in Python
NumPyLoggingTiming and ProgressReading files
The elves are having trouble with a keypad in a bathroom. They need your help to figure out the correct code. The keypad is a 3x3 grid of numbers:
1 2 3
4 5 6
7 8 9
You are given a series of instructions, where each instruction is a sequence of movements (U, D, L, R). ‘U’ means move up, ‘D’ means move down, ‘L’ means move left, and ‘R’ means move right. You start at the ‘5’ button. If a movement would take you off the keypad, you stay in your current position. At the end of each line of instructions, you record the number of the button you are currently on. The final code is the sequence of recorded button presses.
For example, the input data might look like this:
ULL
RRDDD
LURDL
UUUUD
What is the bathroom code?
My approach to solving this problem involves representing the keypad as a 2D NumPy array. This allows for easy navigation and boundary checking.
keypad = np.arange(1, 10).reshape(3, 3)
This creates a 3x3 NumPy array:
[[1 2 3]
[4 5 6]
[7 8 9]]
The core logic for navigating the keypad and recording button presses is encapsulated in the get_combo
function.
def get_combo(keypad: np.ndarray, instructions: list, row: int, col: int) -> list:
rows, cols = keypad.shape
keypresses = []
for line in instructions:
for char in line:
# Apply movement
col += NavigationConstants.VECTORS[char][0]
row += NavigationConstants.VECTORS[char][1]
# Boundary checking: if off the edge, revert to edge
if col < 0:
col = 0
if col > cols - 1:
col = cols - 1
if row < 0:
row = 0
if row > rows - 1:
row = rows - 1
keypresses.append(str(keypad[row][col]))
return keypresses
NavigationConstants.VECTORS
maps ‘U’, ‘D’, ‘L’, ‘R’ to [col_change, row_change]
respectively.row
and col
are updated.row
and col
stay within the keypad’s dimensions.(row, col)
is recorded. # Part 1
# initialise the array in the form of a numeric keypad, with digits 1-9
keypad = np.arange(1, 10).reshape(3, 3)
# set starting position to be digit 5 (row 1, col 1 in 0-indexed array)
row = col = 1
keypresses = get_combo(keypad, data, row, col)
logging.info("".join(keypresses))
The elves have a new, more complex keypad:
1
2 3 4
5 6 7 8 9
A B C
D
The rules for navigation are the same, but now some positions are empty. If a movement would take you to an empty position, you stay in your current position.
What is the new bathroom code?
For Part 2, the keypad is represented as a 5x5 NumPy array, with empty spaces represented by " "
:
keypad = np.array([[" ", " ", "1", " ", " "],
[" ", "2", "3", "4", " "],
["5", "6", "7", "8", "9"],
[" ", "A", "B", "C", " "],
[" ", " ", "D", " ", " "]])
The get_combo
function is reused, but with an additional check to handle empty spaces:
# if current array position is ' ', then reverse (ignore) last instruction
if str(keypad[row][col]) == ' ':
col -= NavigationConstants.VECTORS[char][0]
row -= NavigationConstants.VECTORS[char][1]
If a movement leads to an empty space, the row
and col
are reverted to their previous values, effectively ignoring the invalid move.
# Part 2
# initialise the array with the weird keypad
keypad = np.array([[" ", " ", "1", " ", " "],
[" ", "2", "3", "4", " "],
["5", "6", "7", "8", "9"],
[" ", "A", "B", "C", " "],
[" ", " ", "D", " ", " "]])
# set starting position to be '5' (row 2, col 0 in 0-indexed array)
row = 2
col = 0
keypresses = get_combo(keypad, data, row, col)
logging.info("".join(keypresses))
The starting position for Part 2 is ‘5’, which corresponds to (row=2, col=0)
in the 0-indexed NumPy array.
This problem effectively demonstrates the utility of NumPy arrays for grid-based problems, especially when combined with clear navigation logic and boundary/validity checks. The solution is concise and handles both parts of the puzzle by simply changing the keypad representation and initial position.