# 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: ![double-six](https://upload.wikimedia.org/wikipedia/commons/thumb/8/8d/Dominomatrix.svg/220px-Dominomatrix.svg.png) 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 ```