Tic Tac Toe (3)

In today’s lecture, we will continue our discussion on building a simple text-based board game (Tic Tac Toe). In the process, we will learn about the benefits of inheritance, encapsulation and more generally of object-oriented design.

Tic Tac Toe Letter

Last time we looked at TTTBoard, which inherits from Board and also adds Tic Tac Toe specific features. Today we’ll look at the final two classes: TTTLetter and TTTGame.

# TTTLetter class
"""Implements the functionality of a letter in Boggle."""

from graphics import *
from board import *

class TTTLetter:
    """A TTT letter has several attributes that define it:
           *  _row, _col coordinates indicate its position in the grid (ints)
           *  _textObj denotes the Text object from the graphics module,
              which has attributes such as size, style, color, etc
              and supports methods such as getText(), setText() etc.
           *  _rect denotes the Rectangle object from the graphics module,
              which has attributes such as color and supports methods such as 
              getFillColor(), setFillColor() etc.
    """

    __slots__ = ['_row', '_col', '_textObj', '_rect']

    def __init__(self, board, col=-1, row=-1, letter=""):

        # variables needed for graphical testing
        xInset = board.getXInset()
        yInset = board.getYInset()
        size = board.getSize()
        win = board.getWin()

        # set row and column attributes
        self._col = col
        self._row = row

        # make rectangle and add to graphical window
        p1 = Point(xInset + size * col, yInset + size * row)
        p2 = Point(xInset + size * (col + 1), yInset + size * (row + 1))        
        self._rect = board._makeRect(p1, p2, "white")

        # update text in center of rectangle
        self._textObj = Text(self._rect.getCenter(), letter)
        self._textObj.draw(win)

    def getLetter(self):
        """Returns letter (text of type str) associated with self._textObj"""
        return self._textObj.getText()

    def setLetter(self, char):
        self._textObj.setText(char)
        if char == 'X':
            self._rect.setFillColor("light blue")
        elif char == 'O':
            self._rect.setFillColor("pink")
        else:
            self._rect.setFillColor("white")

    def __str__(self):
        l, col, row = self.getLetter(), self._col, self._row
        return "{} at Board position ({}, {})".format(l, col, row)

    def __repr__(self):
        return str(self)
# Would be in if __name__ == "__main__":
win = GraphWin("Tic Tac Toe", 400, 400)
board = Board(win, rows=3, cols=3)

letter = TTTLetter(board, 1, 1, "A")
letter2 = TTTLetter(board, 1, 2, "O")
letter3 = TTTLetter(board, 2, 1, "B")
letter2.setLetter("O")
print(letter2)
win.close()

Tic Tac Toe Game

Finally, let’s put it all together and implement the game logic in TTTGame.

from graphics import GraphWin
from tttboard import TTTBoard
from tttletter import TTTLetter

class TTTGame:
    __slots__ = [ "_board", "_numMoves", "_player" ]

    def __init__(self, win):
        self._board = TTTBoard(win)
        self._numMoves = 0
        self._player = "X"

    def doOneClick(self, point):
        """
        Implements the logic for processing
        one click. Returns True if play
        should continue, and False if the game is over.
        """
        # step 1: check for exit button and exit (return False)
        if self._board.inExit(point):
            # game over
            return False

        # step 2: check for reset button and reset game
        elif self._board.inReset(point):
            self._board.reset()
            self._board.setStringToUpperText("")
            self._numMoves = 0
            self._player = "X"

        # step 3: check if click is on a cell in the grid
        elif self._board.inGrid(point):

            # get the letter at the point the user clicked
            tlet = self._board.getTTTLetterAtPoint(point)

            # make sure this square is vacant
            if tlet.getLetter() == "":
                tlet.setLetter(self._player)

                # valid move, so increment numMoves
                self._numMoves += 1

                # check for win or draw
                winFlag = self._board.checkForWin(self._player)
                if winFlag:
                    self._board.setStringToUpperText(self._player + " WINS!")
                elif self._numMoves == 9:
                    self._board.setStringToUpperText("DRAW!")
                # not a win or draw, swap players
                else:
                    # set player to X or O
                    if self._player == "X":
                        self._player = "O"
                    else:
                        self._player = "X"

		# keep going!
        return True
# Would be in if __name__ == "__main__":
win = GraphWin("Tic Tac Toe", 400, 400)
game = TTTGame(win)
keepGoing = True
while keepGoing:
    point = win.getMouse()
    keepGoing = game.doOneClick(point)
win.close()