initial checkin
This commit is contained in:
commit
ec09a98a24
2 changed files with 430 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
kana_stats.db
|
429
kana.py
Normal file
429
kana.py
Normal file
|
@ -0,0 +1,429 @@
|
|||
import random
|
||||
import time
|
||||
import sqlite3
|
||||
from typing import Dict, List, Tuple
|
||||
from wcwidth import wcswidth
|
||||
|
||||
# Hiragana monographs (basic)
|
||||
hiragana_monographs = {
|
||||
'あ': 'a', 'い': 'i', 'う': 'u', 'え': 'e', 'お': 'o',
|
||||
'か': 'ka', 'き': 'ki', 'く': 'ku', 'け': 'ke', 'こ': 'ko',
|
||||
'さ': 'sa', 'し': 'shi', 'す': 'su', 'せ': 'se', 'そ': 'so',
|
||||
'た': 'ta', 'ち': 'chi', 'つ': 'tsu', 'て': 'te', 'と': 'to',
|
||||
'な': 'na', 'に': 'ni', 'ぬ': 'nu', 'ね': 'ne', 'の': 'no',
|
||||
'は': 'ha', 'ひ': 'hi', 'ふ': 'fu', 'へ': 'he', 'ほ': 'ho',
|
||||
'ま': 'ma', 'み': 'mi', 'む': 'mu', 'め': 'me', 'も': 'mo',
|
||||
'や': 'ya', 'ゆ': 'yu', 'よ': 'yo',
|
||||
'ら': 'ra', 'り': 'ri', 'る': 'ru', 'れ': 're', 'ろ': 'ro',
|
||||
'わ': 'wa', 'を': 'wo', 'ん': 'n'
|
||||
}
|
||||
# Hiragana monographs with diacritics
|
||||
hiragana_monographs_diacritics = {
|
||||
'が': 'ga', 'ぎ': 'gi', 'ぐ': 'gu', 'げ': 'ge', 'ご': 'go',
|
||||
'ざ': 'za', 'じ': 'ji', 'ず': 'zu', 'ぜ': 'ze', 'ぞ': 'zo',
|
||||
'だ': 'da', 'ぢ': 'ji', 'づ': 'zu', 'で': 'de', 'ど': 'do',
|
||||
'ば': 'ba', 'び': 'bi', 'ぶ': 'bu', 'べ': 'be', 'ぼ': 'bo',
|
||||
'ぱ': 'pa', 'ぴ': 'pi', 'ぷ': 'pu', 'ぺ': 'pe', 'ぽ': 'po'
|
||||
}
|
||||
# Hiragana digraphs (yōon)
|
||||
hiragana_digraphs = {
|
||||
'きゃ': 'kya', 'きゅ': 'kyu', 'きょ': 'kyo',
|
||||
'しゃ': 'sha', 'しゅ': 'shu', 'しょ': 'sho',
|
||||
'ちゃ': 'cha', 'ちゅ': 'chu', 'ちょ': 'cho',
|
||||
'にゃ': 'nya', 'にゅ': 'nyu', 'にょ': 'nyo',
|
||||
'ひゃ': 'hya', 'ひゅ': 'hyu', 'ひょ': 'hyo',
|
||||
'みゃ': 'mya', 'みゅ': 'myu', 'みょ': 'myo',
|
||||
'りゃ': 'rya', 'りゅ': 'ryu', 'りょ': 'ryo',
|
||||
'ぎゃ': 'gya', 'ぎゅ': 'gyu', 'ぎょ': 'gyo',
|
||||
'じゃ': 'ja', 'じゅ': 'ju', 'じょ': 'jo',
|
||||
'びゃ': 'bya', 'びゅ': 'byu', 'びょ': 'byo',
|
||||
'ぴゃ': 'pya', 'ぴゅ': 'pyu', 'ぴょ': 'pyo',
|
||||
}
|
||||
# Hiragana digraphs with diacritics (subset already included above)
|
||||
hiragana_digraphs_diacritics = {
|
||||
'ぎゃ': 'gya', 'ぎゅ': 'gyu', 'ぎょ': 'gyo',
|
||||
'じゃ': 'ja', 'じゅ': 'ju', 'じょ': 'jo',
|
||||
'ぢゃ': 'ja', 'ぢゅ': 'ju', 'ぢょ': 'jo',
|
||||
'びゃ': 'bya', 'びゅ': 'byu', 'びょ': 'byo',
|
||||
'ぴゃ': 'pya', 'ぴゅ': 'pyu', 'ぴょ': 'pyo',
|
||||
}
|
||||
# Katakana monographs (basic)
|
||||
katakana_monographs = {
|
||||
'ア': 'a', 'イ': 'i', 'ウ': 'u', 'エ': 'e', 'オ': 'o',
|
||||
'カ': 'ka', 'キ': 'ki', 'ク': 'ku', 'ケ': 'ke', 'コ': 'ko',
|
||||
'サ': 'sa', 'シ': 'shi', 'ス': 'su', 'セ': 'se', 'ソ': 'so',
|
||||
'タ': 'ta', 'チ': 'chi', 'ツ': 'tsu', 'テ': 'te', 'ト': 'to',
|
||||
'ナ': 'na', 'ニ': 'ni', 'ヌ': 'nu', 'ネ': 'ne', 'ノ': 'no',
|
||||
'ハ': 'ha', 'ヒ': 'hi', 'フ': 'fu', 'ヘ': 'he', 'ホ': 'ho',
|
||||
'マ': 'ma', 'ミ': 'mi', 'ム': 'mu', 'メ': 'me', 'モ': 'mo',
|
||||
'ヤ': 'ya', 'ユ': 'yu', 'ヨ': 'yo',
|
||||
'ラ': 'ra', 'リ': 'ri', 'ル': 'ru', 'レ': 're', 'ロ': 'ro',
|
||||
'ワ': 'wa', 'ヲ': 'wo', 'ン': 'n'
|
||||
}
|
||||
# Katakana monographs with diacritics
|
||||
katakana_monographs_diacritics = {
|
||||
'ガ': 'ga', 'ギ': 'gi', 'グ': 'gu', 'ゲ': 'ge', 'ゴ': 'go',
|
||||
'ザ': 'za', 'ジ': 'ji', 'ズ': 'zu', 'ゼ': 'ze', 'ゾ': 'zo',
|
||||
'ダ': 'da', 'ヂ': 'ji', 'ヅ': 'zu', 'デ': 'de', 'ド': 'do',
|
||||
'バ': 'ba', 'ビ': 'bi', 'ブ': 'bu', 'ベ': 'be', 'ボ': 'bo',
|
||||
'パ': 'pa', 'ピ': 'pi', 'プ': 'pu', 'ペ': 'pe', 'ポ': 'po'
|
||||
}
|
||||
# Katakana digraphs (yōon)
|
||||
katakana_digraphs = {
|
||||
'キャ': 'kya', 'キュ': 'kyu', 'キョ': 'kyo',
|
||||
'シャ': 'sha', 'シュ': 'shu', 'ショ': 'sho',
|
||||
'チャ': 'cha', 'チュ': 'chu', 'チョ': 'cho',
|
||||
'ニャ': 'nya', 'ニュ': 'nyu', 'ニョ': 'nyo',
|
||||
'ヒャ': 'hya', 'ヒュ': 'hyu', 'ヒョ': 'hyo',
|
||||
'ミャ': 'mya', 'ミュ': 'myu', 'ミョ': 'myo',
|
||||
'リャ': 'rya', 'リュ': 'ryu', 'リョ': 'ryo',
|
||||
'ギャ': 'gya', 'ギュ': 'gyu', 'ギョ': 'gyo',
|
||||
'ジャ': 'ja', 'ジュ': 'ju', 'ジョ': 'jo',
|
||||
'ビャ': 'bya', 'ビュ': 'byu', 'ビョ': 'byo',
|
||||
'ピャ': 'pya', 'ピュ': 'pyu', 'ピョ': 'pyo',
|
||||
}
|
||||
# Katakana digraphs with diacritics (subset already included above)
|
||||
katakana_digraphs_diacritics = {
|
||||
'ギャ': 'gya', 'ギュ': 'gyu', 'ギョ': 'gyo',
|
||||
'ジャ': 'ja', 'ジュ': 'ju', 'ジョ': 'jo',
|
||||
'ヂャ': 'ja', 'ヂュ': 'ju', 'ヂョ': 'jo',
|
||||
'ビャ': 'bya', 'ビュ': 'byu', 'ビョ': 'byo',
|
||||
'ピャ': 'pya', 'ピュ': 'pyu', 'ピョ': 'pyo',
|
||||
}
|
||||
# Extended Katakana for foreign sounds
|
||||
katakana_extended = {
|
||||
'ウィ': 'wi', 'ウェ': 'we', 'ウォ': 'wo',
|
||||
'ヴァ': 'va', 'ヴィ': 'vi', 'ヴ': 'vu', 'ヴェ': 've', 'ヴォ': 'vo',
|
||||
'ファ': 'fa', 'フィ': 'fi', 'フェ': 'fe', 'フォ': 'fo', 'フュ': 'fyu',
|
||||
'ティ': 'ti', 'ディ': 'di', 'トゥ': 'tu', 'ドゥ': 'du',
|
||||
'チェ': 'che', 'シェ': 'she', 'ジェ': 'je',
|
||||
'ツァ': 'tsa', 'ツィ': 'tsi', 'ツェ': 'tse', 'ツォ': 'tso',
|
||||
'ティ': 'ti', 'ディ': 'di',
|
||||
'デュ': 'dyu', 'テュ': 'tyu',
|
||||
'イェ': 'ye', 'ウュ': 'wyu',
|
||||
'キェ': 'kye', 'ギェ': 'gye',
|
||||
'クァ': 'kwa', 'クィ': 'kwi', 'クェ': 'kwe', 'クォ': 'kwo',
|
||||
'グァ': 'gwa', 'グィ': 'gwi', 'グェ': 'gwe', 'グォ': 'gwo',
|
||||
'スィ': 'si', 'ズィ': 'zi',
|
||||
'ツュ': 'tyu',
|
||||
'ニェ': 'nye', 'ヒェ': 'hye', 'ビェ': 'bye', 'ピェ': 'pye',
|
||||
'ミェ': 'mye', 'リェ': 'rye',
|
||||
'フョ': 'fyo',
|
||||
'ホゥ': 'hu',
|
||||
'モゥ': 'mo',
|
||||
'ロゥ': 'ro',
|
||||
'シィ': 'syi', 'ジィ': 'jyi',
|
||||
'ティ': 'tyi', 'ディ': 'dyi',
|
||||
'トゥ': 'twu', 'ドゥ': 'dwu',
|
||||
'テャ': 'tya', 'テュ': 'tyu', 'テョ': 'tyo',
|
||||
'デャ': 'dya', 'デュ': 'dyu', 'デョ': 'dyo',
|
||||
'フャ': 'fya', 'フュ': 'fyu', 'フョ': 'fyo',
|
||||
'グァ': 'gwa',
|
||||
}
|
||||
|
||||
def choose_kana_sets() -> Dict[str, str]:
|
||||
"""Let user choose which kana sets to practice with. Supports multiple selections."""
|
||||
print("\nWhich kana would you like to practice? (You can select multiple, e.g. 1,3,5)")
|
||||
print("1. Hiragana Monographs")
|
||||
print("2. Hiragana Monographs with Diacritics")
|
||||
print("3. Hiragana Digraphs")
|
||||
print("4. Hiragana Digraphs with Diacritics")
|
||||
print("5. Katakana Monographs")
|
||||
print("6. Katakana Monographs with Diacritics")
|
||||
print("7. Katakana Digraphs")
|
||||
print("8. Katakana Digraphs with Diacritics")
|
||||
print("9. Extended Katakana (foreign sounds)")
|
||||
print("0. All of the above")
|
||||
choice = input("Enter your choice(s) (0-9, comma-separated): ")
|
||||
selected = [c.strip() for c in choice.split(',') if c.strip()]
|
||||
if '0' in selected or choice.strip() == '0':
|
||||
combined = {}
|
||||
combined.update(hiragana_monographs)
|
||||
combined.update(hiragana_monographs_diacritics)
|
||||
combined.update(hiragana_digraphs)
|
||||
combined.update(hiragana_digraphs_diacritics)
|
||||
combined.update(katakana_monographs)
|
||||
combined.update(katakana_monographs_diacritics)
|
||||
combined.update(katakana_digraphs)
|
||||
combined.update(katakana_digraphs_diacritics)
|
||||
combined.update(katakana_extended)
|
||||
return combined
|
||||
group_map = {
|
||||
'1': hiragana_monographs,
|
||||
'2': hiragana_monographs_diacritics,
|
||||
'3': hiragana_digraphs,
|
||||
'4': hiragana_digraphs_diacritics,
|
||||
'5': katakana_monographs,
|
||||
'6': katakana_monographs_diacritics,
|
||||
'7': katakana_digraphs,
|
||||
'8': katakana_digraphs_diacritics,
|
||||
'9': katakana_extended,
|
||||
}
|
||||
combined = {}
|
||||
for sel in selected:
|
||||
if sel in group_map:
|
||||
combined.update(group_map[sel])
|
||||
if not combined:
|
||||
print("No valid selection, defaulting to all groups.")
|
||||
combined.update(hiragana_monographs)
|
||||
combined.update(hiragana_monographs_diacritics)
|
||||
combined.update(hiragana_digraphs)
|
||||
combined.update(hiragana_digraphs_diacritics)
|
||||
combined.update(katakana_monographs)
|
||||
combined.update(katakana_monographs_diacritics)
|
||||
combined.update(katakana_digraphs)
|
||||
combined.update(katakana_digraphs_diacritics)
|
||||
combined.update(katakana_extended)
|
||||
return combined
|
||||
|
||||
def update_kana_db(conn, kana, is_correct, response_time):
|
||||
c = conn.cursor()
|
||||
c.execute('SELECT right, wrong, total_time FROM kana_stats WHERE kana = ?', (kana,))
|
||||
row = c.fetchone()
|
||||
if row is None:
|
||||
right = 1 if is_correct else 0
|
||||
wrong = 0 if is_correct else 1
|
||||
c.execute('INSERT INTO kana_stats (kana, right, wrong, total_time) VALUES (?, ?, ?, ?)',
|
||||
(kana, right, wrong, response_time))
|
||||
else:
|
||||
right, wrong, total_time = row
|
||||
if is_correct:
|
||||
right += 1
|
||||
else:
|
||||
wrong += 1
|
||||
total_time += response_time
|
||||
c.execute('UPDATE kana_stats SET right = ?, wrong = ?, total_time = ? WHERE kana = ?',
|
||||
(right, wrong, total_time, kana))
|
||||
conn.commit()
|
||||
|
||||
def practice_kana(kana_dict: Dict[str, str], conn=None) -> Dict[str, Dict[str, List[float] or int]]:
|
||||
"""Practice kana and return per-character stats. Optionally update stats in DB."""
|
||||
stats = {kana: {'right': 0, 'wrong': 0, 'times': []} for kana in kana_dict}
|
||||
kana_list = list(kana_dict.items())
|
||||
print("\nType 'quit' to end the practice session.")
|
||||
print("Press Enter to start...")
|
||||
input()
|
||||
while True:
|
||||
kana, romaji = random.choice(kana_list)
|
||||
print(f"\nWhat is the romaji for: {kana}")
|
||||
start_time = time.time()
|
||||
answer = input("> ").lower().strip()
|
||||
if answer == 'quit' or answer == '':
|
||||
break
|
||||
response_time = time.time() - start_time
|
||||
is_correct = answer == romaji
|
||||
if is_correct:
|
||||
print("Correct!")
|
||||
stats[kana]['right'] += 1
|
||||
else:
|
||||
print(f"Incorrect. The correct answer is: {romaji}")
|
||||
stats[kana]['wrong'] += 1
|
||||
stats[kana]['times'].append(response_time)
|
||||
if conn is not None:
|
||||
update_kana_db(conn, kana, is_correct, response_time)
|
||||
return stats
|
||||
|
||||
def fetch_overall_stats(conn, kana_list):
|
||||
c = conn.cursor()
|
||||
stats = {}
|
||||
for kana in kana_list:
|
||||
c.execute('SELECT right, wrong, total_time FROM kana_stats WHERE kana = ?', (kana,))
|
||||
row = c.fetchone()
|
||||
if row:
|
||||
right, wrong, total_time = row
|
||||
stats[kana] = {'right': right, 'wrong': wrong, 'total_time': total_time}
|
||||
else:
|
||||
stats[kana] = {'right': 0, 'wrong': 0, 'total_time': 0.0}
|
||||
return stats
|
||||
|
||||
def display_results(session_stats: Dict[str, Dict[str, List[float] or int]], conn=None) -> None:
|
||||
"""Display per-character practice session and overall results."""
|
||||
if not session_stats or all((v['right'] + v['wrong']) == 0 for v in session_stats.values()):
|
||||
print("\nNo results to display.")
|
||||
return
|
||||
kana_list = list(session_stats.keys())
|
||||
overall_stats = fetch_overall_stats(conn, kana_list) if conn else None
|
||||
print("\n=== Per-Character Practice Results ===")
|
||||
print(f"{'Kana':^6} | {'SessR':^5} | {'SessW':^5} | {'SessAcc':^8} | {'SessAvgT':^9} | {'AllR':^5} | {'AllW':^5} | {'AllAcc':^7} | {'AllAvgT':^8}")
|
||||
print("-" * 74)
|
||||
for kana in sorted(kana_list):
|
||||
s = session_stats[kana]
|
||||
sess_total = s['right'] + s['wrong']
|
||||
if sess_total == 0:
|
||||
continue
|
||||
sess_acc = (s['right'] / sess_total) * 100
|
||||
sess_avg_time = sum(s['times']) / sess_total if s['times'] else 0.0
|
||||
if overall_stats:
|
||||
o = overall_stats[kana]
|
||||
all_total = o['right'] + o['wrong']
|
||||
all_acc = (o['right'] / all_total) * 100 if all_total else 0.0
|
||||
all_avg_time = o['total_time'] / all_total if all_total else 0.0
|
||||
print(f"{kana:^6} | {s['right']:^5} | {s['wrong']:^5} | {sess_acc:7.1f}% | {sess_avg_time:8.2f} | {o['right']:^5} | {o['wrong']:^5} | {all_acc:6.1f}% | {all_avg_time:7.2f}")
|
||||
else:
|
||||
print(f"{kana:^6} | {s['right']:^5} | {s['wrong']:^5} | {sess_acc:7.1f}% | {sess_avg_time:8.2f}")
|
||||
|
||||
def init_db(db_path: str = "kana_stats.db"):
|
||||
conn = sqlite3.connect(db_path)
|
||||
c = conn.cursor()
|
||||
c.execute('''
|
||||
CREATE TABLE IF NOT EXISTS kana_stats (
|
||||
kana TEXT PRIMARY KEY,
|
||||
right INTEGER DEFAULT 0,
|
||||
wrong INTEGER DEFAULT 0,
|
||||
total_time REAL DEFAULT 0.0
|
||||
)
|
||||
''')
|
||||
conn.commit()
|
||||
return conn
|
||||
|
||||
def pad_display_width(s, width):
|
||||
display_len = wcswidth(s)
|
||||
if display_len >= width:
|
||||
return s
|
||||
return s + ' ' * (width - display_len)
|
||||
|
||||
def show_all_stats(conn, columns=3):
|
||||
# Define groupings
|
||||
kana_groups = [
|
||||
("Hiragana Monographs", hiragana_monographs),
|
||||
("Hiragana Monographs with Diacritics", hiragana_monographs_diacritics),
|
||||
("Hiragana Digraphs", hiragana_digraphs),
|
||||
("Hiragana Digraphs with Diacritics", hiragana_digraphs_diacritics),
|
||||
("Katakana Monographs", katakana_monographs),
|
||||
("Katakana Monographs with Diacritics", katakana_monographs_diacritics),
|
||||
("Katakana Digraphs", katakana_digraphs),
|
||||
("Katakana Digraphs with Diacritics", katakana_digraphs_diacritics),
|
||||
("Extended Katakana (foreign sounds)", katakana_extended),
|
||||
]
|
||||
c = conn.cursor()
|
||||
kana_col_width = 8
|
||||
col_header = f"{'Kana':<{kana_col_width}} {'Right':>5} {'Wrong':>5} {'Total':>5} {'Acc%':>6} {'AvgT':>6}"
|
||||
sep = ' '
|
||||
print("\n=== Overall Kana Stats ===")
|
||||
for group_name, group_dict in kana_groups:
|
||||
sorted_kana = sorted(group_dict.keys())
|
||||
if not sorted_kana:
|
||||
continue
|
||||
# Gather stats for this group
|
||||
stats = {}
|
||||
for kana in sorted_kana:
|
||||
c.execute('SELECT right, wrong, total_time FROM kana_stats WHERE kana = ?', (kana,))
|
||||
row = c.fetchone()
|
||||
if row:
|
||||
right, wrong, total_time = row
|
||||
else:
|
||||
right, wrong, total_time = 0, 0, 0.0
|
||||
stats[kana] = {'right': right, 'wrong': wrong, 'total_time': total_time}
|
||||
print(f"\n--- {group_name} ---")
|
||||
print(sep.join([col_header]*columns))
|
||||
print(sep.join(['-'*35]*columns))
|
||||
# Prepare rows
|
||||
rows = []
|
||||
group_right = group_wrong = group_total = 0
|
||||
group_time = 0.0
|
||||
for kana in sorted_kana:
|
||||
s = stats[kana]
|
||||
total = s['right'] + s['wrong']
|
||||
accuracy = (s['right'] / total) * 100 if total else 0.0
|
||||
avg_time = s['total_time'] / total if total else 0.0
|
||||
kana_disp = pad_display_width(kana, kana_col_width)
|
||||
row = f"{kana_disp}{s['right']:>5} {s['wrong']:>5} {total:>5} {accuracy:6.1f} {avg_time:6.2f}"
|
||||
rows.append(row)
|
||||
group_right += s['right']
|
||||
group_wrong += s['wrong']
|
||||
group_total += total
|
||||
group_time += s['total_time']
|
||||
# Print in columns
|
||||
for i in range(0, len(rows), columns):
|
||||
print(sep.join(rows[i:i+columns]))
|
||||
# Print summary under the last column
|
||||
group_acc = (group_right / group_total) * 100 if group_total else 0.0
|
||||
group_avg_time = group_time / group_total if group_total else 0.0
|
||||
last_row_len = len(rows) % columns if len(rows) % columns != 0 else columns
|
||||
empty_col = ' ' * 35
|
||||
print(sep.join([empty_col] * (last_row_len - 1)), end='' if last_row_len > 1 else '')
|
||||
summary = f"{'[Summary]':<{kana_col_width}}{group_right:>5} {group_wrong:>5} {group_total:>5} {group_acc:6.1f} {group_avg_time:6.2f}"
|
||||
print(summary)
|
||||
|
||||
def show_most_wrong_kana(conn, top_n=10):
|
||||
# Gather all kana from all groups
|
||||
all_kana = {}
|
||||
all_kana.update(hiragana_monographs)
|
||||
all_kana.update(hiragana_monographs_diacritics)
|
||||
all_kana.update(hiragana_digraphs)
|
||||
all_kana.update(hiragana_digraphs_diacritics)
|
||||
all_kana.update(katakana_monographs)
|
||||
all_kana.update(katakana_monographs_diacritics)
|
||||
all_kana.update(katakana_digraphs)
|
||||
all_kana.update(katakana_digraphs_diacritics)
|
||||
all_kana.update(katakana_extended)
|
||||
c = conn.cursor()
|
||||
stats = []
|
||||
for kana in all_kana:
|
||||
c.execute('SELECT right, wrong, total_time FROM kana_stats WHERE kana = ?', (kana,))
|
||||
row = c.fetchone()
|
||||
if row:
|
||||
right, wrong, total_time = row
|
||||
else:
|
||||
right, wrong, total_time = 0, 0, 0.0
|
||||
stats.append((kana, right, wrong, total_time))
|
||||
# Sort by wrong answers descending, then by kana
|
||||
stats.sort(key=lambda x: (-x[2], x[0]))
|
||||
print(f"\n=== Kana with Most Wrong Answers (Top {top_n}) ===")
|
||||
print(f"{'Kana':^6} | {'Wrong':^5} | {'Right':^5} | {'Total':^5} | {'Accuracy':^8} | {'Avg Time (s)':^12}")
|
||||
print("-" * 56)
|
||||
shown = 0
|
||||
for kana, right, wrong, total_time in stats:
|
||||
total = right + wrong
|
||||
if wrong == 0:
|
||||
continue
|
||||
accuracy = (right / total) * 100 if total else 0.0
|
||||
avg_time = total_time / total if total else 0.0
|
||||
print(f"{kana:^6} | {wrong:^5} | {right:^5} | {total:^5} | {accuracy:7.1f}% | {avg_time:11.2f}")
|
||||
shown += 1
|
||||
if shown >= top_n:
|
||||
break
|
||||
if shown == 0:
|
||||
print("No wrong answers yet! Great job!")
|
||||
|
||||
def stats_menu(conn):
|
||||
while True:
|
||||
print("\n=== Stats Menu ===")
|
||||
print("1. Show all stats")
|
||||
print("2. Show most wrong kana")
|
||||
print("q. Return to main menu")
|
||||
choice = input("Enter your choice (1, 2, or q): ").strip().lower()
|
||||
if choice == '1':
|
||||
show_all_stats(conn)
|
||||
input("\nPress Enter to return to the stats menu...")
|
||||
elif choice == '2':
|
||||
show_most_wrong_kana(conn)
|
||||
input("\nPress Enter to return to the stats menu...")
|
||||
elif choice == 'q':
|
||||
break
|
||||
else:
|
||||
print("Invalid choice. Please enter 1, 2, or q.")
|
||||
|
||||
def main_menu():
|
||||
conn = init_db()
|
||||
while True:
|
||||
print("\n=== Kana Practice Main Menu ===")
|
||||
print("1. Practice kana")
|
||||
print("2. Show stats")
|
||||
print("q. Quit")
|
||||
choice = input("Enter your choice (1, 2, or q): ").strip().lower()
|
||||
if choice == '1':
|
||||
kana_dict = choose_kana_sets()
|
||||
stats = practice_kana(kana_dict, conn)
|
||||
display_results(stats, conn)
|
||||
input("\nPress Enter to return to the main menu...")
|
||||
elif choice == '2':
|
||||
stats_menu(conn)
|
||||
elif choice == 'q':
|
||||
print("Thanks for practicing! さようなら!")
|
||||
break
|
||||
else:
|
||||
print("Invalid choice. Please enter 1, 2, or q.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main_menu()
|
Loading…
Add table
Add a link
Reference in a new issue