DEV Community

Cover image for 😻Build your own CLI version of MonkeyType 🙈

😻Build your own CLI version of MonkeyType 🙈

Shrijal Acharya on February 18, 2024

TL;DR In this easy-to-follow tutorial, you will learn how to build your own CLI version of MonkeyType in minutes. 😎 What you will learn...
Collapse
 
aayyusshh_69 profile image
Aayush Pokharel

Long yet beautiful article. Well documented. Thank you

Collapse
 
aayyusshh_69 profile image
Aayush Pokharel

Can I have a link to the full source code?

Collapse
 
shricodev profile image
Shrijal Acharya

I think I have shared it in the article itself. here you go: github.com/shricodev/blogs/tree/ma...
I have also added docstrings to make it easy to understand. 😄

Collapse
 
shricodev profile image
Shrijal Acharya

You're welcome, @aayyusshh_69 👍

Collapse
 
shekharrr profile image
Shekhar Rajput

Bookmarked! Will take a look later. 👍

Collapse
 
shricodev profile image
Shrijal Acharya

Thank you, @shekharrr :)

Collapse
 
coderatul profile image
Atul Kushwaha • Edited

what tool do you use for thumbnail ?

Collapse
 
shricodev profile image
Shrijal Acharya

lexica.art mostly for the AI stock images. leiapix for animations.

Collapse
 
ooosys profile image
oOosys • Edited

Nice explanations and a nice motivation to come up with own typing speed game!
After stripping the class overhead and fixing some issues (e.g. with more code as necessary or with blinking of the speed and accuracy output) the number of code lines shrinks from a total in two files of 164 to 79 lines in a single file. Here how it looks like:

Image description

#!/usr/bin/python3
# https://dev.to/shricodev/build-your-own-cli-version-of-monkeytype-bm7
from curses import *
import random
from time import time as T
tgtLines=open("typing_texts.txt").read().split("\n")
clrEnd=12*" "
def main(stdscr):
    curs_set(0) # 0 -> invisible cursor
    init_pair(1,0,2); blackOnGreen  =color_pair(1)
    init_pair(2,0,1); blackOnRed        =color_pair(2)
    init_pair(3,0,7); blackOnWhite  =color_pair(3)
    init_pair(4,0,3); blackOnYellow =color_pair(4)
    stdscr.clear()
    stdscr.addstr(2,8,"     Welcome to the speed type test")
    stdscr.addstr(3,8,"Press any key to start playing the game!")
    stdscr.getkey()
    nextRound=1
    while nextRound: # of typing a Line
        usrLine = []
        tgtLine = random.choice(tgtLines)
        lenTgt = len(tgtLine)
        stdscr.clear() # make the Terminal text area blank (free of any text) 
        stdscr.addstr(3-1,9-1, tgtLine, blackOnWhite) 
        # ^-- write the text of the line at third line starting with 9-th character
        usrLine.append( stdscr.getkey() ) # wait for pressing a key on the keyboard 
        sT = T()-0.15 # time starts running after first char of the Line is typed
        # ^-- speed of typing the first character is deliberately set to 400 chars/min
        if usrLine[-1]  == "\x1B":  # exit the program with the Esc key
            nextRound = 0; continue # <-- equivalent to  using  'break' instead  

        runTest=1
        while runTest:
            lenUsr = len(usrLine)
            if lenUsr == lenTgt: # <- entire text line was typed in
                stdscr.addstr(3, 8, "  CONGRATULATIONS! (next:'Esc',quit:'Esc,Esc')")
                stdscr.addstr(0, 8, f"'{usrLine[-1]}'"+clrEnd)
                stdscr.addstr(3-1,9-1+(lenUsr-1), tgtLine[lenUsr-1], 
                    blackOnGreen if usrLine[-1]==tgtLine[lenUsr-1] else blackOnRed )
                stdscr.addstr(8, 8, f"{''.join(usrLine):{lenTgt}s}", blackOnWhite)
                stdscr.getkey()
                runTest = 0
                continue
            if usrLine[-1]=="KEY_BACKSPACE": 
                #     [-1] means last item/character in list/string
                stdscr.addstr(0, 8, f"'{usrLine[-1]}'"+clrEnd)
                usrLine=usrLine[:-2] # remove two entries from the list
                if lenUsr >= 2:
                    stdscr.addstr(2, 8+(lenUsr-2), tgtLine[lenUsr-2], blackOnWhite)
                usrLine.append( stdscr.getkey() )
                continue
            if usrLine[-1]  == "\x1B":      # stop typing the line with Esc
                runTest = 0; continue   # <-- equivalent to  using  'break' instead  
            if len(usrLine[-1]) > 1: # special key typed ... IGNORE
                usrLine.pop(); usrLine.append( stdscr.getkey() ) # get another one
                continue

            # --- ^ -- special cases are handled - handling default case --v: 

            stdscr.addstr(0, 8, f"'{usrLine[-1]}'"+clrEnd)
            dT=T()-sT
            stdscr.addstr(5, 8,
    f" Speed   : { int( 60 * lenUsr / (T()-sT) ):3d} Char/min ",    blackOnYellow)
            stdscr.addstr(6, 8, 
f" Accuracy: {int( 100 * sum([ 1 for i in range(lenUsr) if usrLine[i]==tgtLine[i] ]) / lenUsr ):3d}    %     ", 
                blackOnYellow)
            stdscr.addstr(3-1,9-1+(lenUsr-1), tgtLine[lenUsr-1], 
                blackOnGreen if usrLine[-1]==tgtLine[lenUsr-1] else blackOnRed )
            stdscr.addstr(8, 8, f"{''.join(usrLine):{lenTgt}s}", blackOnWhite)
            stdscr.refresh()

            # Now wait for the next input character 
            usrLine.append( stdscr.getkey() )
            continue # and continue running the loop (THIS line can be skipped/removed)

if __name__ == "__main__":
    wrapper(main) # has the advantage to restore Terminal session on Errors
    # using: stdscr = initscr(); start_color() would allow skipping def main(stdscr)
    #   but in case of errors the Terminal would be screwed (e.g. echo disabled).
Enter fullscreen mode Exit fullscreen mode
Collapse
 
shricodev profile image
Shrijal Acharya

@ooosys Wow, it's great that you created your custom version of this. 😄
Doing it your way is wonderful, but I wanted to make it more modular and somewhat maintainable. I have a small take on this: it is not just about the number of code lines but also about whether it is understandable and somewhat about the prod application type.

Collapse
 
ooosys profile image
oOosys

Yes, this is why I suggest that the 'oOo way' is another one for each individual, What is better understandable for you is harder to understand for me and if I infer it right from what you have written the same is the other way also the case (you prefer higher level of hierarchy considering it better understandable, I consider it an obstacle on the way to understanding). What remains as common part is the English language required to be less different from individual to individual in order to play as means for interpersonal exchange of ideas, You are for example spending 21 MByte of Internet bandwidth to share an image in an article which essence are 21 KByte of actual useful data - this makes a factor of 1000 times more for the size of the decoration. It's like spending 21 million Dollar on paining the walls of a house worth 21 thousand $.
Never mind, more ore less it does not really matter as the amount of people reading THIS is so small that the impact is as good as none for both versions and the majority of people will stick with 10fastfingers or monkeytype (for me a hint what the site thinks about its users).