Skip to content

Commit

Permalink
Find slots with rotation, for #56.
Browse files Browse the repository at this point in the history
  • Loading branch information
donkirkby committed Apr 21, 2024
1 parent e61e45d commit 95e2d10
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .idea/four-letter-blocks.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 16 additions & 2 deletions four_letter_blocks/block_packer.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def rotated_positions(self):
rotated_positions[rotated_shape].append((x, y))
return rotated_positions

def find_slots(self) -> dict[str, np.ndarray]:
def find_slots(self, is_rotation_allowed=False) -> dict[str, np.ndarray]:
if self.state is None:
raise RuntimeError('Cannot find slots with invalid state.')

Expand All @@ -100,7 +100,14 @@ def find_slots(self) -> dict[str, np.ndarray]:
has_even = np.logical_not(np.any(is_uneven, axis=(2, 3)))

usable_slots = np.logical_and(open_slots, has_even)
slots[shape] = usable_slots
if not is_rotation_allowed or len(shape) == 1:
slots[shape] = usable_slots
else:
reported_shape = shape[0]
already_usable = slots.get(reported_shape)
if already_usable is not None:
usable_slots = np.logical_or(usable_slots, already_usable)
slots[reported_shape] = usable_slots
return slots

def display(self, state: np.ndarray | None = None) -> str:
Expand Down Expand Up @@ -355,6 +362,13 @@ def shape_coordinates() -> typing.Dict[str, typing.List[np.ndarray]]:

@cache
def build_masks(width: int, height: int) -> dict[str, np.ndarray]:
""" Build the masks for each shape in each position.
:return: {shape_name: mask_array}, where mask_array is a four-dimensional
array of occupied spaces with index (start_row, start_col, row, col). In
other words, if the shape starts at (start_row, start_col), is
(row, col) filled?
"""
all_coordinates = shape_coordinates()
all_masks = {}
for shape, coordinate_list in all_coordinates.items():
Expand Down
40 changes: 40 additions & 0 deletions tests/test_block_packer.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ def test_fill_fail():
assert not is_filled


# noinspection DuplicatedCode
def test_find_slots():
packer = BlockPacker(start_text=dedent("""\
#..#.
Expand All @@ -378,6 +379,45 @@ def test_find_slots():
assert np.array_equal(o_slots, expected_o_slots)


# noinspection DuplicatedCode
def test_find_slots_rotation_allowed():
packer = BlockPacker(start_text=dedent("""\
#..#.
.....
..#..
.....
.#..#"""))
# Not at (1, 3) or (2, 0), because they cut off something.
expected_s0_slots = np.array(object=[[1, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 0, 0]],
dtype=bool)
expected_s1_slots = np.array(object=[[0, 0, 1, 0, 0],
[0, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]],
dtype=bool)
expected_s_slots = np.array(object=[[1, 0, 1, 0, 0],
[0, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 0, 0]],
dtype=bool)

is_rotation_allowed = False
s0_slots = packer.find_slots(is_rotation_allowed)['S0']
s1_slots = packer.find_slots()['S1']
is_rotation_allowed = True
s_slots = packer.find_slots(is_rotation_allowed)['S']

assert np.array_equal(s0_slots, expected_s0_slots)
assert np.array_equal(s1_slots, expected_s1_slots)
assert np.array_equal(s_slots, expected_s_slots)


def test_find_slots_after_fail():
packer = BlockPacker(start_text=dedent("""\
#..#.
Expand Down

0 comments on commit 95e2d10

Please sign in to comment.