From 1882d6c8cbbb0a1bd6d492a4bbdbad8d9c8ece73 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Fri, 9 May 2025 23:37:37 +0100 Subject: [PATCH] 2017: d14: ex1: add solution --- 2017/d14/ex1/ex1.py | 57 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100755 2017/d14/ex1/ex1.py diff --git a/2017/d14/ex1/ex1.py b/2017/d14/ex1/ex1.py new file mode 100755 index 0000000..0875b32 --- /dev/null +++ b/2017/d14/ex1/ex1.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +import functools +import itertools +import operator +import sys + + +def solve(input: str) -> int: + def knot_hash(byte_string: str) -> str: + def compute_sparse_hash(lengths: list[int]) -> list[int]: + circle = list(range(256)) + cur_pos, skip_size = 0, 0 + for _ in range(64): + for n in lengths: + # Invalid length + assert n < len(circle) # Sanity check + # Reverse + for i, j in zip( + range(cur_pos, cur_pos + n // 2), + # Avoid off-by-one by going further than necessary + range(cur_pos + n - 1, cur_pos, -1), + ): + i %= len(circle) + j %= len(circle) + circle[i], circle[j] = circle[j], circle[i] + # Move + cur_pos += n + skip_size + # Increase + skip_size += 1 + return circle + + def compute_dense_hash(sparse_hash: list[int]) -> list[int]: + assert len(sparse_hash) == 256 # Sanity check + return [ + functools.reduce(operator.xor, chunk) + for chunk in itertools.batched(sparse_hash, 16) + ] + + lengths = [ord(c) for c in byte_string] + lengths += [17, 31, 73, 47, 23] # Additional lengths + sparse_hash = compute_sparse_hash(lengths) + dense_hash = compute_dense_hash(sparse_hash) + return "".join(f"{n:02x}" for n in dense_hash) + + input = input.strip() + hashes = [int(knot_hash(f"{input}-{i}"), 16) for i in range(128)] + return sum(n.bit_count() for n in hashes) + + +def main() -> None: + input = sys.stdin.read() + print(solve(input)) + + +if __name__ == "__main__": + main()