#!/usr/bin/env python3 # RETOOR import sys import termios import tty import random import time import select key_mapping = { "[A": "up", "[B": "down", "[C": "right", "[D": "left", "2A": "shift+up", "2B": "shift+down", "2C": "shift+right", "2D": "shift+left", "5A": "ctrl+up", "5B": "ctrl+down", "5C": "ctrl+right", "5D": "ctrl+left", "\x17": "C-w", "\x0f": "C-o", "\x03": "C-c", "\x07": "C-g", } def get_key(key_previous=None): fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) special_char = "\x1b" if key_previous is None: key_previous = special_char try: tty.setraw(fd) key = sys.stdin.read(1) if key in key_mapping: return key_mapping[key] if key == "[": key += sys.stdin.read(1) if key in key_mapping: return key_mapping.get(key, key) if key[-1] == "1": # shift plus special key if sys.stdin.read(1) == ";": # ;followed by key key = sys.stdin.read(2) return key_mapping.get(key, key) if key == special_char: time.sleep(0.1) if sys.stdin in select.select([sys.stdin], [], [], 0)[0]: key += sys.stdin.read(2) key = key_mapping.get(key, key) finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) return key def clear_terminal(): sys.stdout.write("\033[2J") # Move cursor sys.stdout.write("\033[H") sys.stdout.flush() class Randoms: input_choices = list("abcdefghijklmnopqrstuvwxyz") word_choices = word_list = [ "apple", "banana", "cherry", "date", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon", "mango", "nectarine", "orange", "pear", "quince", "raspberry", "strawberry", "tangerine", "ugli", "vaccine", "watermelon", "xigua", "yam", "zucchini", ] def __init__(self): self.replace = {} self.replace[""] = random.choice(self.input_choices) self.replace[""] = random.choice(self.input_choices) self.replace[""] = random.choice(self.input_choices) self.replace[""] = random.randint(2, 5) self.replace[""] = random.randint(2, 5) self.replace[""] = random.randint(2, 5) self.replace[""] = random.choice(self.word_choices) self.replace[""] = random.choice(self.word_choices) self.replace[""] = random.choice(self.word_choices) def apply(self, task): task.question = task.base_question task.keyboard_input = task.base_keyboard_input for key, value in self.replace.items(): task.question = task.question.replace(key, str(value)) task.keyboard_input = task.keyboard_input.replace( key, ",".join(list(str(value))) ) task.applied_random = ( task.question != task.base_question or task.keyboard_input != task.base_keyboard_input ) class Task: questions_total = 0 def __init__(self, question, keyboard_input): Task.questions_total += 1 self.question_number = Task.questions_total self.base_question = question self.base_keyboard_input = keyboard_input self.question = question self.keyboard_input = keyboard_input self.success = False self.tasks = [] self.applied_random = False self.first_time_executed = True r = Randoms() r.apply(self) def add_task(self, task): self.tasks.append(task) def execute(self): if not self.first_time_executed: r = Randoms() r.apply(self) self.first_time_executed = False print("{}".format(self.question)) index = 0 mistake = False key_previous = None for expected in self.keyboard_input.split(","): key = get_key(key_previous) key_previous = key if key == "\x1b": key = get_key(key_previous) key_previous = key if key == "C-c": raise KeyboardInterrupt() if key == "\x17": print("CTRL+W") else: print(key, end="", flush=True) if expected == key: index += 1 else: mistake = True if mistake: print('\n"{}" is incorrect.'.format(repr(key))) print( '\nExpected input: "{}".'.format( self.keyboard_input.replace(",", "") ) ) print("\nPress any key to continue...") get_key(None) break if key == "q": break if not mistake: self.success = True print("") print( random.choice( ["Great!", "Excelent!", "Awesome!", "Keep it up!", "Perfect!"] ) ) print("") self.success = all([task.execute() for task in self.tasks]) time.sleep(0.40) return self.success tasks = [ Task("Open terminal\n", ":,terminal,\r"), Task("Delete from the cursor to the end of the word.", "d,e"), Task("Delete from the cursor to the end of the line.", "d,$"), Task("Delete from the cursor to the beginning of the next word.", "d,w"), Task("Delete lines using a numeric value.", ",d,d"), Task("Move backward in the search results.", "N"), Task("Move forward in the search results.", "n"), Task("Move to line number .", ",G"), Task("Undo all changes on the current line.", "U"), Task("Move to the end of the word.", "e"), Task("Move to the beginning of the line.", "0"), Task("Search backward for .", ":,?,,\r"), Task("Search forward for .", ":,/,,\r"), Task("Display the current location in the status bar.", "C-g"), Task("Indent the selected text.", ">,>"), Task("De-indent the selected text.", "<,<"), Task("Save the document as .py.", ":,w, ,,.,p,y,\r"), Task("Replace the first occurrence of with .", ":,s,/,,/,,/,g"), Task( "Replace all occurrences of with in the entire file.", ":,%,s,/,,/,,/,g", ), Task( "Replace all occurrences of with in the entire file, with confirmation for each change.", ":,%,s,/,,/,,/,g,c", ), Task( "Select the next five characters and save them to a file.", "v,right,right,right,right,:,w", ), Task("Exit Vim.", ":,q"), Task("Split the screen vertically.", ":,v,s,p,l,i,t,\r"), Task("Split the screen horizontally.", ":,s,p,l,i,t,\r"), Task("Merge the file .txt into the current file.", ":,r, ,,.,t,x,t,\r"), Task( "Move three words to the left without using numeric values.", "ctrl+left,ctrl+left,ctrl+left", ), Task( "Move three words to the right without using numeric values.", "ctrl+right,ctrl+right,ctrl+right", ), Task("Return to the previous position.", "C-o"), Task("Type .", ""), Task("Indent the current line and the two lines below.", "v,down,down,>,>"), Task("Enable case-sensitive search.", ":,s,e,t, ,n,o,i,c"), Task("Enable case-insensitive search.", ":,s,e,t, ,i,c"), Task("Copy the word under the cursor.", "y,w"), Task("Replace the text under the cursor with .", "R,"), Task("Insert text at the end of the line.", "A"), Task("Insert text after the cursor.", "a"), Task("Insert a new line below the current line.", "o"), Task("Insert a new line above the current line.", "O"), Task("Move to the beginning of the document.", "g,g"), Task("Move to the end of the line.", "$"), Task("Move to the end of the document.", "G"), Task( "Select the next four characters and copy them.", "v,right,right,right,right,y" ), Task("Switch to the next window.", "C-w,C-w"), Task("Swap the position of the current window with another.", "C-w,r"), Task("Copy the current line.", "y,y"), Task("Copy all content.", "y"), Task("Paste the copied content.", "p"), Task("Replace the character under the cursor with ''.", "r,"), Task("Delete the character under the cursor.", "x"), Task("Delete the line under the cursor.", "d,d"), Task("Cut the current line.", "c,c"), Task("Type .", ""), ] # shift select Move with>> def main(): questions_correct = [] questions_incorrect = [] question_count = 0 durations = [] while tasks: clear_terminal() if not durations: avg_reaction_time = 0 else: avg_reaction_time = sum(durations) / len(durations) num_correct = len(questions_correct) num_incorrect = len(questions_incorrect) avg_time = round(sum(durations) / len(durations), 2) if durations else 0 print( "Correct: {}\tIncorrect: {}\tAvg reaction time: {}".format( num_correct, num_incorrect, avg_time ) ) print("") question_count += 1 task = random.choice(tasks) print("{}. ".format(question_count), end="") time_start = time.time() if task.execute(): tasks.remove(task) questions_correct.append(task) else: questions_incorrect.append(task) time_end = time.time() durations.append(time_end - time_start) if __name__ == "__main__": main()