Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- from time import sleep
- from turtle import Turtle
- from random import randint
- from TicTacToeBoardTurtle import TicTacToeBoardTurtle, rect, BoardSpaceOccupied, \
- TicTacToeBoard, boxIndexAtCoordinates
- from TicTacToeBoardTurtle import windowToTurtleCoordinates
- Difficulty = {
- "easy": 0,
- "normal": 1,
- "hard": 2
- }
- aiDebug = True
- def aiTurn(board, difficulty):
- """Perform the AI's turn. The AI should never lose."""
- if difficulty > Difficulty["easy"] or randint(1, 2) == 1:
- if aiDebug:
- print("botWin")
- # Check if bot can win in the next move
- for i in range(9):
- # Copy the board
- fakeBoard = TicTacToeBoard()
- for j in range(9):
- letter = board.getSpace(j % 3, j // 3) # j % 3, j // 3 converts 0-8 to (0-2,0-2)
- if letter != board.Empty:
- (fakeBoard.putX if letter == board.X else fakeBoard.putO)(j % 3, j // 3)
- if not fakeBoard.getSpace(i % 3, i // 3): # If the space is empty
- (fakeBoard.putX if currentTurn == board.X else fakeBoard.putO)(i % 3, i // 3) # Try to put an AI piece
- won = fakeBoard.won() # Then check if the AI won
- if won and won[0] == currentTurn:
- (board.drawX if currentTurn == board.X else board.drawO)(i % 3, i // 3) # If it did, win!
- return
- if difficulty > Difficulty["easy"] or randint(1, 2) == 1:
- # Check if player could win on his next move
- if aiDebug:
- print("playerWin")
- for i in range(9):
- # Copy the board
- fakeBoard = TicTacToeBoard()
- for j in range(9):
- letter = board.getSpace(j % 3, j // 3)
- if letter != board.Empty:
- (fakeBoard.putX if letter == board.X else fakeBoard.putO)(j % 3, j // 3)
- if not fakeBoard.getSpace(i % 3, i // 3): # If the space is empty
- (fakeBoard.putX if currentTurn == board.O else fakeBoard.putO)(i % 3, i // 3) # Try to put player piece
- won = fakeBoard.won() # Then check if the player won
- if won and won[0] == (board.X if currentTurn == board.O else board.O):
- (board.drawX if currentTurn == board.X else board.drawO)(i % 3, i // 3) # If they won, block there.
- return
- if difficulty == Difficulty["hard"]:
- # Check for player crosses
- if aiDebug:
- print("crossCheck")
- for i in range(9):
- if not board.getSpace(i % 3, i // 3):
- for cross in (
- [(1, 0), (0, 1), (0, 0)], [(1, 0), (2, 1), (2, 0)], [(1, 2), (0, 1), (0, 2)],
- [(1, 2), (2, 1), (2, 2)]):
- if board.getSpace(*cross[0]) == board.getSpace(*cross[1]) == \
- (board.X if currentTurn == board.O else board.O) and not board.getSpace(*cross[2]):
- (board.drawX if currentTurn == board.X else board.drawO)(*(cross[2]))
- return
- if difficulty == Difficulty["hard"] or randint(1, 2 ** difficulty) == 2:
- # If the middle is free, take it
- if aiDebug:
- print("centerCheck")
- if not board.getSpace(1, 1):
- (board.drawX if currentTurn == board.X else board.drawO)(1, 1)
- return
- if difficulty == Difficulty["hard"] or randint(1, 2 ** difficulty) == 2:
- # Take sides if the center isn't taken, otherwise take corners.
- corners = board.getSpace(1, 1) != currentTurn != board.Empty
- if aiDebug:
- print("cornerSidesCheck", corners and "Corners" or "Sides")
- move = None
- if corners:
- global move
- move = not board.getSpace(0, 0) and (0, 0) or \
- not board.getSpace(0, 2) and (0, 2) or \
- not board.getSpace(2, 0) and (2, 0) or \
- not board.getSpace(2, 2) and (2, 2) # Corners
- else:
- global move
- move = not board.getSpace(1, 0) and (1, 0) or \
- not board.getSpace(0, 1) and (0, 1) or \
- not board.getSpace(2, 1) and (2, 1) or \
- not board.getSpace(1, 2) and (1, 2) # Sides
- if move:
- (board.drawX if currentTurn == board.X else board.drawO)(*tuple(move)) # PyCharm complained without tuple()
- return
- # Otherwise, take a random space
- if aiDebug:
- print("rand")
- remainingMoves = [(i % 3, i // 3) for i in range(9) if not board.getSpace(i % 3, i // 3)]
- if len(remainingMoves) == 0:
- return
- (board.drawX if currentTurn == board.X else board.drawO)(*remainingMoves[randint(0, len(remainingMoves) - 1)])
- pen = Turtle()
- pen.hideturtle()
- running = False
- def main():
- """The runner for the game."""
- running = False
- pen.getscreen().tracer(5) # Make drawing nearly instant
- width, height = pen.getscreen().window_width(), pen.getscreen().window_height()
- minMeasure = min(width, height) # Used for centering board on the next line
- board = TicTacToeBoardTurtle(width * .5 - minMeasure * .35, height * .5 - minMeasure * .35, minMeasure * .7,
- pen=pen)
- pen.getscreen().onscreenclick(board.onClick) # Bind the onclick event
- goto = lambda _pen, x, y: pen.goto( # Convert window coordinates to turtle coordinates for pen.goto
- *windowToTurtleCoordinates(x, y, _pen.getscreen().window_width(), _pen.getscreen().window_height()))
- def drawBtn1(color):
- """Draws the PvP button."""
- rect(pen, width * .15, height * .15, width * .7, height * .35, color)
- pen.penup()
- goto(pen, width * .5, height * .325 + 90 * .75)
- pen.pendown()
- pen.pencolor(0, 0, 0)
- pen.write("PvP", align="center", font=("Arial", "90", "normal"))
- pen.penup()
- def drawBtn2(colorEasy, colorNormal, colorHard):
- """Draws the PvC button."""
- i = -1
- for color in (colorEasy, colorNormal, colorHard):
- i += 1
- rect(pen, width * (.15 + .7 / 3 * i), height * .5, width * .7 / 3, height * .35, color)
- pen.pencolor(0, 0, 0)
- pen.penup()
- goto(pen, width * (.15 + .7 / 3 * (i + .5)), height * .775)
- pen.pendown()
- pen.write(("Easy", "Normal", "Hard")[i], align="center", font=("Arial", "50", "normal"))
- pen.penup()
- goto(pen, width * .5, height * .575 + 90 * .75)
- pen.pendown()
- pen.pencolor(0, 0, 0)
- pen.write("PvC", align="center", font=("Arial", "90", "normal"))
- def startGame(PvP, difficulty):
- """Start the game."""
- global running
- running = True
- pen.clear() # Clear the screen
- board.drawBoard() # Draw the board
- global currentTurn
- currentTurn = TicTacToeBoard.X # X always goes first.
- def winCheck():
- if not PvP and currentTurn == board.O:
- # Check if bot can win in the next move
- for i in range(9):
- # Copy the board
- fakeBoard = TicTacToeBoard()
- for j in range(9):
- letter = board.getSpace(j % 3, j // 3)
- if letter != board.Empty:
- (fakeBoard.putX if letter == board.X else fakeBoard.putO)(j % 3, j // 3)
- if not fakeBoard.getSpace(i % 3, i // 3):
- (fakeBoard.putX if currentTurn == board.X else fakeBoard.putO)(i % 3, i // 3)
- won = fakeBoard.won()
- if won and won[0] == currentTurn:
- (board.drawX if currentTurn == board.O else board.drawO)(i % 3, i // 3)
- won = board.isWon() # Check if any player won
- if won:
- # Draw the line across the winning characters in yellow
- print("{} won!".format(won[0] == board.X and "X" or "O"))
- pen.getscreen().tracer(1)
- oldPensize = pen.pensize()
- pen.pensize(board.boardWidth / 2)
- pen.pencolor(1, 1, 0)
- board.drawLine(won[1])
- pen.pencolor(*board.boardColor)
- pen.getscreen().tracer(5)
- sleep(.5) # Wait half a second...
- pen.clear()
- if won[0] == board.X: # Draw an X if X won
- pen.penup()
- goto(pen, *board.boardPosition)
- pen.pendown()
- goto(pen, board.x + board.boardSize, board.y + board.boardSize)
- pen.penup()
- goto(pen, board.x + board.boardSize, board.y)
- pen.pendown()
- goto(pen, board.x, board.y + board.boardSize)
- else: # Draw an O if O won
- pen.penup()
- goto(pen, board.x + board.boardSize / 2, board.y)
- pen.pendown()
- pen.circle(board.boardSize / 2)
- pen.pensize(oldPensize)
- pen.penup()
- pen.getscreen().update()
- sleep(1.5) # Keep the X or O on the screen for 1.5 seconds
- board.clearMouseClickedCallbacks()
- pen.clear()
- restart() # Restart the program back to the buttons.
- return True
- elif won is None: # A tie
- pen.getscreen().update() # Make sure it finished drawing the last X or O
- sleep(.5) # Wait half a second before drawing the tie line
- print("Tie!")
- pen.clear() # Clear the screen, then draw the tie line
- oldPensize = pen.pensize()
- pen.pensize(board.boardWidth / 2)
- pen.penup()
- pen.pencolor(*board.boardColor)
- goto(pen, board.x, board.y + board.boardSize / 2)
- pen.getscreen().tracer(1)
- pen.pendown()
- goto(pen, board.x + board.boardSize, board.y + board.boardSize / 2)
- pen.getscreen().tracer(5)
- pen.pensize(oldPensize)
- pen.penup()
- pen.getscreen().update()
- sleep(1.5) # Keep the tie line on screen for 1.5 seconds, then restart.
- pen.clear()
- restart()
- return True
- return False
- def onBoardClicked(xIndex, yIndex):
- """Run when a space on the board is clicked."""
- if currentTurn == board.X:
- try: # Try to place an X where the player clicked
- board.drawX(xIndex, yIndex)
- except BoardSpaceOccupied as e:
- print(e) # Print the error message
- return
- elif currentTurn == board.O:
- try: # Try to place an O where the player clicked
- board.drawO(xIndex, yIndex)
- except BoardSpaceOccupied as e:
- print(e) # Print the error message
- return
- if winCheck():
- return
- global currentTurn
- currentTurn = board.X if currentTurn == board.O else board.O # Switch turns
- if not PvP: # If the player is against the AI, perform the AI's turn, then switch turns.
- aiTurn(board, difficulty)
- if aiDebug:
- print("")
- currentTurn = board.X if currentTurn == board.O else board.O
- if winCheck():
- return
- def onClick(x, y, btn=1):
- if btn == 1: # Check if it was a left click
- boxIndex = boxIndexAtCoordinates(board.getBoardSize(), board.getBoardWidth(), board.getX(),
- board.getY(), x, y)
- if boxIndex: # If a box was clicked, run onBoardClicked
- onBoardClicked(boxIndex[0], boxIndex[1])
- board.addMouseClickedCallback(onClick) # Add the callback, so it runs as needed.
- def clickBtn1(x, y, btn=1):
- """Run when button 1 is clicked."""
- if width * .15 <= x <= width * .85 and height * .15 <= y <= height * .5 and btn == 1:
- board.clearMouseClickedCallbacks()
- startGame(True, Difficulty["hard"])
- def clickBtn2(x, y, btn=1):
- """Run when button 2 is clicked."""
- if width * .15 <= x <= width * .85 and height * .5 < y <= height * .85 and btn == 1:
- board.clearMouseClickedCallbacks()
- startGame(False, int((x - width * .15) / (width * .7) * 3))
- # Add button click callbacks
- board.addMouseClickedCallback(clickBtn1)
- board.addMouseClickedCallback(clickBtn2)
- # Draw both buttons
- if not running:
- drawBtn1((1, .5, 0))
- drawBtn2((0, 1, 0), (1, 1, 0), (1, 0, 0))
- currentTurn = TicTacToeBoard.X # The first turn is always X.
- restart = main # Called restart only because it is more intuitive.
- main()
- if hasattr(pen.getscreen(), "mainloop"):
- pen.getscreen().mainloop() # Python 3 compat
- else:
- from turtle import mainloop
- mainloop() # Python 2 compat
Add Comment
Please, Sign In to add comment