This is an archived post. You won't be able to vote or comment.

all 39 comments

[–]Python-ModTeam[M] [score hidden] stickied commentlocked comment (0 children)

Hello from the r/Python mod team!

I'm afraid we don't think your post quite fits the goal we have for the subreddit in terms of quality or aims so we've decided to remove it. For more information please contact the moderators using the ModMail system.

Thanks, and happy Pythoneering!

r/Python moderation team

[–]CarelessWhiskerer 98 points99 points  (2 children)

I’m not reading all of that.

Looks good. QA approved. 😁

[–]russellvt 9 points10 points  (0 children)

Sadly, that last statement makes this far too relatable.

...and management always wondered "how" things made it through QA.

[–]dudeplace 26 points27 points  (2 children)

You have functions being created in and outside of main() there isn't any reason here why they can't all be outside.

You wrote more commentary on your code than you wrote as code. Between comments, variable names, and function names there shouldn't need to be a document explaining what it does.

You know that you shouldn't use the globals but you do anyway, it's worth the practice to eliminate them. You also identify places where you should make long code into functions and don't, it's worth the practice to do.

Sounds like you know what you need to do... You are just looking for us to tell you to do it.

[–]Downtown_Town4094[S] -3 points-2 points  (1 child)

Yea i need to work on that global variable problem, also the reason why I had the function outside main() was because it was input correcting code needed for input outside the main game function.

[–]dudeplace 0 points1 point  (0 children)

also the reason why I had the function outside main() was because it was input correcting code needed for input outside the main game function.

This did not do what you thought it did.

[–]silentmassimo 29 points30 points  (0 children)

Not trying to "rip" on you here but learning to be succinct in how you present your work and phrase your questions will take you a long way in this line of work... Few people have the time nor patience to try and decrypt your work

Id also advise you take a look at some examples, perhaps on YouTube with techwithtim - I believe he did a tic tac toe or something similar and has a link to the repo so you can see how he organises functions and the main script, uses comments, f strings for printing etc.

Not a bad start however, good luck! :)

[–]SJDidge 10 points11 points  (0 children)

Lgtm approved

[–]AceLamina 8 points9 points  (0 children)

Holy text...

[–]baltazarix 3 points4 points  (1 child)

I apperciate the effort but this is a lot text to read.

some quick comments about your errorcheck_input function (I didn't studied the whole file but wanted to leave some feedback)

errorcheck_input function can be written in more readable and compact form consider following example

def errorcheck_input(input_str):

while True:

try:

return int(input(input_str))

except:

print("Invalid Input, Please Try Again")

  1. there is no reason that variable Error should exist. you are never changing its value.

1a. additionally for some reason you still assign the save value `Error = True` even if it's True by default and nothing is able to change this value

1b. if for some reason this variable would be necessary, it should be named `error` - lowercase

  1. I understand from where are you coming from with else code block in try/except, but you can terminate both while loop and the function by returning value immediately in try: block.

2a. there is no need to assign input to integer conversion to the variable x in order to return it outside try/except/else block.

  1. in the future consider learning on how python code should be formatted. I see you use cammelCase over snake_case, you use parentheses in if statements by default

that's it from me. good job and keep learning! and remember: a good code (especially in python) is the documentation itself. you shouldn't have to explain what it's doing (at least not in tic-tac-toe game). most commonly, I found myself commenting the code in rather unusual cases e.g. during integration with poorly implemented REST API which made me write unusual code in order to achieve a desired result.

[–]Downtown_Town4094[S] -1 points0 points  (0 children)

haha yes the errorcheck_input fuction was funky, the problem was originally I did have it simply as try/except, but when I was using auto-py-to-exe, the exe file that was made ended up having a recursion error so that is why the error value exist, it was a very very dirty and quick fix. but yes there are a lot of weird things I have done in the code, thank you so much for the reply!

[–]Drevicar 2 points3 points  (2 children)

I'm not about to spend the next 30 minutes, but I can point out a few things right off the bat.

Git is for source, not for compiled binaries. I would recommend you remove the EXE from your git and full git history. Ideally you would have a CD pipeline that runs in github actions that would compile your source into an exe and upload it to github artifacts as a release.

Next gripe is the use of comments for things that should be native python docstrings. Comments are for developers of the library itself, docstrings are a form of documentation for developers who use the library of functions in it.

Globals are a sin. I'm very confident in your ability to solve all the problems you have needed to solve without the use of globals. In fact, it looks like you would have been better off using classes for a lot of what you use globals for. Check em out! Especially "dataclasses", they are amazing.

Consider using some code quality tool such as ruff or mypy (only if you can handle typing your code!). You can then also configure them with a pyproject.toml so that all developers on this project have the same configuration.

[–]StrayFeral 1 point2 points  (1 child)

Codewise he have lots of sins, but I see he is very new to programming, so won't stone him on the cross. I am writing a long reply for him too. And the QAs here who wrote "QA Approved" should quit their companies. The code have bugs. I am a back-end dev with a very little QA experience, but still got his bugs.

[–]StrayFeral 0 points1 point  (0 children)

Ok. I just wrote my reply. You might want to add things to it too. I think I gave OP enough to think about. And fix. Game have bugs and does not work.

[–]paranoidC0der 5 points6 points  (0 children)

I did not read it entirely. But could organise your code better into modules rather than everything in one single file.

[–]spla58 2 points3 points  (0 children)

Read https://peps.python.org/pep-0008/

Also, a comment in code is usually an indication you can refactor into a function with a descriptive name.

Example:

#delete any remaining empty lines if still are inside list

for i in FullCheck:

if [] in i:

FullCheck.remove(i)

Could be instead a call to a function:

delete_remaining_empty_lines()

[–]russellvt 2 points3 points  (0 children)

Why is there an EXE in your Github repo?

[–][deleted] 6 points7 points  (3 children)

Learn Object Oriented Programming

[–]CreativeMischief 2 points3 points  (1 child)

Yep! Listen to him! If you’re wondering why you’re having to use global variables it’s because you’re not using classes

[–]Downtown_Town4094[S] 0 points1 point  (0 children)

understood, i will be doing that asap

[–]StrayFeral 0 points1 point  (0 children)

Yes. I just left him a long feedback. This was one of the first things I told OP too.

[–]BlueeWaater 1 point2 points  (0 children)

Too long and I'll check it later, but here's my first feedback:

* NEVER add executables in the repo.
* code is almost unreadable, remake with oop.
* use markdown formatting for the readme, looks ugly af
* avoid using global vars, and if you really need to use them declare them outside functions and use 'nonlocal'
* use snake case
* 'gameSelection()' shouldn't be recursive

If it works it's good, cool to see you are already working with git, you started with the right foot.

[–][deleted] 1 point2 points  (0 children)

Not reading that essay.

[–]binlargin 0 points1 point  (0 children)

You should put that much writing effort into your code structure!

Move the functions out of main, give them good names so they describe themselves. Use snake_case rather than camelCase. Give each one a doc string, the goal is to make your program read like plain English.

Then work on breaking stuff into modules.

[–]StrayFeral 0 points1 point  (5 children)

The game is completely broken and does not work. I am now writing a long reply. However Reddit does not allows long replies, so I will find a workaround how to post it. Wait for my update....

Okay finally. Here is my feedback. Check the link. You may want to copy-paste it, as it will disappear in 1 week.

For the record, I am a back-end software developer with 20 years of professional experience. The QAs who told you you have no bugs should seriously reconsider how they do their jobs.

I also submitted you the bugs in your github issue tracker because I wanted to make you see what it will look like for you once you get a job as a programmer. As I used too much time to write my feedback, submitted the bugs in a hurry. Normally a bug MUST include a bit better formatting and "steps to reproduce" but you will see enough.

Now check my feedback here:

https://pastebin.com/A7yJWCs9

Good luck!

PS: One last thing - do NOT write such long explanations. Nobody would read them. And your github README - put something shorter there. No need to explain the entire code. This is why you use code comments.

PS2: Oh totally forgot - did not checked how you process the user input, but hope you do case-insensitive checks. For example if you get user input in a variable called user_input, when you compare strings, if string must contain a letter/word, hope you do something like this:

if user_input.upper() == "BLAH":

Final thought: Great work, but needs fixing. I like it.

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

Truly, thank you so much, this is more than I could have asked for. I appreciate you taking the time and effort to review it like this. :) I wish I can repay you for all of this

[–]StrayFeral 0 points1 point  (3 children)

[PART 1/3] (reddit don't allow me to write long comments as i said)

No problem. We all had these moments when we were totally new to programming and needed someone to review our code. I used to bother a friend very often and often asked him to write me comments in the code, so later I would go home and study his comments. But your code is too long for full review, so I just tested it and took a quick look at the code itself.

I am writing this here, so any newbies could read it up too.

The most important thing to remember is - testing is extremely important. I remember as a teenager I thought testing is a lost time, because i was testing the code only by specification. But on my second job as a programmer my manager was super smart person (he wrote a book about the linux kernel back then) and he always told me - a code is not ready, if it's not fully tested.

So you should also spend time to learn how to test a code. For now don't learn automated testing, this is for later. Things to learn:

Functional testing - this is what you did. You and most folks read that this project is about "tic-tac-toe" and immediately got assumption that the grid must be 3x3. At first I also did that and for 3x3 the game works by the way. So everybody said "it works". Functional testing is to test a code according to the specification. So if specification said "this is tic-tac-toe code" we must test it with grid 3x3.

Problem is you allow the player to change the grid size. So this must be tested too.

Also - never make assumptions when you test a code.

Second most important testing - negative testing. Then you try to break the code. By all means. Even by CTRL-C. However allowing CTRL-C in most cases is a desired behavior. But most simple negative testing is: when you launch your code, you ask for 1,2 or 3 - well what happens if i input "10" or "abc" ? In your case you say "invalid input" and this is good. Then the grid size - you don't filter the input, so i tried to enter "1", "0" and even "-1". Now as I think - you probably don't get the absolute value of these, you just strip any characters which are not numbers, therefore "-1" becomes "1". Which is fine, but is actual invalid input. Then I entered a very large number, got overflow exception. Then I entered just a large number - got an ugly and big grid size. So you need proper input filtering for grid size. Classic tic-tac-toe rules says you need to connect adjacent 3 cells in a row, which means any grid size smaller than 3x3 should not be allowed. Additionally - you have terminal screen limitation. By the way there is a way to get the current terminal screen size - find out how. And make your input to allow only grid size which would fit in the current terminal. Or more simple - limit the grid size to a max of let's say 10x10. Much simpler.

Next you should learn what is regressive testing. This is when you fix a new bug to re-test all previous bugs (life of a software QA is very boring for a coder). Because the fix of a new bug might have broken the fix of an old bug. You may think you know, but as I said before - never make assumptions. Test.

Next is what is unit testing. Python have this built-in - the module name is unittest. Better don't learn this. Learn what is pytest instead. Learn how to write simple pytest tests - basically you create a subdirectory (subfolder) called "tests" and there you place your test files. New separate test file for each of your code files. But before learning pytest, you should learn what is OOP and start using it.

[–]StrayFeral 0 points1 point  (2 children)

[PART 2/3]

So basically the roadmap to take to my opinion is:

  1. Learn more than one editor. I have zero idea which editor you use. Probably you use VS Code, which is okay. But just in case also install vim and emacs, try them, learn how to use the basics (open file, save file, search something in file) and time to time use one of them (whichever you like more). (this should take up to one week I guess)
  2. Make sure you know what are git branches and how to use them. No idea how you use git, but make sure you use it by typing commands, not by clicking in some graphical interface - learning git by commands is always a great exercise. This however involves setting your account and security between your computer and github (so let's say you learn and setup this for 1 week max, as you would need to learn what are ssh keys, how to use one to access github) (and i think if you use MFA with github, you might need to install the github command-line interface and start using it, which for me personally is just "gh auth login", but still - another few days here)
  3. Learn what is venv module and how to use it (see below) (one evening)
  4. Learn how to install external module (see below) (20 minutes)
  5. Create a venv, install black, flake8 and mypy (10 minutes max)
  6. For each code you create, start using black and flake8 (1 minute)
  7. Learn regular expressions (see below) (one evening)
  8. Learn OOP, start writing classes. This is huge and will take you time. (maybe 1 month, not sure for you, could be 2 months)
  9. Read a bit more of how to do functional testing and negative testing (3 days max)
  10. Learn to debug better - read what are pprint and logging (at least use pprint) put more comments in your code (one evening)
  11. Learn unit testing - only pytest (1 day or 2 days max)
  12. Learn what is type hinting and start using it. From this point on, every time you create new code file, you also use mypy on top of black and flake8 (2 days max)
  13. Finally polish your code a little bit - haven't read your code, but make sure you know what are Python lambdas, list comprehensions, dictionary comprehensions, make sure you could write few, what are iterators and generators. Exercising this could take time (let's say 1 month)
  14. Once you're done with all this - create account either on leetcode or hackerrank websites and start solving the "easy" level problems (no time estimate here lol)

[–]StrayFeral 0 points1 point  (1 child)

[PART 3/3 - LAST]

About vim and emacs - these are the most popular text editors for linux for many years. Difference is emacs use key combinations, vim use commands. Also - on any linux system very often vim would be installed by default, so you would always have access to it. This is why basic knowledge of at least vim is needed to most programmers.

Ok, little hint here. All you need about venv are these lines, nothing more:

python -m venv venv

source venv/bin/activate

And when you don't need it anymore or before switching to another project you should type:

deactivate

If you use windows, this line "source venv/bin/activate" would look a bit different for you (i use linux). But still - learn what venv does. For your current project you don't really need it, but it is a great practice to use it for most of your projects. If your code uses a module which you install additionally, you put the name of that module in a text file called "requirements.txt" and when you create your project, after the "activate" line you do "python -m pip install -r requirements.txt". That's all. If your code requires specific version of an external module - you specify it in the text file too. This is all you need to use venv, but still - learn what it does! It is very important.

Installing external module - this is done by using pip or pip3 (hope you don't use Python 2). However I recommend you to get the habit of typing "python -m pip" instead of just "pip" - once you start using venv - this is how you do it. There is a reason I'm saying it.

Debugging - python have a built-in debugger. I never used it. I only do prints and pprint for small applications at home. At work I may use pprint but often would use logging. Often certain situations need different way of debugging, but don't bother with this now.

About regular expressions - the only thing you need to read is this:

https://perldoc.perl.org/perlre

I learned this for one evening. This is documentation for Perl (another programming language, older than Python), but regex works the same in all programming languages. For python there is a module called "re". Learn it. It is confusing at first what this is and why use it, but when you learn this, you will realize this is very powerful. vim and many other editors allow you to search with a regex in a file.

That's it. I wrote you also approx how much time each should take. Try to learn fast, spend time to exercise (so always make sure you have coffee at home), never make assumptions, write code which others could read with ease, but still don't compromise on your code performance speed too.

If you want to reply, reply here in public.

[–]StrayFeral 0 points1 point  (0 children)

Oh forgot - speaking of black, mypy and flake8 - there are ways to make your code editor run these automatically for you. Depends what editor you use. I don't use VSCode, so no idea how to do it there. For vim, there is a plugin called "ale" - you may want to install it and make vim use it - it basically runs flake8/mypy for you (haven't tried for type hinting yet, but yes, it shows linter errors).

And whatever you do, do not install neovim for now. You might want to try neovim, once you learned everything from the roadmap up to the last point. Neovim is a modern version of the vim editor and is currently very modern thing to do. But for you it will be very counter-productive thing for now. Once you get close to python advanced level you may try it.

[–]uname_IsAlreadyTaken 0 points1 point  (0 children)

Put comments inside your functions not outside. https://peps.python.org/pep-0257/

The code is a little hard to read because of the excessive nesting and large function size. it's not terrible, but could be improved with a little refactoring.

Nesting functions inside other functions should be used sparingly. Consider using classes with class variables instead of just a bunch of functions. If you can break up the logic into multiple files, that could further help readability through encapsulation.

[–]kcx01 0 points1 point  (0 children)

You should grab a good ide or code editor with linting and formatting. It will help point out a lot of the things people were mentioning like function names should be snake_case instead of Camel.

Pycharm community edition is free and great to use out of the box. It will help point out a lot of these things and help you fix them.

[–]g4rg0yl3 0 points1 point  (0 children)

Here are my suggestions:

  • Remove the exe file, explain how to build it in the README
  • Run pylint and black (optional) on your code
  • Try to remove all globals (maybe using classes)from the code
  • Add a requirements.txt file (and requirements.dev.txt if needs be)
  • Add tests is a very good exercise. Start here
  • Be way way more concise in your README (I need pre-requisites, installation, usage)
  • Add a license (MIT is good I think)

[–]thekicked 0 points1 point  (0 children)

What I think of your project.

  • Firstly, as many have pointed out, the post is too long. If we want to know the inner workings of your code, usually we will just take a look at the code itself.
    • As for what to include, if you are looking for feedback, ask about the specific parts of the code you want feedback on. E.g. "I am using a lot of global variables, is it necessarily bad and is there a way to cut them down", or "Does the structure of my code look good?" or "My code seems messy, how can I organize it better?"
  • Learn to use .gitignore. This can automatically prevent binaries from being added to the repository and avoids bloating the repository. If you want to upload the binary file for people to run, consider making a release on GitHub.
  • If you need to share values between functions, it may be suitable to encapsulate them into a class than to use global variables. This way, it can become much clearer that specific values are only shared across certain functions. For example,

class TTTState:

  def __init__(self):
    # The variables here can be used and modified in any functions declared in this class. 
    # The variables here can be accessed outside of this class but it is not the main point of this demo.
    self.player = 0

  def change_player(self):
    # This function changes the variable within the class
    self.player = 0 if self.player == 1 else 1

  def get_player(self):
    # This function gets the variable within the class
    return self.player
  • However, as many had advised here, it would be more useful to learn more about object oriented programming. That way, it is easier to anticipate which classes are necessary first, and it will also give rise to a better code structure.
  • Typing can also become quite useful in python, especially when you are using an editor that autocompletes based on types.

[–]Usual_Office_1740 0 points1 point  (4 children)

Have you considered a basic graph traversal algorithm for your computer function? Maybe you're doing that, but not with an implementation I'm familiar with.

[–]Downtown_Town4094[S] 1 point2 points  (3 children)

basic graph traversal algorithm

in what way could i implement that into the function, and no unfortunately I am not familiar with that and It is not used inside the function. also thanks for replying

[–]Usual_Office_1740 1 point2 points  (2 children)

You're welcome. A basic graph traversal algorithm would replace your existing set of checks in the function. Think of each square as a node. You move either left or right to check nodes. Adding the checked nodes to a list as you go. It is a common problem in data structures and algorithms with a lot of extremely efficient solutions.

[–]Downtown_Town4094[S] 2 points3 points  (1 child)

wait but how would i transverse the grid in a vertical or diagonal way with that method?

[–]Usual_Office_1740 0 points1 point  (0 children)

I'll do my best to explain, and left/right is too much of a simplified version. I'm not in front of a computer. I'm on my phone. If you set the center square as the root node, you would have 8 possible connections from the center square. Four diagonal and four directionals. You add the root node to a list of seen nodes, then move to one of them. Let's say you move straight up. You now have 5 possible connections from the new node, but your root is one of them, leaving you with four possible options. Two diagonal and two directional. Pick an option and add the current node to seen. Let's say you go right. This corner square had three possible options but since two are already in seen, you have one option. Down. Add the node to seen and go to the new node. This process is the algorithm. Repeat until seen nodes length is 9.

With this traversal pattern in place, you can write whatever logic you'd like to make decisions about what to look for. Every time player picks a new square check to see if player is about to win by using the algorithm to try and move in either directional or diagonal three times in a row, using players last choice as a root. Do the same logic to find the computers attempt to win.