Solution to Problem 14 in Python
This commit is contained in:
parent
e79ebef6af
commit
77731980bf
161
src/Year_2017/P14.py
Normal file
161
src/Year_2017/P14.py
Normal 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()
|
Loading…
Reference in New Issue
Block a user