euler/python/e161.py

109 lines
2.6 KiB
Python

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)