Solution to Problem 14 in Python

This commit is contained in:
David Doblas Jiménez 2023-05-29 18:29:05 +02:00
parent e79ebef6af
commit 77731980bf
1 changed files with 161 additions and 0 deletions

161
src/Year_2017/P14.py Normal file
View File

@ -0,0 +1,161 @@
# --- Day 14: Disk Defragmentation ---
# Suddenly, a scheduled job activates the system's disk defragmenter. Were the
# situation different, you might sit and watch it for a while, but today, you
# just don't have that kind of time. It's soaking up valuable system resources
# that are needed elsewhere, and so the only option is to help it finish its
# task as soon as possible.
# The disk in question consists of a 128x128 grid; each square of the grid is
# either free or used. On this disk, the state of the grid is tracked by the
# bits in a sequence of knot hashes.
# A total of 128 knot hashes are calculated, each corresponding to a single row
# in the grid; each hash contains 128 bits which correspond to individual grid
# squares. Each bit of a hash indicates whether that square is free (0) or used
# (1).
# The hash inputs are a key string (your puzzle input), a dash, and a number
# from 0 to 127 corresponding to the row. For example, if your key string were
# flqrgnkx, then the first row would be given by the bits of the knot hash of
# flqrgnkx-0, the second row from the bits of the knot hash of flqrgnkx-1, and
# so on until the last row, flqrgnkx-127.
# The output of a knot hash is traditionally represented by 32 hexadecimal
# digits; each of these digits correspond to 4 bits, for a total of 4 * 32 = 128
# bits. To convert to bits, turn each hexadecimal digit to its equivalent binary
# value, high-bit first: 0 becomes 0000, 1 becomes 0001, e becomes 1110, f
# becomes 1111, and so on; a hash that begins with a0c2017... in hexadecimal
# would begin with 10100000110000100000000101110000... in binary.
# Continuing this process, the first 8 rows and columns for key flqrgnkx appear
# as follows, using # to denote used squares, and . to denote free ones:
# ##.#.#..-->
# .#.#.#.#
# ....#.#.
# #.#.##.#
# .##.#...
# ##..#..#
# .#...#..
# ##.#.##.-->
# | |
# V V
# In this example, 8108 squares are used across the entire 128x128 grid.
# Given your actual key string, how many squares are used?
# Your puzzle input is jxqlasbh.
from collections import deque
input = "jxqlasbh"
# from P10 part2
def simulated_hash(circle, sequence, curr_pos, skip):
for length in sequence:
circle.rotate(-curr_pos)
# creates a copy of the list
rotated_l = list(circle)
# reverse the order of the partial list
rotated_l[:length] = reversed(rotated_l[:length])
# as the list is circular, it wraps around when it has to
circle = deque(rotated_l)
circle.rotate(curr_pos)
# move forward the current position
curr_pos = (curr_pos + length + skip) % 256
skip += 1
return circle, curr_pos, skip
def knothash(inp):
ascii_lengths = [ord(c) for c in inp]
ascii_lengths.extend([17, 31, 73, 47, 23])
list_of_numbers = deque(list(range(256)))
curr_pos, skip = 0, 0
for _ in range(64):
list_of_numbers, curr_pos, skip = simulated_hash(
list_of_numbers, ascii_lengths, curr_pos, skip
)
sparse = list(list_of_numbers)
dense = []
for block in range(0, 256, 16):
hashed = 0
group = sparse[block : block + 16]
for n in group:
hashed ^= n
dense.append(hashed)
return "".join(f"{n:02x}" for n in dense)
def part_1() -> None:
used = 0
for i in range(128):
hash = knothash(input + "-" + str(i))
bin_hash = bin(int(hash, 16))[2:].zfill(128)
used += bin_hash.count("1")
print(f"There are {used} squares used")
# --- Part Two ---
# Now, all the defragmenter needs to know is the number of regions. A region is
# a group of used squares that are all adjacent, not including diagonals. Every
# used square is in exactly one region: lone used squares form their own
# isolated regions, while several adjacent squares all count as a single region.
# In the example above, the following nine regions are visible, each marked with
# a distinct digit:
# 11.2.3..-->
# .1.2.3.4
# ....5.6.
# 7.8.55.9
# .88.5...
# 88..5..8
# .8...8..
# 88.8.88.-->
# | |
# V V
# Of particular interest is the region marked 8; while it does not appear
# contiguous in this small view, all of the squares marked 8 are connected when
# considering the whole 128x128 grid. In total, in this example, 1242 regions
# are present.
# How many regions are present given your key string?
def part_2() -> None:
grid = set()
for i in range(128):
hash = knothash(input + "-" + str(i))
bin_hash = bin(int(hash, 16))[2:].zfill(128)
for col, val in enumerate(bin_hash):
if val == "1":
grid.add((i, col))
regions = 0
while grid:
queued = [
(next(iter(grid))),
]
while queued:
(x, y) = queued.pop(0)
if (x, y) in grid:
grid.remove((x, y))
queued += (x - 1, y), (x + 1, y), (x, y + 1), (x, y - 1)
regions += 1
print(f"There are {regions} regions")
if __name__ == "__main__":
part_1()
part_2()