★Advent of Code 202225 days solved.Source ↗
“Massive speedup by caching blizzard movements :D”Git commit.
from helpers import load_data

def print_board(squares, x_count, y_count, traveler):
	for y in reversed(range(y_count)):
		for x in range(x_count):

			if complex(x, y) == traveler:
				print('T', end='')
			else:
				if len(squares[complex(x, y)]) == 1:
					print(squares[complex(x, y)][0], end='')
				elif len(squares[complex(x, y)]) == 0:
					print('.', end='')
				else:
					print(len(squares[complex(x, y)]), end='')
		print()

# TODO could speed up by updating blizzards in place?
def update_blizzard_locations(squares, board_width, board_height):
	result = {}
	for coord, symbols in squares.items():
		for symbol in symbols:
			match symbol:
				case '<':
					new_coord = complex((coord.real - 1) % board_width, coord.imag)
				case '>':
					new_coord = complex((coord.real + 1) % board_width, coord.imag)
				case 'v':
					new_coord = complex(coord.real, (coord.imag - 1) % board_height)
				case '^':
					new_coord = complex(coord.real, (coord.imag + 1) % board_height)
				case _: raise Exception(symbol)

			if new_coord not in result.keys():
				result[new_coord] = [symbol]
			else:
				result[new_coord].append(symbol)

	return result

# TODO this is likely slow
def possible_traveler_squares(updated_squares, traveler, destination, board_width, board_height):
	result = []
	potential_squares = [traveler, traveler + complex(-1, 0), traveler + complex(1, 0), traveler + complex(0, -1),traveler + complex(0, 1)]
	for potential_square in potential_squares:
		# TODO the potential_square == traveler just allows the elf to stay in the start position
		if (potential_square == destination) or (potential_square == traveler) or (potential_square.real >= 0 and potential_square.real < board_width and potential_square.imag >= 0 and potential_square.imag < board_height):
			if potential_square not in updated_squares.keys():
				result.append(potential_square)
	return result

def bfs(blizzard_cache, traveler, destination, board_width, board_height, start_time):
	# our nodes / states are a given position at a given time
	queue = [(traveler, start_time)]
	seen_states = set()
	
	while len(queue) > 0:

		position, time = queue.pop(0)

		if position == destination:
			return time

		if time + 1 not in blizzard_cache.keys():
			blizzard_cache[time + 1] = update_blizzard_locations(blizzard_cache[time], board_width, board_height)
		new_blizzards = blizzard_cache[time + 1]

		potential_moves = possible_traveler_squares(new_blizzards, position, destination, board_width, board_height)
		for potential_move in potential_moves:
			new_state = (potential_move, time + 1)
			if new_state not in seen_states:
				seen_states.add(new_state)
				queue.append(new_state)

if __name__ == "__main__":
	data = load_data("day-24-input.txt")

	# load data into a data structure
	row_count = len(data)
	col_count = len(data[0])
	traveler = None
	destination = None
	squares = {}
	for y in range(row_count):
		for x in range(col_count):
			symbol = data[row_count - 1 - y][x]
			match symbol:
				case '#':
					pass
				case '.':
					if y == 0:
						destination = complex(x - 1, y - 1)
					elif y == row_count - 1:
						traveler = complex(x - 1, y - 1)
				# should be the >, <, v, and ^ cases
				case _:
					squares[complex(x - 1, y - 1)] = [symbol]

	# the width and height of the board without walls
	x_count = col_count - 2
	y_count = row_count - 2

	blizzard_cache = {0: squares}
	first_trip_distance = bfs(blizzard_cache, traveler, destination, x_count, y_count, 0)

	# part 1 (answer: 308)
	print(first_trip_distance)

	# part 2 (answer: 908)
	second_trip_distance = bfs(blizzard_cache, destination, traveler, x_count, y_count, first_trip_distance)
	third_trip_distance = bfs(blizzard_cache, traveler, destination, x_count, y_count, second_trip_distance)
	print(third_trip_distance)

No writeup for this day yet.