import tkinter as tk from tkinter import messagebox import random import time from collections import Counter # ============================================================ # ACAAN MASTER TRAINER # 카드 52장 × 숫자 3~27 = 1,300개 조합 # Python 3 + Tkinter만 사용 # ============================================================ SUITS = ["♠", "♥", "♦", "♣"] RANKS = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"] CARDS = [rank + suit for suit in SUITS for rank in RANKS] NUMBERS = list(range(3, 28)) ALL_PAIRS = [(card, number) for card in CARDS for number in NUMBERS] SUIT_KR = {"♠": "스페이드", "♥": "하트", "♦": "다이아", "♣": "클로버"} RANK_KR = {"A": "에이스", "J": "잭", "Q": "퀸", "K": "킹"} class ACAANApp: def __init__(self, root): self.root = root self.root.title("ACAAN MASTER TRAINER") self.root.geometry("720x820") self.root.minsize(620, 720) self.root.configure(bg="#0d1117") self.used_pairs = set() self.history = [] self.card_counts = Counter() self.number_counts = Counter() self.current_card = None self.current_number = None self.start_time = None self.timer_job = None self.revealed = True self.difficulty = "보통" self.build() self.new_round() def build(self): # Header header = tk.Frame(self.root, bg="#0d1117") header.pack(fill="x", padx=24, pady=(18, 8)) tk.Label( header, text="♠ ACAAN MASTER TRAINER ♥", font=("Arial", 25, "bold"), fg="white", bg="#0d1117" ).pack() tk.Label( header, text="실전 아칸 연습 · 52장 × 숫자 3~27", font=("Arial", 11), fg="#8d99aa", bg="#0d1117" ).pack(pady=(4, 0)) # Difficulty diff = tk.Frame(self.root, bg="#151b24") diff.pack(fill="x", padx=24, pady=8) tk.Label( diff, text="난이도", font=("Arial", 11, "bold"), fg="#dce3ee", bg="#151b24" ).pack(side="left", padx=(14, 8), pady=10) self.diff_var = tk.StringVar(value="보통") for name in ["쉬움", "보통", "실전"]: tk.Radiobutton( diff, text=name, variable=self.diff_var, value=name, command=self.change_difficulty, font=("Arial", 10, "bold"), fg="#dce3ee", bg="#151b24", selectcolor="#283244", activebackground="#151b24", activeforeground="white" ).pack(side="left", padx=8) # Main challenge box = tk.Frame( self.root, bg="#181f2a", highlightthickness=1, highlightbackground="#303a4c" ) box.pack(fill="x", padx=24, pady=10) tk.Label( box, text="관객이 고른 카드", font=("Arial", 11, "bold"), fg="#8d99aa", bg="#181f2a" ).pack(pady=(17, 2)) self.card_label = tk.Label( box, text="", font=("Arial", 48, "bold"), bg="#181f2a" ) self.card_label.pack() self.card_kr = tk.Label( box, text="", font=("Arial", 12), fg="#aeb9c9", bg="#181f2a" ) self.card_kr.pack() tk.Label( box, text="선택된 숫자", font=("Arial", 11, "bold"), fg="#8d99aa", bg="#181f2a" ).pack(pady=(14, 0)) self.number_label = tk.Label( box, text="", font=("Arial", 57, "bold"), fg="#65e6a5", bg="#181f2a" ) self.number_label.pack(pady=(0, 3)) self.timer_label = tk.Label( box, text="시간 0.00초", font=("Arial", 12, "bold"), fg="#f0c96b", bg="#181f2a" ) self.timer_label.pack(pady=(0, 7)) self.info_label = tk.Label( box, text="", font=("Arial", 10), fg="#69768a", bg="#181f2a" ) self.info_label.pack(pady=(0, 15)) # Controls controls = tk.Frame(self.root, bg="#0d1117") controls.pack(pady=10) self.next_button = tk.Button( controls, text="🎲 새 문제", command=self.new_round, font=("Arial", 13, "bold"), bg="#4d6fff", fg="white", activebackground="#6983ff", relief="flat", padx=25, pady=11, cursor="hand2" ) self.next_button.grid(row=0, column=0, padx=5) self.hide_button = tk.Button( controls, text="🙈 가리기", command=self.hide_reveal, font=("Arial", 13, "bold"), bg="#293343", fg="white", activebackground="#39465a", relief="flat", padx=22, pady=11, cursor="hand2" ) self.hide_button.grid(row=0, column=1, padx=5) tk.Button( controls, text="✓ 성공", command=lambda: self.finish(True), font=("Arial", 13, "bold"), bg="#217852", fg="white", activebackground="#2c9668", relief="flat", padx=20, pady=11, cursor="hand2" ).grid(row=0, column=2, padx=5) tk.Button( controls, text="✕ 실패", command=lambda: self.finish(False), font=("Arial", 13, "bold"), bg="#863c4c", fg="white", activebackground="#a84b5e", relief="flat", padx=20, pady=11, cursor="hand2" ).grid(row=0, column=3, padx=5) # Progress prog = tk.Frame(self.root, bg="#151b24") prog.pack(fill="x", padx=24, pady=8) self.progress_text = tk.Label( prog, text="", font=("Arial", 11, "bold"), fg="#dce3ee", bg="#151b24" ) self.progress_text.pack(pady=(11, 2)) self.progress = tk.Canvas( prog, height=12, bg="#252d3a", highlightthickness=0 ) self.progress.pack(fill="x", padx=14, pady=(3, 13)) self.progress.bind("", lambda e: self.draw_progress()) # Stats row stats = tk.Frame(self.root, bg="#0d1117") stats.pack(fill="x", padx=24, pady=8) self.stats_label = tk.Label( stats, text="", font=("Arial", 11), fg="#aeb9c9", bg="#0d1117", justify="left" ) self.stats_label.pack(side="left") tk.Button( stats, text="📊 통계", command=self.show_stats, font=("Arial", 10, "bold"), bg="#252e3d", fg="white", relief="flat", padx=15, pady=8, cursor="hand2" ).pack(side="right") # Quick explanation tip = tk.Frame(self.root, bg="#111722") tip.pack(fill="x", padx=24, pady=8) tk.Label( tip, text="연습법: 카드와 숫자를 확인 → 실제 아칸 루틴 수행 → 성공/실패 기록", font=("Arial", 10), fg="#8290a5", bg="#111722" ).pack(pady=10) self.update_stats() def change_difficulty(self): self.difficulty = self.diff_var.get() self.new_round() def new_round(self): self.stop_timer() # 모든 1,300개 조합을 한 번씩 사용한 뒤 새 사이클 if len(self.used_pairs) >= len(ALL_PAIRS): self.used_pairs.clear() messagebox.showinfo( "🎉 1사이클 완료!", "1,300개의 카드+숫자 조합을 모두 연습했습니다!\n새 사이클을 시작합니다." ) remaining = [x for x in ALL_PAIRS if x not in self.used_pairs] self.current_card, self.current_number = random.choice(remaining) self.used_pairs.add((self.current_card, self.current_number)) self.revealed = True self.render_challenge() self.start_time = time.perf_counter() self.tick_timer() def render_challenge(self): suit = self.current_card[-1] card_color = "#ef6678" if suit in "♥♦" else "#f3f5f8" self.card_label.config(text=self.current_card, fg=card_color) self.card_kr.config(text=self.card_to_kr(self.current_card)) self.number_label.config(text=str(self.current_number)) self.hide_button.config(text="🙈 가리기") self.info_label.config( text=f"남은 미사용 조합: {len(ALL_PAIRS) - len(self.used_pairs)}" ) if self.difficulty == "쉬움": self.card_label.config(font=("Arial", 48, "bold")) self.number_label.config(font=("Arial", 57, "bold")) elif self.difficulty == "보통": self.card_label.config(font=("Arial", 48, "bold")) self.number_label.config(font=("Arial", 57, "bold")) else: # 실전 모드: 짧게 보고 가리는 연습 self.root.after(1300, self.auto_hide) self.draw_progress() def auto_hide(self): if self.revealed and self.current_card is not None: self.hide_reveal(force_hide=True) def hide_reveal(self, force_hide=False): if self.current_card is None: return if force_hide or self.revealed: self.card_label.config(text="?", fg="#778399") self.card_kr.config(text="카드 숨김") self.number_label.config(text="?", fg="#65e6a5") self.hide_button.config(text="👁 다시 보기") self.revealed = False else: suit = self.current_card[-1] self.card_label.config( text=self.current_card, fg="#ef6678" if suit in "♥♦" else "#f3f5f8" ) self.card_kr.config(text=self.card_to_kr(self.current_card)) self.number_label.config(text=str(self.current_number)) self.hide_button.config(text="🙈 가리기") self.revealed = True def tick_timer(self): if self.start_time is None: return elapsed = time.perf_counter() - self.start_time self.timer_label.config(text=f"시간 {elapsed:.2f}초") self.timer_job = self.root.after(50, self.tick_timer) def stop_timer(self): if self.timer_job: self.root.after_cancel(self.timer_job) self.timer_job = None def finish(self, success): if self.current_card is None: return elapsed = time.perf_counter() - self.start_time if self.start_time else 0 self.stop_timer() self.history.append({ "card": self.current_card, "number": self.current_number, "success": success, "time": elapsed }) self.card_counts[self.current_card] += 1 self.number_counts[self.current_number] += 1 result = "성공 ✓" if success else "실패 ✕" self.info_label.config( text=f"{result} · {elapsed:.2f}초 · '새 문제'를 눌러 계속" ) self.update_stats() def card_to_kr(self, card): return f"{SUIT_KR[card[-1]]} {RANK_KR.get(card[:-1], card[:-1])}" def update_stats(self): total = len(self.history) wins = sum(x["success"] for x in self.history) rate = wins / total * 100 if total else 0 avg = sum(x["time"] for x in self.history) / total if total else 0 self.stats_label.config( text=f"연습 {total}회 | 성공률 {rate:.1f}% | 평균 {avg:.2f}초" ) self.progress_text.config( text=f"전체 조합 진행률 {len(self.used_pairs):,} / 1,300" ) self.draw_progress() def draw_progress(self): self.progress.delete("all") w = max(self.progress.winfo_width(), 1) ratio = min(len(self.used_pairs) / 1300, 1) self.progress.create_rectangle( 0, 0, w * ratio, 12, fill="#4d6fff", outline="" ) def show_stats(self): total = len(self.history) wins = sum(x["success"] for x in self.history) losses = total - wins rate = wins / total * 100 if total else 0 avg = sum(x["time"] for x in self.history) / total if total else 0 card_top = self.card_counts.most_common(5) num_top = self.number_counts.most_common(5) cards = "\n".join( f" {c}: {n}회" for c, n in card_top ) or " 기록 없음" nums = "\n".join( f" {n}: {c}회" for n, c in num_top ) or " 기록 없음" text = ( "🃏 ACAAN MASTER 통계\n\n" f"총 연습: {total}회\n" f"성공: {wins}회\n" f"실패: {losses}회\n" f"성공률: {rate:.1f}%\n" f"평균 기록 시간: {avg:.2f}초\n\n" f"이번 사이클: {len(self.used_pairs):,}/1,300\n\n" "자주 연습한 카드 TOP 5\n" + cards + "\n\n자주 연습한 숫자 TOP 5\n" + nums ) if messagebox.askyesno( "통계", text + "\n\n연습 기록을 초기화할까요?" ): self.history.clear() self.card_counts.clear() self.number_counts.clear() self.update_stats() messagebox.showinfo("완료", "연습 기록을 초기화했습니다.") if __name__ == "__main__": root = tk.Tk() app = ACAANApp(root) root.mainloop()
Hosted & Compiled with HtmlRunner.com