Skip to content

Commit 05e7e21

Browse files
authored
Add files via upload
1 parent 3c9af4b commit 05e7e21

12 files changed

Lines changed: 3247 additions & 0 deletions

File tree

versions/ver1.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import time
2+
import datetime
3+
4+
def get_text():
5+
# Example sentences to type - you can add more!
6+
texts = [
7+
"The quick brown fox jumps over the lazy dog.",
8+
"Python is an awesome programming language.",
9+
"Type as fast and accurately as you can!",
10+
"Artificial intelligence is the future.",
11+
"Practice makes perfect in typing speed games."
12+
]
13+
# For simplicity, just pick the first text.
14+
# You can randomize or choose more dynamically.
15+
return texts[0]
16+
17+
def calculate_wpm(num_chars, elapsed_seconds):
18+
words = num_chars / 5 # Standard word length for typing tests
19+
minutes = elapsed_seconds / 60
20+
if minutes == 0:
21+
return 0
22+
return words / minutes
23+
24+
def calculate_accuracy(original, typed):
25+
correct_chars = sum(1 for o, t in zip(original, typed) if o == t)
26+
accuracy = (correct_chars / len(original)) * 100
27+
return accuracy
28+
29+
def save_score(wpm, accuracy):
30+
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
31+
filename = f"score{timestamp}.txt"
32+
with open(filename, "w") as f:
33+
f.write(f"WPM: {wpm:.2f}\n")
34+
f.write(f"Accuracy: {accuracy:.2f}%\n")
35+
f.write(f"Timestamp: {timestamp}\n")
36+
print(f"Score saved to {filename}")
37+
38+
def typeracer():
39+
print("Welcome to Offline TypeRacer!")
40+
text = get_text()
41+
print("\nType the following text as fast and accurately as you can:\n")
42+
print(text)
43+
input("Press Enter when ready to start...")
44+
45+
start = time.time()
46+
typed = input()
47+
end = time.time()
48+
elapsed = end - start
49+
50+
wpm = calculate_wpm(len(typed), elapsed)
51+
accuracy = calculate_accuracy(text, typed)
52+
53+
print(f"\nTime: {elapsed:.2f} seconds")
54+
print(f"WPM: {wpm:.2f}")
55+
print(f"Accuracy: {accuracy:.2f}%")
56+
57+
save_score(wpm, accuracy)
58+
59+
if __name__ == "__main__":
60+
typeracer()

versions/ver10.py

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
import time
2+
import datetime
3+
import os
4+
import sys
5+
import random
6+
import statistics
7+
import hashlib
8+
import matplotlib.pyplot as plt
9+
10+
# Cross-platform getch: Windows and Unix
11+
if os.name == 'nt':
12+
import msvcrt
13+
14+
def getch():
15+
ch = msvcrt.getwch()
16+
if ch == '\x00' or ch == '\xe0': # Special keys
17+
msvcrt.getwch() # consume next char
18+
return ''
19+
return ch
20+
else:
21+
import tty
22+
import termios
23+
24+
def getch():
25+
fd = sys.stdin.fileno()
26+
old_settings = termios.tcgetattr(fd)
27+
try:
28+
tty.setraw(fd)
29+
ch = sys.stdin.read(1)
30+
if ord(ch) == 3: # Ctrl-C
31+
raise KeyboardInterrupt
32+
finally:
33+
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
34+
return ch
35+
36+
37+
def choose_sentence():
38+
texts = [
39+
"The quick brown fox jumps over the lazy dog.",
40+
"Python is an awesome programming language.",
41+
"Type as fast and accurately as you can!",
42+
"Artificial intelligence is the future.",
43+
"Practice makes perfect in typing speed games.",
44+
"OpenAI develops powerful AI models.",
45+
"Consistency is key to mastery.",
46+
"Always challenge yourself to improve.",
47+
"Debugging is twice as hard as writing code.",
48+
"A journey of a thousand miles begins with a single step."
49+
]
50+
print("Choose a sentence to type:")
51+
for i, sentence in enumerate(texts, 1):
52+
print(f"{i}. {sentence}")
53+
print(f"{len(texts)+1}. Random sentence")
54+
while True:
55+
choice = input(f"Enter choice (1-{len(texts)+1}): ").strip()
56+
if choice.isdigit():
57+
choice_num = int(choice)
58+
if 1 <= choice_num <= len(texts):
59+
return texts[choice_num - 1]
60+
elif choice_num == len(texts) + 1:
61+
return random.choice(texts)
62+
print("Invalid choice. Please try again.")
63+
64+
65+
def calculate_wpm(num_chars, elapsed_seconds):
66+
words = num_chars / 5 # Standard word length
67+
minutes = elapsed_seconds / 60
68+
if minutes == 0:
69+
return 0
70+
return words / minutes
71+
72+
73+
def calculate_accuracy(original, typed):
74+
correct_chars = sum(1 for o, t in zip(original, typed) if o == t)
75+
accuracy = (correct_chars / len(original)) * 100
76+
return accuracy
77+
78+
79+
def detect_machine_input(time_stamps):
80+
MIN_TIME_THRESHOLD = 0.03 # 30ms minimum between keystrokes suspiciously fast
81+
MAX_STD_THRESHOLD = 0.005 # very low std dev = very consistent timing
82+
if not time_stamps:
83+
return False
84+
min_time = min(time_stamps)
85+
std_dev = statistics.stdev(time_stamps) if len(time_stamps) > 1 else 0
86+
return min_time < MIN_TIME_THRESHOLD and std_dev < MAX_STD_THRESHOLD
87+
88+
89+
def save_score(wpm, accuracy, time_between_letters, is_cheating, sentence):
90+
# Ensure stats folder exists
91+
stats_folder = "stats"
92+
os.makedirs(stats_folder, exist_ok=True)
93+
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
94+
score_filename = os.path.join(stats_folder, f"stats{timestamp}.txt")
95+
stats_filename = os.path.join(stats_folder, "stats.txt")
96+
97+
if is_cheating:
98+
# Create a cheat hash using WPM, accuracy, timestamp
99+
cheat_string = f"{wpm:.2f}{accuracy:.2f}{timestamp}"
100+
cheat_hash = hashlib.sha256(cheat_string.encode('utf-8')).hexdigest()
101+
cheat_content = (f"CHEAT DETECTED\nHash: {cheat_hash}\n"
102+
"This session's stats are invalid due to detected macro or automated input.\n"
103+
f"Sentence: {sentence}\n")
104+
105+
# Write cheat hash instead of normal stats
106+
with open(score_filename, "w") as f:
107+
f.write(cheat_content)
108+
print(f"Cheating detected! Invalid stats saved to {score_filename}")
109+
110+
# Append invalid marker to cumulative stats.txt
111+
with open(stats_filename, "a") as sf:
112+
sf.write(f"{timestamp}, CHEAT DETECTED, WPM: 0.00, Time: 0.00s, Accuracy: 0.00%, "
113+
f"AvgTimeBetweenLetters: 0.000s, Sentence: {sentence}\n")
114+
else:
115+
# Save normal stats to individual file WITH SENTENCE
116+
with open(score_filename, "w") as f:
117+
f.write(f"WPM: {wpm:.2f}\n")
118+
f.write(f"Accuracy: {accuracy:.2f}%\n")
119+
f.write(f"Timestamp: {timestamp}\n")
120+
f.write(f"Sentence: {sentence}\n")
121+
if time_between_letters:
122+
avg_time = sum(time_between_letters) / len(time_between_letters)
123+
f.write(f"Avg Time Between Letters: {avg_time:.3f} sec\n")
124+
f.write("Time Between Letters (s): " + ", ".join(f"{t:.3f}" for t in time_between_letters) + "\n")
125+
print(f"Score saved to {score_filename}")
126+
127+
# Append normal stats to cumulative stats.txt
128+
avg_time = sum(time_between_letters) / len(time_between_letters) if time_between_letters else 0
129+
elapsed_global = elapsed if 'elapsed' in globals() else 0.0
130+
with open(stats_filename, "a") as sf:
131+
sf.write(f"{timestamp}, WPM: {wpm:.2f}, Time: {elapsed_global:.2f}s, Accuracy: {accuracy:.2f}%, "
132+
f"AvgTimeBetweenLetters: {avg_time:.3f}s, Sentence: {sentence}\n")
133+
134+
135+
class Colors:
136+
RED = "\033[31m"
137+
GREEN = "\033[32m"
138+
CYAN = "\033[36m"
139+
RESET = "\033[0m"
140+
UNDERLINE = "\033[4m"
141+
142+
143+
def print_with_highlight(original_text, current_index):
144+
print("\r", end="")
145+
for i, c in enumerate(original_text):
146+
if i == current_index:
147+
print(f"{Colors.CYAN}{Colors.UNDERLINE}{c}{Colors.RESET}", end="")
148+
else:
149+
print(c, end="")
150+
print(" ", end="", flush=True)
151+
152+
153+
def plot_stats():
154+
stats_folder = "stats"
155+
stats_filename = os.path.join(stats_folder, "stats.txt")
156+
if not os.path.isfile(stats_filename):
157+
print("No stats file found. Please complete at least one typing test first.")
158+
return
159+
timestamps, wpms, times_, accuracies, avg_times_btwn_letters = [], [], [], [], []
160+
with open(stats_filename, "r") as sf:
161+
for line in sf:
162+
parts = line.strip().split(", ")
163+
if len(parts) < 6:
164+
continue # Skip lines that don't have expected columns
165+
# Skip cheat detected lines
166+
if "CHEAT DETECTED" in parts[1]:
167+
continue
168+
timestamps.append(parts[0])
169+
try:
170+
wpm = float(parts[1].split(": ")[1])
171+
t = float(parts[2].split(": ")[1].replace('s', ''))
172+
acc = float(parts[3].split(": ")[1].replace('%', ''))
173+
avg_t = float(parts[4].split(": ")[1].replace('s', ''))
174+
except (IndexError, ValueError):
175+
continue
176+
wpms.append(wpm)
177+
times_.append(t)
178+
accuracies.append(acc)
179+
avg_times_btwn_letters.append(avg_t)
180+
if not wpms:
181+
print("No valid stats data found to plot.")
182+
return
183+
x_vals = list(range(1, len(wpms) + 1))
184+
185+
fig, axs = plt.subplots(2, 2, figsize=(12, 8))
186+
axs[0, 0].plot(x_vals, wpms, marker='o', color='blue')
187+
axs[0, 0].set_title('WPM over Runs')
188+
axs[0, 0].set_xlabel('Run Number')
189+
axs[0, 0].set_ylabel('WPM')
190+
191+
axs[0, 1].plot(x_vals, accuracies, marker='o', color='green')
192+
axs[0, 1].set_title('Accuracy (%) over Runs')
193+
axs[0, 1].set_xlabel('Run Number')
194+
axs[0, 1].set_ylabel('Accuracy (%)')
195+
196+
axs[1, 0].plot(x_vals, times_, marker='o', color='red')
197+
axs[1, 0].set_title('Time Taken (seconds) over Runs')
198+
axs[1, 0].set_xlabel('Run Number')
199+
axs[1, 0].set_ylabel('Time (s)')
200+
201+
axs[1, 1].plot(x_vals, avg_times_btwn_letters, marker='o', color='purple')
202+
axs[1, 1].set_title('Avg Time Between Letters (seconds) over Runs')
203+
axs[1, 1].set_xlabel('Run Number')
204+
axs[1, 1].set_ylabel('Avg Time Between Letters (s)')
205+
206+
plt.tight_layout()
207+
208+
def on_key(event):
209+
if event.key.lower() == 'c':
210+
plt.close(event.canvas.figure)
211+
212+
fig.canvas.mpl_connect('key_press_event', on_key)
213+
plt.show()
214+
215+
216+
def keydash():
217+
global elapsed # For access in save_score
218+
219+
while True:
220+
text = choose_sentence()
221+
print("\nType the following text as fast and accurately as you can:\n")
222+
print(text)
223+
print("\nPress Enter when ready to start...")
224+
225+
while True:
226+
ch = getch()
227+
if ch == '\r' or ch == '\n':
228+
break
229+
230+
print("\nStart typing:\n")
231+
typed = []
232+
current_index = 0
233+
time_stamps = []
234+
start = time.time()
235+
last_time = start
236+
print_with_highlight(text, current_index)
237+
238+
while current_index < len(text):
239+
ch = getch()
240+
241+
# Handle backspace
242+
if ch in ('\b', '\x7f'):
243+
if typed:
244+
typed.pop()
245+
current_index -= 1
246+
print_with_highlight(text, current_index)
247+
continue
248+
249+
if ch == '':
250+
continue
251+
252+
expected_char = text[current_index]
253+
if ch != expected_char:
254+
print(f"\n{Colors.RED}Incorrect letter '{ch}'. Please type '{expected_char}'.{Colors.RESET}")
255+
# Wait here until correct letter is pressed, do not move ahead
256+
continue
257+
258+
typed.append(ch)
259+
now = time.time()
260+
time_stamps.append(now - last_time)
261+
last_time = now
262+
current_index += 1
263+
print_with_highlight(text, current_index)
264+
265+
end = time.time()
266+
elapsed = end - start
267+
typed_str = ''.join(typed)
268+
wpm = calculate_wpm(len(typed_str), elapsed)
269+
accuracy = calculate_accuracy(text, typed_str)
270+
avg_time_between_letters = sum(time_stamps) / len(time_stamps) if time_stamps else 0
271+
272+
# Anti-cheat detection
273+
is_cheating = detect_machine_input(time_stamps)
274+
275+
if is_cheating:
276+
print(f"\n{Colors.RED}[Anti-Cheat] Warning: Detected unnaturally consistent and rapid keypresses.")
277+
print("This may indicate use of automated input or macros.{Colors.RESET}\n")
278+
279+
print("\n\n--- Results ---")
280+
print(f"Time taken: {elapsed:.2f} seconds")
281+
print(f"WPM: {wpm:.2f}")
282+
print(f"Accuracy: {accuracy:.2f}%")
283+
print(f"Average time between letters: {avg_time_between_letters:.3f} seconds")
284+
print("Time between letters (seconds):")
285+
print(', '.join(f"{t:.3f}" for t in time_stamps))
286+
287+
save_score(wpm, accuracy, time_stamps, is_cheating, text)
288+
289+
print("\nWhat would you like to do next?")
290+
print("1. Play again")
291+
print("2. View performance graph")
292+
print("3. Exit")
293+
294+
while True:
295+
choice = input("Enter choice (1-3): ").strip()
296+
if choice == "1":
297+
print("\nStarting new game...\n")
298+
break
299+
elif choice == "2":
300+
print("\nLoading performance graph...\n")
301+
plot_stats()
302+
print("\nWhat would you like to do next?")
303+
print("1. Play again")
304+
print("2. View performance graph")
305+
print("3. Exit")
306+
elif choice == "3":
307+
print("Goodbye!")
308+
return
309+
else:
310+
print("Invalid choice. Please enter 1, 2, or 3.")
311+
312+
313+
def main_menu():
314+
while True:
315+
print("=== Offline KeyDash Main Menu ===")
316+
print("1. Typing test (choose sentence)")
317+
print("2. View overall stats graph")
318+
print("3. Exit")
319+
choice = input("Enter your choice (1-3): ").strip()
320+
if choice == "1":
321+
keydash()
322+
elif choice == "2":
323+
plot_stats()
324+
elif choice == "3":
325+
print("Exiting program. Goodbye!")
326+
break
327+
else:
328+
print("Invalid choice. Please enter 1, 2, or 3.")
329+
330+
331+
if __name__ == "__main__":
332+
try:
333+
main_menu()
334+
except KeyboardInterrupt:
335+
print("\nTyping test interrupted.")

0 commit comments

Comments
 (0)