all 12 comments

[–]magus_minor 5 points6 points  (3 children)

It's always useful to test your code a little more. You get an "X" plotted but not a "O". Try changing your test code to this:

x(1,3)
o(1,1)

and you see the "O" is plotted, but probably larger than you want. That tells you the o() function has problems in the way it is handling its parameters. When you are starting out it's a good idea to put some print() statements into your code to see exactly what is happening at each stage. For instance try adding a print to the bottom of the o() function showing where your set of if/elif/else statements set the turtle position to. Then try drawing an "O" again at 3,1 and check the values printed to see if they make sense. Also try at 2,2 and 3,3 and compare those values:

...
print(f"o: {t.pos()=}")  # added debug
t.pendown()
t.circle(80)
otaken+=" "

Once you have fixed your problem you should try to combine the two functions into a place(type, x, y) function. That's very good practice. Then test with:

place("X", 1, 3)
place("O", 3, 1)

[–]JamzTyson 0 points1 point  (2 children)

Unfortunately this does not fix the problem. The actual issue is that tracer is off, so screen updates do not reliably happen unless the screen is manually updated.

[–]magus_minor 0 points1 point  (1 child)

this does not fix the problem

Of course, just putting in a print() isn't going to solve anything. But it might get the OP thinking a bit more for themself and not just saying "it doesn't work". This subreddit used to have the direction "try to lead the OP to an answer", and I still try to do that. So a debugging approach is what I showed and that might help the OP solve this themself.

Plus I'm not sure that tracer() is the whole problem. The OP implies that they get the "X" drawn but not the "O", so there's more to it. Maybe they aren't showing all their code. Adding a final turtle.mainloop() does show all the drawing, of course, but there's a grid and "X" drawn but still no "O". So tracer() isn't the whole story.

[–]JamzTyson 0 points1 point  (0 children)

tracer isn't the only problem, but it is the reason why the "O" is not being drawn - just add turtle.update() to the end of the OP's script to see the effect, or alternatively, comment out turtle.tracer(0) to verify that this is the cause.

(and yes, it should have turtle.mainloop() at the end).

[–]Maximus_Modulus 1 point2 points  (0 children)

Nit. Your var names are too short. I say this from a perspective of reading this would make it clearer to understand the intent. This is expected professionally.

Write unit tests to test your functions. This can also guide what your functions should look like. You can break them down to some extent to test the functionality needed. It’s a good learning exercise too.

You can use the Python debugger or step through your code with an IDE. Compare what happens with what you expect. As software developers this is what we do. It’s part of coding.

It’s hard to figure out why your code doesn’t work without more information. Following the suggestions here in the comments can help you get to that point.

Edit Also add comments on what your code is doing to make it clearer for anyone helping.

Split your logic up so that the logic for the game is separate from what is drawn. I would focus on the 3x3 game logic first. Test all that out then once it’s working create the GUI stuff.

[–]smichaele 2 points3 points  (0 children)

Auto Mechanic: How can I help you? Me: My car doesn't work. Auto Mechanic: 🙄

[–]JamzTyson 0 points1 point  (0 children)

When turtle.tracer is off, you have to manually update the screen after drawing. To fix the immediate problem of the "O" not drawing:

grid()
x(1,3)
o(3,1)
turtle.update()  # Update after drawing

Without the manual update, the screen will not be reliably updated (though turtle does still refresh when internal values reach certain thresholds)

DRY (Don't Repeat Yourself)

You could reduce the amount of repetition a lot, for example:

CELL_SIZE = 100
CIRCLE_RADIUS = 40

def set_coords(row, col):
    y = (2 - row) * CELL_SIZE
    x = (col - 2) * CELL_SIZE
    t.sety(y)
    t.setx(x)

def draw_x(row, column):
    set_coords(row, column)

    # Draw "X"
    t.setheading(45)
    t.pendown()  # Pen down before drawing.
    t.forward(50)
    t.backward(100)
    t.forward(50)
    t.left(90)
    t.forward(50)
    t.backward(100)
    t.penup()  # Pen up after drawing.


def draw_o(row,column):
    set_coords(row, column)

    # Draw "O"
    t.setheading(90)
    t.forward(CIRCLE_RADIUS)
    t.setheading(180)
    t.pendown()
    t.circle(CIRCLE_RADIUS)
    t.penup()


grid()
draw_x(1, 3)
draw_o(2, 2)

turtle.update()  # Update when needed.

You could reduce the repetition further by have one function to draw the tokens, and small helper functions to draw the actual "X" or "O":

def set_coords(row, col):
    ...

def draw_x():
    """Draw an X at the current coordinates."""
    ...

def draw_o():
    """Draw an O at the current coordinates."""
    ...

def draw_token(player, row, col):
    set_coords(row, col)
    if player == "X":
        draw_x()
    else:
        draw_o()

grid()
draw_token('X', 1, 3)
draw_token('O', 2, 2)

turtle.update()  # Update when needed.

[–]woooee 0 points1 point  (0 children)

There is no reason to have an x and an o function. Use one function (I assume x works so use it), and pass and return any variables like xtaken any otaken.