moomoomoo309

TicTacToeRunner

Dec 9th, 2016
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 13.27 KB | None | 0 0
  1. from time import sleep
  2. from turtle import Turtle
  3. from random import randint
  4.  
  5. from TicTacToeBoardTurtle import TicTacToeBoardTurtle, rect, BoardSpaceOccupied, \
  6.     TicTacToeBoard, boxIndexAtCoordinates
  7. from TicTacToeBoardTurtle import windowToTurtleCoordinates
  8.  
  9. Difficulty = {
  10.     "easy": 0,
  11.     "normal": 1,
  12.     "hard": 2
  13. }
  14.  
  15. aiDebug = True
  16.  
  17.  
  18. def aiTurn(board, difficulty):
  19.     """Perform the AI's turn. The AI should never lose."""
  20.  
  21.     if difficulty > Difficulty["easy"] or randint(1, 2) == 1:
  22.         if aiDebug:
  23.             print("botWin")
  24.         # Check if bot can win in the next move
  25.         for i in range(9):
  26.             # Copy the board
  27.             fakeBoard = TicTacToeBoard()
  28.             for j in range(9):
  29.                 letter = board.getSpace(j % 3, j // 3)  # j % 3, j // 3 converts 0-8 to (0-2,0-2)
  30.                 if letter != board.Empty:
  31.                     (fakeBoard.putX if letter == board.X else fakeBoard.putO)(j % 3, j // 3)
  32.  
  33.             if not fakeBoard.getSpace(i % 3, i // 3):  # If the space is empty
  34.                 (fakeBoard.putX if currentTurn == board.X else fakeBoard.putO)(i % 3, i // 3)  # Try to put an AI piece
  35.                 won = fakeBoard.won()  # Then check if the AI won
  36.                 if won and won[0] == currentTurn:
  37.                     (board.drawX if currentTurn == board.X else board.drawO)(i % 3, i // 3)  # If it did, win!
  38.                     return
  39.  
  40.     if difficulty > Difficulty["easy"] or randint(1, 2) == 1:
  41.         # Check if player could win on his next move
  42.         if aiDebug:
  43.             print("playerWin")
  44.         for i in range(9):
  45.             # Copy the board
  46.             fakeBoard = TicTacToeBoard()
  47.             for j in range(9):
  48.                 letter = board.getSpace(j % 3, j // 3)
  49.                 if letter != board.Empty:
  50.                     (fakeBoard.putX if letter == board.X else fakeBoard.putO)(j % 3, j // 3)
  51.  
  52.             if not fakeBoard.getSpace(i % 3, i // 3):  # If the space is empty
  53.                 (fakeBoard.putX if currentTurn == board.O else fakeBoard.putO)(i % 3, i // 3)  # Try to put player piece
  54.                 won = fakeBoard.won()  # Then check if the player won
  55.                 if won and won[0] == (board.X if currentTurn == board.O else board.O):
  56.                     (board.drawX if currentTurn == board.X else board.drawO)(i % 3, i // 3)  # If they won, block there.
  57.                     return
  58.  
  59.     if difficulty == Difficulty["hard"]:
  60.         # Check for player crosses
  61.         if aiDebug:
  62.             print("crossCheck")
  63.         for i in range(9):
  64.             if not board.getSpace(i % 3, i // 3):
  65.                 for cross in (
  66.                         [(1, 0), (0, 1), (0, 0)], [(1, 0), (2, 1), (2, 0)], [(1, 2), (0, 1), (0, 2)],
  67.                         [(1, 2), (2, 1), (2, 2)]):
  68.                     if board.getSpace(*cross[0]) == board.getSpace(*cross[1]) == \
  69.                             (board.X if currentTurn == board.O else board.O) and not board.getSpace(*cross[2]):
  70.                         (board.drawX if currentTurn == board.X else board.drawO)(*(cross[2]))
  71.                         return
  72.  
  73.     if difficulty == Difficulty["hard"] or randint(1, 2 ** difficulty) == 2:
  74.         # If the middle is free, take it
  75.         if aiDebug:
  76.             print("centerCheck")
  77.         if not board.getSpace(1, 1):
  78.             (board.drawX if currentTurn == board.X else board.drawO)(1, 1)
  79.             return
  80.  
  81.     if difficulty == Difficulty["hard"] or randint(1, 2 ** difficulty) == 2:
  82.         # Take sides if the center isn't taken, otherwise take corners.
  83.         corners = board.getSpace(1, 1) != currentTurn != board.Empty
  84.         if aiDebug:
  85.             print("cornerSidesCheck", corners and "Corners" or "Sides")
  86.         move = None
  87.         if corners:
  88.             global move
  89.             move = not board.getSpace(0, 0) and (0, 0) or \
  90.                    not board.getSpace(0, 2) and (0, 2) or \
  91.                    not board.getSpace(2, 0) and (2, 0) or \
  92.                    not board.getSpace(2, 2) and (2, 2)  # Corners
  93.         else:
  94.             global move
  95.             move = not board.getSpace(1, 0) and (1, 0) or \
  96.                    not board.getSpace(0, 1) and (0, 1) or \
  97.                    not board.getSpace(2, 1) and (2, 1) or \
  98.                    not board.getSpace(1, 2) and (1, 2)  # Sides
  99.         if move:
  100.             (board.drawX if currentTurn == board.X else board.drawO)(*tuple(move))  # PyCharm complained without tuple()
  101.             return
  102.  
  103.     # Otherwise, take a random space
  104.     if aiDebug:
  105.         print("rand")
  106.     remainingMoves = [(i % 3, i // 3) for i in range(9) if not board.getSpace(i % 3, i // 3)]
  107.     if len(remainingMoves) == 0:
  108.         return
  109.     (board.drawX if currentTurn == board.X else board.drawO)(*remainingMoves[randint(0, len(remainingMoves) - 1)])
  110.  
  111.  
  112. pen = Turtle()
  113. pen.hideturtle()
  114. running = False
  115.  
  116.  
  117. def main():
  118.     """The runner for the game."""
  119.     running = False
  120.     pen.getscreen().tracer(5)  # Make drawing nearly instant
  121.     width, height = pen.getscreen().window_width(), pen.getscreen().window_height()
  122.     minMeasure = min(width, height)  # Used for centering board on the next line
  123.     board = TicTacToeBoardTurtle(width * .5 - minMeasure * .35, height * .5 - minMeasure * .35, minMeasure * .7,
  124.         pen=pen)
  125.     pen.getscreen().onscreenclick(board.onClick)  # Bind the onclick event
  126.     goto = lambda _pen, x, y: pen.goto(  # Convert window coordinates to turtle coordinates for pen.goto
  127.         *windowToTurtleCoordinates(x, y, _pen.getscreen().window_width(), _pen.getscreen().window_height()))
  128.  
  129.     def drawBtn1(color):
  130.         """Draws the PvP button."""
  131.         rect(pen, width * .15, height * .15, width * .7, height * .35, color)
  132.         pen.penup()
  133.         goto(pen, width * .5, height * .325 + 90 * .75)
  134.         pen.pendown()
  135.         pen.pencolor(0, 0, 0)
  136.         pen.write("PvP", align="center", font=("Arial", "90", "normal"))
  137.         pen.penup()
  138.  
  139.     def drawBtn2(colorEasy, colorNormal, colorHard):
  140.         """Draws the PvC button."""
  141.         i = -1
  142.         for color in (colorEasy, colorNormal, colorHard):
  143.             i += 1
  144.             rect(pen, width * (.15 + .7 / 3 * i), height * .5, width * .7 / 3, height * .35, color)
  145.             pen.pencolor(0, 0, 0)
  146.             pen.penup()
  147.             goto(pen, width * (.15 + .7 / 3 * (i + .5)), height * .775)
  148.             pen.pendown()
  149.             pen.write(("Easy", "Normal", "Hard")[i], align="center", font=("Arial", "50", "normal"))
  150.         pen.penup()
  151.         goto(pen, width * .5, height * .575 + 90 * .75)
  152.         pen.pendown()
  153.         pen.pencolor(0, 0, 0)
  154.         pen.write("PvC", align="center", font=("Arial", "90", "normal"))
  155.  
  156.     def startGame(PvP, difficulty):
  157.         """Start the game."""
  158.         global running
  159.         running = True
  160.         pen.clear()  # Clear the screen
  161.         board.drawBoard()  # Draw the board
  162.         global currentTurn
  163.         currentTurn = TicTacToeBoard.X  # X always goes first.
  164.  
  165.         def winCheck():
  166.             if not PvP and currentTurn == board.O:
  167.                 # Check if bot can win in the next move
  168.                 for i in range(9):
  169.                     # Copy the board
  170.                     fakeBoard = TicTacToeBoard()
  171.                     for j in range(9):
  172.                         letter = board.getSpace(j % 3, j // 3)
  173.                         if letter != board.Empty:
  174.                             (fakeBoard.putX if letter == board.X else fakeBoard.putO)(j % 3, j // 3)
  175.  
  176.                     if not fakeBoard.getSpace(i % 3, i // 3):
  177.                         (fakeBoard.putX if currentTurn == board.X else fakeBoard.putO)(i % 3, i // 3)
  178.                         won = fakeBoard.won()
  179.                         if won and won[0] == currentTurn:
  180.                             (board.drawX if currentTurn == board.O else board.drawO)(i % 3, i // 3)
  181.  
  182.             won = board.isWon()  # Check if any player won
  183.             if won:
  184.                 # Draw the line across the winning characters in yellow
  185.                 print("{} won!".format(won[0] == board.X and "X" or "O"))
  186.                 pen.getscreen().tracer(1)
  187.                 oldPensize = pen.pensize()
  188.                 pen.pensize(board.boardWidth / 2)
  189.                 pen.pencolor(1, 1, 0)
  190.                 board.drawLine(won[1])
  191.                 pen.pencolor(*board.boardColor)
  192.                 pen.getscreen().tracer(5)
  193.                 sleep(.5)  # Wait half a second...
  194.                 pen.clear()
  195.                 if won[0] == board.X:  # Draw an X if X won
  196.                     pen.penup()
  197.                     goto(pen, *board.boardPosition)
  198.                     pen.pendown()
  199.                     goto(pen, board.x + board.boardSize, board.y + board.boardSize)
  200.                     pen.penup()
  201.                     goto(pen, board.x + board.boardSize, board.y)
  202.                     pen.pendown()
  203.                     goto(pen, board.x, board.y + board.boardSize)
  204.                 else:  # Draw an O if O won
  205.                     pen.penup()
  206.                     goto(pen, board.x + board.boardSize / 2, board.y)
  207.                     pen.pendown()
  208.                     pen.circle(board.boardSize / 2)
  209.                 pen.pensize(oldPensize)
  210.                 pen.penup()
  211.                 pen.getscreen().update()
  212.                 sleep(1.5)  # Keep the X or O on the screen for 1.5 seconds
  213.                 board.clearMouseClickedCallbacks()
  214.                 pen.clear()
  215.                 restart()  # Restart the program back to the buttons.
  216.                 return True
  217.             elif won is None:  # A tie
  218.                 pen.getscreen().update()  # Make sure it finished drawing the last X or O
  219.                 sleep(.5)  # Wait half a second before drawing the tie line
  220.                 print("Tie!")
  221.                 pen.clear()  # Clear the screen, then draw the tie line
  222.                 oldPensize = pen.pensize()
  223.                 pen.pensize(board.boardWidth / 2)
  224.                 pen.penup()
  225.                 pen.pencolor(*board.boardColor)
  226.                 goto(pen, board.x, board.y + board.boardSize / 2)
  227.                 pen.getscreen().tracer(1)
  228.                 pen.pendown()
  229.                 goto(pen, board.x + board.boardSize, board.y + board.boardSize / 2)
  230.                 pen.getscreen().tracer(5)
  231.                 pen.pensize(oldPensize)
  232.                 pen.penup()
  233.                 pen.getscreen().update()
  234.                 sleep(1.5)  # Keep the tie line on screen for 1.5 seconds, then restart.
  235.                 pen.clear()
  236.                 restart()
  237.                 return True
  238.             return False
  239.  
  240.         def onBoardClicked(xIndex, yIndex):
  241.             """Run when a space on the board is clicked."""
  242.             if currentTurn == board.X:
  243.                 try:  # Try to place an X where the player clicked
  244.                     board.drawX(xIndex, yIndex)
  245.                 except BoardSpaceOccupied as e:
  246.                     print(e)  # Print the error message
  247.                     return
  248.             elif currentTurn == board.O:
  249.                 try:  # Try to place an O where the player clicked
  250.                     board.drawO(xIndex, yIndex)
  251.                 except BoardSpaceOccupied as e:
  252.                     print(e)  # Print the error message
  253.                     return
  254.             if winCheck():
  255.                 return
  256.             global currentTurn
  257.             currentTurn = board.X if currentTurn == board.O else board.O  # Switch turns
  258.             if not PvP:  # If the player is against the AI, perform the AI's turn, then switch turns.
  259.                 aiTurn(board, difficulty)
  260.                 if aiDebug:
  261.                     print("")
  262.                 currentTurn = board.X if currentTurn == board.O else board.O
  263.             if winCheck():
  264.                 return
  265.  
  266.         def onClick(x, y, btn=1):
  267.             if btn == 1:  # Check if it was a left click
  268.                 boxIndex = boxIndexAtCoordinates(board.getBoardSize(), board.getBoardWidth(), board.getX(),
  269.                     board.getY(), x, y)
  270.                 if boxIndex:  # If a box was clicked, run onBoardClicked
  271.                     onBoardClicked(boxIndex[0], boxIndex[1])
  272.  
  273.         board.addMouseClickedCallback(onClick)  # Add the callback, so it runs as needed.
  274.  
  275.     def clickBtn1(x, y, btn=1):
  276.         """Run when button 1 is clicked."""
  277.         if width * .15 <= x <= width * .85 and height * .15 <= y <= height * .5 and btn == 1:
  278.             board.clearMouseClickedCallbacks()
  279.             startGame(True, Difficulty["hard"])
  280.  
  281.     def clickBtn2(x, y, btn=1):
  282.         """Run when button 2 is clicked."""
  283.         if width * .15 <= x <= width * .85 and height * .5 < y <= height * .85 and btn == 1:
  284.             board.clearMouseClickedCallbacks()
  285.             startGame(False, int((x - width * .15) / (width * .7) * 3))
  286.  
  287.     # Add button click callbacks
  288.     board.addMouseClickedCallback(clickBtn1)
  289.     board.addMouseClickedCallback(clickBtn2)
  290.     # Draw both buttons
  291.     if not running:
  292.         drawBtn1((1, .5, 0))
  293.         drawBtn2((0, 1, 0), (1, 1, 0), (1, 0, 0))
  294.  
  295.  
  296. currentTurn = TicTacToeBoard.X  # The first turn is always X.
  297.  
  298. restart = main  # Called restart only because it is more intuitive.
  299.  
  300. main()
  301. if hasattr(pen.getscreen(), "mainloop"):
  302.     pen.getscreen().mainloop()  # Python 3 compat
  303. else:
  304.     from turtle import mainloop
  305.  
  306.     mainloop()  # Python 2 compat
Add Comment
Please, Sign In to add comment