Coding Help With Coding A Tic Tac Toe Game

Thanks for all the feedback! After reading it, I do have a few
questions.

  1. Is there a website or library somewhere that shows me the built-in importable functions? The ones I know of now are only because I googled how to do specific things, but it would be cool to be able to know what my options are before hand.

Googling to do things isn’t a great way to find things unless you’re at
a loss. Decide how you want to do something, then look for the tools to
do it that way. Not always feasible, but more directed when it works.

A list of the functions is, of course, in the index: Index — Python 3.12.1 documentation
but you’re better browsing the supplied modules by topic: Python Module Index — Python 3.12.1 documentation
Pick the likely one to help your problem and have a read.

I also suggest you visit Doug Hellmann’s Python Module of the Week
blogs: Python 3 Module of the Week — PyMOTW 3 which will run through various modules with
explainations.

  1. Can you explain, and provide simple examples if possible, the instances in which situations the snake case, camel case, and pascal case? Initially I thought they were interchangable, which technically they are, but I want to make my code easy for others to follow.

A lot of the Python coding style starts with PEP 8: PEP 8 – Style Guide for Python Code | peps.python.org
which is actually for the standard library code, but many people use it
for their own code, or as the basis for their preferred style. Also read
this: https://pep8.org/

  1. Can you help me understand how the return function works?

Return’s a statement. It exits the current function, optionally with a
value. If you do not supply a value, a None will be provided for you. So
a function which doesn’t “return a value” eg is “does something” like
clearing the display, actually returns None.

  1. In that assign_stats function, I notice you formatted it in a way that took up several lines. Can you explain how the syntax for that works? It’s something I don’t recall seeing anywhere before and would love to learn more about it.

Python expressions basicly live on one line unless you have an unclosed
bracket. So I opened a bracket and closed it later. That leaves you free
to format the stuff inside for readability.

Cheers,
Cameron Simpson cs@cskk.id.au

Hey, sorry for the delay—I had a reply drafted yesterday, but never got the chance to send it and got busy with several other projects.

@cameron covered pretty much all the most important bits in great detail, of course, so I won’t repeat his excellent advice. Instead, I’ll just add and expand upon some more general, broader takeaways, as well as some extra particular items:

Here you’re confusing two different meanings of difficulty: you first define it as a function, then declare a difficulty variable inside that function. This is potentially very confusing to use the same name in the same scope in two different ways, and also prevents you from calling the function inside itself. It is a general rule that functions and methods should be named as imperative verbs, and variables (and classes) named with nouns, matching their purpose and making them easy to tell apart at a glance, so you might want to rename the function something like set_difficulty.

Instead of documenting a function in a simple code comment that you have to scroll through the source to find, you can use a docstring, like this:

def remaining_doors(door, size):
    """Set all doors as safe."""
    while door <= size:
    ...

This is just a string with three """s before and after, in the next line after the function, method, or class definition, and expressed as a complete sentence in the imperative, that describes the function (it can be as long as needed, but at least one line is good to start). Not only is it visible in the code, but it shows up when you run help(remaining_doors), and is used by documentation generators, IDE help systems and much more.

Instead of putting your code in the top level of the module, place all top-level code in a main() function (or better yet, with another layer of indirection, in a play_game() function which you call in a main() function, which is then called inside an if __name__ == "__main__" block (see my previous commented example code for more details and an example). This means if you import your code as a module, your game won’t run and you can use the functions, classes and constants you define there freely. This gets much more important when you want to make more than a few hundred line script and split your program up into multiple modules, or when you want to test it, re-use it, etc.

There’s a few issues to note here:

  • As Cameron mentioned, its considered pretty “rude” to clear the user’s terminal; in my case, I’ll often have thousands of lines of scrollback that I refer to frequently; I’d be pretty upset if that was indiscriminately wiped by running a simple Python script.
  • If you need to clear the line you just printed, you can do so by printing "'\x1b[2K'", and move the cursor up to clear multiple lines, you can use the cursor up char '\x1b[1A'.
  • Calling out to the system is rather inefficient, taking several orders of magnitude more time than just printing backspace chars.
  • clear will not work on Windows; cls is needed instead.
  • Finally, subprocess.call has been superceded by subprocess.run in all modern, currently supported versions of Python (>=3.5) which offers a cleaner interface, is better and more powerful.

To re-iterate a general point that @cameron made in the specific several times, it should almost always be possible to avoid the use of global, and you pretty much always should do so, since once your program gets large it can make your logic much harder to understand, debug and refactor—instead of just the variables passed into your function in a defined way, you have others floating around that can be modified anywhere at any time, I can hardly think of any time once I got to programming seriously where I actually needed one. In general, if you’re using globals, its almost always a sign that its time to rethink the structure or strategy of your program.

@cameron provided some specific examples for how to do so here; in general, you’ll want to replace global objects with arguments passed to the function, or a class that either bundles them together and gets passed around, or one that has the data as attributes and the functions as methods. Typically, most non-trivial games are based around classes and object-oriented paradigms, since typically they are a natural fit for the types of interactions games have—for example, a player object that has certain stats (health, attack, defense, lives, etc) and can move, attack, save, quit, die, etc., enemy objects that have their own stats and methods, doors that the player can .open() and store where they want to go, etc. While it does take a bit of learning, it can greatly simplify the logic once you’re used to it.

Like global, in almost all cases where you see code that repeats very similarly again and again, there’s a better way to write it to avoid all that repetition. Not only is it more work to write, but it is more complex to read and follow, increases the chance of bugs (as you saw in your game above) and takes much more effort to make changes. This even has a name—“code smell”. Think about some of the strategies @cameron and I have mentioned and demonstrated in how to simplify these sorts of long if-else chains, and other code smells, when you come across them.

I’ve noticed your indentation is rather wonky in a number of places. The levels don’t at all match with the actual true nesting level of the code, making it confusing to read and follow and very easy to hide bugs (which you saw happen above on your previous code). Look carefully through your code and make sure that indents are four spaces greater than the block ending in : that starts them.

In addition to a namedtuple, if you’re using Python 3.7+ you could also consider a dataclass. This is often much cleaner than a namedtuple (especially internally…namedtuple is a real hack), considerably more powerful and flexible, has a much nicer syntax, and follows the normal class semantics; the only real downside is that it isn’t a drop-in replacement for a tuple (but is also an upside if you don’t want to accidentally use it like one). There’s also typing.namedtuple in Python 3.6+ that works like namedtuple but with cleaner definition semantics, and of course just regular Python classes, if you want your data objects to be able to take actions.

To expand a bit on some related points, technically speaking, “builtin” and “importable” functions are two different things—the builtin functions are those that can be accessed without importing them, and are all listed in the docs, while the importable modules and their functions, classes, methods etc. that come with Python are called Python’s standard library (as in standard library modules, standard library functions, etc), and you can find the docs under the appropriate standard library module you imported.

Also, you can list all the attributes (functions, methods, values, classes, etc) of an object, including a module, using the dir() function (e.g. import os; dir(os)), and get help on one with the help() function. There are orders of magntiude more third-party modules you can find on PyPI, each with their own docs that you can search for.

As @cameron mentioned, the PEP 8 style guide is the widely used and accepted standard in Python, and is what essentially all common code checkers, linters and formatters enforce. To summarize:

  • PascalCase for class names (class StatsAssignment)
  • UPPER_SNAKE_CASE for (typically module or class level) constants (SIZE = 10)
  • lowercase (or if necessary, snake_case) for module names (import random)
  • snake_case for everything else: variables (door = door + 1), function, method and parameter names (def player_turn(size)), attribute names, etc.

camelCase is not used in idiomatic Python style, except for consistency with projects that wrap other languages that have that convention, or old legacy modules.

Welcome!

I’m not sure how the code you presented has anything at all to do with a Tic Tac Toe game, the stated topic of this thread.

Further, its not clear from the information provided what the specific task your code is intended to accomplish, what your input nor output data is supposed to look like, what the actual behavior or error you are experiencing is, nor even what your actual code looks like (since it isn’t properly formatted as code, which strips indents and mangles other critical syntax for interpreting it). As such, we can only guess blindly at what your problem might be, and at least briefly scanning the code as-is, I didn’t spot anything obvious, which leads me to believe the problem may be due to something in the information that hasn’t been provided—input, output, error message, indentation, other details, etc.

As such, in order to get useful help, I suggest you create a new one with the following:

  • An appropriate, descriptive title making clear the specific issue you are having that you are looking for help on
  • A description/problem statement/specification of exactly what your code is expected to do
  • Example(s) of expected input and output of your code (in particular, a minimal but self-contained snippet of the sgc.tab file contents, and exactly what you expect
  • Stating specifically the problem(s) you are experiencing: if you are getting an error message, the full message and traceback, or if you are not getting the desired output or other behavior, describing exactly what happens/is output and how it differs from what you expect.
  • Including your full code inside a properly formatted code block, like this (you can see if it looks correct by checking the preview):
```python
YOUR CODE HERE
```

Best of luck!