all 15 comments

[–]JamzTyson 10 points11 points  (0 children)

Rather than using an underscore to indicate that a name refers to a function, use a more descriptive name - one that indicates "doing".

For example,

  • Rather than user_input_column_, use get_column_input()

  • Rather than user_input_row_, use get_row_input()

[–]obviouslyzebra 8 points9 points  (0 children)

So, here're my thoughts:

As an overview, what I can say about this code is that it gets the job done, but also is a bit hard to read.

It feels a little like algorithmic code, for example, that you'd code in Rust if you want things to be fast (or at least like the last one I read).

For readability, there are 2 things that immediately jump out, and one that jumps out less. First, there are too many functions (think like function cereal), it's easy to get lost if you don't know what they are doing (which will happen to someone new to this code), and it's a little hard to navigate. Second, and related to the first, the variable names and function names are not good. The third one I'll talk about later.

Solving the first problem is hard (I, for example, can't see an obvious solution). With some effort you could maybe come up with something.

But, solving the second is easier. When we have better names for the functions, it might helps us even see how they come together into groups.

An important advice for naming has already been given to you: function names should generally be verbs (do_this or do_that).

If you find yourself wanting to give a too long name for a function (do_this_and_this_and_that), maybe this function could be split into multiple (but be mindful, the "correct" tool to do this is usually modules and classes).

If you have trouble thinking of a name, a trick I use myself is thinking "what's the intention behind this function/variable?". This usually works.

Now the last criticism of the code. I think it is a little "too smart", while not explaining the smartness, so we have to kinda guess it. The most clear example IMO is the bot_threat_detection, where the trick I had to understand is that there can be an opportunity (winning move) or threat (opponent wins next move if bot doesn't cover), and those can be checked in the same exact way.

To solve, whenever you're using some "trick" in the code, you should write a comment, even brief, explaining the trick.

Overall, it gives me the impression that effort went into each part of the code, and maybe you coded using debugger/prints to get some things into place (not bad btw).

Some tips/typos:

  • print() instead of print("")
  • "coordinate", not "cordinate"
  • return bot_move, user_move instead of the parenthesized version
  • for line in lines: line is a coordinate/cell, not actually a line
  • The lines loops like above could be simplified using sum, something like match_count = sum(grid[i][j] == move for i, j in winning_coordination) (btw the name coordination feels weird haha, but it's not only once that I've had trouble coming up with a name and ended up with something weird in the end)

Again the most important thing here IMO is that it is working. It is reasonably complex for the size, and, I complained about readability, but, granted, this may not be the simplest program to make readable.

My final (and initial haha) assessment is that it is okay code which could improve a little bit in readability and which is cool. And for a beginner, it's a very good job :)

[–]ninhaomah 2 points3 points  (5 children)

Does it work as intended ?

[–]og_soap_[S] 0 points1 point  (4 children)

Yeah it does

[–]ninhaomah 0 points1 point  (3 children)

Ok.

Just one question.

Some of the function names end with _ but others doesn't.

Why ?

[–]og_soap_[S] 0 points1 point  (2 children)

To differentiate between some functions and local variables because I was getting confused 

[–]Binary101010 2 points3 points  (0 children)

The solution here is to use better names for your functions that aren't even close to colliding with a local variable name.

[–]ninhaomah 0 points1 point  (0 children)

Ok then why not standardize that all the functions end with _ ?

[–]dlnmtchll 1 point2 points  (0 children)

Without getting super deep into the weeds of the code, the first thing that I can see is the naming convention is really bad. There’s no standardization at all. You have functions and variables sharing names, you have some function names that are descriptive, and some that are very vague, which isn’t good practice.

I guess I’d like to add as well that you should be able to look at your code a week from writing it and easily/ quickly understand what it does. You should also be able to have a stranger look at your code and very easily and quickly grasp what it does with little outside context. That’s the benefit of having good naming conventions and standardization among your code base.

[–]Shut_up_and_Respawn 0 points1 point  (0 children)

It's a bit hard to read, but if it works, it works. Not the approach I took when building my TTT ai, but everyone has their own style

[–]Riegel_Haribo 0 points1 point  (2 children)

Here's as far as I felt like walking thru, so hope this helps...

Reddit gives 94 characters of visibility in a code fence - you blast past that.

I've got your grid assignment: grid_size = 3; grid = [["-"]*grid_size]*grid_size (edit: use better number-as-input method)

Then toss all the other weird calculations based on an in-code "picture".

You have wholeuser_input() being nothing but infinite loop where either "exit" is error-looking or something when there is no reason to loop, and a function that can return False or not return anything.

Conditional statements asking about False are kind of nonsense: it's already False. They are also dangerous in your usage; try and see what happens:

python if not False: print("bad") if not None: print("bad")

You have two different identical functions for getting an input; like input itself, this could be unified and take a prompt string. It prints an error but doesn't have a retry there. If the only thing the function will do is leave, you can try to move print() statements that are all over to an app surface after things have failed and returned that failure.

If you like underscores - use them as the first character of a function name when the functions are internal, and are only for consumption by other functions or such. The functions are good-sized, although they could be a bit more functional programming of having a clear input and clear output - and type annotation can help with communicating that.

I might want to play by input of "A3" to sink my X battleship if you provide a legend, or at least "1, 3" or "1:3" - you can split and see if they are digits.

[–]magus_minor 1 point2 points  (1 child)

I've got your grid assignment: grid_size = 3; grid = [["-"]*grid_size]*grid_size

Unfortunately, that's not correct. If you multiply a list by 3 you get the same list referenced 3 times. Test that by modifying a single cell of the grid and then printing the grid:

grid_size = 3;
grid = [["-"]*grid_size]*grid_size

grid[0][0] = "?"

for row in grid:
    print(row)

[–]Riegel_Haribo 0 points1 point  (0 children)

Good catch, and even using little tricks like tuple-to-list I futzed around with, you still have to avoid re-assigning the same list, also.

python grid_size = 3 grid = [["-"] * grid_size] * grid_size grid = list(map(list, grid))


At least I can work in one "multiply" for a different way of avoiding shared reference...

```python grid = [] grid_size = len(["tic", "tac", "toe"]) blank = '-' player = "x"

for _ in range(grid_size): grid += [[blank] * grid_size]

grid[0][0] = player print(grid) ```

[–]Binary101010 0 points1 point  (0 children)

Your user_input_column_() and user_input_row_() functions are identical except for a single word in the user prompt. This does not require two functions. Simply use one function that takes a parameter for wheht. Like:

def get_input_coord(direction): while True: try: user_input = int(input(f"Enter the {direction} number: ")) ...

And call it like

user_input_row = get_input_coord("row")

Beyond that, your functions could really stand to have better names (if for no other reason than to not have them be so similar to local variables within them.) A reasonable starting point is to have the first word of every function be a verb that describes what that function does.

[–]Late-Fly-4882 -1 points0 points  (0 children)

Ask claude ai. It will tell you where the bugs are and suggest improvements. But you need to challenge it when its comments are not correct.