Piet Mondrian was a Dutch painter and considered one of the great artists of the 20th century. Some of Mondrian's artwork has a unique, geometric style.

Piet Mondrian artwork
Piet Mondrian was a Dutch painter and considered one of the great artists of the 20th century. Some of Mondrian's artwork has a unique, geometric style.

In a Mondrian art puzzle, you have to tile a square $$n \times n$$ grid ($$n$$ rows and $$n$$ columns) with non-overlapping rectangles that each cover a number of grid cells.

The dimensions of an $$h \times w$$ rectangle are described by two integers: the number of rows $$h$$ and the number of columns $$w$$ of the grid covered by the rectangle. The area of an $$h \times w$$ rectangle is equal to the number of squares covered: $$h \times w$$. It is not allowed to use two rectangles of the same dimensions: if you cover part of the grid with an $$h \times w$$ rectangle, the tiling may not use any other $$h \times w$$ or $$w \times h$$ rectangles. However, a tiling may have two rectangles of the same area.

The score of a tiling is equal to the area of the largest rectangle minus the area of the smallest rectangle. The goal is to find a tiling with the smallest possible score.

For example, this tiling covering a $$10 \times 10$$ grid has score $$42 - 7 = 35$$.

10x10 puzzle with score 35
Tiling of a $$10 \times 10$$ grid with score $$42 - 7 = 35$$.

Better tilings are possible. For example, here's a tiling with score $$30 - 7 = 23$$. Still suboptimal, but much better than what we had before.

10x10 puzzle with score 23
Tiling of a $$10 \times 10$$ grid with score $$30 - 7 = 23$$.

Note that although the $$4 \times 3$$ rectangle and the $$6 \times 2$$ rectangle have the same area ($$12$$), this is a valid tiling because the rectangles have different dimensions. However, there is an even better tiling with score $$25 - 7 = 18$$.

10x10 puzzle with score 18
Tiling of a $$10 \times 10$$ grid with score $$25 - 7 = 18$$.

The tiling below might look like the best one we have found so far, but it is not. It violates the rule that tilings cannot have rectangles of the same dimensions. This tiling has a $$4 \times 3$$ rectangle and a $$3 \times 4$$ rectangle, and that's not allowed. The $$5 \times 2$$ rectangle and the $$1 \times 10$$ rectangle have the same area ($$10$$), but because they have different dimensions, they can co-habit the same tiling.

10x10 puzzle with invalid tiling
Invalid tiling of a $$10 \times 10$$ grid: it violates the rule that tilings cannot have rectangles of the same dimensions. The tiling has a $$4 \times 3$$ rectangle and a $$3 \times 4$$ rectangle, and that is not allowed. The $$5 \times 2$$ rectangle and the $$1 \times 10$$ rectangle have the same area (10), but because they have different dimensions, they can co-habit the same tiling.

As a bonus, we colored all rectangles using the fewest number of colors possible so that no two rectangles of the same color touch along an edge or a vertex.

Assignment

Define a class Mondrian that allows tiling square $$n \times n$$ grids ($$n$$ rows and $$n$$ columns) according to the rules of a Mondrian art puzzle. The grid dimension $$n$$ (int) must be passed when creating a new tiling (Mondrian).

To locate grid cells, we number the rows of the grid from top to bottom and the columns from left to right, each time starting from zero. As such, we can represent the position of a cell as a tuple $$(r, c)$$, with $$r$$ the number of the row ($$0 \leq r < n$$) and $$c$$ the number of the column ($$0 \leq c < n$$) containing the cell.

It should be possible to add rectangles to and remove rectangles from a tiling. When adding a rectangle, it is indicated by the first unused capital of the alphabet. A tiling $$\mathcal{M}$$ (Mondriaan) must support at least the following methods:

If a tiling $$\mathcal{M}$$ (Mondrian) is passed to the built-in function str, a string representation (str) of the grid of tiling $$\mathcal{M}$$ must be returned. Each row of the grid is represented as a separate line, and each cell is represented by the capital indicating the rectangle covering the cell in tiling $$\mathcal{M}$$, or an underscore (_) if the cell is not covered by a rectangle in tiling $$\mathcal{M}$$.

Example

In the following interactive session, we use the optimal tiling of a $$10 \times 10$$ grid with score 18 from the introduction as an example to illustrate how the class Mondrian must behave.

>>> puzzle = Mondrian(10)
>>> print(puzzle)
__________
__________
__________
__________
__________
__________
__________
__________
__________
__________
>>> puzzle.coverage()
0
>>> puzzle.score()
-1
>>> puzzle.next_letter()
'A'
>>> puzzle.cells(row=4, col=0, height=5, width=2) # doctest: +SKIP
{(4, 0), (4, 1), (5, 0), (5, 1), (6, 0), (6, 1), (7, 0), (7, 1), (8, 0), (8, 1)}
>>> print(puzzle.add_rectangle(row=4, col=0, height=5, width=2))
__________
__________
__________
__________
AA________
AA________
AA________
AA________
AA________
__________
>>> puzzle.coverage()
10
>>> puzzle.score()
-1
>>> puzzle.next_letter()
'B'
>>> puzzle.cells(row=1, col=3, height=3, width=7) # doctest: +SKIP
{(1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8), (1, 9), (2, 3), (2, 4), (2, 5), (2, 6), (2, 7), (2, 8), (2, 9), (3, 3), (3, 4), (3, 5), (3, 6), (3, 7), (3, 8), (3, 9)}
>>> print(puzzle.add_rectangle(row=1, col=3, height=3, width=7))
__________
___BBBBBBB
___BBBBBBB
___BBBBBBB
AA________
AA________
AA________
AA________
AA________
__________
>>> puzzle.cells(row=8, col=8, height=3, width=3)           # rectangle not enclosed within square
Traceback (most recent call last):
AssertionError: invalid rectangle
>>> puzzle.add_rectangle(row=8, col=8, height=3, width=3)   # rectangle not enclosed within square
Traceback (most recent call last):
AssertionError: invalid rectangle
>>> puzzle.add_rectangle(row=5, col=4, height=2, width=5)   # other rectangle has same dimensions
Traceback (most recent call last):
AssertionError: invalid rectangle
>>> puzzle.add_rectangle(row=2, col=1, height=3, width=3)   # rectangle overlaps with other rectangle
Traceback (most recent call last):
AssertionError: invalid rectangle
>>> print(puzzle)
__________
___BBBBBBB
___BBBBBBB
___BBBBBBB
AA________
AA________
AA________
AA________
AA________
__________
>>> puzzle.next_letter()
'C'
>>> print(puzzle.add_rectangle(row=6, col=7, height=3, width=3))
__________
___BBBBBBB
___BBBBBBB
___BBBBBBB
AA________
AA________
AA_____CCC
AA_____CCC
AA_____CCC
__________
>>> puzzle.next_letter()
'D'
>>> print(puzzle.add_rectangle(row=9, col=0, height=1, width=10))
__________
___BBBBBBB
___BBBBBBB
___BBBBBBB
AA________
AA________
AA_____CCC
AA_____CCC
AA_____CCC
DDDDDDDDDD
>>> puzzle.remove_rectangle('X')
Traceback (most recent call last):
AssertionError: invalid rectangle
>>> puzzle.next_letter()
'E'
>>> print(puzzle.remove_rectangle('C'))
__________
___BBBBBBB
___BBBBBBB
___BBBBBBB
AA________
AA________
AA________
AA________
AA________
DDDDDDDDDD
>>> puzzle.next_letter()
'C'
>>> print(puzzle.add_rectangle(row=0, col=0, height=4, width=3))
CCC_______
CCCBBBBBBB
CCCBBBBBBB
CCCBBBBBBB
AA________
AA________
AA________
AA________
AA________
DDDDDDDDDD
>>> puzzle.coverage()
53
>>> puzzle.score()
-1
>>> print(puzzle.add_rectangle(0, 3, 1, 7).add_rectangle(4, 7, 5, 3).add_rectangle(4, 2, 5, 5))
CCCEEEEEEE
CCCBBBBBBB
CCCBBBBBBB
CCCBBBBBBB
AAGGGGGFFF
AAGGGGGFFF
AAGGGGGFFF
AAGGGGGFFF
AAGGGGGFFF
DDDDDDDDDD
>>> puzzle.coverage()
100
>>> puzzle.score()
18

The solutions you submit are also tested using these tilings.

5x5 puzzle with score 4
Tiling of a $$5 \times 5$$ grid with optimal score $$10 - 6 = 4$$.
6x6 puzzle with score 5
Tiling of a $$6 \times 6$$ grid with optimal score $$8 - 3 = 5$$.
7x7 puzzle with score 5
Tiling of a $$7 \times 7$$ grid with optimal score $$9 - 4 = 5$$.
8x8 puzzle with score 6
Tiling of a $$8 \times 8$$ grid with optimal score $$12 - 6 = 6$$.
9x9 puzzle with score 6
Tiling of a $$9 \times 9$$ grid with optimal score $$12 - 6 = 6$$.

Epilogue

What's in a name? PIET MONDRIAN is an anagram of I PAINT MODERN.

Piet Mondrian also gave his name to the stack-based esoteric programming language Piet1 in which programs look like abstract paintings.

Epilogue

For a Mondrian art puzzle with an $$n \times n$$ grid, there are no fast algorithms to find tilings with a minimum score. However, we do know that the score is bounded above by \[ \left\lceil \frac{n}{\log{n}} \right\rceil + 3 \] We also don't know if there is a dimension $$n$$ for which there exists a tiling with score 0.