Thursday, April 15, 2010

Card Simulation

Lately I have been documenting my experience with creating a deck simulator here, which is being written with Python 2.5.6.


I plan to continue documenting my progress at the link above, as well as on this blog. I posted the above link so that anyone that would like to can see all previous posts I've made up to my current point of development, which will be explained throughout this post.


I have chosen to create a card game called MauMau. MauMau is a game very similar to Uno, the main difference being that MauMau is played with a traditional deck of cards.


I currently have created a card class, deck class, MauMau class, and hand class. I manipulate these within the main function, which is the game loop. I also have created a function for determining if a player move is a valid move.


My immediate plans for the game is to:


- Create the used stack pile.
- Allow players to make a move by playing a card onto the used stack pile.
- Create an Ai which plays available cards, and draws if unavailable.
- Create rules for unique cards, like lose a turn, etc.



Here is my current build:


"""
To do:
- Create the used stack pile.
- Allow players to make a move by playing a card onto the used stack pile.
- Create an Ai which plays available cards, and draws if unavailable.
- Create rules for unique cards, like lose a turn, etc.

Created by: CecilSunkure
"""

import sys
import random

ACES_HIGH = True
GAME_RUNNING = True

class card:
    suits = ["Clubs",
             "Diamonds",
             "Hearts",
             "Spades" ]
    ranks = ["Ace",
             "2",
             "3",
             "4",
             "5",
             "6",
             "7",
             "8",
             "9",
             "10",
             "Jack",
             "Queen",
             "King" ]

    def __init__(self, suit=0, rank=0):
        self.suit = suit
        self.rank = rank

    def __str__(self):
        return (self.ranks[self.rank] + " of " + self.suits[self.suit])

class deck:
    def __init__(self):
        self.cards = []
        for suit in range(4):
            for rank in range(12):
                self.cards.append(card(suit, rank))

    def __str__(self):
        s = ''
        for i in range(len(self.cards)):
            s = s + str(self.cards[i]) + '\n'
        return s
          

    def shuffle(self):
        import random
        cards_length = len(self.cards)
        for i in range(cards_length):
            j = random.randrange(i, cards_length)
            self.cards[i], self.cards[j] = self.cards[j], self.cards[i]

    def remove(self, card):
        if card in self.cards:
            self.cards.remove(card)
            return True
        else:
            return False

    def draw(self):
        return self.cards.pop()

    def is_empty(self):
        return (len(self.cards) == 0)

    def deal(self, hands, num_cards=999):
        for i in range(num_cards):
            if self.is_empty(): break
            card = self.pop()
            hand = hands[i % len(hands)]
            hand.add(card)

    def pop(self):
        return self.cards.pop()


class hand(deck):
    def __init__(self, player=''):
        self.cards = []
        self.player = player

    def add(self, card):
        self.cards.append(card)

    def __str__(self):
        s = self.player + "'s Hand"
        t = 0
        if self.is_empty():
            s = s + " is empty\n"
        else:
            s = s + " contains\n"
        for i in range(len(self.cards)):
            t += 1
            s = s + str(t) + '. ' + str(self.cards[i]) + '\n'
        return s

class MauMau:
    def __init__(self, stack=0, topCard=0):
        self.deck = deck()
        self.deck.shuffle()
        self.players = []
        self.stack = stack
        self.topCard = topCard

    def getName(self):
        print('Welcome to Mau Mau!')
        player = raw_input()
        print('What is your name?')
        player = raw_input()
        print('')
        return player

    def startCard(self):
        self.topCard = self.deck.pop()

    def cmp_topCard(self, other):
        if self.topCard.suit == other.suit:
            if self.topCard.rank == other.rank:
                return True

def isValidMove(move, hand, game, handlen):
    """Used to determine if a move by checking to see if the move is an integer, followed by and testing to see
    if the proposed move is within the length of the hand passed to this function. Finally, this function
    makes sure that the proposed card to be played is valid within the rules of the game (same rank)."""
    x = []
    for i in range(1, (handlen+2)):
        x.append(i)
  
    try:
        int(move)
    except:
        print('That is not a valid move (not int).\n')
        return False

    if int(move) not in x:
        print('That is not a valid move (not 1-' + str(handlen+1) + ').\n')
        return False

    if move != str(handlen+1):
        if game.topCard.rank == hand.cards[(int(move)-1)].rank:
            return True
        else:
            return False
    elif move == str(handlen+1):
        return True

def main():
    """This function is the main loop used for running the Class MauMau. The turns between Ai and human are
    represented by integers, and those integers are used in if statements to determine which player is currently
    taking their turn."""
    #turn = random.randrange(2) + 1 <-- A comment for now, so for testing the human always goes.
    turn = 0
    game = MauMau()
    name = game.getName()
    hand1 = hand(name)
    hand2 = hand("CPU")
    game.deck.deal([hand1], 5)
    game.deck.deal([hand2], 5)
    game.startCard()
  
    if turn == 1:
        print('The computer will go first, as decided at random.')
    else:
        print('You will move first, as decided at random.')
  
    while GAME_RUNNING:
        print ('///' + '\n' + '/// ' + str(game.topCard) + '\n' + '///' + '\n')
        print (hand1)

        if turn == 1:
            print ('The computer will now take its turn, hit enter to proceed.')
            Move = raw_input()
        else:
            print ('Choose a card to play (1-' + str(len(hand1.cards)) + ') or draw a card (' + str((len(hand1.cards)+1)) + ').')
            Move = raw_input()

            if isValidMove(Move, hand1, game, len(hand1.cards)):
                print('Valid move check passed.\n')

                if Move == str(len(hand1.cards)+1):
                    hand1.add(game.deck.draw())
            else:
                print('Valid move check failed.\n')
              

if __name__ == "__main__":
    main()


In order to use the stack of cards for the face up cards that were already played, I plan to implement a common stack. A stack would be a list or array that is used to grab values out of in the order of the last appended first. I created a test module (.py file) before actually using it in the game. I have yet to implement something similar, but the source for this test is here:

 #A test module for creating a stack.

run = True
stack = []

while run:
    print('Stack, Pop, end, or continue? (1, 2, 3, enter)')
    yesno = raw_input()

    if yesno == '1':
        print('Enter a value to stack')
        a = raw_input()
        stack.append(a)

    if yesno == '2':
        try:
            stack.pop()
            b = stack.pop()
            print(b)
        except:
            print('Stack is empty.\n')

    if yesno == '3':
        run = False


    
Development is moving at a decent pace. Most of the time I am debugging the code I have written, but I am learning a ton. I've become very much more familiar with object orientation through the use of all my classes, and this project is definitely helping to enhance my overall coding ability. It's really pretty exciting :)

Here is a link to a .exe of my current build: http://download503.mediafire.com/m5ydqyl9zzng/nyfzyamykzi/Cards.7z

Saturday, March 27, 2010

ActionScript 3 (Flash)

Well, I to be honest haven't taken a break from coding; I've been dabbling in ActionScript. I took a look at the Shootorials on Kongregate, and I realized how easy AS3 was to use. I honestly composed this game found in this tutorial (on page 1) in about 6 hours. I'm still contemplating spending my summer with C/C++, or ActionScript. Seeing how easy AS3 is to use, I may just spend my summer trying to develop a game in order to make some spare cash. On the other hand, I could spend my summer coding in C/C++ to get a bit of a headstart for DigiPen.


I found the website http://freeactionscript.com, and immediately fell in love with all of the sample code. Take a look here if you are at all interesting in coding flash games.


I've also been spending my time reading Rules of Play: Game Design Fundamentals in order to better understand what makes games fun to play. I plan to utilize this knowledge to finish my RPG which is being constructed as a StarCraft 1 map file.


So far, I plan to balance out the three of these projects during my summer, focusing mainly on finishing my RPG, and coding a decent AS3 game, all the while keeping brushed up on C/C++ with my collection of C/C++ books I have sitting on my computer.


If you have any suggestions, comments, or questions about the above links or ideas, please feel free to let me know by commenting!!

Thursday, February 25, 2010

Caeser Cipher

Now I have finished up the encryption chapter of this book. The entire idea behind the encryption is to shift each character of a string over by a certain amount. Whenever a character of a string exited the parameters of a-z or A-Z, you just add or subtract 26, thus "wrapping" your encryption around and back, so that letters are only encrypted into letters. This took me about 30 minutes total to create. Here is my source code:

import sys

def getMode():
    print('Do you want to encrypt decrypt, or brute force a message?')
    mode = raw_input().lower()
    if mode in 'encrypt decrypt e d brute b'.split():
        return mode
    else:
        print('You need to enter in one of the following: e, d, b')

def getMessage():
    print('Enter your message.')
    return raw_input()

def getKey():
    key = 0

    while True:
        print('Enter in a number for your key, 1-25.')
        key = int(raw_input())
        if (key >= 1 and key <= 25):
            return key

def getTranslatedMessage(key, mode, message):
    if mode[0] == 'd':
        key = -key

    translated = ''

    for symbol in message:
        if symbol.isalpha():
            num = ord(symbol)
            num += key

            if symbol.isupper():
                if num > ord('Z'):
                    num -= 26
                elif num < ord('A'):
                    num += 26
            elif symbol.islower():
                if num > ord('z'):
                    num -= 26
                elif num < ord('a'):
                    num += 26

            translated += chr(num)
        else:
            translated += symbol

    return translated

def playAgain():
    print('Do you wish to continue? (yes or no)')
    return raw_input().lower().startswith('y')

Again = True

while True:
    mode = getMode()
    message = getMessage()
    if mode != 'b':
        Key = getKey()
    print('Your translated text is:')
    if mode != 'b':
        print(getTranslatedMessage(Key, mode, message))
    else:
        for key in range(1, 26 + 1):
            print(key, getTranslatedMessage(key, 'decrypt', message))
    if not playAgain():
        sys.exit



The next chapter of this book is here. This chapter goes over the Reversi game. I won't be constructing a program from this chapter as it doesn't really introduce any new concepts. The Ai for the reversi game just runs through each possible move and chooses the one that results in the most points. I will be skipping over chapter 14, and chapter 15, and heading straight into chapter 16. Chapter 16 is going over graphics and animation; I'll be starting to use the Pygame engine.

Wednesday, February 24, 2010

Sonar

I have finished up creating a game that functions almost the same as the game in this chapter. The game I created is slightly different in how the code works in a few specific areas, but altogether the example code in this chapter, and mine, should be fairly similar. Here is my source code:

#Sonar

import random
import sys

def drawBoard(board):
    hline = '    '
    for i in range(1, 6):
        hline += (' ' * 9) + str(i)
  
    print(hline)
    print('   ' + ('0123456789' * 6))
    print('')
  
    for i in range(15):
        if i < 10:
            es = ' '
        else:
            es = ''
        print('%s%s %s %s' % (es, i, getRow(board, i), i))
          
    print('')
    print('   ' + ('0123456789' * 6))
    print(hline)

def getRow(board, row):
    boardc = ''
  
    for i in range(60):
        boardc += board[i][row]
    return boardc

def getNewBoard():
    board = []

    for x in range(60):
        board.append([])
        for y in range(15):
            if random.randint(0, 1) == 1:
                board[x].append('~')
            else:
                board[x].append('`')

    return board

def placeChests(chestAmount):
    chests = []
  
    for i in range(chestAmount):
        chests.append([random.randint(0, 59), random.randint(0, 14)])
    return chests

def isValidMove(x, y):
    if (x >= 0 and x <= 59 and y >= 0 and y <= 14):
        return True
    else:
        return False

def makeMove(board, chests, x, y):
    if not isValidMove(x, y):
        return False
  
    smalld = 100
    for cx, cy in chests:
        if (abs(cx - x) > abs(cy - y)):
            distance = abs(cx - x)
        else:
            distance = abs(cy - y)
          
        if distance < smalld:
            smalld = distance

    if smalld == 0:
        chests.remove([x, y])
        return 'You have found a treasure chest!'
    else:
        if smalld < 10:
            board[x][y] = str(smalld)
            return 'Treasure has been detected at a distance of %s' % (smalld)
        else:
            board[x][y] = '0'
            return 'Treasure is nowhere to be found within range.'

def enterPlayerMove():
    print('Enter in two integers (0-59, 0-14), otherwise type quit to exit the game.')

    while True:
        move = raw_input()
        if move.lower() == 'quit':
            sys.exit

        move = move.split()
        if len(move) == 2 and move[0].isdigit() and move[1].isdigit() and isValidMove(int(move[0]), int(move[1])):
            return [int(move[0]), int(move[1])]
      
        print('Enter in two integers (0-59, 1-15), otherwise type quit to exit the game.')

def playAgain():
    print('Do you want to play again? (yes or no)')
    return input().lower().startswith('y')

print('S O N A R !')

while True:
    sonarDevices = 16
    theBoard = getNewBoard()
    previousMoves = []
    playerMovec = []
    drawBoard(theBoard)
    print('Where do you want to place your sonar device?')
    secretChests = placeChests(3)

    while sonarDevices > 0:
      
        if sonarDevices > 1: extraSsonar = 's'
        else: extraSsonar = ''
        if len(secretChests) > 1: extraSchest = 's'
        else: extraSchest = ''
        print('You have %s sonar device%s left. %s treasure chest%s remaining.' % (sonarDevices, extraSsonar, len(secretChests), extraSchest))

        x, y = enterPlayerMove()
        playerMovec.append([x, y])
        result = makeMove(theBoard, secretChests, x, y)

        if sonarDevices < 16:
            if playerMovec[len(previousMoves)-1] == previousMoves[len(previousMoves)-1]:
                sonarDevices -= 1
            else:
                result = False
                drawBoard(theBoard)
                print('You have already placed a sonar device here!')
 
        previousMoves.append([x, y])

        if sonarDevices == 16:
            sonarDevices -= 1
        if result == False:
            continue
        else:
            if result == 'You have found a treasure chest!':
                for x, y in previousMoves:
                    makeMove(theBoard, secretChests, x, y)
            drawBoard(theBoard)
            print(result)
        if len(secretChests) == 0:
                print('You have found all the sunken treasure chests! Congratulations and good game!')
                break

    if sonarDevices == 0:
        print('We\'ve run out of sonar devices! Now we have to turn the ship around and head')
        print('for home with treasure chests still out there! Game over.')
        print('    The remaining chests were here:')
        for x, y in theChests:
            print('    %s, %s' % (x, y))

    if not playAgain():
        sys.exit()



A large difference between my code, and the example code, is the fact that I have support for allowing the player to suffer no negative penalty (a loss of one of the sonar beacons) for trying to place a sonar over an area that was placed during the previous turn. This honestly, took me forever to figure out how to do. I finally decided on creating two copies of all previous turns, and then use these two copies to see if the last values in the two lists equal one another, before the value in the second list had been appended. This way, I compared the value of entered coordinates to the value of previous coordinates, and didn't subtract from the overall sonar amount when this happened.


The math is simple, and the whole Cartesian Plane didn't really teach me anything new. Although, if you don't understand how the math in my makeMove() function doesn't work, you might need to read over this chapter again.


I plan to go other the thirteenth chapter, here, for my next post. This seems rather interesting, as it is about cryptology.


Let me know if you see something I could improve upon in this code. Hopefully my lack of commenting the code isn't detrimental, if it is let me know and I'll add in comments.

Tuesday, February 23, 2010

Bagels

The name of the game I have created for this post, is the game Bagels. Bagels is a game where the user guesses a three digit number, and the computer responds with "Fermi" for every digit that is in the right place that you guessed, and "Pico" for every digit that you guessed which is in the wrong place, but is still a digit in the secret number.

The concepts that this game goes over are hardcoding and nested loops, the chapter is this one: http://inventwithpython.com/chapter10.html.

A nested loop is simply a loop within a loop. This is useful for when you want to iterate something x times, for every time y appears, or something similar.

Hardcoding is a coding practice where the coder codes the program so that changing of simple variables requires changing lots of code. This program was programmed without hardcoding, where a couple constants (variables that do not change while the program is running, which are represented with caps, although, are actually just normal variables in Python) can be changed to change how the game is played.

import random

def getSecretNumbers(numDigits):

    numbers = list(range(10))
    random.shuffle(numbers)

    secretNum = ''
    for i in range(numDigits):
        secretNum += str(numbers[i])

    return secretNum

def getClues(guess, secretNum):

    if guess == secretNum:
        return 'You have guessed the correct number!'

    clue = []

    for i in range(len(guess)):
        if guess[i] == secretNum[i]:
            clue.append('Fermi')
        elif guess[i] in secretNum:
            clue.append('Pico')

    if len(clue) == 0:
        return 'Bagels'

    clue.sort()

    return ' '.join(clue)

def isOnlyDigits(num):

    if num == '':
        return False

    for i in num:
        if i not in '1 2 3 4 5 6 7 8 9'.split():
            return False

    return True

def playAgain():

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

NUMDIGITS = 3
MAXGUESS = 10

print('I am thinking of a %s-digit number. Try to guess what it is.' % (NUMDIGITS))
print('Here are some clues:')
print('When I say:    That means:')
print('  Pico         One digit is correct but in the wrong position.')
print('  Fermi        One digit is correct and in the right position.')
print('  Bagels       No digit is correct.')

while True:
    secretNum = getSecretNumbers(NUMDIGITS)
    print('I have thought up a number. You have %s guesses to get it.' % (MAXGUESS))

    numGuesses = 1
    while numGuesses <= MAXGUESS:
        guess = ''
        while len(guess) != NUMDIGITS or not isOnlyDigits(guess):
            print('Guess #%s: ' % (numGuesses))
            guess = raw_input()

        clue = getClues(guess, secretNum)
        print(clue)
        numGuesses += 1

        if guess == secretNum:
            break
        if numGuesses > MAXGUESS:
            print('You ran out of guesses. The answer was %s.' % (secretNum))

    if not playAgain():
        break


My code should be pretty much identicle to the code in the example program, as there aren't really any easier ways to code this program; this is a really rather cut and draw program to create.

This part of the program confused me for a moment:

def getSecretNumbers(numDigits):

    numbers = list(range(10))
    random.shuffle(numbers)


These lines create a list of numbers and then shuffle them. When I first saw these lines, the first one confused me: numbers = list(range(10)). I wasn't sure why the auther was changing the range() to list() inside of the range. Although, in the book it explained that if I wanted to store a list of integers, they needed to be converted from an iterator (value of range) into a list. This made sense, although the author didn't go into detail about exactly what the value of a range was. I assume it's just an amount of iterations to perform, which isn't what I would want if I wanted a list to store variables in.

I plan to run through chapter eleven of this book for my next post. This should be a relatively easy chapter, as it just goes through the basics of a Cartesian coordinate system.

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."

Starting up Python!

Well, sadly, I have no more time for C++, and I probably won't for a couple months. Things are getting really busy. Although, I have good news; I started a programming club at my highschool that meets once a week, where I teach them all how to code in Python. For the time being, my next posts will be about my learnings of Python.

I chose Python after doing a little bit of research; Python is a language that was created to allow coders to create code quickly and efficiently. Readability of Python was also a concern while developing the Python environment. As such, Python is great for readability and for quickly coding programs. Although Python is great at what it was intended to be used for, Python isn't made to create highly optimized code for intense programming (things like 3D rendering). Although, you can create visual programs that render in 3D, Python code can only be optimized so much.

I spent this weekend reading some of the chapters from this excellent book on programming games in Python, with the Pygame engine. Here is the book I am currently reading: http://inventwithpython.com/chapters/. I am currently on nine.

I am working with Python 2.6, as this has the most up-to-date third party support from things like Pygame and Py2Exe.


I recommend that anyone reading this check out the Pygame.org website, and sift around through the tutorials there, as well as check out the source code for some of the more simple programs that are on display there.

Over the course of chapter nine, I created a game of TicTacToe. The player can choose to play as X's or O's, and I have a functioning Ai.


At this point in time, I do have a working program, although, I created it while using the example code in the book as reference. As an exercise, my next post will be about me trying to create the entire program without reference code whatsoever.


Here is my source code for my program:

import random

def drawBoard(board):
    #This function draws the board whenever it is called. The board is represented as an array.
  
    print(' ' + board[7] + ' | ' + board[8] + ' | ' + board[9])
    print('   |   |')
    print('-----------')
    print('   |   |')
    print(' ' + board[4] + ' | ' + board[5] + ' | ' + board[6])
    print('   |   |')
    print('-----------')
    print('   |   |')
    print(' ' + board[1] + ' | ' + board[2] + ' | ' + board[3])
    print('   |   |')

def getPlayerLetter():
    #This function allows the human to choose whether they play as an X or O.

    letter = ''

    while not (letter == 'X' or letter == 'O'):
        print('What would you like to play as? X or O?')
        letter = raw_input().upper()

        if letter != 'X' and letter != 'O':
            print('You can only pick X or O to play as.')

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

def whoGoesFirst():
    #This function randomly picks which player goes first.

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

def getPlayerMove(board):
    #Captures the player's input on their desired move for their turn.

    move = ' '

    while move not in '1 2 3 4 5 6 7 8 9'.split() or not isSpaceFree(board, int(move)):
        print('What is your next move?')
        move = raw_input()
    return int(move)

def isSpaceFree(board, move):
    #Return true if the move is available on the board.
  
    return board[move] == ' '

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

def isWinner(bo, le):
    # Given a board and a player's letter, this function returns True if that player has won.
    # We use bo instead of board and le instead of letter so we don't have to type as much.
  
    return ((b[7] == l and b[8] == l and b[9] == l) or # across the top
    (b[4] == l and b[5] == l and b[6] == l) or # across the middle
    (b[1] == l and b[2] == l and b[3] == l) or # across the bottom
    (b[7] == l and b[4] == l and b[1] == l) or # down the left side
    (b[8] == l and b[5] == l and b[2] == l) or # down the middle
    (b[9] == l and b[6] == l and b[3] == l) or # down the right side
    (b[7] == l and b[5] == l and b[3] == l) or # diagonal
    (b[9] == l and b[5] == l and b[1] == l)) # diagonal

def isBoardFull(board):
    #Checks to see if the board is full or not.

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

    return True

def getComputerMove(board, computerLetter):
    #Determines how to react depending on which letter (X or O) the computer player has.

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

    # Check if the computer can win in the next move.
    for i in range(1, 10):
        copy = getBoardCopy(board)
        if isSpaceFree(copy, i):
            makeMove(copy, computerLetter, i)
            if isWinner(copy, computerLetter):
                return i

    # Check if the player could win on his next move, and block them.
    for i in range(1, 10):
        copy = getBoardCopy(board)
        if isSpaceFree(copy, i):
            makeMove(copy, playerLetter, i)
            if isWinner(copy, playerLetter):
                return i
          
    # Try to take one of the corners, if they are free.
    move = chooseRandomMoveFromList(board, [1, 3, 7, 9])
    if move != None:
        return move

    # Try to take the center, if it is free.
    if isSpaceFree(board, 5):
        return 5

    # Move on one of the sides.
    return chooseRandomMoveFromList(board, [2, 4, 6, 8])

def chooseRandomMoveFromList(board, movesList):
    # Returns a valid move from the passed list on the passed board.
    # Returns None if there is no valid move.
  
    possibleMoves = []
    for i in movesList:
        if isSpaceFree(board, i):
            possibleMoves.append(i)

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

def getBoardCopy(board):
    #This returns a copy of the actual board, used to simulate or test things.

    dupeBoard = []

    for i in board:
        dupeBoard.append(i)

    return dupeBoard

def playAgain():
    # This function returns True if the player wants to play again, otherwise it returns False.
  
    print('Do you want to play again? (yes or no)')
    return raw_input().lower().startswith('y')
      

while True:
    #Resets the board.
    theBoard = [' '] * 10
    playerLetter, computerLetter = getPlayerLetter()
    currentTurn = whoGoesFirst()
    print('The ' + currentTurn + ' player will go first.')

    gameIsPlaying = True

    while gameIsPlaying:
        if currentTurn == 'player':
            #Player's turn.

            drawBoard(theBoard)
            move = getPlayerMove(theBoard)
            makeMove(theBoard, playerLetter, move)

            if isWinner(theBoard, playerLetter):
                drawBoard(theBoard)
                print('You have won the game!')
                gameIsPlaying = False
            else:
                if isBoardFull(theBoard):
                    drawBoard(theBoard)
                    print('The game is a tie!')
                    break
                else:
                    currentTurn = 'computer'

        else:
            #Computer player's turn.
            move = getComputerMove(theBoard, computerLetter)
            makeMove(theBoard, computerLetter, move)

            if isWinner(theBoard, computerLetter):
                drawBoard(theBoard)
                print('The computer has beaten you! You lose.')
                gameIsPlaying = False
            else:
                if isBoardFull(theBoard):
                    drawBoard(theBoard)
                    print('The game is a tie!')
                    break
                else:
                    currentTurn = 'player'

    if not playAgain():
        break


As you might notice, this code is pretty darn similar to my reference code. As I mentioned, I will as my next project re-write this entire program without any reference.

I would say that the most interesting part of creating this program, was the Ai. The Ai really only cycles through a copy of the currently existing board, and searches for any winning moves from either player. If it finds one, it takes it. If not, it moves to one of the currently open corners, randomly. If all the corners are taken, the Ai moves into the middle space. If that is taken, it moves onto one of the four last spaces, at random. This algorithm is pretty darn simple, but it is effective at playing Tic Tac Toe.

Overall, I would say I gained a better understanding of Python syntax more than anything while writing this program.

I tried to include a source file, and my exe, although I'm currently having troubles uploading. I'll try to remember to modify this post with some links, in case anyone wanted to download the exe or the source. Edit: No, I will not be supplying the exe//source code this time ;P