# Lecture 37 [video](https://pomona.box.com/s/8tpqobg80wo59iyc3mgm7h0qv6d6in3w)
In this lecture we will explore the use of Python classes to implement
(part of) a game for playing dominoes with two players.
The game implementation will consist of a number of simple classes to model individual dominoes, "hands" of dominoes,
the board; and the game (which we won't cover). In these notes I provide specifications for three classes `domino`,
`hand`, and, `board. Your **exercise** is to try to implement these classes.
## About Dominoes
There are many types of games involving dominoes. The game we will consider uses a "double-six" set of 28 tiles:

We will be implementing the *Block* game ([article](https://en.wikipedia.org/wiki/List_of_domino_games))
>The Block game for two players is the simplest basic domino variant. It requires a double-six set,
from which each player must draws seven tiles; the remainder is not used. The first player places a tile on the table which starts the line of play. The players alternately extend it with one matching tile at one of its two ends. A player who cannot do this passes. The game ends when one player wins by playing their last tile, or when the game is blocked because neither player can play. The winner's score is the total remaining pip count of the loser's hand. The winner of a blocked game is the player who has a lower pip count, and the score of the game is the difference of the pip counts.[2]
There are also variants for four players.
The following provides an (annotated) partial run of a game. At each step of the game, consisting of the board, the hands
of the two players, and the identity of the next player are shown. Furthermore, each move taken is printed.
```plaintext
board:
hand 1: [[4|5],[5|6],[1|5],[1|4],[4|4],[0|1],[0|5],[6|6]]
hand 2: [[3|4],[2|2],[1|3],[2|6],[2|5],[0|0],[1|1],[3|6]]
----
Player 1 played [4|5] on right
Player 2 goes next
board: [4|5]
hand 1: [[5|6],[1|5],[1|4],[4|4],[0|1],[0|5],[6|6]]
hand 2: [[3|4],[2|2],[1|3],[2|6],[2|5],[0|0],[1|1],[3|6]]
----
Player 2 played [5|2] on right
Player 1 goes next
board: [4|5][5|2]
hand 1: [[5|6],[1|5],[1|4],[4|4],[0|1],[0|5],[6|6]]
hand 2: [[3|4],[2|2],[1|3],[2|6],[0|0],[1|1],[3|6]]
----
Player 1 played [1|4] on left
Player 2 goes next
board: [1|4][4|5][5|2]
hand 1: [[5|6],[1|5],[4|4],[0|1],[0|5],[6|6]]
hand 2: [[3|4],[2|2],[1|3],[2|6],[0|0],[1|1],[3|6]]
----
Player 2 played [1|3] on left
Player 2 goes next
board: [3|1][1|4][4|5][5|2]
hand 1: [[5|6],[1|5],[4|4],[0|1],[0|5],[6|6]]
hand 2: [[3|4],[2|2],[2|6],[0|0],[1|1],[3|6]]
```
Notice that the board is maintained by correctly connecting dominoes by their matching ends.
## Simple Support Classes
We will discuss classes for *domino*, *hand*, and *board*.
### The `domino` class
Begin with the `domino class`. The following provides
a description of the class methods.
* A domino consists of a pair of values representing the left and right sides. Thus a domino object is
"initialized" with a pair (tuple) of two values.
* A domino has two *attributes*, left and right, that return the left and right sides of the domino respectively
* A domino has three *standard* methods that are supported by many classes
- `__repr__` returns a string representation of a domino. By providing this method, `str(d)`, and `print(d)` for
domino d "just work"
- `__eq__` allows us to compare two dominoes as in `d1 == d2`. We consider two dominoes equal if they share the same value.
- `__contains__` allows us to check if a domino contains a value as in `3 in d` where `d` is a domino
* Because the board requires dominoes to be added with matching ends, the method `invert` copies a domino with its
left and right ends swapped.
The following code illustrates the desired behavior:
```python
>>> d1 = domino((1,3))
>>> str(d1)
'[1|3]'
>>> d1.left
1
>>> d1.right
3
>>> d2 = d1.invert()
>>> str(d2)
'[3|1]'
>>> 3 in d1
True
>>> 4 in d1
False
>>> d1 == d2
True
```
Here is an outlne of the class we will discuss in lecture:
```python
class domino:
"""
def __init__(self, p):
pass
@property
def left(self):
# return the "left" domino value
pass
@property
def right(self):
# return the "right" domino value
pass
def invert(self):
# return a new domino with left, right swapped
pass
def __repr__(self):
# return a string representation of the domino
pass
def __eq__(self, other):
# return True if the two dominos, self and other, have the same
# values even if self is the 'mirror' of the other
pass
def __contains__(self,n):
# return True if n is equal to either left or right
pass
```
#### An aside on *attributes*
Often, it is desirable to provide methods to *get* values of an objects attributes, but not to allow them to
be directly modified. the following technique provides a sortcut to creating such methods:
it possible to create read-only properties easily using property() as a decorator:
```python
class Parrot:
def __init__(self):
self._voltage = 100000
@property
def voltage(self):
"""Get the current voltage."""
return self._voltage
p = Parrot()
print(p.voltage)
```
The @property decorator turns the voltage() method into a “getter” for a read-only attribute
with the same name, and it sets the docstring for voltage to “Get the current voltage.” [python documentation](https://docs.python.org/3/library/functions.html?highlight=property#property)
### The `hand` class
The *hand* class provides a way to manage hands in a dominoes game. A hand consists of a sequence of dominoes. The class
consists of the following methods:
* `__init__` accepts a list of dominoes
* `__repr__` provides a string representation of a hand for example `[[1,3],[2,4]]`
* `__contains__` checks if a particular domino is in the hand
* `__len__` returns the number of dominoes in the hand
* `has_a` checks if there is a domino in the hand with a particular (end) value
* `points` returns the sum of all the end values for the dominoes in the hand
* `play` attempts to remove a domino from the hand
The following illustrates the behavior of the hand class:
```python
l = []
>>> for i in range(3):
... for j in range(i,3):
... l.append(domino.domino((i,j)))
>>> h = hand(l)
>>> print(h)
[[0|0],[0|1],[0|2],[1|1],[1|2],[2|2]]
>>> h.has_a(1)
True
>>> h.has_a(6)
False
>>> l[0] in h
True
>>> h.points()
12
>>> str(h.play(domino.domino((1,2))))
'[1|2]'
>>> str(h.play(domino.domino((0,5))))
'None'
>>> str(h)
'[[0|0],[0|1],[0|2],[1|1],[2|2]]'
```
```python
class hand:
def __init__(self,dlist):
# create a hand give a list of dominoes
pass
def play(self,d):
# if domino d is in the hand, remove and return it, otherwise return None
pass
def __repr__(self):
pass
def has_a(self,num):
# if there is a domino in the hand with an a side with value num, return True
pass
def points(self):
# add up the "points" in the hand -- the sum of all the ends and return the value
pass
def __contains__(self,d):
# return True if the hand contains domino d
pass
def __len__(self):
# return the number of dominoes in the hand
pass
```
### `board.py`
The *board* class provides a representation of a game board. The class has the following methods
* `__init__` create and empty board
* `__repr__` create a string representation of the board, e.g. [[1|2][2|3]]
* `left` return the value of the left end (or None)
* `right` return the value of the right end (or None)
* `empty` return True if the board is empty
* `play` play a tile on right (True) or left end. Note -- you may need to invert the tile !
```python
>>> b = board.board()
>>> b.empty
True
>>> str(b)
''
>>> d1 = domino.domino((1,2))
>>> d2 = domino.domino((2,3))
>>> d3 = domino.domino((1,0))
>>> b.left
>>> str(b.left)
'None'
>>> b.play(d1,True)
True
>>> str(b)
'[1|2]'
>>> b.left
1
>>> b.right
2
>>> b.play(d2,True)
True
>>> str(b)
'[1|2][2|3]'
>>> b.play(d3,False)
True
>>> str(b)
'[0|1][1|2][2|3]'
```
```python
class board:
def __init__(self):
# create an empty board
pass
@property
def empty(self):
# return True if the board is empty
@property
def left(self):
# return the value on the left of the board (or None for an empty board)
@property
def right(self):
# return the value on the right of the board (or None)
def play(self, d, onright):
# play a domino on the right (True) or left
# invert the tile if necessary when inserting in the board
# return True if the play was completed
pass
def __repr__(self):
pass
```