2

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

Community
  • 1
  • 1

1 Answers1

1

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        
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
jottbe
  • 4,228
  • 1
  • 15
  • 31
  • Thanks for your help it works :D but it sayes that player O or player X wins is there anyway to make it so that it sayes the username wins? – needhelp786 Sep 01 '19 at 17:02
  • You could create a dictionary like `name_dict={'X': username1, 'O': username2}`. Then `print('Player {won} has won the game'.format(won=name_dict[won])` – jottbe Sep 01 '19 at 17:05
  • Could you please explain the check_win part in a easier way please thanks – needhelp786 Sep 01 '19 at 17:31
  • The idea of the logic is, that you have to check all rows and all columns as well as the diagonals. All of them consist of three entries. So I create a list with the start cell index (x and y of the first cell to be checked in the sequence) and the increment used to check the next cell (incr_y and incr_x). – jottbe Sep 01 '19 at 17:46
  • For the row-checks I start in cell (0, 0) for the first row, (1, 0) for the second row and (2, 0) for the third row and I increment the x but not the y so incr_x=1 and incr_y=0. so I compare (1,0) to (1,1) and (1,2). Likewise for the rows. E.g. for the second row I start with y=0, x=1 and apply incr_y=1 and incr_x=0 which results in comparing (0,1) with (1,1) and (2,1). For the first diagonal I start at x=0, y=0 and use incr_y=1, incr_x=1, so I compare (0,0) with (1,1) and (2,2). – jottbe Sep 01 '19 at 17:47
  • The other diagonal starts with x=3, y=0 and uses incr_x=-1 and incr_y=1, which results in comparing (0,2), (1,1) and (2,0). Hope that makes it clearer. – jottbe Sep 01 '19 at 17:47
  • You can do that as well with nested loops, but that results in a lot of if statements. – jottbe Sep 01 '19 at 17:48
  • Please see my edit, then you understand what I mean. The alternative version of this check is rather longish and contains redundant code. – jottbe Sep 01 '19 at 17:56
  • Thanks alot it makes alot more sense now :D – needhelp786 Sep 01 '19 at 18:07