From a1b58057278093fed98091be69f9d6db704743ff Mon Sep 17 00:00:00 2001 From: Maik Date: Sat, 13 Jun 2026 22:10:39 +0200 Subject: [PATCH 1/6] add start and stats screen --- gui/components/buttons.py | 11 +++ gui/components/stats.py | 38 ++++++++++- gui/gui.py | 78 ++++++++++++++++++++-- static/index.html | 136 ++++++++++++++++++++++++++++++++------ 4 files changed, 238 insertions(+), 25 deletions(-) diff --git a/gui/components/buttons.py b/gui/components/buttons.py index 67f199c..19ca322 100755 --- a/gui/components/buttons.py +++ b/gui/components/buttons.py @@ -82,6 +82,17 @@ class ActionButtons: self.next_round_button.pack(pady=10) self.next_round_button.configure(state="disabled") + self.exit_button = ctk.CTkButton( + self.parent, + text="Exit", + command=self.gui._exit, + fg_color="#555", + text_color="#ccc", + font=("Arial", 16), + hover_color="#666" + ) + self.exit_button.pack(pady=(4, 10)) + def _enable_action_buttons(self, enable): """Original enable/disable functionality""" state = "normal" if enable else "disabled" diff --git a/gui/components/stats.py b/gui/components/stats.py index c0d4025..c5a97ff 100755 --- a/gui/components/stats.py +++ b/gui/components/stats.py @@ -53,8 +53,9 @@ class StatsDisplay: self.stats_window.title("Session Results") self.stats_window.geometry("500x600") self.stats_window.overrideredirect(True) + self.stats_window.update_idletasks() self.stats_window.grab_set() - + # Original positioning stats_x = self.parent.winfo_x() + (self.parent.winfo_width() - 500) // 2 stats_y = self.parent.winfo_y() + (self.parent.winfo_height() - 600) // 2 @@ -74,6 +75,41 @@ class StatsDisplay: self.stats_window.protocol("WM_DELETE_WINDOW", self._terminate_app) + def _show_exit_popup(self, stats, on_back_to_start): + """Exit stats popup with back-to-start button""" + self.overlay = ctk.CTkFrame(self.parent, fg_color="black") + self.overlay.place(relwidth=1, relheight=1) + + self.stats_window = ctk.CTkToplevel(self.parent) + self.stats_window.title("Session Results") + self.stats_window.geometry("500x600") + self.stats_window.overrideredirect(True) + self.stats_window.update_idletasks() + self.stats_window.grab_set() + + stats_x = self.parent.winfo_x() + (self.parent.winfo_width() - 500) // 2 + stats_y = self.parent.winfo_y() + (self.parent.winfo_height() - 600) // 2 + self.stats_window.geometry(f"+{stats_x}+{stats_y}") + + self._load_stats_content(self.stats_window, stats) + + ctk.CTkButton( + self.stats_window, + text="Back to Start", + command=lambda: self._close_exit_popup(on_back_to_start), + font=("Arial", 16), + height=40, + fg_color="#2aa44f" + ).pack(pady=20, side="bottom", anchor="center") + + def _close_exit_popup(self, callback): + """Close the exit popup and trigger callback""" + if self.overlay and self.overlay.winfo_exists(): + self.overlay.destroy() + if hasattr(self, 'stats_window') and self.stats_window and self.stats_window.winfo_exists(): + self.stats_window.destroy() + callback() + def _terminate_app(self): """Proper termination handling""" if self.overlay and self.overlay.winfo_exists(): diff --git a/gui/gui.py b/gui/gui.py index 579720a..25e7ccd 100755 --- a/gui/gui.py +++ b/gui/gui.py @@ -1,4 +1,5 @@ import customtkinter as ctk +from blackjack_game import BlackjackGame from game_logic import can_split, get_split_decision, get_correct_decision from .utils import load_card_images from .components.cards import CardDisplay @@ -13,7 +14,7 @@ class BlackjackGUI: self.card_images = load_card_images() self._setup_main_window() self._create_components() - self.start_new_round() + self._create_start_screen() def _setup_main_window(self): """Initialize main application window""" @@ -150,9 +151,76 @@ class BlackjackGUI: self.buttons.next_round_button.configure(state="normal") def _on_main_window_close(self): - """Handle window close event""" - self.app.configure(state="disabled") - self.stats._show_final_stats(self.game_logic.get_stats()) + """Handle window close event — just close directly""" + self.app.destroy() + + def _create_start_screen(self): + """Create the start screen overlay""" + if hasattr(self, 'start_frame') and self.start_frame and self.start_frame.winfo_exists(): + return + self.start_frame = ctk.CTkFrame(self.app, fg_color="#2b2b2b", corner_radius=0) + self.start_frame.place(relx=0, rely=0, relwidth=1, relheight=1) + + content = ctk.CTkFrame(self.start_frame, fg_color="transparent") + content.place(relx=0.5, rely=0.5, anchor="center") + + ctk.CTkLabel( + content, + text="\U0001F0CF Blackjack Theory Trainer", + font=("Arial", 28, "bold"), + text_color="white" + ).pack(pady=(0, 20)) + + ctk.CTkLabel( + content, + text="This is a blackjack basic strategy trainer and not a real blackjack game.\n\nEach round you're dealt a hand and asked to make the correct play (Hit, Stand, Double, or Split) according to basic strategy. You get immediate feedback and track your accuracy over time.", + font=("Arial", 15), + text_color="#ccc", + wraplength=450, + justify="center" + ).pack(pady=(0, 30)) + + ctk.CTkButton( + content, + text="Start Training", + command=self._start_training, + fg_color="#2aa44f", + text_color="white", + font=("Arial", 20, "bold"), + height=45, + width=200 + ).pack(pady=(0, 10)) + + ctk.CTkButton( + content, + text="Quit", + command=self._on_main_window_close, + fg_color="#555", + text_color="#ccc", + font=("Arial", 16), + height=35, + width=120, + hover_color="#666" + ).pack() + + def _start_training(self): + """Start button handler — enter game mode""" + if hasattr(self, 'start_frame') and self.start_frame: + self.start_frame.destroy() + self.start_frame = None + self.start_new_round() + + def _exit(self): + """Exit button handler — show stats popup""" + self.buttons._enable_action_buttons(False) + self.buttons.next_round_button.configure(state="disabled") + self.buttons.hint_button.configure(state="disabled") + self.stats._show_exit_popup(self.game_logic.get_stats(), self._exit_to_start) + + def _exit_to_start(self): + """Reset game state and return to start screen""" + self.game_logic = BlackjackGame() + self._create_start_screen() def show_hint(self): """Delegate hint display to the HintSystem""" @@ -161,4 +229,4 @@ class BlackjackGUI: def run(self): """Start application main loop""" - self.app.mainloop() \ No newline at end of file + self.app.mainloop() diff --git a/static/index.html b/static/index.html index 6f6ee95..cff6de7 100644 --- a/static/index.html +++ b/static/index.html @@ -28,31 +28,69 @@ body{background:#2b2b2b;color:#fff;font-family:Arial,sans-serif;display:flex;jus #lightbox{display:none;position:fixed;inset:0;background:rgba(0,0,0,.85);justify-content:center;align-items:center;z-index:999;cursor:pointer} #lightbox.open{display:flex} #lightbox img{max-width:90vw;max-height:90vh;border-radius:8px} +#start-screen{display:flex;justify-content:center;align-items:center;min-height:80vh;width:100%} +#start-content{text-align:center;max-width:500px} +#start-content h1{font-size:32px;margin-bottom:24px;color:#fff} +#start-content p{font-size:16px;line-height:1.6;color:#ccc;margin-bottom:32px} +#btn-start{background:#2aa44f;color:#fff;font-size:22px;border:none;border-radius:8px;padding:14px 48px;cursor:pointer;font-weight:700} +#btn-start:hover{background:#238c44} +#game-screen{width:100%} +#stats-modal{display:none;position:fixed;inset:0;background:rgba(0,0,0,.85);justify-content:center;align-items:center;z-index:999} +#stats-modal.open{display:flex} +#stats-content{background:#2b2b2b;padding:32px;border-radius:12px;max-width:450px;width:90%;text-align:center} +#stats-content h2{font-size:24px;margin-bottom:20px;color:#fff} +#stats-data{text-align:left;margin-bottom:24px} +.stat-row{display:flex;justify-content:space-between;padding:6px 0;font-size:16px;color:#ccc;border-bottom:1px solid #444} +.stat-row:last-child{border-bottom:none} +.stat-label{color:#aaa} +.stat-value{color:#fff;font-weight:700} +.scenario-header{font-size:16px;font-weight:700;color:#fff;margin-top:16px;margin-bottom:8px} +#btn-back-to-start{background:#2aa44f;color:#fff;font-size:18px;border:none;border-radius:8px;padding:12px 36px;cursor:pointer;font-weight:700;margin-top:8px} +#btn-back-to-start:hover{background:#238c44} +#btn-exit{background:#555;color:#ccc;font-size:16px;border:none;border-radius:6px;padding:8px 24px;cursor:pointer;font-weight:400;margin-top:4px} +#btn-exit:hover{background:#666;color:#fff}
-
From 16103310c068823680283c6b34e52eb38ee0751c Mon Sep 17 00:00:00 2001 From: Maik Date: Sat, 13 Jun 2026 22:42:48 +0200 Subject: [PATCH 4/6] bug fixes --- gui/components/buttons.py | 52 ++++++--------------------------------- gui/gui.py | 2 ++ static/index.html | 9 +++++-- 3 files changed, 17 insertions(+), 46 deletions(-) diff --git a/gui/components/buttons.py b/gui/components/buttons.py index bc43c51..249569d 100755 --- a/gui/components/buttons.py +++ b/gui/components/buttons.py @@ -14,48 +14,14 @@ class ActionButtons: def _create_action_buttons(self): """Replicates original button creation exactly""" - self.hit_button = ctk.CTkButton( - self.button_frame, - text="Hit", - command=self.gui._hit, - fg_color="white", - text_color="black", - text_color_disabled="white", - font=("Arial", 20) - ) + common = dict(fg_color="white", hover_color="white", text_color="black", text_color_disabled="white", font=("Arial", 20)) + self.hit_button = ctk.CTkButton(self.button_frame, text="Hit", command=self.gui._hit, **common) self.hit_button.grid(row=0, column=0, padx=10, pady=10) - - self.stand_button = ctk.CTkButton( - self.button_frame, - text="Stand", - command=self.gui._stand, - fg_color="white", - text_color="black", - text_color_disabled="white", - font=("Arial", 20) - ) + self.stand_button = ctk.CTkButton(self.button_frame, text="Stand", command=self.gui._stand, **common) self.stand_button.grid(row=0, column=1, padx=10, pady=10) - - self.double_button = ctk.CTkButton( - self.button_frame, - text="Double", - command=self.gui._double, - fg_color="white", - text_color="black", - text_color_disabled="white", - font=("Arial", 20) - ) + self.double_button = ctk.CTkButton(self.button_frame, text="Double", command=self.gui._double, **common) self.double_button.grid(row=1, column=0, padx=10, pady=10) - - self.split_button = ctk.CTkButton( - self.button_frame, - text="Split", - command=self.gui._split, - fg_color="white", - text_color="black", - text_color_disabled="white", - font=("Arial", 20) - ) + self.split_button = ctk.CTkButton(self.button_frame, text="Split", command=self.gui._split, **common) self.split_button.grid(row=1, column=1, padx=10, pady=10) def _enable_action_buttons(self, enable): @@ -68,12 +34,10 @@ class ActionButtons: def _reset_button_colors(self): """Original color reset""" - self.hit_button.configure(fg_color="white") - self.stand_button.configure(fg_color="white") - self.double_button.configure(fg_color="white") - self.split_button.configure(fg_color="white") + for b in (self.hit_button, self.stand_button, self.double_button, self.split_button): + b.configure(fg_color="white", hover_color="white") def _mark_button(self, button, is_correct): """Original button marking""" color = "green" if is_correct else "red" - button.configure(fg_color=color) \ No newline at end of file + button.configure(fg_color=color, hover_color=color) \ No newline at end of file diff --git a/gui/gui.py b/gui/gui.py index 956e161..5275338 100755 --- a/gui/gui.py +++ b/gui/gui.py @@ -38,6 +38,7 @@ class BlackjackGUI: text="Hint", command=self.show_hint, fg_color="white", + hover_color="white", text_color="black", font=("Arial", 20) ) @@ -59,6 +60,7 @@ class BlackjackGUI: text="Next Round", command=self.start_new_round, fg_color="white", + hover_color="white", text_color="black", font=("Arial", 20) ) diff --git a/static/index.html b/static/index.html index cd111ca..3eaa83f 100644 --- a/static/index.html +++ b/static/index.html @@ -169,8 +169,13 @@ async function startRound(){ enableActions(false); btnNext.disabled=false; } - if(d.can_split){btnSplit.disabled=false} - else{btnSplit.disabled=true} + if(d.can_split){ + btnSplit.disabled=false; + if(!d.has_blackjack){ + feedback.textContent='You have a pair! Consider splitting.'; + feedback.style.color='white'; + } + }else{btnSplit.disabled=true} }catch(e){feedback.textContent='Error connecting to server';feedback.style.color='red'} } From 9d8752ab40d0542bdb1c5c9083e57ab79385107a Mon Sep 17 00:00:00 2001 From: Maik Date: Sat, 13 Jun 2026 22:56:24 +0200 Subject: [PATCH 5/6] bug fixes --- static/index.html | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/static/index.html b/static/index.html index 3eaa83f..1418b62 100644 --- a/static/index.html +++ b/static/index.html @@ -163,19 +163,16 @@ async function startRound(){ btnNext.disabled=true; enableActions(true); resetButtonColors(); + btnSplit.disabled=!d.can_split; if(d.has_blackjack){ feedback.textContent='Blackjack! You win or get a push!'; feedback.style.color='green'; enableActions(false); btnNext.disabled=false; + }else if(d.can_split){ + feedback.textContent='You have a pair! Consider splitting.'; + feedback.style.color='white'; } - if(d.can_split){ - btnSplit.disabled=false; - if(!d.has_blackjack){ - feedback.textContent='You have a pair! Consider splitting.'; - feedback.style.color='white'; - } - }else{btnSplit.disabled=true} }catch(e){feedback.textContent='Error connecting to server';feedback.style.color='red'} } From 97ffb52703589417a8316979f8040d8bf88105c3 Mon Sep 17 00:00:00 2001 From: Maik Date: Sat, 13 Jun 2026 23:01:43 +0200 Subject: [PATCH 6/6] bug fix --- gui/gui.py | 5 +++-- static/index.html | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/gui/gui.py b/gui/gui.py index 5275338..e84dcd0 100755 --- a/gui/gui.py +++ b/gui/gui.py @@ -103,6 +103,7 @@ class BlackjackGUI: self.next_round_button.configure(state="disabled") self.buttons._enable_action_buttons(True) self.buttons._reset_button_colors() + self.buttons.split_button.configure(text="") if hasattr(self.notifications, 'streak_label') and self.notifications.streak_label: self.notifications.streak_label.configure(text="") @@ -127,13 +128,13 @@ class BlackjackGUI: # Handle split possibility can_split_bool = can_split(self.game_logic.player_hand) if can_split_bool: - self.buttons.split_button.configure(state="normal") + self.buttons.split_button.configure(state="normal", text="Split") self.feedback_label.configure( text="You have a pair! Consider splitting.", text_color="white" ) else: - self.buttons.split_button.configure(state="disabled") + self.buttons.split_button.configure(state="disabled", text="") # Update notifications stats = self.game_logic.get_stats() diff --git a/static/index.html b/static/index.html index 1418b62..3f36ff3 100644 --- a/static/index.html +++ b/static/index.html @@ -163,6 +163,7 @@ async function startRound(){ btnNext.disabled=true; enableActions(true); resetButtonColors(); + btnSplit.textContent=d.can_split?'Split':''; btnSplit.disabled=!d.can_split; if(d.has_blackjack){ feedback.textContent='Blackjack! You win or get a push!';