Upload files to "/"
This commit is contained in:
332
cosmic_timer.py
Normal file
332
cosmic_timer.py
Normal file
@@ -0,0 +1,332 @@
|
||||
import json
|
||||
import random
|
||||
import os
|
||||
import time
|
||||
import sqlite3
|
||||
import sys
|
||||
import select
|
||||
import math
|
||||
|
||||
DATA_FILE = "cosmic_data.json"
|
||||
TRIVIA_FILE = "trivia_facts.json"
|
||||
DB_FILE = "cosmic_timer.db"
|
||||
|
||||
TRIVIA_WEIGHTS = {
|
||||
"300": 1, # 5 minutes
|
||||
"600": 2, # 10 minutes
|
||||
"1800": 5, # 30 minutes
|
||||
"3600": 10, # 1 hour
|
||||
}
|
||||
|
||||
CARBON_PER_SECOND_KG = 0.0000005 # Estimated kg CO2 per second of runtime
|
||||
|
||||
def init_db():
|
||||
conn = sqlite3.connect(DB_FILE)
|
||||
c = conn.cursor()
|
||||
# Create or update users table to include carbon_footprint_kg and entropy_score
|
||||
c.execute('''
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
username TEXT PRIMARY KEY,
|
||||
lifetime_seconds INTEGER DEFAULT 0,
|
||||
trivia_points INTEGER DEFAULT 0,
|
||||
carbon_footprint_kg REAL DEFAULT 0,
|
||||
entropy_score REAL DEFAULT 0
|
||||
)
|
||||
''')
|
||||
# If running on existing DB missing the columns, add them
|
||||
c.execute("PRAGMA table_info(users)")
|
||||
columns = [col[1] for col in c.fetchall()]
|
||||
if "carbon_footprint_kg" not in columns:
|
||||
c.execute("ALTER TABLE users ADD COLUMN carbon_footprint_kg REAL DEFAULT 0")
|
||||
if "entropy_score" not in columns:
|
||||
c.execute("ALTER TABLE users ADD COLUMN entropy_score REAL DEFAULT 0")
|
||||
|
||||
c.execute('''
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
session_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL,
|
||||
start_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
end_time TIMESTAMP,
|
||||
duration_seconds INTEGER DEFAULT 0,
|
||||
FOREIGN KEY(username) REFERENCES users(username)
|
||||
)
|
||||
''')
|
||||
c.execute('''
|
||||
CREATE TABLE IF NOT EXISTS trivia_events (
|
||||
event_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL,
|
||||
session_id INTEGER,
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
trivia_time_seconds INTEGER,
|
||||
points_awarded INTEGER,
|
||||
FOREIGN KEY(username) REFERENCES users(username),
|
||||
FOREIGN KEY(session_id) REFERENCES sessions(session_id)
|
||||
)
|
||||
''')
|
||||
c.execute('''
|
||||
CREATE TABLE IF NOT EXISTS leaderboard_history (
|
||||
record_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL,
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
lifetime_rank INTEGER,
|
||||
trivia_rank INTEGER,
|
||||
FOREIGN KEY(username) REFERENCES users(username)
|
||||
)
|
||||
''')
|
||||
conn.commit()
|
||||
return conn
|
||||
|
||||
def list_tables():
|
||||
conn = sqlite3.connect(DB_FILE)
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
||||
tables = [row[0] for row in c.fetchall()]
|
||||
conn.close()
|
||||
print("\n📜 Tables in cosmic_timer.db:")
|
||||
for t in tables:
|
||||
print(f" - {t}")
|
||||
|
||||
def ensure_user_exists(c, username):
|
||||
c.execute("SELECT 1 FROM users WHERE username = ?", (username,))
|
||||
if not c.fetchone():
|
||||
c.execute("INSERT INTO users (username) VALUES (?)", (username,))
|
||||
|
||||
def calculate_carbon_footprint(lifetime_seconds):
|
||||
return lifetime_seconds * CARBON_PER_SECOND_KG
|
||||
|
||||
def calculate_entropy_score(trivia_events_count, total_seconds):
|
||||
# Prevent division by zero or log(0)
|
||||
if total_seconds == 0 or trivia_events_count == 0:
|
||||
return 0.0
|
||||
p = trivia_events_count / total_seconds
|
||||
# Entropy per second using binary log (bits)
|
||||
entropy = -p * math.log2(p) - (1 - p) * math.log2(1 - p) if 0 < p < 1 else 0
|
||||
# Scale by total_seconds to get total entropy "bits" accumulated
|
||||
return entropy * total_seconds
|
||||
|
||||
def update_user_stats(c, username, session_seconds, trivia_points_this_session):
|
||||
# Count trivia events for this user from trivia_events table
|
||||
c.execute("SELECT COUNT(*) FROM trivia_events WHERE username = ?", (username,))
|
||||
trivia_events_count = c.fetchone()[0] or 0
|
||||
|
||||
c.execute("SELECT lifetime_seconds, trivia_points FROM users WHERE username = ?", (username,))
|
||||
row = c.fetchone()
|
||||
lifetime_seconds = (row[0] if row else 0) + session_seconds
|
||||
trivia_points = (row[1] if row else 0) + trivia_points_this_session
|
||||
carbon_footprint = calculate_carbon_footprint(lifetime_seconds)
|
||||
entropy_score = calculate_entropy_score(trivia_events_count, lifetime_seconds)
|
||||
|
||||
c.execute('''
|
||||
UPDATE users SET lifetime_seconds = ?, trivia_points = ?, carbon_footprint_kg = ?, entropy_score = ? WHERE username = ?
|
||||
''', (lifetime_seconds, trivia_points, carbon_footprint, entropy_score, username))
|
||||
|
||||
return lifetime_seconds, trivia_points, carbon_footprint, entropy_score
|
||||
|
||||
def update_session_end(c, session_id, session_seconds):
|
||||
c.execute('''
|
||||
UPDATE sessions SET end_time = CURRENT_TIMESTAMP, duration_seconds = ? WHERE session_id = ?
|
||||
''', (session_seconds, session_id))
|
||||
|
||||
def insert_trivia_event(c, username, session_id, trivia_time_seconds, points_awarded):
|
||||
c.execute('''
|
||||
INSERT INTO trivia_events (username, session_id, trivia_time_seconds, points_awarded)
|
||||
VALUES (?, ?, ?, ?)
|
||||
''', (username, session_id, trivia_time_seconds, points_awarded))
|
||||
|
||||
def insert_leaderboard_snapshot(c, username):
|
||||
c.execute("SELECT username FROM users ORDER BY lifetime_seconds DESC")
|
||||
lifetime_rank_list = [r[0] for r in c.fetchall()]
|
||||
lifetime_rank = lifetime_rank_list.index(username) + 1 if username in lifetime_rank_list else None
|
||||
|
||||
c.execute("SELECT username FROM users ORDER BY trivia_points DESC")
|
||||
trivia_rank_list = [r[0] for r in c.fetchall()]
|
||||
trivia_rank = trivia_rank_list.index(username) + 1 if username in trivia_rank_list else None
|
||||
|
||||
c.execute('''
|
||||
INSERT INTO leaderboard_history (username, lifetime_rank, trivia_rank)
|
||||
VALUES (?, ?, ?)
|
||||
''', (username, lifetime_rank, trivia_rank))
|
||||
return lifetime_rank, trivia_rank
|
||||
|
||||
def load_trivia_data(path, default={}):
|
||||
if os.path.exists(path):
|
||||
try:
|
||||
with open(path, 'r') as f:
|
||||
data = json.load(f)
|
||||
return {str(k): v for k, v in data.items()}
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to load {path} due to {e}, loading default.")
|
||||
else:
|
||||
print(f"Notice: {path} not found, initializing new data structure.")
|
||||
return default
|
||||
|
||||
def format_time(seconds):
|
||||
hrs = seconds // 3600
|
||||
mins = (seconds % 3600) // 60
|
||||
secs = seconds % 60
|
||||
return f"{hrs:02d}:{mins:02d}:{secs:02d}"
|
||||
|
||||
def pick_random_fact(facts):
|
||||
return random.choice(facts) if facts else None
|
||||
|
||||
def clear_terminal():
|
||||
os.system('cls' if os.name == 'nt' else 'clear')
|
||||
|
||||
def display_leaderboard(title, leaderboard, key, limit=10):
|
||||
print(f"\n {title}\n")
|
||||
if not leaderboard:
|
||||
print("(No users yet)")
|
||||
return
|
||||
print(f"{'Rank':>4} {'Username':<20} {title}")
|
||||
print("-" * 36)
|
||||
for i, entry in enumerate(leaderboard[:limit], start=1):
|
||||
name = entry['username']
|
||||
value = entry[key]
|
||||
display_val = format_time(value) if key == 'lifetime_seconds' else str(value)
|
||||
print(f"{i:>4} {name:<20} {display_val}")
|
||||
|
||||
def display_entropy_leaderboard(c, limit=10):
|
||||
c.execute("SELECT username, entropy_score FROM users ORDER BY entropy_score DESC LIMIT ?", (limit,))
|
||||
entropy_lb = [{"username": r[0], "entropy_score": r[1]} for r in c.fetchall()]
|
||||
|
||||
print("\n Top 10 Users by Entropy Score\n")
|
||||
if not entropy_lb:
|
||||
print("(No users yet)")
|
||||
return
|
||||
print(f"{'Rank':>4} {'Username':<20} Entropy Score (bits)")
|
||||
print("-" * 36)
|
||||
for i, entry in enumerate(entropy_lb, start=1):
|
||||
print(f"{i:>4} {entry['username']:<20} {entry['entropy_score']:.4f}")
|
||||
|
||||
def non_blocking_input(timeout=0.0):
|
||||
"""Non-blocking input on Unix and fallback on Windows."""
|
||||
if os.name == 'nt':
|
||||
import msvcrt
|
||||
if msvcrt.kbhit():
|
||||
return msvcrt.getwch()
|
||||
return None
|
||||
else:
|
||||
if select.select([sys.stdin], [], [], timeout)[0]:
|
||||
return sys.stdin.readline().strip()
|
||||
return None
|
||||
|
||||
def run_timer():
|
||||
username = input("Enter your username for leaderboard tracking: ").strip() or "Anonymous"
|
||||
|
||||
conn = init_db()
|
||||
c = conn.cursor()
|
||||
trivia_data = load_trivia_data(TRIVIA_FILE, {})
|
||||
|
||||
ensure_user_exists(c, username)
|
||||
conn.commit()
|
||||
|
||||
def session_loop():
|
||||
c.execute("INSERT INTO sessions (username) VALUES (?)", (username,))
|
||||
session_id = c.lastrowid
|
||||
conn.commit()
|
||||
|
||||
session_seconds = 0
|
||||
trivia_points_this_session = 0
|
||||
shown_markers = set()
|
||||
shown_facts = []
|
||||
|
||||
print("Cosmic Timer started.")
|
||||
print("Type 'p' to pause, 'q' to quit, or 't' to list DB tables.\n")
|
||||
|
||||
intervals = [300, 600, 1800, 3600] # base intervals to loop after 1 hour
|
||||
|
||||
try:
|
||||
while True:
|
||||
time.sleep(1)
|
||||
session_seconds += 1
|
||||
|
||||
# Determine the trivia key based on current session_seconds
|
||||
if session_seconds <= 3600:
|
||||
key = str(session_seconds)
|
||||
else:
|
||||
# Loop intervals after the first hour
|
||||
elapsed_after_hour = session_seconds - 3600
|
||||
cycle_time = elapsed_after_hour % 3600
|
||||
|
||||
# Find if cycle_time matches one of the intervals exactly
|
||||
if cycle_time in intervals:
|
||||
key = str(cycle_time)
|
||||
else:
|
||||
key = None
|
||||
|
||||
if key and key in trivia_data and session_seconds not in shown_markers:
|
||||
fact = pick_random_fact(trivia_data[key])
|
||||
if fact:
|
||||
shown_facts.append(f"⏱ {format_time(session_seconds)} → {fact}")
|
||||
shown_markers.add(session_seconds)
|
||||
weight = TRIVIA_WEIGHTS.get(key, 1)
|
||||
trivia_points_this_session += weight
|
||||
insert_trivia_event(c, username, session_id, session_seconds, weight)
|
||||
conn.commit()
|
||||
|
||||
clear_terminal()
|
||||
print(f" Cosmic Timer — User: {username}")
|
||||
print(f"Session Time: {format_time(session_seconds)}\n")
|
||||
print(" Trivia So Far:")
|
||||
for f in shown_facts:
|
||||
print(f)
|
||||
|
||||
cmd = non_blocking_input()
|
||||
|
||||
if cmd:
|
||||
cmd = cmd.lower()
|
||||
if cmd == 'p':
|
||||
input("\n=== PAUSED === Press Enter to continue...")
|
||||
elif cmd == 'q':
|
||||
raise KeyboardInterrupt
|
||||
elif cmd == 't':
|
||||
list_tables()
|
||||
input("\nPress Enter to continue...")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
return session_seconds, trivia_points_this_session, shown_facts, session_id
|
||||
|
||||
while True:
|
||||
session_seconds, trivia_points_this_session, shown_facts, session_id = session_loop()
|
||||
|
||||
lifetime_seconds, total_trivia_points, carbon_footprint, entropy_score = update_user_stats(
|
||||
c, username, session_seconds, trivia_points_this_session)
|
||||
update_session_end(c, session_id, session_seconds)
|
||||
lifetime_rank, trivia_rank = insert_leaderboard_snapshot(c, username)
|
||||
conn.commit()
|
||||
|
||||
clear_terminal()
|
||||
print("\n\nExiting Cosmic Timer.")
|
||||
print(f"User: {username}")
|
||||
print(f"Session Runtime: {format_time(session_seconds)}")
|
||||
print(f"Lifetime Runtime: {format_time(lifetime_seconds)}")
|
||||
print(f"Estimated Carbon Footprint: {carbon_footprint:.6f} kg CO2")
|
||||
print(f"Trivia Points This Session: {trivia_points_this_session}")
|
||||
print(f"Total Trivia Points: {total_trivia_points}")
|
||||
print(f"Entropy Score: {entropy_score:.4f} bits")
|
||||
|
||||
if shown_facts:
|
||||
print("\n Grand Cosmic Fact:")
|
||||
print(random.choice(shown_facts))
|
||||
else:
|
||||
print("\n(No cosmic facts were obtained this run.)")
|
||||
|
||||
c.execute("SELECT username, lifetime_seconds FROM users ORDER BY lifetime_seconds DESC LIMIT 10")
|
||||
lifetime_lb = [{"username": r[0], "lifetime_seconds": r[1]} for r in c.fetchall()]
|
||||
c.execute("SELECT username, trivia_points FROM users ORDER BY trivia_points DESC LIMIT 10")
|
||||
trivia_lb = [{"username": r[0], "trivia_points": r[1]} for r in c.fetchall()]
|
||||
|
||||
display_leaderboard("Top 10 Users by Lifetime Time", lifetime_lb, "lifetime_seconds")
|
||||
display_leaderboard("Top 10 Users by Trivia Points", trivia_lb, "trivia_points")
|
||||
display_entropy_leaderboard(c)
|
||||
|
||||
choice = input("\nStart a new session? (y/n): ").strip().lower()
|
||||
if choice != 'y':
|
||||
break
|
||||
|
||||
conn.close()
|
||||
print("\n May you be as productive as the universe is efficent!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_timer()
|
||||
|
||||
Reference in New Issue
Block a user