- Notifications
You must be signed in to change notification settings - Fork1
Welcome to The Zen of Typing! The only place you can improve your typing speed and brush up on some programming principles at the same time...
loosenthedark/zen-of-typing
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
The Zen of Typing is a Python terminal project whose primary purpose is to enable users to practice and improve their touch typing skills.
Secondarily, it serves to reinforce various (mainly Python-related) programming principles and aphorisms through the use of carefully-selected practice texts and extracts.
Users can customise their experience by choosing from a range of options before the game itself gets underway. Once all necessary pre-game instructions have been displayed and the target text is loaded, the user's performance is monitored as they attempt to replicate each line of text. As soon as they finish and hitEnter, they receive feedback in the form of a breakdown of their typing speed and accuracy. They may then choose to either restart the game or quit (and exit the application).
click to view
The Zen of Typing is loosely modelled on classic typing programmes such as'Mavis Beacon Teaches Typing!', which the developer was known to spend countless hours practicing throughout his misspent youth.
The title is also a play on'The Zen of Python', PythoneerTim Peters' list of fundamental commandments for the then-nascent programming language, which was first issued in 1999 and has since come to be seen as something of a cornerstone document.
To begin, a welcome message is communicated on the start screen and the user is asked if they would like to warm up by tackling a practice text (which is subsequently revealed to beTom Cargill's humorous observation now known as the 'ninety-ninety rule').
Following this optional practice session - which they can take as many times as they wish - the user is given a multiple-choice menu of target texts, from which they must choose one by using the Up, Down and Enter keys on their keyboard. There are six texts in all, each of which is denoted by a slightly cryptic title/acronym. If the user finds that they are struggling to decide on a text, they may opt to effectively 'roll the dice' by asking the computer to pick one at random for them.
Once a text has been chosen, a subsequent menu similarly asks the listener to choose from a range of options, this time corresponding to the number of lines they wish to type. The list is once again navigated using the Up, Down and Enter keys.
Having selected both a text and the number of lines to be typed, the user is next asked if they know the secret password. This time they must provide an answer by inputting either 'Y' or 'N' on their keyboard.
- If theydon't know it, a summary of their choices is displayed and the target text is outputted in italics. A bold yellow "Off you go!!!" message signals that the game is underway and they must begin the typing task.
If, on the other hand, theydo know the secret password (and indicate so by pressing 'Y'), they are prompted to enter it as an input string.
- If the password they enter happens to be incorrect, they are alerted to this fact by a bold red feedback message, after which the password functionality is discarded and the game begins in the usual fashion (see above)
- If they enter a correct password, meanwhile, they unlock a bonus submenu offering the chance to acticate the 'Beast Mode' feature (explained in more detail below).
click to view
click to view
The prevalent blue and yellow design palette both draws the user's attention and reinforces the fact that this is very much aPython application. Elsewhere, semantic text feedback is displayed in a familiar and intuitive fashion, e.g. error messages in red, success alerts in green. While many terminal projects can look drab and monotone, it was the developer's intention that The Zen of Typing should be anything but.
Immersive sequential flow of multiple-choice menus and questions (enabled c/o thePyInquirer module)
click to view
From a UX standpoint, theclosed-ended nature of almost all of the questions with which the user is presented minimises the risk of error and all but eliminates the possibility of invalid user input. This saves time (for both developer and user), while also delivering a neat and concise pre-game interface.
click to view
Not everyone is a super-fast expert typist. Similarly, not everyone produces their finest work under pressure. Bearing this in mind, The Zen of Typing allows users to practice their typing in a relaxed fashion without having to worry about performance metrics (the practice mode is not 'recorded', i.e. no speed/accuracy calculations are made). They may then progress to the stricter in-game conditions whenever they feel ready.
click to view
Hick's Law states that "the time it takes to make a decision increases with the number and complexity of choices". If, therefore, the user feels somewhat overwhelmed at having to choose between the six available target texts, they can simply ask therandom module to help lighten their cognitive load by deciding for them.
click to view
Five of the six available target texts are directly related to computer programming, with a strong Python emphasis. While it would arguably have been simpler to work with generic/filler content, this way the Zen of Typing user stands to kill two birds with one stone (so to speak) by rounding out their coding knowledge as they're working on their typing speed.
click to view
One of the five programming-related target texts mentioned above is actually a dynamically-generated random list of responses from an end-point associated with thepyjokes ("jokes as a service") API.
click to view
One surefire way to drive user engagement is to harness the principles ofoperant conditioning when designing an interactive application. The Zen of Typing adheres to this objective on at least two fronts:
- The user is provided with instant feedback in the form of a results breakdown, consisting of overall time taken, accuracy and average speed (in words typed per minute). In most cases, this alone should be enough of a hook to encourage them to keep playing in the hope of improving on their current personal best score(s)
- The secret password/'Beast Mode' functionality is initially alluded to in passing, but quickly becomes a central aspect of the game. Crucially, the user is never directly informed as to how and when the next character of this password will be revealed, so they are kept guessing (and wanting more) to a large extent.
click to view
The one open-ended question demanding user input is the secret password prompt. This is handled in a straightforward binary fashion: if the user enters anything other than the correct (case-sensitive) password, they are informed of their mistake and the game simply starts as normal. This is accomplished by using a compound if statement within the game class's main
activate()
method.
click to view
click to view
Given more time, an additional UX feature I would like to have included to boost gamification is a way of storing and updating their current high score (in wpm) in memory. This could be implemented by using conditional logic (attached to a class method) and writing the high score to a project
.txt
file. A good example can be found inthis video tutorialFollow-up email feature Ask user to enter email and send them a breakdown of their typing speed/accuracy (via smtplib module)?
click to view
Another way to keep the user hooked and ensure they return to the application would be if they received a summary breakdown of their performance (average typing speed, accuracy, high score etc.) after they have exited the programme. This would probably be a good use case for the
smtplib
moduleclick to view
Allowing users to share their new high score (see above) etc. via social media would be a great way to publicise the app and start growing a community of TZOT users. The actively maintainedtwython module looks like it would be ideal for this purpose, Twitter being the channel that arguably lends itself best to this type of functionality. There's even scope foruploading an image as part of a status update, which would be useful for sharing in-game screenshots with the world at large.
click to view
At present, users are only able to get an indication of how quickly and accurately they are typingafter they have completed a typing task (i.e. as soon as they press Enter at the end of a target text, they are given a results breakdown). A more dynamic means of providing user feedback would be to have a running timer situated somewhere in the UI, as well as displaying a 'live' words-per-minute calculation. Going further, incorrectly-typed text could also be highlighted in real time so that the user would be able to go back and correct mistake(s) (cf.this example). On reflection, it's likely that this functionality would have required quite extensive refactoring, and so a decision was made to leave it out of this initial app version.
click to view
From a cosmetic standpoint, terminal text by default is not particularly pleasing to the eye. Part of the reason for this is the lack of layout and spacing options, which can often lead to things looking quite bunched and difficult to read. With this in mind, it would be nice to be able to add some extra spacing between the left and right borders of the mock terminal and the text output (chiefly the target text and/or user input) in order to improve readability and overall appearance (cf.this blog post).
click to view
At the time of writing, the user is rewarded via the application's incremental secret password reveal functionality based solely on typingspeed. More specifically, they are shown the first character of this password when they record a speed of 20 words per minute, the second when they reach 30 wpm, the third at 40 wpm and the fourth and final character once they achieve a speed of 50 wpm. However, this happens irrespective of typingaccuracy - theoretically, a user could therefore speed-type gibberish (i.e. record an extremely low accuracy rate) and still manage to unlock the password and access Beast Mode. This could be prevented using more sophisticated and nuanced conditional logic, and is something the developer intends to implement in future revisions of the app.
click to view
- Python 3.9.2: used to anchor the project and direct all application behaviour
- #"https://en.wikipedia.org/wiki/HTML" rel="nofollow">HTML used to construct the elements involved in building the mock terminal in the browser
click to view
Python modules/packages:
Standard library imports:
python-future
: used to ensure a fully Python 2/3-compatible codebaserandom
: used to implement pseudo-random number generationsys
: used to provide various functions and variables that are used to manipulate different parts of the Python runtime environmenttextwrap
: used for wrapping and formatting of plain text throughout the project (made necessary due to the width and height constraints of the browser mock terminal)time
: used to provide ad hoc stopwatch-like functionality when calculating and recording user typing speed
Third-party imports:
- PyInquirer: used to provide a collection of common interactive command line user interfaces, e.g. for compiling multiple-choice questions and managing in-app hierarchical prompts in an intuitive and efficient manner
- pyjokes: used to supply the project with a randomly-assembled feed of one-line programming jokes, which is then repurposed into one of the app's target texts availabel to the user
- Prompt Toolkit: cross-platform foundational library on top of which PyInquirer (see above) is built
Visual Studio Code: used as the online IDE for the project
Git: used to handle version control throughout the project's evolution
GitHub: used to compile and remotely store the project's codebase following successive local commits initiated from the command line
Heroku: used to deploy the site and aid workflow in line with serverless continuous deployment best practices
pyjokes API: used to request and compile lists of programming jokes as needed (via the module's
get_jokes
function)Valentin Bryukhanov'sPEP8 online checker was used tovalidate the project's Python code, in line with best practice.
Ezgif image converter: used to convert the Python logo used in the creation of a project favicon from
.svg
to.png
formatPicResize: used to crop and resize images
- Editor.md: used to format project Markdown in line with best practices
click to view
ATypingText
class has been used as the main application model.
This class's__init__
state initialisation method creates properties to store the following app-related information:
- game mode: By default, the class's
beast
property is set toFalse
. What this means is that the game's Beast Mode (which involves the user having to type target text lines backwards) has not been enabled, and so the normal mode of standard text display is in effect. - (boolean) game state flags pertaining to each of the following:
- whether or not the game has
started
- whether or not the game has
finished
- whether or not the game is currently
running
- whether or not the game has
- string values (initially set to empty strings) corresponding to the self-explanatory properties of
text_typed
andtext_to_be_typed
- user performance- and results-related properties (all of which are initially set to
0
):start_time
total_time
typing_accuracy
wpm
(words per minute)
The class also has three game-centric methods - the names of which are again self-explanatory - that are used to coordinate various aspects of functionality:
activate()
game_restart()
calculate_results()
It was not deemed necessary to create any instances of this class, as the game functionality doesn't really call for it.
The application was rigorously tested throughout its development life cycle. This process ranged from manual testing (e.g. by deliberately entering an incorrect password when prompted) through to automated validator testing. As always, numerous bugs were identified at various stages - some of which have since been resolved, while others have proven to be more persistent and will have to be tackled at some point in the future.
click to view
An example of a bug (one of many) that the developer managed to rectify is theValeError
shown in the screenshot above. Such error messages were invariably returned when an inappropriate value was being passed as an argument to a function, and required careful and continuous debugging.
Flashing/Blinking terminal text, e.g. when asking the user if they wish to play the game again, was a feature that failed to launch - for reasons that aren't immediately evident to the developer. It's likely to be related to ANSI SGR behaviour and how this is rendered in the browser. Whatever the cause(s) of this bug, unfortunately the examples of 'blinking' outputseen here could not be replicated in TZoT for the time being.
One rather obvious blind spot in the game's design is that it is possible to completely cheat and record an impossibly high wpm score by simply copying and pasting the target text as it is printed to the screen into the terminal as user input. Going forward it would obviously be a good idea to mitigate against this type of user behaviour - though it would require some further exploration, e.g. to find a module tailored to this purpose.
Finally, one more design flaw within the application is the fact that theTypingText
class'scalculate_results
method only allows for direct character-by-character comparison when calculating user accuracy. Thus, if the user mistakenly types a single extra character somewhere, all subsequent characters in that user input string are likely to be deemed 'mistakes' - even if they happen to be completely accurate in terms of what the user is attempting to type at that particular moment in time. This problem is perhaps best illustrated using an in-game screenshot:
click to view
Valentin Bryukhanov'sonline validation tool was used to ensure that all of the project's Python source code isPep 8-compliant. This checking was done by simply copying and pasting the contents of both therun.py
file into the relevant field and clicking onCheck code. Initially a number of non-compliance errors were returned; these were mostly in the form of "E501: line too long" messages. After making all necessary modifications, the Python code now passes through this validator without any problems:
click to view
This project was developed in a repository built on top of theCode Institute Python Essentials template repository, thus inheriting the latter's main directory structure and starter files.
Creating the repository from the template involved following each of these steps:
- On GitHub.com, navigate to the main page of the template repository - in this case,this page.
- Above the file list, clickUse this template.
- On the next screen, select the account you want to own the repository from theOwner drop-down menu.
- Enter aRepository name, as well as an optionalDescription.
- Choose a repository visibility. NB: To meet Code Institute project submission criteria, this must be set toPublic.
- ClickCreate repository from template.
For a more detailed explanation, see'Creating a repository from a template' (GitHub Docs)
click to view
It is possible to fork this project's GitHub repository to view and/or make changes without affecting the original. This is achieved by following these steps...
NB: The steps outlined below assume that you already haveGit set up on your computer - for an overview of how to download, install, and configure Git, consult theGitHub Docs
Sign in to your GitHub account and locate therelevant repository.
Click onFork, located near the top right-hand corner of the repository page.
- You will now have a copy of this project's repository in your own GitHub account.
click to view
It is possible to copy the repository to your local machine so that you can fix merge conflicts, add or remove files and push larger commits without affecting the original project code. Cloning a repository pulls down a full copy of all the repo data that GitHub has at that point in time. See theGitHub Docs for further information, and below for a brief summary...
Sign in to your GitHub account and locate therelevant repository.
Click on theCode dropdown next to the greenGitpod button. This will reveal theClone option.
- In order to clone the repository using
HTTPS
, selectHTTPS and copy the link shown (there is a copy button to the right of the URL).
Next, openGit Bash (seehere for an overview of download options, if required).
Change the current working directory on your local machine to the location where you want the cloning to be made.
Type
git clone
into your IDE terminal followed by the URL you copied in Step 3 above, i.e.
git clone https://github.com/loosenthedark/zen-of-typing.git
PressEnter.
Your local clone has now been created.
See theGitHub Docs for more information on all of the above processes.
click to view
The application is deployed onHeroku, and can be accessed using the following URL:https://zen-of-typing.herokuapp.com/
The steps involved in deploying to Heroku were as follows:
- Create a
requirements.txt
file from the command line, and populate it with a list of project dependencies:
pip freeze > requirements.txt
Save, commit and push your changes to GitHub.
Create a
Procfile
echo web: node index.js > Procfile
NB: This file comes baked-in with theCode Institute project template repository, so can be skipped if you are using that in your own build
Create an account with Heroku, selecting Python as the primary development language.
Log in to your account, and in the top right-hand corner of theDashboard click onNew > Create new app
Enter a unique name for your app and select your region. Click onCreate app.
Go toSettings
- Click on theReveal Config Vars button in the 'Config Vars' section.
- Enter PORT in the KEY field and 8000 in the VALUE field.
- In the 'Buildpacks' section further down the settings page, click onAdd buildpack.
- Selectpython andnodejs from the menu of "officially supported buildpacks".
NB: Python must be placed at the top of your app's buildpack list. You can drag and drop your buildpacks to reposition them if necessary.
- Go toDeploy.
- SelectGitHub in the 'Deployment method' section.
- In the 'Connect to GitHub' section, search for the repository you wish to use, then clickConnect.
- Ensure that the project's main or master branch (depending on which is being used as the primary branch) is selected under 'Deploy a GitHub branch' in the 'Manual deploy' section.
NB: If you choose toEnable Automatic Deploys, Heroku will rebuild the app every time you push a change to GitHub (which is considered best practice in most instances).
- After clicking on theDeploy Branch button, you should see a message confirming that "Your app was successfully deployed" followed by aView button which can be clicked to launch and view the app.
Where code blocks/snippets/suggestions have been incorporated from external sources into this project's code, these have been noted through the use of comments. Beyond this, the developer made use of the following articles, workarounds and learning resources while building the site:
click to view
'How to let the user select an input from a finite list?' (Stack Overflow)
'How to print colored text to the terminal' (Stack Overflow)
'ANSI escape code' (Wikipedia)
'Ternary Operator in Python' (GeeksforGeeks)
'How to Do Ternary Operator Assignment in Python' (Webucator)
'How to measure elapsed time in Python?' (Stack Overflow) (as suggested by mymentor)
'PEP 257 -- Docstring Conventions' (Python.org)
'Using global variables in a function' (Stack Overflow)
'How to split up a long f-string in python?' (Stack Overflow)
'Absolute vs Relative Imports in Python' (Real Python)
'From virtualenv, pip freeze > requirements.txt give TONNES of garbage! How to trim it out?'
'Create Your Own Python Projects' (LinkedIn Learning)
'12 Beginner Python Projects - Coding Course' (Kylie Ying/freeCodeCamp)
click to view
All of the
body
text (game instructions, user feedback etc.) was composed by the developerThe short introductory practice text is a direct citation lifted from Tom Cargill's'Ninety-ninety rule' (Wikipedia)
Five of the six target texts provided are abridged versions of external resources that have been redirected into locally-stored
.txt
files. Those external resources are as follows:'Don't repeat yourself' (Wikipedia)
'Object-oriented programming' (Wikipedia)
'History of Python' > 'Version 3' (Wikipedia)
The text content of the project's
sunscreen.txt
file is an edited version of the lyrics toBaz Luhrmann's 1997 spoken-word single 'Everybody’s Free (To Wear Sunscreen)' (Genius)'PEP 20 -- The Zen of Python' (Python.org)
As documentedabove, the sixth list of lines to be typed is in fact a dynamically-loaded response from apyjokes API end-point.
click to view
Media title/description | Media format | Credit | Link to original media source(s) |
---|---|---|---|
Python icon ASCII art | .txt | Matthew Barber (honno) on GitHub | Reddit (Python subreddit) |
Python background wallpaper | .png | Python subreddit | |
Python logo used to create favicon | .svg | Wikipedia | Wikipedia |
pyjokes logo | .png | pyjokes Docs) | pyjokes Docs |
Typewriter ASCII art | .jpg | Pinterest) | chelleline.com |
Computer ASCII art | .jpg | Pinterest) | text-mode.tumblr.com/ |
- As with my previous two projects, my number one cheerleader, product tester and source of both inspiration and sanity throughout the current build process has been my amazing partner Ana.Muito obrigado, menina ❤️
- My mentorTim Nelson was also on hand once more to provide invaluable guidance and encouragement. Among many other things, he helped me identify an issue with my
requirements.txt
file that had escaped my attention 🙌 😅
This site has been created for development purposes only.