3
\$\begingroup\$

This is my second attempt to make Tic Tac Toe in Python. It's a part of my learning. Tomorrow I'll work on some simple AI and better menu. What do you think about it? What changes do you suggest? Code:

class Field:
    val = " "
    pos = 0
    weight = 0

    def __init__(self, n):
        self.pos = n
        self.val = str(n)


def display_board(b):
    br = 1

    for i in b:
        if br >= 3:
            print("[", i.val, "]", end='\n')
            br = 0
        else:
            print("[", i.val, "]", end='')

        br += 1


def did_player_won(b, t):
    indexes = (
        (0, 1, 2), (3, 4, 5), (6, 7, 8),  # Rows
        (0, 3, 6), (1, 4, 7), (2, 5, 8),  # Cols
        (0, 4, 8), (2, 4, 6)              # Diag
    )

    for idx in indexes:
        if sum(b[i].weight for i in idx if b[i].val == t) == 15:
            return True


def get_move(b, turn):
    # Get user input while it's wrong
    while True:
        try:
            move = int(input("Player %s turn!\n>>>" % turn))
        # if input isn't an integer
        except ValueError:
            print("Wrong field!")
            continue
        else:
            if 0 < move < 10:
                for item in b:
                    if item.pos == move:
                        # Check if place is taken
                        if item.val == "X" or item.val == "O":
                            print("Field is taken!")
                            continue
                        else:
                            item.val = turn
                            item.weight = 5
                            print("Player %s took place %d" % (turn, move))
                            return True
            else:
                print("Incorrect field!")
                continue


def next_turn(currentturn):
    if currentturn == "X":
        return "O"
    else:
        return "X"


def reset_game():
    global board
    global turn
    global turns

    board = [Field(7), Field(8), Field(9),
             Field(4), Field(5), Field(6),
             Field(1), Field(2), Field(3)]

    turn = "X"
    turns = 0


board = [Field(7), Field(8), Field(9),
         Field(4), Field(5), Field(6),
         Field(1), Field(2), Field(3)]

turn = "X"
turns = 0
moveMessage = "Player (%s) move: " % turn

while True:
    display_board(board)

    if turns >= 9:
        display_board(board)
        print("Draw!")
        choice = input(">>>")

        if choice == "play":
            reset_game()
        else:
            break

    if get_move(board, turn):
        if did_player_won(board, turn):
            display_board(board)
            print("Player %s won! Congrats Comrade! It's OUR win!" % turn)
            choice = input(">>>")

            if choice == "play":
                reset_game()
            else:
                break
        else:
            turn = next_turn(turn)
            turns += 1
\$\endgroup\$
1
\$\begingroup\$

There is a general principle in programming that you want to avoid repeating yourself. For example, you define the starting game set up twice, once in the reset function and once in the main script. You could just have the function and then call it in the main script to set up the first game. This means if you ever want to change it, you just have to update one place.

When using try and catch, it is often easier to read if your logic for when stuff works goes in the try. That helps with reading the code in sequence as it is meant to be executed. This is a bit of a balancing act because you do want to be clear about what possible failures you are catching. In this case I would have padded it out.

My last suggestion is to consider using a class to hold the game state. This just helps keep things neat and tidy, especially when you look at doing AI. After all, your AI will need to imagine a bunch of game boards, and if you just have one board variable it will be hard.

The main loop that takes player input should be outside the class, but most other things should be in it.

\$\endgroup\$
  • \$\begingroup\$ Thanks for suggestions! I know the "don't repeat yourself rule" but I completely forgot about changing game setup. \$\endgroup\$ – TheAmazingRak Apr 20 '18 at 19:42
-2
\$\begingroup\$

Not a major suggestion, but just a style suggestion.
You could use indent spacing of 2 instead of 4.
It would make the code look nicer.

Example

4 spaces indenting
def hi(crazy):
    if crazy:
        print("...")
    else:
        print("Hi there")
2 spaces indenting
def hi(crazy):
  if crazy:
    print("...")
  else:
    print("Hi there")
\$\endgroup\$
  • 1
    \$\begingroup\$ PEP 8 python.org/dev/peps/pep-0008 recommends 4 spaces. While you are free to format your code at will you should not give misleading advice to others. \$\endgroup\$ – stefan Apr 20 '18 at 18:24
  • \$\begingroup\$ I accept your answer. But just FYI, according to Google developers, they have an internal guideline that dictate 2 space instead of 4 spaces. \$\endgroup\$ – Wong Jia Hau Apr 21 '18 at 2:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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