From 18180491c228d018eef3eaea9fdc8fdce60adc09 Mon Sep 17 00:00:00 2001 From: felixm Date: Fri, 6 Oct 2023 21:44:41 +0200 Subject: [PATCH] Solve first 70% problem 161. --- python/e111.py | 2 +- python/e161.py | 108 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 python/e161.py diff --git a/python/e111.py b/python/e111.py index 14137c2..dd3be37 100644 --- a/python/e111.py +++ b/python/e111.py @@ -35,7 +35,7 @@ def euler_111(): for p in get_permutations(base, 2): if is_prime(p): subresult += p - assert subresult > 0, "More than to permutations required to yield prime" + assert subresult > 0, "More than two permutations required to yield prime" result += subresult return result diff --git a/python/e161.py b/python/e161.py new file mode 100644 index 0000000..591fed6 --- /dev/null +++ b/python/e161.py @@ -0,0 +1,108 @@ +from copy import copy +from typing import Optional, Tuple + + +CACHE = {} +EMPTY = '⬛' +FIELDS = ['🔴', '🟠', '🟡', '🟢', '🔵', '🟣', '⚪', '🟤', '⭐'] + + +# We use [row, col] indexing. +BLOCKS = [ + ((0, 0), (0, 1), (0, 2)), # dark blue + ((0, 0), (1, 0), (2, 0)), # black + ((0, 0), (0, 1), (1, 0)), # red + ((0, 0), (0, 1), (1, 1)), # green + ((0, 0), (1, 0), (1, 1)), # blue + ((0, 0), (1, 0), (1, -1)), # orange +] + + +class Field: + + def __init__(self, N_COLS, N_ROWS): + self.N_COLS = N_COLS + self.N_ROWS = N_ROWS + self.state = 0 + + def __hash__(self): + return hash(self.state) + + def get_first_empty(self) -> Optional[Tuple[int, int]]: + for row in range(self.N_ROWS): + for col in range(self.N_COLS): + if self.is_empty(row, col): + return (row, col) + return None + + def is_empty(self, row: int, col: int) -> bool: + index = row * self.N_COLS + col + result = not bool(self.state & (1 << index)) + return result + + def set(self, row: int, col: int): + index = row * self.N_COLS + col + self.state |= (1 << index) + + def __getitem__(self, row: int): + return [self.is_empty(row, col) for col in range(self.N_COLS)] + + def print(self): + for row in range(self.N_ROWS): + row_str = "" + for col in range(self.N_COLS): + row_str += '⬛' if self.is_empty(row, col) else '⚪' + print(row_str) + + +def fits(field, block, coord): + N_ROWS = field.N_ROWS + N_COLS = field.N_COLS + + for rel_coord in block: + abs_row = coord[0] + rel_coord[0] + abs_col = coord[1] + rel_coord[1] + + if abs_row < 0 or abs_col < 0 or abs_row >= N_ROWS or abs_col >= N_COLS: + return None + + if not field.is_empty(abs_row, abs_col): + return None + + new_field = copy(field) + for rel_coord in block: + abs_row = coord[0] + rel_coord[0] + abs_col = coord[1] + rel_coord[1] + new_field.set(abs_row, abs_col) + + return new_field + + +def count(field): + global CACHE + + if hash(field) in CACHE: + return CACHE[hash(field)] + + first_empty = field.get_first_empty() + if first_empty is None: + return 1 + + result = 0 + for block in BLOCKS: + if (new_field := fits(field, block, first_empty)) is not None: + result += count(new_field) + + CACHE[hash(field)] = result + return result + + +def euler_161(): + return count(Field(9, 12)) + + +if __name__ == "__main__": + solution = euler_161() + print("e161.py: " + str(solution)) + assert(solution == 20574308184277971) +