4
\$\begingroup\$

how would I go about making this code "cleaner"? Right now it's a pain to look at and try to understand.

from msvcrt import getwch, kbhit
from os import system
from time import sleep
from random import randint

def check_type(num, type):
    # Check if int, if not return 0
    if type == int:
        try:
            float(num)
        except:
            print(" Error: Not number.")
            print()
            return 0
        else:
            # Check if has decimal
            try:
                int(num)
            except:
                print(" Error: Number can't have a decimal.")
                print()
                return 0
    # Check if float
    elif type == float:
        try:
            float(num)
        except:
            print(" Error: Not number.")
            print()
            return 0
    # Check if string
    elif type == str:
        try:
            str(num)
        except:
            print(" Error: Not string.")
            print()
            return 0
    return type(num)

exit = 0
while exit == 0:
    print("Game settings")

    """ Board """
    board = []

    # Board size input
    board_size = check_type(input(" Board size: "), int)
    if board_size == 0:
        continue

    aspect_ratio = 16 / 9
    for num in range(board_size):
        board.append(["."] * round(board_size * (aspect_ratio)))

    """ Variables """
    # Defaults x and y to middle of the board
    x = round((board_size * (aspect_ratio)) / 2) - 1
    y = round(board_size / 2)

    # Defaults to moving right
    directionx = 1
    directiony = 0

    key_press = 0
    count = -1
    apple_count = 0
    x2 = []
    y2 = []
    applex = 0
    appley = 0
    apple_count_count = 0

    # Speed input
    while 1:
        speed = check_type(input(" Speed (lower = faster): "), float)
        if speed == 0:
            continue
        else:
            break

    speed_increment = 0
    x2.append(x)
    y2.append(y)

    """ Main """
    while board[y][x] != "x":
        # Loops board while no key presses
        while kbhit() == 0:
            x += directionx
            y += directiony
            # Border overflow check
            if x >= round(board_size * (aspect_ratio)) and directionx == 1:
                x = 0
            elif x < 0 and directionx == -1:
                x = round(board_size * (aspect_ratio)) - 1
            if y >= board_size and directiony == 1:
                y = 0
            elif y < 0 and directiony == -1:
                y = board_size - 1
            # Checks to see if crash
            if board[y][x] == "x":
                while 1:
                    print()
                    print("You have crashed!")
                    # User input
                    while 1:
                        exit = check_type(input("Restart? (yes/no): "), str)
                        if exit == 0:
                            continue
                        else:
                            break
                    if exit.lower() == "yes":
                        exit = 0
                        break
                    elif exit.lower() == "no":
                        exit = 1
                        break
                system("cls")
                break
            # If eat apple
            elif board[y][x] == "o":
                apple_count += 1
                apple_count_count = 0
                # Updates x position
            board[y][x] = "X"
            x2.append(x)
            y2.append(y)
            # Makes body small x
            board[y2[count + 1]][x2[count + 1]] = "x"
            # Tail eater
            if count - apple_count >= 0:
                board[y2[count - apple_count]][x2[count - apple_count]] = "."
            count += 1
            # Only allows one apple at a time
            if apple_count_count <= 10:
                # Makes sure apple doesnt spawn on anything
                while apple_count_count == 10:
                    applex = randint(0, round(board_size * (aspect_ratio)) - 1)
                    appley = randint(0, board_size - 1)
                    if board[appley][applex] != ".":
                        continue
                    board[appley][applex] = "o"
                    break
                apple_count_count += 1
            # Clears previous board then prints updated one
            system("cls")
            for row in board:
                print(" ".join(row))
            # Prints extra info
            print("Speedup: +%d%%  Apple count: %d" % (round(((speed - (speed - speed_increment)) / speed) * 100), apple_count))
            # Delay the loop
            sleep(round(speed - speed_increment, 2))
            if speed - speed_increment >= speed / 2:
                speed_increment += speed / 1000
            key_press = 0
        # Game logic
        else:
            key = getwch()
            if key == "w" and directiony != 1 and key_press == 0:
                directionx = 0
                directiony = -1
                key_press = 1
            elif key == "a" and directionx != 1 and key_press == 0:
                directionx = -1
                directiony = 0
                key_press = 1
            elif key == "s" and directiony != -1 and key_press == 0:
                directionx = 0
                directiony = 1
                key_press = 1
            elif key == "d" and directionx != -1 and key_press == 0:
                directionx = 1
                directiony = 0
                key_press = 1

I was thinking about turning some while loops into functions but I'm not sure if that would make the game run "slower". I'm planning on getting into website back-end development so knowing how to write code with good performance would be handy.

Also, how should I go about commenting? Creating a newline at the end of every section I comment doesn't seem efficient at all. Is there some sort of guideline about commenting?

Any help would be greatly appreciated!

\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

Functions

You can greatly improve your use of functions. For example, you could wrap the game logic into a function called game_logic() and call that. The same goes for your 'main' loop, which can be wrapped in game_loop().

Improving check_type()

  • Using a plain except clause is not a good idea. Especially for longer and more complicated projects, this might inadvertently catch unrelated exceptions (and even things like keyboard interrupts). To avoid this, use except ValueError.

  • Returning 0 can confuse people because it looks similar to the exit code (0). Consider returning False.

Simplifying game logic

To shorten the length of the main game logic, you could put the second part in a boolean variable:

conditional = not directory and not key_press
if key in ("w", "a", "s", "d"):
    key_press = True

if key == "w" and conditional:
    directionx, directiony = 0, -1
elif key == "a" and conditional:
    directionx, directiony = -1, 0
elif key == "s" and conditional:
    directionx, directiony = 0, 1
elif key == "d" and conditional:
    directionx, directiony = 1, 0

Don't use os.system()

If you need to clear the screen, you can do something like print("\n" * y) where y is the vertical height of your terminal. You could also use a carriage return. This has the added advantage of being cross-platform, as opposed to system calls.

Questionable code

Here's some things I thought were quite unusual:

  • Why are you starting print() calls with a space? And why are you doing that inconsistently?

  • Why are you using docstrings where they are not needed?

  • You have some redundant comments, for example # Speed input to explain the use of a variable called speed_input.

  • You are using 1 and 0 a lot for boolean logic, which I find quite confusing. Where possible, it would be better to use bool, i.e. True, False.

  • It is not needed to check the type of exit. input() will always return a string.

Bugs

  • If there's an error converting board_size to integer, the board size will remain 0. Set a default size to avoid this.

  • Trying to turn a float into an integer will never raise an exception, so using that to check for decimal numbers is useless.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Hey, thanks for all of your suggestions! It really opened my mind about the things I was doing wrong, although I do have a few questions. Firstly, where would I find a list of all the exception handlers? Seems like an important thing to know. Also, why shouldn't I be using os.system("cls")? And what is a main() guard? \$\endgroup\$
    – prijupafra
    Jun 25, 2017 at 20:49
  • \$\begingroup\$ Briefly addressing your questions: 1 You can take a look the Python docs, which has all the built-in exceptions nicely listed and explained. 2 Using os.system() is dangerous because it opens gates for attackers (shell hijacking). 3 A name guard is an if-statement that makes sure code such as unit tests or code 'logic' only work if the file is run as 'main' program. That way, if you import it into another file as module, the logic won't run. See also \$\endgroup\$
    – Daniel
    Jun 26, 2017 at 6:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.