#๐ Tic Tac Toe not Tic Tac Toe-ing
153 messages ยท Page 1 of 1 (latest)
@devout sierra
Remember to:
- Ask your Python question, not if you can ask or if there's an expert who can help.
- Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
- Explain what you expect to happen and what actually happens.
:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.
https://paste.pythondiscord.com/JGWQ
here's my code
no matter what i try, i can't seem to get it to recognise a draw
please help :((
what is happening, and what do you expect to happen
i'm expecting line 109's condition to evaluate to True when all cells are filled and set outcome to 'Draw' and then loop back and reach line 99's elif to print out that it's been a draw and stop asking the user for any more input
it's a tic tac toe game where user's input goes against random input from the computer to fill up gameBoard 's nested arrays [[U0,U1,U2], [M0,M1,M2]...]
i'm sorry if it's a bit of a mess and painful to read. i'm a beginner
why did you use UMD?
does it print "Oh no, it's a draw!"?
but it doesn't
it dont :((
i tried popping it into win_listener() but that was making things worse
i tested a bunch, whenever i run into a draw it's stuck on an infinite loop
i thought i'd do
U = up, M = mid, D = down
your win_listener seems to update outcome, and it doesn't know about draws
def win_listener():
global matchfin
matchfin = False
#diagonals
if gameBoard[0][0] == gameBoard[1][1] == gameBoard[2][2] != ' ':
matchfin = True
return [True, matchfin] if gameBoard[1][1] == 'X' else [False, matchfin]
elif gameBoard[0][2] == gameBoard[1][1] == gameBoard[2][0] != ' ':
matchfin = True
return [True, matchfin] if gameBoard[1][1] == 'X' else [False, matchfin]
for index, i in enumerate(gameBoard):
#row
if i.count(i[0]) == 3 and i[0] != ' ':
matchfin = True
return [True, matchfin] if i[0] == 'X' else [False, matchfin]
#columns
if gameBoard[0][index] == gameBoard[1][index] == gameBoard[2][index] != ' ':
matchfin = True
return [True, matchfin] if gameBoard[0][index] == 'X' else [False, matchfin]
if not any(cell == ' ' for row in gameBoard for cell in row): #since user plays last, check if all cells used up to avoid infinite robot loop
return 'Draw'
Hey @devout sierra!
It looks like you're trying to paste code into this channel.
Discord has support for Markdown, which allows you to post code with full syntax highlighting. Please use these whenever you paste code, as this helps improve the legibility and makes it easier for us to help you.
To do this, use the following method:
```py
print('Hello, world!')
```
This will result in the following:
print('Hello, world!')```
You can **edit your original message** to correct your code block.
tried popping the condition into win_listener(), it's still stuck in an infinite loop when we draw
i've updated the code in the link
global matchfin
matchfin = False
#diagonals
if gameBoard[0][0] == gameBoard[1][1] == gameBoard[2][2] != ' ':
matchfin = True
return [True, matchfin] if gameBoard[1][1] == 'X' else [False, matchfin]
elif gameBoard[0][2] == gameBoard[1][1] == gameBoard[2][0] != ' ':
matchfin = True
return [True, matchfin] if gameBoard[1][1] == 'X' else [False, matchfin]
for index, i in enumerate(gameBoard):
#row
if i.count(i[0]) == 3 and i[0] != ' ':
matchfin = True
return [True, matchfin] if i[0] == 'X' else [False, matchfin]
#columns
if gameBoard[0][index] == gameBoard[1][index] == gameBoard[2][index] != ' ':
matchfin = True
return [True, matchfin] if gameBoard[0][index] == 'X' else [False, matchfin]
if not any(cell == ' ' for row in gameBoard for cell in row): #since user plays last, check if all cells used up to avoid infinite robot loop
return 'Draw'
sorry
this is win_listener() after putting the Draw condition in
that's surprising to me. i made the same change and it seems to work
that's odd. i'm using vs in case that's relevant
that wouldn't change things
oh, that is not the kind of infinite loop i see
did you change anything else after sending your original code snippet
should i revert the code in the link to what it was previously?
if you want
i only removed the condition that was under handle_input() for user at line 111
and placed it into win_listener()
okay i'll do that now
yeah
only add the condition to win_listener
that should be the only change necessary
otherwise your bot will loop forever
because there are no moves for it to make
yea i thought so, which was why i popped it right before so it'd continue and go back up
handle_input(user_input, 'User') #user sends choice
if not any(cell == ' ' for row in gameBoard for cell in row): #since user plays last, check if all cells used up to avoid infinite robot loop
outcome = 'Draw'
continue
get_n_check_robot_input()
handle_input(robot_input, 'Robot') #robot sends choice
``` could it be a scope issue with outcome being assigned 'Draw' and trying to catch the value on the next iteration?
Hey @devout sierra!
It looks like you pasted Python code without syntax highlighting.
Please use syntax highlighting to improve the legibility of your code and make it easier for us to help you.
To do this, use the following method:
```py
print('Hello, world!')
```
This will result in the following:
print('Hello, world!')```
You can **edit your original message** to correct your code block.
handle_input(user_input, 'User') #user sends choice
if not any(cell == ' ' for row in gameBoard for cell in row): #since user plays last, check if all cells used up to avoid infinite robot loop
outcome = 'Draw'
continue
get_n_check_robot_input()
handle_input(robot_input, 'Robot') #robot sends choice
did you make the change to win_listener?
could it be a scope issue on the outcome being assigned 'Draw' and trying to fetch it later on the next iteration?
did you make the change to win_listener, as suggested?
i'm not sure what to put in win_listener.
you had it already
so the condition should be in both the win_listener() func and above the robot's get_input right?
yes -- the only change is to add the condition to win_listener
the reason is that you're overwriting the value of outcome with the return value of win_listener, but you don't check for draws inside of win_listener
checking for draw doesn't suffice though.
in what way
awesome!
now the robot can still win when the player did.
i could just switch out the condition above the function getting robot's input to something that 'continue's if the list is full yes?
i don't understand. how would this happen?
yeah now that you've fixed the bug, you'd probably want to refactor your code so you're not checking the same condition in two places
you're only checking for a winner after the robot did its move.
but the robot can't do it's move
there's a continue under the condition
so if the list is full, i am reiterating
the robot can't do a move when the list is full.
but it can do a move when the player has 3 in a row.
from what i've tested i haven't come across that issue.. let me try a few more real quick
no it seems to work just fine
ideally instead of if not any(cell == ' ' for row in gameBoard for cell in row), you would check win_listener()
inside your main while loop
yea was thinking of doing something like that but my brain is short circuiting
yes, you need to call win_listener after every move, not only after the robot's move.
fwiw jenna, i don't see this scenario happening
ah nevermind
the bot overwrote the value at the top
i think jenna's scenario would happen only if user scores a XXX and on the exact next move so does the bot?
here i got it to happen
ah shitsticks
i'll rewrite this whole bugger tomorrow. i think my brain is just on power saving mode today
@manic ivy i really appreciate you, if possible could you give me a little feedback on how my code looks?
readability wise
how long have you been learning python
about 2 months
tictactoe is one really difficult problem readability wise
if you're in vscode, you should install the "ruff" extension
it'll help with formatting, and a bunch of lints
then also enable format on save
yea i wasn't expecting it to be this difficult. i really underestimated it
ctrl+, > text editor > formatting > format on save
oh god bless, i'll def add it
are you familiar with classes?
i do have prettier though, could the formatting of the two clash somehow?
i am but wasn't confident enough to use them in this project
you'd want to avoid the global keyword basically always
instead, you can have your state stored in an instance of a class
potentially. you should not use prettier to format python, though
also i confuse python classes with javascript's so that's gonna take a bit of getting used to ;((
oh gotcha
your win_listener function is pretty complex. it's actually kinda hard for me to see at a glance what you're trying to do
the names of the variables ("matchfin", for index, i) make it kinda hard
yea i'm sorry for putting you through that. it even takes me a minute of reading to realise what i was doing
it's not really something to apologize for
it's just code
the return value could be made simpler
i'm kinda confused why it returns an array of booleans
why not return a string that is either "draw", "user", "robot", or "none"
also it abuses ternaries. you don't need ternaries here -- you can just use an if statement
matchfin is a global, so no need to return it
i'm not sure what i was thinking but i guess it was supposed to be [whether the user won, whether the match had ended], not sure why i wanted 'whether the match had ended' but by the time i noticed it was redundant my brain was a bit fried and i based too many things around it
i think i'll do exactly this tomorrow
more nitpicky, but get_n_check_robot_input shouldn't be able to get into an infinite loop
oh god yea.. yikes
instead of a while loop that randomly picks any position, you could just have it random.choice(filtered list of keys where positions[robot_input] == " ")
maybe remove positions or gameBoard. you only need one.
that would crash if the array were empty, rather than get into an infinite loop. which is 10x easier to debug
gotcha
this has been so helpful, i can't thank you two enough @fallow canyon @manic ivy
i'll remake this soon, fingers crossed i don't have to induce any more headaches because of this :'))
the other big thing would be, you don't need to check the entire board after a user makes a play
you only need to check the row and column (and potentially diagonal) in which the move was played
ehh nevermind. i was looking at some old code and thought that would actually be easier to implement
but you would need to pass the position then.
sure, that's just a part of making the change
but actually that optimization is probably more annoying to implement
but if im doing this again from scratch i'd imagine it shouldn't be too hard to do
also is this chat going to stick around or will it get automatically deleted?
actually i'll just screenshot it all just in case lol
i think this chat will exist as long as the server.
so you can just copy the link to the chat. but a screenshot won't hurt.
https://discord.com/channels/267624335836053506/1241926097369829519
got iti
and do definitely install ruff. it resolved a bunch of style nits i had
for sure
other style nits, instead of (choice[0] == "U" or choice[0] == "M" or choice[0] == "D") you can just do choice[0] in "UMD". int(choice[1]) in list(range(3)) => int(choice[1]) in range(3)
ahahahaha omg wait now i feel a little brain dead
currently you could even do choice in positions.
no idea why i didn't think of that
that comes with time. finding all the ways to make things easier.
yea man. i'm finally not freaking out before starting a project so hopefully i can get there soon ;')
This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.
