""" Updated: - Add line-type 'note' with syntax '=' - Add line-type 'list' with syntax '&' - Change syntax of line-type 'title' to '+' instead of '-' - Remove unnecessary spaces in input file To be updated: - Learn answers - Remove unnecessary line breaks in input file - Answer judged correct if contains keyword - Title and subtitle also printed if answer in the section is incorrect - Consider to use json input file - Graphic user interface - Another program to help create related input file - Optimize """ try: import re import difflib def check_string(line): string = r"[\w\d\s\?\.\"\(\)\[\]\{\}\-!,;'/]*" return bool( re.match( f"([+~=] {string}\Z)|(! version: \d+\.?\d*\s?\Z)|(# {string}: {string}\Z)|(& ({string}: )+{string}\Z)", line, ) ) def compare_string(answer, response): diff = list(difflib.ndiff(answer, response)) diff_count = 0 for line in diff: if line[0] != " ": diff_count += 1 return round(1 - (diff_count / len(diff)), 4) * 100 file_name = input("Enter file name (with postfix) >> ") if file_name == "": file_name = "test_2.0.rvwt" with open(file_name) as f: data = f.readlines() count_prompts = 0 count_correct = 0 correct_param = 70 incorrect = [] if len(data) == 0: raise Exception("Error: file empty") if data.pop(0).split(".")[0].replace("\n", "") != "! version: 2": raise Exception("Error: file version not provided or does not match") for line_index in range(len(data)): if not check_string(data[line_index]): raise Exception( f"Error: invalid syntax at line {line_index+2} of file '{file_name}'" ) for line_index in range(len(data)): line = data[line_index] if line[0] == "+": line = line[2:].replace("\n", "") print("\n\n- " + line + " -\n") elif line[0] == "~": line = line[2:].replace("\n", "") print("\n" + line + "\n") elif line[0] == "=": line = line[2:].replace("\n", "") print(line) elif line[0] == "#": count_prompts += 1 line = line[2:].split(":") prompt = line[0] answer = line[1][1:-1] if prompt != "": prompt += " " response = input(prompt + ">> ") if response.lower() == answer.lower(): print("Correct") count_correct += 1 elif compare_string(response.lower(), answer.lower()) >= correct_param: print( f"Probably correct: answer is '{answer}', similarity {round(compare_string(response.lower(), answer.lower()), 4)}%" ) judge = input("Is your answer correct? [[y]/n] ").lower() if judge == "n": incorrect.append((prompt, answer)) else: count_correct += 1 if judge != "y": print("Warning: invalid input, answer judged as correct") else: print( f"Probably incorrect: answer is '{answer}', similarity {round(compare_string(response.lower(), answer.lower()), 4)}%" ) judge = input("Is your answer correct? [y/[n]] ").lower() if judge == "y": count_correct += 1 else: incorrect.append((prompt, answer)) if judge != "n": print("Warning: invalid input, answer judged as incorrect") elif line[0] == "&": line = line[2:].split(":") prompt = line.pop(0) answers = list(map(lambda x: x[1:].lower().replace("\n", ""), line)) items = len(answers) count_prompts += items correct_answers = 1 answers_max = items print(prompt) while answers_max >= correct_answers: response = input(f"{correct_answers}/{answers_max} >> ") if response == "": break elif response.lower() in answers: print("Correct") answers.pop(answers.index(response.lower())) correct_answers += 1 else: max_similarity = 0 max_index = 0 for answer in answers: if compare_string(response.lower(), answer) > max_similarity: max_similarity = compare_string(response.lower(), answer) max_index = answers.index(answer) if max_similarity >= correct_param: print( f"Probably correct: closest answer is '{answers[max_index]}', similarity {round(compare_string(response.lower(), answers[max_index]), 4)}%" ) judge = input("Is your answer correct? [[y]/n] ").lower() if judge == "n": answers_max -= 1 else: answers.pop(max_index) correct_answers += 1 if judge != "y": print("Warning: invalid input, answer judged as correct") else: print("Probably incorrect") answers_max -= 1 if len(answers) != 0: print("You didn't get the following answers correct: ", end="") for answer in answers: incorrect.append((prompt, answer)) print("'" + answer+ "' ", end="") count_correct += correct_answers - 1 if len(answers) != 0: print() else: continue if count_prompts <= 1: print(f"\n\nOut of {count_prompts} question, ", end="") else: print(f"\n\nOut of {count_prompts} questions, ", end="") if count_correct <= 1: print(f"{count_correct} was answered correctly, ", end="") else: print(f"{count_correct} were answered correctly, ", end="") print(f"which is {round(count_correct / count_prompts * 100, 2)}%. ") if count_prompts == count_correct: print("Congratulations for finishing perfectly! ") else: if count_prompts - count_correct <= 1: print(f"The question with incorrect response is listed below: \n") else: print(f"The questions with incorrect responses are listed below: \n") for line in incorrect: (prompt, answer) = line print(prompt + "-> " + answer) except Exception as e: print("\nException: " + str(e)) except KeyboardInterrupt: print("\nProcess quitted")