9
\$\begingroup\$

A few weeks ago, I developed a terminal game that increases your typing speed. The game is all about typing, providing difficulty levels from easy to hard. I feel like this terminal game won't become as popular as my others, so I need tips on how I could improve this code to make it shorter, easier to run, and more entertaining.

Here is the code developed by me, PYWPM:

import time
import random

logo = '''
  _____    __          _______  __  __ 
 |  __ \   \ \        / /  __ \|  \/  |
 | |__) |   \ \  /\  / /| |__) | \  / |
 |  ___/ | | \ \/  \/ / |  ___/| |\/| |
 | |   | |_| |\  /\  /  | |    | |  | |
 |_|    \__, | \/  \/   |_|    |_|  |_|
         __/ |                         
        |___/                          
'''
print(logo)
print(" ")
difficulty = input("Enter difficulty level (easy/hard): ")
if difficulty == "easy":
  openPlz = open('easywordbank.txt','r')
  readPlz = openPlz.read()
  wordBank = readPlz.split()
elif difficulty == "hard":
  openPlz = open('hardwordbank.txt','r')
  readPlz = openPlz.read()
  wordBank = readPlz.split()

open2 = open('highscore.txt','r+')
open2lst = open2.readlines()

stat = True
strike = 0
score = 0

def gameMain(wordBank):
    #Primary game loop. Returns a lst:
    #lst[0] = added points, lst[1] = added strikes
    lst = [0,0]
    start = time.time()
    wordQuiz = wordBank[random.randint(0,(len(wordBank)-1))]
    wordType = input('Enter the word, '+ wordQuiz + ' : ')
    if wordType == wordQuiz and time.time()-start < 3:
        lst[0] += 1
    elif time.time()-start >= 3:
        print('STRIKE! Too Slow! ')
        lst[1] += 1
    else:
        print('STRIKE! Watch your spelling. Be careful with strikes!')
        lst[1] += 1
    return lst

def highScore(name,score,highScoreLst,zFile):    
    for line in highScoreLst:
        if score >= int(line[-3:-1]): 
            highScoreLst.insert(highScoreLst.index(line),name+'-'+str(score)+'\n')
            highScoreLst.pop()
            zFile.seek(0,0)
            zFile.writelines(highScoreLst)
            break

def rsg():
    print('Ready?')
    time.sleep(1)
    print('Set?')
    time.sleep(1)
    print('Go!')
    time.sleep(1)

name = input('Enter a username for this session: ')
print("Type the word then press enter in under 3 seconds!")
time.sleep(2)
rsg()

#MainState

while stat == True:
    lst = gameMain(wordBank)
    score += lst[0]
    strike += lst[1]
    if strike == 3:
        time.sleep(.5)
        print('Game Over! The game has ended..!\n')
        time.sleep(2)
        print('Your Typing & Accuracy Score: ' + str(score))
        highScore(name,score,open2lst,open2)
        time.sleep(2)
        break
print('\nHighscores for PyWPM:')
time.sleep(2)
for line in open2lst:
    print(line, end='')
    time.sleep(1.5)
time.sleep(5)



openPlz.close()
open2.close()
  1. Yes, this game includes a word bank that randomizes words.
  2. The high scores aren't global.

How could I make this better?

\$\endgroup\$
4
\$\begingroup\$

I made some changes to your code, here's the description:

  • Indentation correction;
  • Removal of possible spaces in user input;
  • Change of While stat ==. True for While stat. It's the same thing;
  • PEP8 (and good practices) use 1 blank space after a comma and 2 line breaks before and after the functions;
  • You can also simplify the import of packages by specifying exactly what you are going to use: I did it in the code below;
  • Finally, the documentation always provides for an empty line break at the end of the code.
from time import time, sleep
from random import randint

logo = '''
  _____    __          _______  __  __ 
 |  __ \   \ \        / /  __ \|  \/  |
 | |__) |   \ \  /\  / /| |__) | \  / |
 |  ___/ | | \ \/  \/ / |  ___/| |\/| |
 | |   | |_| |\  /\  /  | |    | |  | |
 |_|    \__, | \/  \/   |_|    |_|  |_|
         __/ |                         
        |___/                          
'''
print(logo)
print(" ")
difficulty = input("Enter difficulty level (easy/hard): ").strip
if difficulty == "easy":
    openPlz = open('easywordbank.txt', 'r')
    readPlz = openPlz.read()
    wordBank = readPlz.split()
elif difficulty == "hard":
    openPlz = open('hardwordbank.txt', 'r')
    readPlz = openPlz.read()
    wordBank = readPlz.split()

open2 = open('highscore.txt', 'r+')
open2lst = open2.readlines()

stat = True
strike = 0
score = 0


def gameMain(wordBank):
    """
    Primary game loop. Returns a lst:
    #lst[0] = added points, lst[1] = added strikes
    :param wordBank:
    :return:
    """
    lst = [0, 0]
    start = time()
    wordQuiz = wordBank[randint(0,(len(wordBank)-1))]
    wordType = input('Enter the word, '+ wordQuiz + ' : ')
    if wordType == wordQuiz and time()-start < 3:
        lst[0] += 1
    elif time()-start >= 3:
        print('STRIKE! Too Slow! ')
        lst[1] += 1
    else:
        print('STRIKE! Watch your spelling. Be careful with strikes!')
        lst[1] += 1
    return lst


def highScore(name, score, highScoreLst, zFile):
    for line in highScoreLst:
        if score >= int(line[-3:-1]):
            highScoreLst.insert(highScoreLst.index(line), name+'-'+str(score)+'\n')
            highScoreLst.pop()
            zFile.seek(0, 0)
            zFile.writelines(highScoreLst)
            break


def rsg():
    print('Ready?')
    sleep(1)
    print('Set?')
    sleep(1)
    print('Go!')
    sleep(1)


name = input('Enter a username for this session: ')
print("Type the word then press enter in under 3 seconds!")
sleep(2)
rsg()

# MainState
while stat:
    lst = gameMain(wordBank)
    score += lst[0]
    strike += lst[1]
    if strike == 3:
        sleep(.5)
        print('Game Over! The game has ended..!\n')
        sleep(2)
        print('Your Typing & Accuracy Score: ' + str(score))
        highScore(name, score, open2lst, open2)
        sleep(2)
        break
print('\nHighscores for PyWPM:')
sleep(2)
for line in open2lst:
    print(line, end='')
    sleep(1.5)
sleep(5)

openPlz.close()
open2.close()

\$\endgroup\$
0
5
\$\begingroup\$

There's a lot.

  • logo should be LOGO since it's a constant. The other global code, such as acquiring the difficulty, should be moved into functions
  • Consider generating the difficulty list based off of a file glob from your actual dictionaries
  • openPlz is not a great variable name; consider word_bank_file
  • stat is wholly unneeded and you only need a while True; or better yet a loop whose predicate checks the number of strikes
  • Use monotonic instead of time; the latter will fail in weird and wonderful ways on some time edge cases
  • Save 3 to a global constant
  • Consider adding a maximum number of saved high scores
  • Doing a simple sort will reduce complexity in highScore
  • highScore should be named high_score by PEP8
  • rsg is a poor function name; consider countdown
  • Do not return a list from gameMain; instead return a tuple of two integers, which is more standard
  • Call random.choice instead of juggling list indices

Suggested

from time import sleep, monotonic
import random
from pathlib import Path
from typing import List, Tuple, TextIO, Iterable, Sequence

LOGO = '''
  _____    __          _______  __  __ 
 |  __ \   \ \        / /  __ \|  \/  |
 | |__) |   \ \  /\  / /| |__) | \  / |
 |  ___/ | | \ \/  \/ / |  ___/| |\/| |
 | |   | |_| |\  /\  /  | |    | |  | |
 |_|    \__, | \/  \/   |_|    |_|  |_|
         __/ |                         
        |___/                          

'''

TIMEOUT = 3
MAX_SCORES = 10


def load_words(bank_dir: Path) -> List[str]:
    """
    Pulled from:
    https://simple.wikipedia.org/wiki/Wikipedia:List_of_1000_basic_words
    https://raw.githubusercontent.com/dwyl/english-words/master/words.txt
    """
    paths = tuple(bank_dir.glob('*-word-bank.txt'))
    stems = {p.stem.split('-', 1)[0].lower(): p for p in paths}
    diffs = '/'.join(stems.keys())
    prompt = f'Enter difficulty level ({diffs}): '

    while True:
        diff = input(prompt)
        path = stems.get(diff.lower())
        if path is None:
            print('Invalid difficulty')
        else:
            break

    with path.open() as f:
        return [line.rstrip() for line in f]


def countdown() -> None:
    for msg in ('Ready?', 'Set?', 'Go!'):
        sleep(1)
        print(msg, end=' ')
    print()


def game_main(word_bank: Sequence[str]) -> Tuple[
    int,  # added points
    int,  # added strikes
]:
    start = monotonic()
    request = random.choice(word_bank)
    answer = input(f'Enter the word - {request} : ')

    if monotonic() - start >= TIMEOUT:
        print('STRIKE! Too Slow!')
        return 0, 1
    if request == answer:
        return 1, 0

    print('STRIKE! Watch your spelling. Be careful with strikes!')
    return 0, 1


def update_high_scores(name: str, score: int, file: TextIO) -> List[Tuple[int, str]]:
    file.seek(0)
    scores = [(score, name)]
    for line in file:
        name, score = line.rsplit(' - ', 1)
        scores.append((int(score), name))

    scores.sort(reverse=True)
    scores = scores[:MAX_SCORES]
    file.truncate(0)
    for score, name in scores:
        file.write(f'{name} - {score}\n')

    return scores


def print_high_scores(scores: Iterable[Tuple[int, str]]) -> None:
    print('Highscores for PyWPM:')
    for score, name in scores:
        print(f'{name} - {score}')
        sleep(0.5)


def main() -> None:
    print(LOGO)

    word_bank = load_words(Path('.'))

    name = input('Enter a username for this session: ')
    print(f"Type the word then press enter in under {TIMEOUT} seconds!")
    countdown()

    strike, score = 0, 0
    while strike < 3:
        score_addend, strike_addend = game_main(word_bank)
        score += score_addend
        strike += strike_addend

    print(
        'Game Over! The game has ended..!\n'
        f'Your Typing & Accuracy Score: {score}'
    )
    with open('highscore.txt', 'a+') as highscore_file:
        scores = update_high_scores(name, score, highscore_file)

    print_high_scores(scores)


if __name__ == '__main__':
    main()
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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