Search code examples
pythonpython-3.xtkintertic-tac-toeguizero

How to know who is winner in Tic Tac Toe in python using guizero


I have created a game called Tic Tac Toe. There are 2 players one of them are Xs one of them are Os all you have to do is get your symbol 3 in a row without the other person blocking you.

The Gui for the game looks like this:

enter image description here

Code:

from guizero import App, TextBox, PushButton, Text, info

empty = '   '
player = "X"
def clicked(z):
    button = buttonlist[int(z)] # Finds out which button was pressed
    global empty, player
    if button.text != empty:
        pass # If button already pushed do nothing
    else:
        # Marks button with user's go
        button.text = player
        # Switches players
        if player == "X":
            player = "O"
        else:
            player = "X"
    return

def instructions():
    info("Instructions","There are 2 players one of them are Xs one of them are Os. All you have to do is you have to try getting your symbol 3 times in a row without the other player blocking you")
def quit_game():
    app.destroy()
    
app = App(title="Tic Tac Toe", layout="grid", width=200, height=250)
buttonlist = [] # Empty list to contain a list of Buttons

Player1_label = Text(app, text="Player 1",  align="top", grid=[0, 0, 2 , 1])
Player1 = TextBox(app, text=empty, align="top", grid=[2, 0, 3, 1])# Player 1 enters the username

Player2_label = Text(app, text="Player 2",  align="top", grid=[0, 1, 2 , 1])
Player2 = TextBox(app, text=empty, align="top", grid=[2, 1, 3, 1])# Player 2 enters the username

Instructions = PushButton(app, command=instructions, text="Instructions", grid=[0, 5, 3, 1])# Display the instructions
                          
Quit = PushButton(app, command=quit_game ,text="Quit",grid=[10, 0, 3, 2])

# Create Buttons for game, in 3 rows of 3
z=0
for x in range(3):
    for y in range(2, 5):
        buttonlist.append(PushButton(app, text=empty, args=str(z), grid=[x, y], command=clicked))
        z+=1

app.display()

The problem I have is in displaying who is the winner. I know how to make a window appear to show the winner and the line that does this is:

app.info("Tic Tac Toe Winner","The winner is" + Player1.value)

But the problem that i am having is knowing who is the winner at the end of the game so and displaying that they are the winner also I have a feeling that to find out the winner its got something to do with buttonlist list that i made but i just cannot figure out how to do it so I would appriciate if some one could help me.

Thanks


Solution

  • Nice game. To work with the game board, I would suggest to create an internal data representation rather than working with the gui elements all the time according to common practice.

    The reason is, that if you work with the gui elements everywhere, it might be slower, as it is not the ideal data representation. You could not save the data with pickle etc and you also add a strong dependency to the gui framework you use. E.g. if you later plan to convert it to a web application etc. you will have a very hard time.

    So I just created a simple data representation in which I store the field in a two dimensional array.

    # check_win checks if there are three markings
    # in one row/column or diagonally
    def check_win():
        # the checks array is just a trick to
        # reduce the amount of code,
        # you could avoid it by just adding
        # one nested loop where the outer iterates 
        # over y and the inner over x
        # one with the outer on x and the inner on y
        # and then two others with just one loop
        # for the diagonals
        # the body of the inner loops would
        # esentially look the same as for the while
        # loop below
        checks= [(0, 0, 1, 1), (0, 2, 1, -1)]
        for i in range(3):
            checks.append((i, 0, 0, 1))
            checks.append((0, i, 1, 0))
        won= None
        for y, x, incr_y, incr_x in checks:
            player= field[y][x]
            found= player is not None
            while found and x < 3 and y < 3:
                found= (player == field[y][x])
                y+= incr_y
                x+= incr_x
            if found:
                won= player
                break
        return won
    
    # I also changed your clicked method a bit, so
    # it calles the check_win method
    # and also changed the signature, so
    # it gets the y and x coordinate of the
    # button which makes maintaining the
    # field array inside the clicked method
    # a bit simpler and also seems more natural
    # to me
    def clicked(y, x):
        button = buttonlist[y][x] # Finds out which button was pressed
        global empty, player
        if button.text != empty:
            pass # If button already pushed do nothing
        else:
            # Marks button with user's go
            button.text = player
            field[y][x] = player
            # Switches players
            if player == "X":
                player = "O"
            else:
                player = "X"
        won= check_win()
        if won is not None:
            print(f'Player {won} has won the game')
        return
    
    # now I initialize the field array
    # it will contain None for an untouched
    # cell and X/O if taken by the corresponding
    # user
    field= [[None] * 3 for i in range(3)]
    buttonlist= list()
    
    # to get the buttons to call the 
    # clicked function with the new
    # signature and also maintain the
    # buttons in a two dimensional array
    # I changed the order of your loops
    # and the args argument
    for y in range(0, 3):
        rowlist= list()
        buttonlist.append(rowlist)
        for x in range(3):
            rowlist.append(PushButton(app, text=empty, args=(y, x), grid=[x, y+2], command=clicked))
            z+=1
    
    app.display()
    
    
    # a plain vanilla version of check_win would look something like:
    
    def check_win():
        for start in range(3):
            x= start
            mark= field[0][x]
            for y in range(1, 3):
                if field[y][x] != mark:
                    # the sequence is not complete
                    mark= None
            if mark is not None:
                # player who set the mark won
                return mark
            y= start
            mark= field[y][0]
            for x in range(1, 3):
                if field[y][x] != mark:
                    # the sequence is not complete
                    mark= None
            if mark is not None:
                # player who set the mark won
                return mark
        mark= field[0][0]
        for x in range(1, 3):
            if field[y][x] != mark:
                # the sequence is not complete
                mark= None
        if mark is not None:
            # player who set the mark won
            return mark
        mark= field[0][3]
        for x in range(1, 3):
            if field[y][2-x] != mark:
                # the sequence is not complete
                mark= None
        if mark is not None:
            # player who set the mark won
            return mark
        return None