6
\$\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!

asked Jun 25, 2017 at 4:01
\$\endgroup\$

1 Answer 1

6
\$\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.

answered Jun 25, 2017 at 7:23
\$\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\$ Commented 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\$ Commented Jun 26, 2017 at 6:24

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.