Tic Tac Toe
Contents
Tic Tac Toe¶
In today’s lecture, we will experiment with a graphics package to build a simple text-based board game (Tic Tac Toe).
Using the Graphics Module¶
We will experiment with a few commands to get started with the graphics module (provided in graphics.py
).
from graphics import *
# takes title and size of window
win = GraphWin("Name", 400, 400)
# create point obj at x, y coordinate in window
pt = Point(200, 200)
# create circle with center at pt and radius 100
c = Circle(pt, 100)
# draw the circle on the window
c.draw(win)
# set color to blue
c.setFill("blue")
# Pause until a mouse is clicked on window
win.getMouse()
# Close window when done
win.close()
Building a Board Class¶
Let’s think about the design of a basic board class for building games. Even though we are thinking about Tic Tac Toe today, this class is a generic class that we want to use for multiple games. Thus we should not include any game-specific details.
'''board.py: The Board class provides a basic game board interface, including
useful methods for creating and manipulating a grid of squares, methods for
converting screen coordinates to grid coordinates and vice versa, and methods
for setting and appending text to various locations outside of the grid. It
also draws an exit and reset button and provides methods for checking for mouse
clicks inside of those regions.'''
from graphics import *
class Board:
# _win: graphical window on which we will draw our board
# _xInset: avoids drawing in corner of window
# _yInset: avoids drawing in corner of window
# _rows: number of rows in grid of squares
# _cols: number of columns in grid of squares
# _size: edge size of each square
__slots__ = [ '_xInset', '_yInset', '_rows', '_cols', '_size', \
'_win', '_exitButton', '_resetButton', \
'_textArea', '_lowerWord', '_upperWord']
def __init__(self, win, xInset=50, yInset=50, rows=3, cols=3, size=50):
# update class attributes
self._xInset = xInset; self._yInset = yInset
self._rows = rows; self._cols = cols
self._size = size
self._win = win
#self.drawBoard()
# getter methods for attributes
def getWin(self):
return self._win
def getXInset(self):
return self._xInset
def getYInset(self):
return self._yInset
def getRows(self):
return self._rows
def getCols(self):
return self._cols
def getSize(self):
return self._size
def getBoard(self):
return self
def __makeTextArea(self, point, fontsize=18, color="black", text=""):
textArea = Text(point, text)
textArea.setSize(fontsize)
textArea.setTextColor(color)
textArea.setStyle("normal")
textArea.draw(self._win)
return textArea
def _makeRect(self, point1, point2, fillcolor="white", text=""):
rect = Rectangle(point1, point2, fillcolor)
rect.draw(self._win)
text = Text(rect.getCenter(), text)
text.setTextColor("black")
text.draw(self._win)
return rect
def __drawTextAreas(self):
"""Draw the text area to the right/lower/upper side of main grid"""
# draw main text area (right of grid)
self._textArea = self.__makeTextArea(Point(self._xInset * self._rows + self._size * 2,
self._yInset + 50), 14)
#draw the text area below grid
self._lowerWord = self.__makeTextArea(Point(160, 275))
#draw the text area above grid
self._upperWord = self.__makeTextArea(Point(160, 25), color="red")
def __drawGrid(self):
"""Creates a row x col grid, filled with empty squares"""
for x in range(self._cols):
for y in range(self._rows):
# create first point
p1 = Point(self._xInset + self._size * x,
self._yInset + self._size * y)
# create second point
p2 = Point(self._xInset + self._size * (x + 1),
self._yInset + self._size * (y + 1))
# create rectangle and add to graphical window
self._makeRect(p1, p2)
#Text(Point(self._xInset + 15 + self._size * x, \
# self._yInset + 15+ self._size * y), \
# "{},{}".format(x,y)).draw(win)
def __drawButtons(self):
"""Add buttons to board"""
p1 = Point(50, 300); p2 = Point(130, 350)
self._resetButton = self._makeRect(p1, p2, text="RESET")
p3 = Point(170, 300); p4 = Point(250, 350)
self._exitButton = self._makeRect(p3, p4, text="EXIT")
def drawBoard(self):
# this creates a row x col grid, filled with squares, including buttons
self._win.setBackground("white smoke")
self.__drawGrid()
self.__drawTextAreas()
self.__drawButtons()
# convert Point to grid position (tuple)
def getPosition(self, point):
'''
Converts a window location (Point) to a grid position (tuple).
Window locations are x, y coordinates.
Note: Grid positions are always returned as col, row.
'''
pX = point.getX()
pY = point.getY()
if pY < self._yInset:
row = -1
else:
row = int((pY - self._yInset) / self._size)
if pX < self._xInset:
col = -1
else:
col = int((pX - self._xInset) / self._size)
return (col, row)
# check for click inside specific rectangular region
def __inRect(self, point, rect):
'''
Returns True if a Point (point) exists inside a specific
Rectangle (rect) on screen.
'''
pX = point.getX()
pY = point.getY()
rLeft = rect.getP1().getX()
rTop = rect.getP1().getY()
rRight = rect.getP2().getX()
rBottom = rect.getP2().getY()
return pX > rLeft and pX < rRight and pY > rTop and pY < rBottom
# check for click in grid
def inGrid(self, point):
'''
Returns True if a Point (point) exists inside the grid of squares.
'''
ptX = point.getX()
ptY = point.getY()
maxY = self._size * (self._rows + 1)
maxX = self._size * (self._cols + 1)
return ptX <= maxX and ptY <= maxY and ptX >= self._xInset and ptY >= self._yInset
# clicked in exit button?
def inExit(self, point):
'''
Returns true if point is inside exit button (rectangle)
'''
return self.__inRect(point, self._exitButton)
# clicked in reset button?
def inReset(self, point):
'''
Returns true if point is inside exit button (rectangle)
'''
return self.__inRect(point, self._resetButton)
# set text to text area on right
def getStringFromTextArea(self):
'''
Sets text to text area to right of grid.
Overwrites existing text.
'''
return self._textArea.getText()
# set text to text area on right
def setStringToTextArea(self, text):
'''
Sets text to text area to right of grid.
Overwrites existing text.
'''
self._textArea.setText(text)
# add text to text area below grid
def getStringFromLowerText(self):
'''
Set text to text area below grid.
Overwrites existing text.
'''
return self._lowerWord.getText()
# add text to text area below grid
def setStringToLowerText(self, text):
'''
Set text to text area below grid.
Overwrites existing text.
'''
self._lowerWord.setText( text )
# add text to text area above grid
def getStringFromUpperText(self):
'''
Add text to text area above grid.
Does not overwrites existing text.'''
return self._upperWord.getText()
# set text to text area above grid
def setStringToUpperText(self, text):
'''
Set text to text area above grid.
Overwrites existing text.
'''
self._upperWord.setText(text)
win = GraphWin("Tic Tac Toe", 400, 400) # we need to create a window before we call draw
# create new board with default values
board = Board(win)
type(board)
# draw Board
board.drawBoard()
# test getters and verify attribute values
board.getRows()
board.getCols()
board.getSize()
# set string above grid
board.setStringToUpperText("Upper text")
# set and update string below grid
board.setStringToLowerText("Lower text: ")
string = board.getStringFromLowerText() + "hi!"
board.setStringToLowerText(string)
# set string to text area to right of grid
board.setStringToTextArea("Text area")
# wait for a mouse click
point = win.getMouse()
# calculate x and y value from point
x,y = point.getX(), point.getY()
print("Clicked coord {}".format((x,y)))
print("Clicked in reset?", board.inReset(point))
print("Clicked in exit?", board.inExit(point))
print("Clicked in grid?", board.inGrid(point))
if board.inGrid(point):
print("Grid coord: {}".format(board.getPosition(point)))
win.close()