6
\$\begingroup\$

I've been coding since about one year in JS now, and started with Python this week. I've created this TicTacToe game to get familiar with Python and would like to know what you think about this code, if it seems unusual in some way, or you'd have done something different, etc.

class TicTacToe(object):

    # Init
    def __init__(self, rows, columns, nInARow):
        # Create Field
        self.field = [[None for y in range(rows)] for x in range(columns)]

        #Set Variables
        self.currentPlayer = "X"
        self.nInARow = nInARow
        self.playing = True

        print ("A %sx%s TicTacToe game has been created\nYou need %s in a row to win!\nGood luck, have Fun!\n\n" % (rows, columns, nInARow))

    #Updates the game
    def update(self):
        print("Player %s's turn" % (self.currentPlayer))

        self.renderField()
        self.getInput()
        if self.checkWin():
            print ("\nPlayer %s won the game!" % (self.currentPlayer))
            self.renderField()
            self.playing = False

        #Update current Player
        self.currentPlayer = "X" if self.currentPlayer == "O" else "O"

    #Returns True if game is finished
    def gameEnded(self):
        return (not self.playing)

    #Renders the field to the console
    def renderField(self):

        #Loop through the field
        for y in range (len(self.field)):
            line = ""
            for x in range (len(self.field[y])):
                #Add Value of field or "_" (or "" if last row)
                line += self.field[y][x] if self.field[y][x] else ("_" if y < len(self.field)-1 else " ")
                if (x < len(self.field[y]) - 1):
                    line += "|"

            print (line)

    #Gets user input and add X | 0 to the selected box
    def getInput(self):
        xCoord = -1
        yCoord = -1
        #Loop until an empty field is chosen
        while ( (xCoord < 0 or xCoord > len(self.field[0]) - 1) or 
                (yCoord < 0 or yCoord > len(self.field) - 1) or
                (self.field[yCoord][xCoord] != None)):
            xCoord = int(input("Please enter the x-coordinate (1 to %s): " % (len(self.field[0])))) - 1
            yCoord = int(input("Please enter the y-coordinate (1 to %s): " % (len(self.field)))) -1
            print()

        #Set box
        self.field[yCoord][xCoord] = self.currentPlayer

    #Checks if the current Player won the game (n in a row)
    def checkWin(self):
        #Search for n in a row

        #Loop for boxes with the curPlayer-symbol
        #Then check if there are n in a row in several directions
        #
        for y in range (len(self.field)):
            for x in range (len(self.field[y])):
                if self.field[y][x] == self.currentPlayer:
                    #Box with curPlayer-symbol found

                    #Directions in which to check n in a row
                    #[x,y]
                    directions = [
                        [1,0],
                        [0,1],
                        [1,1],
                        [-1,1]
                    ]

                    #Check for each direction
                    for vec in directions:
                        #Check if (n-1) boxes are available in that direction
                        if (len(self.field[y]) - x >= self.nInARow*vec[0]       #(n-1) boxes to the right"""
                        and len(self.field) - y >= self.nInARow*vec[1]          #(n-1) boxes to the bottom"""
                        and (vec[0] >= 0 or x >= self.nInARow - 1)              #(n-1) boxes to the left"""
                        and (vec[1] >= 0 or y >= self.nInARow - 1)):            #(n-1) boxes to the top"""
                            for z in range (1, self.nInARow):
                                if self.field[y+z*vec[1]][x+z*vec[0]] != self.currentPlayer:
                                    break
                                elif z == self.nInARow - 1:
                                    return True


# Create a 3x3 game where you need 3 in row to win
game = TicTacToe(3,3,3)
while not game.gameEnded():
    game.update()
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Style

First, a style check. Python has an official style-guide, PEP8, which programmers are encouraged to adhere to. It recommends using lower_case for variables and functions (and PascalCase for classes, which you did use).

In addition, Python has docstrings, defined in PEP257, which allow adding a string describing a class or function. These are usually of the form

def f(a, b):
    """Add `a` and `b`"""
    return a + b

or, if you need multiple lines:

def g(*args):
    """
    Sum all `args`

    Randomly does something else.
    """
    if random.random() > 0.1:
        return sum(args)
    raise RuntimeError("Bad luck")

In Python we try to have as few unnecessary things as possible. One of these things are redundant parenthesis. The if keyword (and similar the while and for keywords) don't need parenthesis around their arguments (and neither did the print keyword in Python 2.x, but since it is a function in Python 3.x you can add them for compatibility).

You should always guard the execution of your code with a if __name__ == '__main__': guard. This allows you to simply import all your functions from another script without actually starting the game.


Code

to be continued...

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.