PJ02 - Typing Speed


Overview

In this project you will implement a terminal-based typing speed game that calculates the player’s typing words per minute given some randomly generated phrase to type.

This project will challenge you to read data from at least one text file, break down a program into functions of your design, and learn a bit about how to work with the concept of time in a program.

Design

Before you attempt to implement a non-trivial program, you should start with thinking through its design. This is often done best on paper or a whiteboard.

It’s often useful to first think through the user experience:

  • What happens when the program begins?
  • How does the user know what to do next?
  • What information should be presented?
  • How does the user try again? How do they stop?

Once you have designed how you want the game interactions to go, then turn your attention toward what steps you need your program to complete in order to carry out each interaction. Some steps will be simple, some will be more complex. For the more complex interactions, try to break them down into a list of next steps. The process of turning these kinds of written steps into functions is an art that takes practice, but often you will find there is a close correspondence.

One thing you should look into and explore in a REPL is the time module’s time function whose documentation you can read here: https://docs.python.org/3/library/time.html#time.time. The short story is this function will return to you the amount of time, in seconds, that has passed since the beginning of time widely considered to be January 1st, 1970. Think through how you can use that knowledge to determine how long it took the user to give you an input string.

You will need to calculate words per minute based on the user’s input and the time it took (above). If this metric assumes a word is 5 characters long, on average, not including spaces, how will you calculate words per minute? Think through this algorithm and write out the algebraic expression you will use.

When it comes to data files with words, you are free to curate your own and search around. If you use a dictionary file from the internet, just include a link to its source in your program’s docstring. Here’s one with 1,000 common English words, for example. If you’d like to produce phrases that read closer to real sentences, feel free to make use of multiple dictionary files for different kinds of words. Also consider producing a dictionary file of words (or symbols!) meaningful to you in your field. For example, if you wanted to practice typing for computer science, including some symbols in your game such as [, ], (, ), ~, /, :, while, and so on, could be helpful typing practice.

When choosing a random word from the dictionary file(s) you read in, read through the random module’s functions that are made for working with sequences like Lists: https://docs.python.org/3/library/random.html#functions-for-sequences

Setup

Since this project will have not only your Python code, but dictionary file(s) you will submit, as well, lets setup a new directory for it.

  1. In your comp110 directory, add a directory named typing_speed.
    • This directory should be a child directory of comp110 and a sibling to your previous projects directory.
  2. In the typing_speed directory, add two files named game.py and game_test.py
    • Go ahead and add a placeholder docstring and __author__ global variable.
    • Your program will use the main function idiom and should call main if __name__ is equal to __main__. Other than global/named constants, all statements should be in function bodies.
  3. In the typing_speed directory, add a file named words.txt
    • You can add your dictionary words here. You can setup other dictionary files as well, but for them to be packaged in submission zip files you will want to be sure they are in this directory.
    • The path to this file that you would use to open it is "comp110/typing_speed/words.txt"

Requirements

  • 15pts - Program is logically broken down into functions outside of main
    • Most functions should have one specific purpose and return a useful value. A rule of thumb here is each function’s docstring should be able to describe its purpose in a single, terse sentence. That one purpose likely still takes many steps, but those steps should be related to a single task.
    • Some specific purposes that likely deserve their own functions: reading a file path into a list of words, generating a random phrase, timing user input (consider returing a tuple of a string and time in seconds), calculating words per minute, and printing information.
  • 10pts - Read a dictionary of words from a plain-text file. Reading more than one file is OK. Must include any such dictionary files in the same directory as the project.

  • 10pts - Generate random phrases whose words are at least partially sourced from words in dictionary files.

  • 10pts - Correctly calculate the user’s typed words per minute based on a word being considered 5-characters long on average, not including spaces. Words per minute reported should be rounded to the nearest int (see Python’s built-in round function).

  • 10pts - Write some tests for at least one of your program’s pure functions in game_test.py. A pure function is one that does not involve side-effects. At least one task is prime for being broken out into its own pure functions and tested: calculating words per minute given a string and the number of seconds it took to type it. Many other functions in this program are likely to involve side-effects, including those that print, generate random numbers, promp for input, or involve input/output. Testing functions which involve side-effects is more challenging and beyond our current concerns.

  • 10pts - User should be able to continue attempting different phrases, and be told their words per minute after each attempt. User should also be given an obvious way to quit. The user experience of using your typing speed program should be pleasant.

  • 15pts - Program should meet linted, style expectations.

  • 15pts - Program should make full use of static type hints in function declarations.

  • 5pts - Go above and beyond with one or more of the extensions below.

Extensions

You are encouraged to attempt as many as these as you’re feeling up to the challenge of. For any you attempt, be sure to document in your

  • Make the game multiplayer for any number of players using Lists and taking turns typing the same phrase in rounds.
  • Generate phrases that have some randomness, yet are still gramatically correct. Use multiple dictionary files for different types of words or add extra data to your dictionary file for distinguishing types of words and changing the logic of your program to accomodate.
  • Report on the “number of errors” in user’s input versus the random, prompted phrase
    • Calculate and report the number of characters that do not match between the generated phrase and the user’s input.
    • For a higher level of difficulty, implement the Levenshtein “edit distance” algorithm to report on errors. This algorithm produces the number of “edits” (deletes, inserts, substitutions) between two strings https://en.wikipedia.org/wiki/Levenshtein_distance
  • Keep a running average words per minute as the user plays and also report their average words per minute after each subsequent attempt.
  • Search for how to write strings to storage using IO in Python and keep a score list.
    • For a higher level of difficulty, look into sorting and keep a high score list like an old arcade game.
  • Write tests for your functions that involve side-effects.
    • Easy - Testing functions that involve randomness, consider how you could prove you are getting different from the same function call at least most of the time? Also consider using the random module’s seed function in your test cases to make the “random” functions always return the same sequence of random numbers.
    • Medium - Testing functions that involve file input often involves having some shorter “mock” files whose contents you know in advance and comparing whether your functions that involve reading a file return the expected results. There are other ways of testing file input, such as these techniques, but they tend to be more involved.
    • Difficult - Testing functions that involve user input, read through the concept of capturing https://docs.pytest.org/en/stable/capture.html and consider using the time module’s sleep function: https://docs.python.org/3/library/time.html#time.sleep