blackjack-theory-game/gui/gui.py
2026-06-13 22:10:39 +02:00

233 lines
8.5 KiB
Python
Executable File

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
from .components.buttons import ActionButtons
from .components.notifications import NotificationSystem
from .components.hints import HintSystem
from .components.stats import StatsDisplay
class BlackjackGUI:
def __init__(self, blackjackGame):
self.game_logic = blackjackGame
self.card_images = load_card_images()
self._setup_main_window()
self._create_components()
self._create_start_screen()
def _setup_main_window(self):
"""Initialize main application window"""
self.app = ctk.CTk()
self.app.title("Blackjack Theory Trainer")
self.app.geometry("600x650+800+200")
self.app.protocol("WM_DELETE_WINDOW", self._on_main_window_close)
ctk.set_appearance_mode("dark")
def _create_components(self):
"""Create all UI components"""
# Card display
self.card_display = CardDisplay(self.app, self.card_images)
# Buttons (pass reference to self for callbacks)
self.buttons = ActionButtons(self.app, self)
# Notifications
self.notifications = NotificationSystem(self.app)
# Hint system
self.hints = HintSystem(self.app)
self.hints.set_hint_button(self.buttons.hint_button)
# Stats display
self.stats = StatsDisplay(self.app)
# Feedback label (only remaining widget in main file)
self.feedback_label = ctk.CTkLabel(
self.app,
text="",
font=("Arial", 20),
wraplength=550,
anchor="w"
)
self.feedback_label.pack(pady=20, padx=10, fill="x")
def start_new_round(self):
"""Start a new round of the game"""
self._reset_ui()
self.game_logic.start_new_round()
self._update_display()
def _reset_ui(self):
"""Reset UI for new round"""
self.hints._close_hint_window()
self.buttons.hint_button.configure(state="normal")
self.feedback_label.configure(text="")
self.buttons.next_round_button.configure(state="disabled")
self.buttons._enable_action_buttons(True)
self.buttons._reset_button_colors()
if hasattr(self.notifications, 'streak_label') and self.notifications.streak_label:
self.notifications.streak_label.configure(text="")
def _update_display(self):
"""Update all display elements"""
# Update card display
self.card_display.update_display(
self.game_logic.dealer_card,
self.game_logic.player_hand
)
# Handle blackjack
if self.game_logic.has_blackjack:
self.feedback_label.configure(
text="Blackjack! You win or get a push!",
text_color="green"
)
self.buttons._enable_action_buttons(False)
self.buttons.next_round_button.configure(state="normal")
# 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.feedback_label.configure(
text="You have a pair! Consider splitting.",
text_color="white"
)
else:
self.buttons.split_button.configure(state="disabled")
# Update notifications
stats = self.game_logic.get_stats()
self.notifications._update_streak_display(stats["current_streak"])
if stats.get("pending_streak_notification"):
self.notifications._show_streak_notification(stats["pending_streak_notification"])
def _hit(self):
self._handle_action("Hit", self.buttons.hit_button)
def _stand(self):
self._handle_action("Stand", self.buttons.stand_button)
def _double(self):
self._handle_action("Double", self.buttons.double_button)
def _split(self):
self._handle_action("Split", self.buttons.split_button)
def _handle_action(self, action, button):
"""Process player actions"""
# Determine scenario type
scenario_type = "hard_totals"
if can_split(self.game_logic.player_hand):
scenario_type = "pairs"
elif 'A' in [card.split('_')[-1] for card in self.game_logic.player_hand]:
scenario_type = "soft_totals"
# Check if player can split
if can_split(self.game_logic.player_hand):
decision, feedback = get_split_decision(
self.game_logic.player_hand,
self.game_logic.dealer_card
)
correct = action == decision
else:
decision, feedback = get_correct_decision(
self.game_logic.player_hand,
self.game_logic.player_total,
self.game_logic.dealer_card
)
correct = action == decision
# Update UI
color = "green" if correct else "red"
self.feedback_label.configure(text=feedback, text_color=color)
self.buttons._mark_button(button, correct)
self.game_logic.update_stats(correct, scenario_type)
# Update button states
self.buttons._enable_action_buttons(False)
self.buttons.next_round_button.configure(state="normal")
def _on_main_window_close(self):
"""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"""
self.hints._disable_hint_button()
self.hints.show_hint()
def run(self):
"""Start application main loop"""
self.app.mainloop()