Monday, February 22, 2010

Finishing Tic Tac Toe

Aha! So I coded up my own version of Tic Tac Toe. I did take a look at my old program that I posted up before (here) in a couple spots where I couldn't pinpoint my own bugs, but other than that I wrote this thing without a reference. This, pretty much just solidified my understanding and familiarity of Python syntax even further. Not much else to explain. Here is my source code:


import random

def drawBoard():
    print(board[1] + '|' + board[2] + '|' + board[3])
    print('-----')
    print(board[4] + '|' + board[5] + '|' + board[6])
    print('-----')
    print(board[7] + '|' + board[8] + '|' + board[9])

def pickYourLetter():

    letter = ''

    while (letter != 'X' and letter != 'O'):
        print('Would you like to X or O?')
        letter = raw_input().upper()

        if (letter != 'X' and letter != 'O'):
              print('Uhm, I said X or O.')

    if letter == 'X':
        return ['X', 'O']
    else:
        return ['O', 'X']

def whoGoesFirst():

    if random.randint(0, 1) == 1:
        return 'player'
    else:
        return 'computer'

def getPlayerMove(theBoard):

    move = ' '

    while move not in '1 2 3 4 5 6 7 8 9'.split() or not isSpaceFree(theBoard, int(move)):
        print('It is your turn to move.')
        move = raw_input()

    return int(move)

def isSpaceFree(theBoard, move):

    return board[move] == ' '

def makeMove(board, letter, move):

    board[move] = letter

def isWinner(b, l):
    return ((b[1] == l and b[2] == l and b[3] == l) or
    (b[4] == l and b[5] == l and b[6] == l) or
    (b[7] == l and b[8] == l and b[9] == l) or
    (b[1] == l and b[4] == l and b[7] == l) or
    (b[2] == l and b[5] == l and b[8] == l) or
    (b[3] == l and b[6] == l and b[9] == l) or
    (b[1] == l and b[5] == l and b[9] == l) or
    (b[3] == l and b[5] == l and b[7] == l))

def isTie(board):

    for i in range(1, 10):
        if isSpaceFree(board, i):
            return False
  
    return True


def getBoardCopy(theBoard):

    dupeBoard = []

    for i in board:
        dupeBoard.append(i)

    return dupeBoard

def getComputerMove(theBoard, letter):

    if computerLetter == 'X':
        playerLetter = 'O'
    else:
        playerLetter = 'X'

    for i in range(1, 10):
        copy = getBoardCopy(theBoard)
        if isSpaceFree(copy, i):
            makeMove(copy, computerLetter, i)
            if isWinner(copy, computerLetter):
                return i

    for i in range(1, 10):
        copy = getBoardCopy(theBoard)
        if isSpaceFree(copy, i):
            makeMove(copy, playerLetter, i)
            if isWinner(copy, playerLetter):
                return i

    move = chooseRandomMove(theBoard, [1, 3, 7, 9])
    if move != None:
        return move

    if isSpaceFree(board, 5):
        return 5

    return chooseRandomMove(board, [2, 4, 6, 8])

def chooseRandomMove(theBoard, moveList):
  
    moves = []
    for i in moveList:
        if isSpaceFree(theBoard, i):
            moves.append(i)

    if len(moves) != 0:
        return random.choice(moves)
    else:
        return None

def playAgain():

    print('Do you want to play again? (yes or no)')
    return raw_input().lower().startswith('y')

      


while True:

    board = [' '] * 10
    print('This is Tic Tac Toe.')

    playerLetter, computerLetter = pickYourLetter()

    gameIsPlaying = True
    currentTurn = whoGoesFirst()

    if currentTurn == 'player':
        print('You will move first, as decided at random.')
    else:
        print('The computer will move first, as decided at random.')

    while gameIsPlaying == True:

        if currentTurn == 'player':
            drawBoard()

            move = getPlayerMove(board)
            makeMove(board, playerLetter, move)

            if isWinner(board, playerLetter):
                drawBoard()
                print('You have won the game!')
                gameIsPlaying = False
            else:
                if isTie(board):
                    drawBoard()
                    print('Game is a tie!!')
                    gameIsPlaying = False
                else:
                    currentTurn = 'computer'

        else:
            move = getComputerMove(board, computerLetter)
            makeMove(board, computerLetter, move)

            if isWinner(board, computerLetter):
                drawBoard()
                print('You have lost the game!')
                gameIsPlaying = False
            else:
                if isTie(board):
                    drawBoard()
                    print('Game is a tie!')
                    gameIsPlaying = False
                else:
                    currentTurn = 'player'    
              
    if not playAgain():
        break

And that is my code. Here is the exe file in case you are interested: http://www.mediafire.com/?oodckz5oxmy


Anyways, I plan to go over this chapter tomorrow and follow through with constructing the program from scratch without referring to the example code http://inventwithpython.com/chapter10.html.


I had a few troubles throughout the program, and the most annoying one was in the definitions of winning. I had constantly placed a 1 in place of a lower case L, and this resulted in bad things. I also had a couple typos here and there, things like typing = instead of ==, and vise versa.


One thing in particular that I found interesting while creating this program, was the "None" value. Here is a quote from the book "Calls to functions that do not return anything (that is, they exit by reaching the end of the function and not from a return statement) will evaluate to None." This was useful in that whenever I wanted the Ai in my simulation to choose a random location to place his letter, say, one of the four corners; if all four of these corners were taken, I could return the value of None instead of one of the values for one of the four corners.


I also learned from this chapter some interesting aspects of list referencing. Take a look at this function:

def makeMove(board, letter, move):
    board[move] = letter

  
It would seem like this function wouldn't do anything due to the change of the board list being of the private scope by default, since it is happening within the function itself. This is because the word "board" isn't actually the list board, it is just a reference to that list passed onto the function as an argument. This means that when we have the line board[move] = letter we aren't modifying the data stored in a variable, but we are modifying the data stored in the list, which is referenced by the word "board". Here is a quote from the book that explains this: "When you assign a list to a variable with the = sign, you are actually assigning a reference to the list. A reference is a value that points to some bit of data, and a list reference is a value that points to a list."

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.