Drop links or images here to add them to the editor.

Turing Machine is a board game that uses punch cards: cards with holes punched in them. Each punch card has a colored label with a single digit on it. Blue labels correspond to the first digit of a three-digit number, yellow labels to the middle (second) digit, and purple labels to the last (third) digit. For example, these are the three punch cards that correspond to the number 421.

three punch cards for number 421 (split)

If you stack these three punch cards on top of each other, only a single hole remains.

three punch cards for number 421 (stacked)

The punch cards used in the Turing Machine game are designed so that each combination of three cards corresponding to a three-digit number has exactly one hole at the same position. In addition, the holes are spread out sufficiently across the punch cards so that the cards do not lose their strength.

Assignment

Define a class PunchCard that can be used to represent punch cards with a rectangular $$m \times n$$ grid with $$m$$ rows and $$n$$ columns. A hole can be punched in each cell of the grid. Unlike in the Turing Machine game, punch cards (PunchCard) do not necessarily have to be square, and they do not have a label. For example, this is a punch card with a $$12 \times 12$$ grid in which 25 holes have been punched.

punch card

The rows of the grid are numbered from top to bottom, and the columns from left to right, starting from zero. This allows us to represent the position of a cell in the grid as a tuple $$(r, k)$$, where $$r$$ (int) is the row number and $$k$$ (int) is the column number where the cell is located.

When creating a new punch card (PunchCard), the number of rows $$m \in \mathbb{N}$$ (int) and the number of columns $$n \in \mathbb{N}$$ (int) of the grid must be specified. Optionally, a collection (list, tuple, or set) can be passed as a third argument, containing positions (tuple) where holes are punched in the card. If no collection is passed, no holes are punched in the card. If the punch card does not have at least two rows and two columns, or if a position is passed that is outside the grid, an AssertionError must be raised with the message invalid punch card.

If a punch card $$p$$ (PunchCard) is passed to the built-in function repr, the function must return a string representation (str) of punch card $$p$$. This representation must read like a Python expression that creates a new punch card (PunchCard) with the same dimensions and holes as punch card $$p$$. In addition, a list must always be passed as the third argument, containing the positions (tuple) of the holes listed in reading order: from left to right and from top to bottom.

If a punch card $$p$$ (PunchCard) is passed to the built-in function str, the function must return a string representation (str) of punch card $$p$$. In this representation, the rows are represented as consecutive lines (listed from top to bottom). Each line consists of a representation of the individual cells in the corresponding row (listed from left to right): a capital letter O if there is a hole in the cell or a hash (#) if there is no hole in the cell.

A punch card $$p$$ (PunchCard) with $$m$$ rows and $$n$$ columns must support at least the following methods:

If the operator == is used to compare two punch cards $$p$$ and $$q$$ (PunchCard) with each other (p == q), they must be equal if and only if they have the same dimensions and holes in the same positions.

If the operator + is used to add two punch cards $$p$$ and $$q$$ (PunchCard) together (p + q), the two punch cards must have the same dimensions. If this is not the case, an AssertionError must be raised with message punch cards are not the same size. Otherwise, the sum must result in a new punch card (PunchCard) with the same dimensions as punch cards $$p$$ and $$q$$, and holes in positions where both punch card $$p$$ and punch card $$q$$ have a hole.

Example

>>> card = PunchCard(4, 8)
>>> card
PunchCard(4, 8, [])
>>> print(card)
########
########
########
########
>>> card.issturdy()
True

>>> card = PunchCard(1, 2)
Traceback (most recent call last):
AssertionError: invalid punch card
>>> card = PunchCard(4, 8, {(1, 2), (3, 6), (5, 12)})
Traceback (most recent call last):
AssertionError: invalid punch card

>>> card = PunchCard(4, 8, ((1, 2), (2, 7), (3, 6)))
>>> card
PunchCard(4, 8, [(1, 2), (2, 7), (3, 6)])
>>> print(card)
########
##O#####
#######O
######O#
>>> card.issturdy()
False
>>> card.punch(0, 5)
PunchCard(4, 8, [(0, 5), (1, 2), (2, 7), (3, 6)])
>>> print(card)
#####O##
##O#####
#######O
######O#
>>> card.issturdy()
False
>>> card.punch(0, 1).punch(0, 4).punch(3, 5)
PunchCard(4, 8, [(0, 1), (0, 4), (0, 5), (1, 2), (2, 7), (3, 5), (3, 6)])
>>> print(card)
#O##OO##
##O#####
#######O
#####OO#
>>> card.issturdy()
False
>>> card.punch(5, 10)
Traceback (most recent call last):
AssertionError: invalid position
>>> card.punch(0, 4)
Traceback (most recent call last):
AssertionError: card already punched at position (0, 4)
>>> card.fill(2, 7)
PunchCard(4, 8, [(0, 1), (0, 4), (0, 5), (1, 2), (3, 5), (3, 6)])
>>> print(card)
#O##OO##
##O#####
########
#####OO#
>>> card.issturdy()
False
>>> card.fill(1, 2).fill(3, 5).fill(0, 4)
PunchCard(4, 8, [(0, 1), (0, 5), (3, 6)])
>>> print(card)
#O###O##
########
########
######O#
>>> card.issturdy()
True
>>> card.fill(5, 10)
Traceback (most recent call last):
AssertionError: invalid position
>>> card.fill(0, 4)
Traceback (most recent call last):
AssertionError: card not punched at position (0, 4)

>>> PunchCard(4, 8, [(0, 1), (0, 5), (1, 2)]) == PunchCard(4, 8, ((1, 2), (0, 1), (0, 5)))
True
>>> PunchCard(4, 8, [(0, 1), (0, 5), (1, 2)]) == PunchCard(4, 8, [(3, 5), (1, 2), (3, 6), (0, 4)])
False
>>> PunchCard(4, 8, [(0, 1), (0, 5), (1, 2)]) == PunchCard(5, 9, ((1, 2), (0, 1), (0, 5)))
False

>>> PunchCard(4, 8, [(0, 1), (0, 5), (1, 2)]) + PunchCard(4, 8, [(3, 5), (1, 2), (3, 6), (0, 4)])
PunchCard(4, 8, [(1, 2)])
>>> print(PunchCard(4, 8, [(0, 1), (0, 5), (1, 2)]) + PunchCard(4, 8, [(3, 5), (1, 2), (3, 6), (0, 4)]))
########
##O#####
########
########
>>> PunchCard(4, 8, [(0, 1), (0, 5), (1, 2)]) + PunchCard(5, 9, [(3, 5), (1, 2), (3, 6), (0, 4)])
Traceback (most recent call last):
AssertionError: punch cards are not the same size