Files
better-catchphrase/random_timer.py
2025-08-09 16:03:12 -04:00

135 lines
4.1 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Catchphrase
Author: Jerick Oates
"""
import random
import time
import threading
import sys
# ---------- CONFIG ----------
WORDS_FILE = "words.txt" # file with one word per line
TIMER_SECONDS = 30 # how long the timer runs
# ---------- HELPERS ----------
def load_words(path=WORDS_FILE):
"""Return a list of non-empty stripped lines from the given file."""
try:
with open(path, "r", encoding="utf-8") as f:
words = [line.strip() for line in f if line.strip()]
if not words:
raise ValueError("Word file is empty.")
return words
except Exception as e:
print(f"❌ Error loading words: {e}")
sys.exit(1)
def pick_random(words):
"""Return a random word from the list."""
return random.choice(words)
# ---------- TIMER THREAD ----------
class CountdownTimer(threading.Thread):
"""
Background thread that counts down for TIMER_SECONDS.
When it finishes, it calls an optional callback.
"""
def __init__(self, seconds=TIMER_SECONDS, on_finish=None):
super().__init__()
self.seconds = seconds
self.on_finish = on_finish # function to call when timer ends
self._running = threading.Event()
self._running.set()
def run(self):
for _ in range(self.seconds):
if not self._running.is_set():
break # stopped early by user
time.sleep(1)
# Timer finished (or was stopped)
self._running.clear()
if self.on_finish:
self.on_finish() # announce completion
def stop(self):
"""Stop the timer before it reaches zero."""
self._running.clear()
@property
def is_running(self):
return self._running.is_set()
# ---------- MAIN LOOP ----------
def main():
words = load_words()
timer_thread = None
print("=== Catchphrase, but Better ===")
print("Commands:")
print(" start - begin the 30-second timer and show a word immediately")
print(" next - show another random word while the timer is running")
print(" stop - cancel the timer early")
print(" exit / quit - leave the program")
# Helper that prints when the timer ends
def announce_finish():
print("\n⏰ Time is up! Returning to menu.")
print("=== Catchphrase ===")
print("Commands:")
print(" start - begin the 30-second timer and show a word immediately")
print(" next - show another random word while the timer is running")
print(" stop - cancel the timer early")
print(" exit / quit - leave the program")
while True:
try:
cmd = input("\n> ").strip().lower()
except EOFError: # CtrlD
break
if cmd in ("exit", "quit"):
if timer_thread and timer_thread.is_running:
timer_thread.stop()
timer_thread.join()
print("👋 Goodbye!")
break
elif cmd == "start":
if timer_thread and timer_thread.is_running:
print("[!] Timer is already running.")
else:
# Start a new timer with the finish callback
timer_thread = CountdownTimer(on_finish=announce_finish)
timer_thread.daemon = True # exit when main thread exits
timer_thread.start()
# Show the first word right away
print(pick_random(words))
print(f"[+] 30-second timer started. ({TIMER_SECONDS}s)")
elif cmd == "next":
if not (timer_thread and timer_thread.is_running):
print("[!] No active timer - use 'start' first.")
else:
print(pick_random(words))
elif cmd == "stop":
if timer_thread and timer_thread.is_running:
timer_thread.stop()
timer_thread.join()
print("[+] Timer stopped early.")
else:
print("[!] There is no running timer to stop.")
else:
print(f"[!] Unknown command: {cmd}")
if __name__ == "__main__":
main()