Aula Jazmati 💡🕊️
Published © MIT

Interactive Wish Clock with Raspberry Pi 🎄✨

A creative digital clock for New Year's Eve that lets users input and display wishes.

BeginnerFull instructions provided3 hours26

Things used in this project

Story

Read more

Code

Clock code

Python
import pygame
import time
import json
import os
import math
from datetime import datetime
from math import sin, cos, radians, pi
from random import randint, uniform

# Initialize Pygame
pygame.init()

# Screen settings
WIDTH, HEIGHT = 1024, 664
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("✨ Interactive Wish Clock 2026 ✨ - Raspberry Pi")

# =============================================
# 🎨 Color Palette
# =============================================
COLORS = {
    'bg_dark_blue': (8, 12, 32),
    'bg_navy': (15, 25, 45),
    'clock_face_start': (30, 40, 80),
    'clock_face_end': (20, 30, 60),
    'clock_rim': (255, 200, 50, 180),
    'hour_hand_start': (255, 100, 100),
    'hour_hand_end': (255, 150, 50),
    'minute_hand_start': (50, 220, 150),
    'minute_hand_end': (100, 255, 200),
    'second_hand': (255, 255, 100),
    'text_primary': (255, 255, 255),
    'text_secondary': (200, 220, 255),
    'text_highlight': (255, 215, 0),
    'text_warning': (255, 100, 100),
    'text_success': (100, 255, 150),
    'wish_box_bg': (25, 35, 65, 220),
    'wish_box_border': (255, 215, 0, 200),
    'input_field_bg': (20, 30, 50),
    'input_field_border': (255, 200, 100),
    'sparkle_gold': (255, 215, 0),
    'sparkle_silver': (200, 220, 255),
    'firework_red': (255, 50, 50),
    'firework_blue': (50, 150, 255),
    'firework_green': (50, 255, 100),
    'firework_purple': (180, 70, 255),
}

# =============================================
# 🌟 Star Class for Animated Background
# =============================================
class Star:
    def __init__(self):
        self.x = randint(0, WIDTH)
        self.y = randint(0, HEIGHT)
        self.size = uniform(0.5, 2.5)
        self.speed = uniform(0.1, 0.5)
        self.brightness = uniform(150, 255)
        self.pulse_speed = uniform(0.5, 2.0)
    
    def update(self):
        self.y += self.speed
        if self.y > HEIGHT:
            self.y = 0
            self.x = randint(0, WIDTH)
        
        self.brightness = 150 + 105 * sin(time.time() * self.pulse_speed)
    
    def draw(self, surface):
        color = (self.brightness, self.brightness, self.brightness)
        pygame.draw.circle(surface, color, (int(self.x), int(self.y)), self.size)

# =============================================
# 🎆 Firework Particle Class
# =============================================
class FireworkParticle:
    def __init__(self, x, y, color):
        self.x = x
        self.y = y
        self.color = color
        self.size = uniform(1.5, 3.0)
        self.speed_x = uniform(-3, 3)
        self.speed_y = uniform(-3, 3)
        self.life = 1.0
        self.decay = uniform(0.02, 0.05)
    
    def update(self):
        self.x += self.speed_x
        self.y += self.speed_y
        self.speed_x *= 0.98
        self.speed_y *= 0.98
        self.life -= self.decay
        return self.life > 0
    
    def draw(self, surface):
        alpha = int(255 * self.life)
        color_with_alpha = (*self.color, alpha)
        particle_surface = pygame.Surface((int(self.size*2), int(self.size*2)), pygame.SRCALPHA)
        pygame.draw.circle(particle_surface, color_with_alpha, 
                          (int(self.size), int(self.size)), int(self.size))
        surface.blit(particle_surface, (int(self.x - self.size), int(self.y - self.size)))

# =============================================
# 📝 Font Setup
# =============================================
try:
    fonts = {
        'title': pygame.font.Font(None, 56),
        'clock': pygame.font.Font(None, 72),
        'wish': pygame.font.Font(None, 36),
        'message': pygame.font.Font(None, 32),
        'small': pygame.font.Font(None, 24)
    }
except:
    fonts = {
        'title': pygame.font.SysFont('Arial', 56, bold=True),
        'clock': pygame.font.SysFont('Arial', 72, bold=True),
        'wish': pygame.font.SysFont('Arial', 36),
        'message': pygame.font.SysFont('Arial', 32),
        'small': pygame.font.SysFont('Arial', 24)
    }

# =============================================
# 📊 Wish Management
# =============================================
wishes_file = "wishes.json"
current_wish = ""
wish_mode = False
wish_history = []
firework_particles = []

def load_wishes():
    global wish_history
    try:
        if os.path.exists(wishes_file):
            with open(wishes_file, 'r') as f:
                wish_history = json.load(f)
    except:
        wish_history = []

def save_wish(wish_text):
    global wish_history
    try:
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        wish_entry = {
            'text': wish_text,
            'timestamp': timestamp,
            'year': 2026
        }
        wish_history.append(wish_entry)
        
        if len(wish_history) > 15:
            wish_history = wish_history[-15:]
        
        with open(wishes_file, 'w') as f:
            json.dump(wish_history, f, indent=2)
        
        create_fireworks(WIDTH // 2, HEIGHT // 2, 30)
        return True
    except:
        return False

# =============================================
# 🎆 Visual Effects
# =============================================
def create_fireworks(x, y, count=20):
    colors = [
        COLORS['firework_red'],
        COLORS['firework_blue'],
        COLORS['firework_green'],
        COLORS['firework_purple'],
        COLORS['sparkle_gold']
    ]
    for _ in range(count):
        color = colors[randint(0, len(colors)-1)]
        firework_particles.append(FireworkParticle(x, y, color))

def update_fireworks():
    global firework_particles
    firework_particles = [p for p in firework_particles if p.update()]

def draw_radial_gradient(surface, center, radius, color_start, color_end):
    for r in range(radius, 0, -1):
        ratio = r / radius
        color = (
            int(color_start[0] * ratio + color_end[0] * (1 - ratio)),
            int(color_start[1] * ratio + color_end[1] * (1 - ratio)),
            int(color_start[2] * ratio + color_end[2] * (1 - ratio))
        )
        pygame.draw.circle(surface, color, center, r)

# =============================================
# 🕰️ Clock Drawing Functions
# =============================================
def draw_analog_clock():
    center_x, center_y = WIDTH // 2, HEIGHT // 3
    clock_radius = 160
    
    # Draw clock face
    draw_radial_gradient(screen, (center_x, center_y), clock_radius,
                        COLORS['clock_face_start'], COLORS['clock_face_end'])
    
    # Draw clock rim
    pygame.draw.circle(screen, COLORS['clock_rim'], 
                      (center_x, center_y), clock_radius, 3)
    
    # Get current time
    now = datetime.now()
    hour = now.hour % 12
    minute = now.minute
    second = now.second
    
    # Draw hour markers
    for i in range(12):
        angle = radians(i * 30 - 90)
        x = center_x + (clock_radius - 25) * cos(angle)
        y = center_y + (clock_radius - 25) * sin(angle)
        
        if i % 3 == 0:
            points = [(x, y-8), (x-8, y+6), (x+8, y+6)]
            pygame.draw.polygon(screen, COLORS['sparkle_gold'], points)
        else:
            pygame.draw.circle(screen, COLORS['text_secondary'], 
                             (int(x), int(y)), 6)
    
    # Draw hour hand
    hour_angle = radians((hour * 30) + (minute * 0.5) - 90)
    hour_length = clock_radius * 0.5
    hour_x = center_x + hour_length * cos(hour_angle)
    hour_y = center_y + hour_length * sin(hour_angle)
    pygame.draw.line(screen, COLORS['hour_hand_start'], 
                    (center_x, center_y), (hour_x, hour_y), 10)
    
    # Draw minute hand
    minute_angle = radians((minute * 6) - 90)
    minute_length = clock_radius * 0.7
    minute_x = center_x + minute_length * cos(minute_angle)
    minute_y = center_y + minute_length * sin(minute_angle)
    pygame.draw.line(screen, COLORS['minute_hand_start'], 
                    (center_x, center_y), (minute_x, minute_y), 6)
    
    # Draw center cap
    pygame.draw.circle(screen, COLORS['sparkle_gold'], 
                      (center_x, center_y), 8)

def draw_digital_clock():
    now = datetime.now()
    time_str = now.strftime("%H:%M:%S")
    date_str = now.strftime("%A, %B %d, %Y")
    
    # Draw time
    time_text = fonts['clock'].render(time_str, True, COLORS['text_primary'])
    time_rect = time_text.get_rect(center=(WIDTH//2, HEIGHT//2 + 50))
    screen.blit(time_text, time_rect)
    
    # Draw date
    date_text = fonts['message'].render(date_str, True, COLORS['text_secondary'])
    date_rect = date_text.get_rect(center=(WIDTH//2, HEIGHT//2 + 100))
    screen.blit(date_text, date_rect)
    
    # Countdown to New Year 2026
    if now.month == 12 and now.day == 31:
        new_year = datetime(2026, 1, 1, 0, 0, 0)
        time_left = new_year - now
        
        if time_left.total_seconds() > 0:
            hours = time_left.seconds // 3600
            minutes = (time_left.seconds % 3600) // 60
            seconds = time_left.seconds % 60
            
            countdown_text = f"Countdown to 2026: {hours:02d}:{minutes:02d}:{seconds:02d}"
            countdown_display = fonts['message'].render(countdown_text, True, COLORS['text_warning'])
            countdown_rect = countdown_display.get_rect(center=(WIDTH//2, HEIGHT//2 + 140))
            screen.blit(countdown_display, countdown_rect)
            
            # Special effects for last 10 minutes
            if hours == 0 and minutes < 10:
                pulse = abs(sin(time.time() * 4)) * 50
                pygame.draw.circle(screen, (*COLORS['firework_red'], 100),
                                 (WIDTH//2, HEIGHT//2 + 140), int(20 + pulse), 2)

def draw_wish_interface():
    # Semi-transparent overlay
    overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
    overlay.fill((0, 0, 0, 150))
    screen.blit(overlay, (0, 0))
    
    # Wish box
    box_rect = pygame.Rect(WIDTH//4, HEIGHT//4, WIDTH//2, HEIGHT//2)
    pygame.draw.rect(screen, COLORS['wish_box_bg'], box_rect, border_radius=15)
    pygame.draw.rect(screen, COLORS['wish_box_border'], box_rect, 3, border_radius=15)
    
    # Title
    title = fonts['title'].render("Your Wish for 2026", True, COLORS['text_highlight'])
    title_rect = title.get_rect(center=(WIDTH//2, HEIGHT//4 + 50))
    screen.blit(title, title_rect)
    
    # Input field
    input_rect = pygame.Rect(WIDTH//4 + 40, HEIGHT//4 + 120, WIDTH//2 - 80, 60)
    pygame.draw.rect(screen, COLORS['input_field_bg'], input_rect, border_radius=10)
    pygame.draw.rect(screen, COLORS['input_field_border'], input_rect, 2, border_radius=10)
    
    # Display current wish text
    wish_display = current_wish + ("_" if int(time.time() * 2) % 2 else " ")
    wish_text = fonts['wish'].render(wish_display, True, COLORS['text_highlight'])
    wish_text_rect = wish_text.get_rect(midleft=(input_rect.x + 10, input_rect.centery))
    screen.blit(wish_text, wish_text_rect)
    
    # Instructions
    instructions = [
        "Type your wish and press ENTER to save",
        "Press ESC to cancel",
        "Your wish will be displayed with the clock"
    ]
    
    for i, instruction in enumerate(instructions):
        inst_text = fonts['message'].render(instruction, True, COLORS['text_secondary'])
        inst_rect = inst_text.get_rect(center=(WIDTH//2, HEIGHT//4 + 220 + i*40))
        screen.blit(inst_text, inst_rect)

def draw_wish_history():
    if wish_history:
        history_title = fonts['message'].render("Recent Wishes:", True, (180, 220, 255))
        screen.blit(history_title, (20, HEIGHT - 150))
        
        for i, wish in enumerate(wish_history[-3:]):
            wish_text = f"• {wish['text'][:30]}..."
            wish_display = fonts['small'].render(wish_text, True, (200, 200, 200))
            screen.blit(wish_display, (40, HEIGHT - 120 + i*30))

def draw_current_wish():
    if current_wish and not wish_mode:
        wish_bg = pygame.Rect(WIDTH//4, HEIGHT - 200, WIDTH//2, 50)
        pygame.draw.rect(screen, (40, 50, 80, 180), wish_bg, border_radius=10)
        
        wish_text = fonts['wish'].render(f"✨ {current_wish} ✨", True, COLORS['text_highlight'])
        wish_rect = wish_text.get_rect(center=(WIDTH//2, HEIGHT - 175))
        screen.blit(wish_text, wish_rect)

# =============================================
# 🎮 Main Application
# =============================================
def main():
    global current_wish, wish_mode, firework_particles
    
    # Initialize stars
    stars = [Star() for _ in range(100)]
    
    # Load wishes
    load_wishes()
    if wish_history:
        current_wish = wish_history[-1]['text']
    
    # Main loop
    clock = pygame.time.Clock()
    running = True
    last_wish_save = 0
    
    print("=" * 50)
    print("Interactive Wish Clock 2026")
    print("Controls: W=Wish Mode, H=Last Wish, R=Random, ESC=Exit")
    print("=" * 50)
    
    while running:
        current_time = time.time()
        
        # Event handling
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            
            elif event.type == pygame.KEYDOWN:
                if wish_mode:
                    if event.key == pygame.K_RETURN:
                        if current_wish.strip():
                            if save_wish(current_wish):
                                last_wish_save = current_time
                            wish_mode = False
                    elif event.key == pygame.K_ESCAPE:
                        wish_mode = False
                        current_wish = ""
                    elif event.key == pygame.K_BACKSPACE:
                        current_wish = current_wish[:-1]
                    elif event.unicode.isprintable():
                        if len(current_wish) < 50:
                            current_wish += event.unicode
                else:
                    if event.key == pygame.K_w:
                        wish_mode = True
                        current_wish = ""
                    elif event.key == pygame.K_ESCAPE:
                        running = False
                    elif event.key == pygame.K_h:
                        if wish_history:
                            current_wish = wish_history[-1]['text']
                    elif event.key == pygame.K_r:
                        sample_wishes = [
                            "Peace and happiness in 2026!",
                            "Learn something new every day",
                            "More time with family and friends",
                            "Success in all my projects",
                            "Health and prosperity for all"
                        ]
                        current_wish = sample_wishes[randint(0, len(sample_wishes)-1)]
        
        # Update stars
        for star in stars:
            star.update()
        
        # Update fireworks
        update_fireworks()
        
        # Auto fireworks on new year's eve
        now = datetime.now()
        if now.month == 12 and now.day == 31 and now.hour == 23 and now.minute >= 59:
            if randint(0, 10) > 8:
                create_fireworks(randint(100, WIDTH-100), randint(100, HEIGHT-100), 15)
        
        # =============================================
        # 🎨 Drawing Section
        # =============================================
        
        # Draw gradient background
        for y in range(HEIGHT):
            ratio = y / HEIGHT
            color = (
                int(COLORS['bg_dark_blue'][0] * (1 - ratio) + COLORS['bg_navy'][0] * ratio),
                int(COLORS['bg_dark_blue'][1] * (1 - ratio) + COLORS['bg_navy'][1] * ratio),
                int(COLORS['bg_dark_blue'][2] * (1 - ratio) + COLORS['bg_navy'][2] * ratio)
            )
            pygame.draw.line(screen, color, (0, y), (WIDTH, y))
        
        # Draw stars
        for star in stars:
            star.draw(screen)
        
        # Draw fireworks
        for particle in firework_particles:
            particle.draw(screen)
        
        # Draw clock
        draw_analog_clock()
        draw_digital_clock()
        
        # Draw wish interface or current wish
        if wish_mode:
            draw_wish_interface()
        else:
            draw_current_wish()
        
        # Draw wish history
        draw_wish_history()
        
        # Draw title
        title = "✨ Interactive Wish Clock 2026 ✨"
        title_text = fonts['title'].render(title, True, COLORS['text_highlight'])
        screen.blit(title_text, (WIDTH//2 - title_text.get_width()//2, 25))
        
        # Draw instructions
        if not wish_mode:
            instruction = fonts['message'].render("Press 'W' to enter your wish for 2026", 
                                                True, (150, 200, 255))
            screen.blit(instruction, (WIDTH//2 - instruction.get_width()//2, HEIGHT - 40))
        
        # Show save confirmation
        if current_time - last_wish_save < 2:
            save_text = "✅ Wish saved successfully!"
            save_surface = fonts['message'].render(save_text, True, COLORS['text_success'])
            save_rect = save_surface.get_rect(center=(WIDTH//2, HEIGHT - 280))
            screen.blit(save_surface, save_rect)
        
        # Update display
        pygame.display.flip()
        clock.tick(60)
    
    pygame.quit()
    print("Goodbye! Wishes saved to wishes.json")

# =============================================
# 🚀 Application Entry Point
# =============================================
if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        print(f"Error: {e}")
        pygame.quit()

Interactive Tree Game

Python
# -*- coding: utf-8 -*-
"""
Created on Wed Dec 31 16:09:27 2025

@author: Aula
"""

import turtle
import time
import random
import math

# =============================================
# Configuration and color palette
# =============================================
# Tree segments: (size, top_y)
TREE_SEGMENTS = [
    (80, 180),
    (120, 140),
    (160, 100),
    (200, 60),
    (240, 20),
]

# Color palette (consistent panel + ground colors)
COLORS = {
    "sky": "#0A2342",           # dark night sky
    "tree": "#0B6623",          # deep tree green
    "trunk": "#5D2906",         # darker trunk brown
    "star": "#FFD700",          # gold star
    "text": "#FFFFFF",          # white text
    "panel": "#1A3A5F",         # base panel color
    "snow": "#E8F4F8",          # soft snow color
    "ornament_gold": "#FFD700", # gold for highlights
    "bg_panel": "#142D4C",      # dark panel background
    "panel_accent": "#1E3D5F",  # panel accent strip
    "ground": "#E8F4F8",        # ground color (snow)
    "present_ribbon": "#FF9F1C" # ribbon accent
}


ORNAMENT_COLORS = [
    "#FF6B6B", "#4ECDC4", "#FFD166", "#06D6A0",
    "#118AB2", "#EF476F", "#7209B7", "#3A86FF",
    "#FB5607", "#8338EC", "#FF006E", "#07BEB8",
]


TOTAL_ORNAMENTS = 15   # number of ornaments on the tree
BASE_CLICK_RADIUS = 18 # base click radius (scaled by ornament size)
UPDATE_INTERVAL_MS = 50  # main loop update interval in milliseconds

# =============================================
# Global game state
# =============================================
window = None
ornaments = []            # list of ornament turtles
ornament_positions = []   # positions used for ornaments
sparkle_turtle = None     # single turtle used to draw sparkles
border_turtle = None      # single turtle used to draw ornament borders (dots)
score_display = None      # turtle used for score text
clicked_ornaments = 0
game_active = False
game_start_time = None
best_time = float('inf')

# =============================================
# Window and basic drawing helpers
# =============================================
def create_window():
    """Create and configure the main window."""
    global window
    window = turtle.Screen()
    window.bgcolor(COLORS["sky"])
    window.title("🎄 Christmas Tree Challenge")
    window.setup(width=1100, height=800)
    window.tracer(0)  # manual screen updates for performance
    return window

def draw_tree_segment(size, top_y, t):
    """Draw a single triangular tree segment using turtle t."""
    t.penup()
    t.goto(0, top_y)
    t.pendown()
    t.begin_fill()
    t.setposition(size, top_y - size)
    t.setposition(-size, top_y - size)
    t.setposition(0, top_y)
    t.end_fill()

def draw_tree_and_trunk():
    """Draw the full tree and trunk."""
    t = turtle.Turtle()
    t.hideturtle()
    t.speed("fastest")
    t.color(COLORS["tree"])
    for size, top in TREE_SEGMENTS:
        draw_tree_segment(size, top, t)

    # Draw trunk
    bottom_y = TREE_SEGMENTS[-1][1] - TREE_SEGMENTS[-1][0]
    trunk = turtle.Turtle()
    trunk.hideturtle()
    trunk.speed("fastest")
    trunk.color(COLORS["trunk"])
    trunk.penup()
    trunk.goto(-25, bottom_y - 10)
    trunk.pendown()
    trunk.begin_fill()
    for _ in range(2):
        trunk.forward(50)
        trunk.left(90)
        trunk.forward(60)
        trunk.left(90)
    trunk.end_fill()

def draw_star():
    """Draw the star on top of the tree."""
    star = turtle.Turtle()
    star.hideturtle()
    star.speed("fastest")
    star.color(COLORS["star"])
    top_y = TREE_SEGMENTS[0][1]
    star.penup()
    star.goto(0, top_y + 30)
    star.pendown()
    star.begin_fill()
    for _ in range(5):
        star.forward(50)
        star.right(144)
    star.end_fill()

# =============================================
# Background: snow and presents 
# =============================================
def draw_snow_and_presents():
    """Draw ground snow and two presents with consistent colors."""
    snow = turtle.Turtle()
    snow.hideturtle()
    snow.speed("fastest")
    snow.color(COLORS["ground"])
    bottom_y = TREE_SEGMENTS[-1][1] - TREE_SEGMENTS[-1][0] - 70
    snow.penup()
    snow.goto(-600, bottom_y)
    snow.pendown()
    snow.begin_fill()
    for _ in range(2):
        snow.forward(1200)
        snow.right(90)
        snow.forward(80)
        snow.right(90)
    snow.end_fill()
    snow.penup()
    # subtle snowflakes
    for _ in range(30):
        x = random.randint(-500, 500)
        y = random.randint(-150, 350)
        snow.goto(x, y)
        snow.dot(random.randint(2, 5), COLORS["snow"])

    # Draw two presents with ribbon accents
    p = turtle.Turtle()
    p.hideturtle()
    p.speed("fastest")
    bottom_y = TREE_SEGMENTS[-1][1] - TREE_SEGMENTS[-1][0] - 40

    # Present 1
    p.penup()
    p.goto(-100, bottom_y)
    p.color("#c94c4c")
    p.pendown()
    p.begin_fill()
    for _ in range(2):
        p.forward(80)
        p.left(90)
        p.forward(50)
        p.left(90)
    p.end_fill()
    p.penup()
    p.goto(-60, bottom_y + 25)
    p.color(COLORS["present_ribbon"])
    p.pendown()
    p.pensize(3)
    p.forward(40)

    # Present 2
    p.penup()
    p.goto(30, bottom_y)
    p.color("#2b8a6b")
    p.pendown()
    p.begin_fill()
    for _ in range(2):
        p.forward(70)
        p.left(90)
        p.forward(40)
        p.left(90)
    p.end_fill()
    p.penup()
    p.goto(65, bottom_y + 20)
    p.color(COLORS["present_ribbon"])
    p.pendown()
    p.pensize(3)
    p.forward(30)

# =============================================
# Ornament placement and creation
# =============================================
def calculate_ornament_positions():

    positions = []
    for seg_idx, (size, top) in enumerate(TREE_SEGMENTS):
        # number of ornaments per segment increases for lower segments
        ornaments_in_segment = 2 + seg_idx
        for i in range(ornaments_in_segment):
            y_ratio = (i + 1) / (ornaments_in_segment + 1)
            y = top - (size * y_ratio)
            max_x = size * 0.7 * (1 - y_ratio * 0.5)
            # alternate horizontal distribution
            if len(positions) % 2 == 0:
                x_options = [-max_x * 0.6, max_x * 0.6]
            else:
                x_options = [-max_x * 0.3, max_x * 0.3, 0]
            x = random.choice(x_options)
            # small random offset
            x += random.uniform(-15, 15)
            y += random.uniform(-10, 10)
            # avoid overcrowding
            too_close = False
            for px, py in positions:
                if math.hypot(x - px, y - py) < 40:
                    too_close = True
                    break
            if not too_close:
                positions.append((x, y))

    # fill remaining positions if needed
    while len(positions) < TOTAL_ORNAMENTS:
        seg = random.choice(TREE_SEGMENTS)
        size, top = seg
        for _ in range(10):
            y = random.uniform(top - size * 0.8, top - size * 0.2)
            max_x = size * 0.7 * (1 - (top - y) / size * 0.5)
            x = random.uniform(-max_x, max_x)
            too_close = False
            for px, py in positions:
                if math.hypot(x - px, y - py) < 40:
                    too_close = True
                    break
            if not too_close:
                positions.append((x, y))
                break

    # trim to exact count
    if len(positions) > TOTAL_ORNAMENTS:
        positions = positions[:TOTAL_ORNAMENTS]
    return positions

def init_border_turtle():
    """Create a single turtle used to draw dark borders behind ornaments (as dots)."""
    global border_turtle
    if border_turtle is None:
        border_turtle = turtle.Turtle()
        border_turtle.hideturtle()
        border_turtle.speed("fastest")
        border_turtle.penup()

def init_sparkle_turtle():
    """Create a single turtle used to draw sparkles (dots)."""
    global sparkle_turtle
    if sparkle_turtle is None:
        sparkle_turtle = turtle.Turtle()
        sparkle_turtle.hideturtle()
        sparkle_turtle.speed("fastest")
        sparkle_turtle.penup()

def draw_sparkles():
    """Draw small sparkles near each unclicked ornament using a single turtle."""
    init_sparkle_turtle()
    sparkle_turtle.clear()
    for o in ornaments:
        if not o.clicked:
            # draw a small cluster of dots around the ornament
            for angle in [0, 90, 180, 270]:
                rad = math.radians(angle)
                dx = math.cos(rad) * 18
                dy = math.sin(rad) * 18
                sparkle_turtle.goto(o.xcor() + dx, o.ycor() + dy)
                sparkle_turtle.dot(6, "#FFFFFF")
                sparkle_turtle.dot(3, o.original_color)

def create_ornaments():
    """Create ornament turtles and draw borders using dot() to reduce object count."""
    global ornaments, ornament_positions
    # hide and clear previous ornament turtles
    for o in ornaments:
        try:
            o.hideturtle()
            o.clear()
        except:
            pass
    ornaments = []

    ornament_positions = calculate_ornament_positions()
    init_border_turtle()

    # draw borders as larger dark dots (single turtle)
    border_turtle.clear()
    for (x, y) in ornament_positions:
        # border size scaled by vertical position for visual depth
        size_factor = 1.5 + (y + 100) / 300 * 0.8
        border_turtle.goto(x, y)
        border_turtle.dot(int(size_factor * 20), "#000000")

    # create ornament turtles on top of borders
    for i, (x, y) in enumerate(ornament_positions):
        o = turtle.Turtle()
        o.hideturtle()
        o.speed("fastest")
        o.shape("circle")
        color = random.choice(ORNAMENT_COLORS)
        o.color(color)
        o.penup()
        o.goto(x, y)
        size_factor = 1.5 + (y + 100) / 300 * 0.8
        o.shapesize(size_factor, size_factor)
        o.original_color = color
        o.clicked = False
        o.size_factor = size_factor
        o.id = i
        o.showturtle()
        o.onclick(handle_ornament_click)
        ornaments.append(o)

    # draw sparkles once
    draw_sparkles()
    window.update()

# =============================================
# Game logic (non-blocking)
# =============================================
def handle_ornament_click(x, y):
    """Handle clicks on ornaments; uses dynamic radius and non-blocking animation."""
    global clicked_ornaments, game_active, game_start_time
    if not game_active:
        # game not active; ignore clicks
        return
    for o in ornaments:
        if not o.clicked:
            click_radius = BASE_CLICK_RADIUS * o.size_factor
            distance = math.hypot(o.xcor() - x, o.ycor() - y)
            if distance < click_radius:
                if clicked_ornaments == 0:
                    game_start_time = time.time()
                clicked_ornaments += 1
                o.clicked = True
                o.color("#FFFFFF")  # ornament lights up (white) when clicked
                animate_ornament_click_nonblocking(o)
                draw_sparkles()
                update_score_display()
                if clicked_ornaments >= TOTAL_ORNAMENTS:
                    # schedule end_game shortly after last click
                    window.ontimer(end_game, 100)
                break

def animate_ornament_click_nonblocking(o):
    """Non-blocking enlarge/revert animation using ontimer."""
    orig = o.shapesize()
    enlarged = (orig[0] * 1.3, orig[1] * 1.3)
    o.shapesize(enlarged[0], enlarged[1])
    window.update()
    def revert():
        o.shapesize(orig[0], orig[1])
        window.update()
    window.ontimer(revert, 150)

def start_game():
    """Start or restart the active round (reset counters and colors)."""
    global game_active, clicked_ornaments, game_start_time
    game_active = True
    clicked_ornaments = 0
    game_start_time = None
    for o in ornaments:
        o.color(o.original_color)
        o.clicked = False
    draw_sparkles()
    update_score_display()

def reset_game():
    """Reset the entire game: recreate ornaments and reset state."""
    global game_active, clicked_ornaments, game_start_time
    game_active = False
    clicked_ornaments = 0
    game_start_time = None
    create_ornaments()
    update_score_display()

def end_game():
    """End the round, compute elapsed time, update best time, and show results."""
    global game_active, best_time
    if not game_active:
        return
    game_active = False
    if game_start_time is not None:
        elapsed = time.time() - game_start_time
        if elapsed < best_time:
            best_time = elapsed
    else:
        elapsed = 0.0
    show_game_results(elapsed)
    victory_celebration()

def victory_celebration():
    """Non-blocking celebratory dots scattered around the screen."""
    celebration = turtle.Turtle()
    celebration.hideturtle()
    celebration.speed("fastest")
    celebration.penup()
    for _ in range(20):
        x = random.randint(-300, 300)
        y = random.randint(-200, 300)
        celebration.goto(x, y)
        for size, color in [(8, "#FFD700"), (5, "#FFFFFF"), (3, "#FF6B6B")]:
            celebration.dot(size, color)
    window.update()

# =============================================
# Score and results display (panel-styled)
# =============================================
def create_score_display():
    """Create a turtle used for score text (reused)."""
    global score_display
    score_display = turtle.Turtle()
    score_display.hideturtle()
    score_display.penup()
    score_display.goto(0, -300)
    return score_display

def update_score_display():
    """Update the bottom panel with current progress and time."""
    if score_display is None:
        return
    score_display.clear()
    # draw background panel behind text
    panel = turtle.Turtle()
    panel.hideturtle()
    panel.penup()
    panel.goto(-250, -330)
    panel.color(COLORS["bg_panel"])
    panel.pendown()
    panel.begin_fill()
    for _ in range(2):
        panel.forward(500)
        panel.left(90)
        panel.forward(70)
        panel.left(90)
    panel.end_fill()
    panel.penup()

    # prepare text
    if game_active:
        if clicked_ornaments > 0 and game_start_time is not None:
            elapsed = time.time() - game_start_time
            text = f"🎯 {clicked_ornaments}/{TOTAL_ORNAMENTS} | ⏱️ {elapsed:.2f}s"
        else:
            text = f"🎯 {clicked_ornaments}/{TOTAL_ORNAMENTS} | Click ornaments!"
    else:
        text = "🎄 Press 'S' to start the game! 🎄"

    score_display.color(COLORS["text"])
    score_display.goto(0, -315)
    score_display.write(text, align="center", font=("Arial", 18, "bold"))

def show_game_results(elapsed):
    """Show a results panel with rating, elapsed time, and best time."""
    results = turtle.Turtle()
    results.hideturtle()
    results.penup()
    results.goto(0, -360)
    # panel background
    results.color(COLORS["bg_panel"])
    results.pendown()
    results.begin_fill()
    for _ in range(2):
        results.forward(500)
        results.left(90)
        results.forward(150)
        results.left(90)
    results.end_fill()
    results.penup()

    # rating text
    results.goto(0, -330)
    if elapsed < 15 and elapsed > 0:
        rating = "🏆 GREAT Effort"
        color = "#00FF9D"
    elif elapsed < 30 and elapsed > 0:
        rating = "🌟 Festive Star!"
        color = "#FFD700"
    elif elapsed > 0:
        rating = "🎄 Tree Decorator!"
        color = "#FF9F1C"
    else:
        rating = "🎅 Thanks!"
        color = "#FF6B6B"

    results.color(color)
    results.write(rating, align="center", font=("Arial", 22, "bold"))

    # elapsed time
    results.goto(0, -370)
    results.color(COLORS["text"])
    results.write(f"⏱️ Time: {elapsed:.2f}s", align="center", font=("Arial", 18, "normal"))

    # best time
    if best_time < float('inf'):
        results.goto(0, -400)
        results.color(COLORS["ornament_gold"])
        results.write(f"🏅 Best Time: {best_time:.2f}s", align="center", font=("Arial", 16, "bold"))

    # footer instructions
    results.goto(0, -430)
    results.color(COLORS["present_ribbon"])
    results.write("Press 'R' to play again | 'ESC' to exit", align="center", font=("Arial", 14, "normal"))

# =============================================
# Instructions panel (single writer turtle)
# =============================================
def draw_instructions_panel():
    """Draw the top instructions panel using a single writer turtle."""
    # Title
    header = turtle.Turtle()
    header.hideturtle()
    header.penup()
    header.goto(0, 340)
    header.color(COLORS["star"])
    header.write("🎄 Christmas Tree Challenge 🎄", align="center", font=("Arial", 28, "bold"))

    # Panel background
    panel = turtle.Turtle()
    panel.hideturtle()
    panel.penup()
    panel.goto(-500, 290)
    panel.color(COLORS["bg_panel"])
    panel.pendown()
    panel.begin_fill()
    for _ in range(2):
        panel.forward(1000)
        panel.right(90)
        panel.forward(120)
        panel.right(90)
    panel.end_fill()
    panel.penup()

    # Accent strip
    accent = turtle.Turtle()
    accent.hideturtle()
    accent.penup()
    accent.goto(-500, 290)
    accent.color(COLORS["panel_accent"])
    accent.pendown()
    accent.begin_fill()
    for _ in range(2):
        accent.forward(1000)
        accent.right(90)
        accent.forward(12)
        accent.right(90)
    accent.end_fill()
    accent.penup()

    # Single writer for all lines (reduces object creation)
    writer = turtle.Turtle()
    writer.hideturtle()
    writer.penup()
    lines = [
        "How to play:",
        "1. Press 'S' to start",
        "2. Find the colored ornaments on the tree and click them",
        "3. Try to finish decorating the tree as fast as possible!",
        "",
        "Keys: S=Start, R=Reset, ESC=Exit"
    ]
    for i, line in enumerate(lines):
        writer.goto(0, 270 - i * 22)
        if i == 0:
            writer.color(COLORS["star"])
            font_size = 16
        elif i == len(lines) - 1:
            writer.color(COLORS["present_ribbon"])
            font_size = 14
        else:
            writer.color(COLORS["text"])
            font_size = 14
        writer.write(line, align="center", font=("Arial", font_size, "normal"))

# =============================================
# Controls and main loop
# =============================================
def setup_controls():
    """Bind keyboard controls to functions."""
    window.listen()
    window.onkeypress(start_game, "s")
    window.onkeypress(start_game, "S")
    window.onkeypress(reset_game, "r")
    window.onkeypress(reset_game, "R")
    window.onkeypress(exit_game, "Escape")

def exit_game():
    """Close the window safely."""
    try:
        window.bye()
    except:
        pass

def game_update_loop():
    """Main non-blocking update loop scheduled with ontimer."""
    if game_active and clicked_ornaments > 0 and game_start_time is not None:
        update_score_display()
    window.update()
    window.ontimer(game_update_loop, UPDATE_INTERVAL_MS)

# =============================================
# Initialization and entry point
# =============================================
def initialize_game():
    """Initialize window, draw static elements, create ornaments, and start loop."""
    global window
    window = create_window()
    draw_snow_and_presents()
    draw_tree_and_trunk()
    draw_star()
    create_ornaments()       # create ornaments before instructions so borders are visible
    draw_instructions_panel()
    create_score_display()
    update_score_display()
    setup_controls()
    game_update_loop()
    print("Initialized. Press 'S' to start the game.")

if __name__ == "__main__":
    initialize_game()
    turtle.mainloop()

INTERACTIVE TREE WISH CLOCK 2026

Python
INTERACTIVE TREE + WISH CLOCK 2026
# =============================================
# 📋 PROJECT: INTERACTIVE CHRISTMAS TREE WISH CLOCK 2026
# =============================================

import pygame
import time
import json
import os
import math
from datetime import datetime
from math import sin, cos, radians
from random import randint, uniform, choice

# =============================================
# 🎮 INITIALIZATION & SETTINGS
# =============================================
pygame.init()

# Display settings optimized for Raspberry Pi
WIDTH, HEIGHT = 1024, 710
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("🎄⏰ Interactive Christmas Tree Wish Clock 2026")

# =============================================
# 🎨 COLOR PALETTE
# =============================================
COLORS = {
    'bg_dark_blue': (8, 12, 32),
    'bg_navy': (15, 25, 45),
    'clock_face_start': (30, 40, 80),
    'clock_face_end': (20, 30, 60),
    'clock_rim': (255, 200, 50, 180),
    'hour_hand_start': (255, 100, 100),
    'hour_hand_end': (255, 150, 50),
    'minute_hand_start': (50, 220, 150),
    'minute_hand_end': (100, 255, 200),
    'second_hand': (255, 255, 100),
    'text_primary': (255, 255, 255),
    'text_secondary': (200, 220, 255),
    'text_highlight': (255, 215, 0),
    'text_warning': (255, 100, 100),
    'text_success': (100, 255, 150),
    'tree_green': (11, 102, 35),
    'trunk_brown': (93, 41, 6),
    'ornament_gold': (255, 215, 0),
    'ornament_red': (255, 107, 107),
    'ornament_blue': (78, 205, 196),
    'ornament_green': (6, 214, 160),
    'panel_bg': (25, 35, 65, 220),
    'panel_border': (255, 215, 0, 200),
    'input_bg': (20, 30, 50),
    'input_border': (255, 200, 100),
    'snow_white': (232, 244, 248),
    'firework_red': (255, 50, 50),
    'firework_blue': (50, 150, 255),
    'firework_green': (50, 255, 100),
    'firework_purple': (180, 70, 255),
}

# Ornament color options
ORNAMENT_COLORS = [
    COLORS['ornament_red'],
    COLORS['ornament_blue'],
    COLORS['ornament_green'],
    COLORS['ornament_gold'],
    COLORS['firework_purple'],
    (255, 209, 102),  # Yellow
    (239, 71, 111),   # Pink
    (50, 150, 255),   # Light blue
]

# =============================================
# 🌟 PARTICLE SYSTEM CLASSES
# =============================================
class Star:
    """Background twinkling star particle"""
    def __init__(self):
        self.x = randint(0, WIDTH)
        self.y = randint(0, HEIGHT)
        self.size = uniform(0.5, 2.5)
        self.speed = uniform(0.1, 0.5)
        self.brightness = uniform(150, 255)
        self.pulse_speed = uniform(0.5, 2.0)
    
    def update(self):
        self.y += self.speed
        if self.y > HEIGHT:
            self.y = 0
            self.x = randint(0, WIDTH)
        self.brightness = 150 + 105 * sin(time.time() * self.pulse_speed)
    
    def draw(self, surface):
        color = (self.brightness, self.brightness, self.brightness)
        pygame.draw.circle(surface, color, (int(self.x), int(self.y)), int(self.size))

class FireworkParticle:
    """Exploding firework particle"""
    def __init__(self, x, y, color):
        self.x = x
        self.y = y
        self.color = color
        self.size = uniform(1.5, 3.0)
        self.speed_x = uniform(-3, 3)
        self.speed_y = uniform(-3, 3)
        self.life = 1.0
        self.decay = uniform(0.02, 0.05)
    
    def update(self):
        self.x += self.speed_x
        self.y += self.speed_y
        self.speed_x *= 0.98
        self.speed_y *= 0.98
        self.life -= self.decay
        return self.life > 0
    
    def draw(self, surface):
        alpha = int(255 * self.life)
        color_with_alpha = (*self.color, alpha)
        particle_surface = pygame.Surface((int(self.size*2), int(self.size*2)), pygame.SRCALPHA)
        pygame.draw.circle(particle_surface, color_with_alpha, 
                          (int(self.size), int(self.size)), int(self.size))
        surface.blit(particle_surface, (int(self.x - self.size), int(self.y - self.size)))

class OrnamentSparkle:
    """Sparkle effect around ornaments"""
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.size = uniform(2, 4)
        self.angle = uniform(0, 2 * math.pi)
        self.distance = uniform(10, 20)
        self.speed = uniform(0.05, 0.1)
        self.opacity = uniform(100, 200)
    
    def update(self):
        self.angle += self.speed
        return True
    
    def draw(self, surface, color):
        pos_x = self.x + math.cos(self.angle) * self.distance
        pos_y = self.y + math.sin(self.angle) * self.distance
        sparkle_color = (*color, int(self.opacity))
        pygame.draw.circle(surface, sparkle_color, (int(pos_x), int(pos_y)), int(self.size))

# =============================================
# 🎄 CHRISTMAS TREE GAME COMPONENTS
# =============================================
class Ornament:
    """Interactive ornament on the tree"""
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.color = choice(ORNAMENT_COLORS)
        self.radius = 12
        self.clicked = False
        self.pulse = 0.0
        self.pulse_speed = uniform(2.0, 4.0)
        self.sparkles = [OrnamentSparkle(x, y) for _ in range(3)]
        self.click_time = 0
    
    def update(self):
        # Pulsing effect for unclicked ornaments
        if not self.clicked:
            self.pulse = sin(time.time() * self.pulse_speed) * 3
            for sparkle in self.sparkles:
                sparkle.update()
    
    def draw(self, surface):
        # Draw sparkles around unclicked ornaments
        if not self.clicked:
            for sparkle in self.sparkles:
                sparkle.draw(surface, self.color)
        
        # Draw ornament with glow effect
        if not self.clicked:
            glow_radius = self.radius + abs(self.pulse)
            glow_surface = pygame.Surface((glow_radius*2, glow_radius*2), pygame.SRCALPHA)
            pygame.draw.circle(glow_surface, (*self.color, 80), 
                             (glow_radius, glow_radius), glow_radius)
            surface.blit(glow_surface, (self.x - glow_radius, self.y - glow_radius))
        
        # Draw ornament
        color = self.color if not self.clicked else (255, 255, 255)
        pygame.draw.circle(surface, color, (int(self.x), int(self.y)), self.radius)
        
        # Draw border
        border_color = COLORS['text_highlight'] if not self.clicked else COLORS['clock_rim']
        pygame.draw.circle(surface, border_color, (int(self.x), int(self.y)), self.radius, 2)
    
    def check_click(self, pos):
        distance = math.sqrt((self.x - pos[0])**2 + (self.y - pos[1])**2)
        if distance <= self.radius + 5:
            self.clicked = True
            self.click_time = time.time()
            return True
        return False

def create_christmas_tree():
    """Generate Christmas tree with ornaments"""
    tree_x = WIDTH // 2
    tree_base_y = HEIGHT - 150
    ornaments = []
    
    # Tree layers (size, y_position, ornament_count)
    layers = [
        (250, tree_base_y - 50, 5),   # Base layer
        (200, tree_base_y - 120, 4),  # Middle layer
        (150, tree_base_y - 180, 3),  # Upper layer
        (100, tree_base_y - 230, 2),  # Top layer
    ]
    
    for width, y_pos, count in layers:
        for i in range(count):
            # Distribute ornaments horizontally across the layer
            x = tree_x - width//2 + (width / (count + 1)) * (i + 1)
            # Add vertical variation
            y_variation = uniform(-15, 15)
            ornaments.append(Ornament(x, y_pos + y_variation))
    
    # Add star on top
    ornaments.append(Ornament(tree_x, tree_base_y - 260))
    ornaments[-1].color = COLORS['ornament_gold']
    ornaments[-1].radius = 8
    
    return ornaments, tree_x, tree_base_y

def draw_christmas_tree(surface, tree_x, tree_base_y, ornaments):
    """Draw the complete Christmas tree"""
    # Draw trunk
    trunk_width = 40
    trunk_height = 80
    trunk_rect = pygame.Rect(tree_x - trunk_width//2, tree_base_y, trunk_width, trunk_height)
    pygame.draw.rect(surface, COLORS['trunk_brown'], trunk_rect)
    
    # Draw tree layers (triangles)
    layers = [
        (250, tree_base_y - 50),   # Base
        (200, tree_base_y - 100),  # Layer 2
        (150, tree_base_y - 150),  # Layer 3
        (100, tree_base_y - 200),  # Layer 4
    ]
    
    for width, y_pos in layers:
        points = [
            (tree_x, y_pos),  # Top
            (tree_x - width//2, y_pos + 80),  # Bottom left
            (tree_x + width//2, y_pos + 80),  # Bottom right
        ]
        pygame.draw.polygon(surface, COLORS['tree_green'], points)
    
    # Draw ornaments
    for ornament in ornaments:
        ornament.draw(surface)
    
    # Draw ground snow
    snow_rect = pygame.Rect(0, tree_base_y + trunk_height, WIDTH, 50)
    pygame.draw.rect(surface, COLORS['snow_white'], snow_rect)
    
    # Draw some snowflakes
    for _ in range(30):
        x = randint(0, WIDTH)
        y = randint(tree_base_y + trunk_height, HEIGHT)
        size = randint(1, 3)
        pygame.draw.circle(surface, COLORS['text_primary'], (x, y), size)

# =============================================
# 🕰️ CLOCK FUNCTIONS
# =============================================
def draw_analog_clock(surface, x, y, radius):
    """Draw analog clock with creative markers"""
    center = (x, y)
    now = datetime.now()
    
    # Draw clock face with gradient
    for r in range(radius, 0, -1):
        ratio = r / radius
        color = (
            int(COLORS['clock_face_start'][0] * ratio + COLORS['clock_face_end'][0] * (1 - ratio)),
            int(COLORS['clock_face_start'][1] * ratio + COLORS['clock_face_end'][1] * (1 - ratio)),
            int(COLORS['clock_face_start'][2] * ratio + COLORS['clock_face_end'][2] * (1 - ratio))
        )
        pygame.draw.circle(surface, color, center, r)
    
    # Draw clock rim
    pygame.draw.circle(surface, COLORS['clock_rim'], center, radius, 3)
    
    # Draw creative hour markers (shapes instead of numbers)
    shapes = ['triangle', 'square', 'circle', 'diamond']
    for i in range(12):
        angle = radians(i * 30 - 90)
        marker_x = x + (radius - 25) * cos(angle)
        marker_y = y + (radius - 25) * sin(angle)
        shape = shapes[i % 4]
        
        if i % 3 == 0:  # Main markers
            marker_color = COLORS['ornament_gold']
            size = 8
        else:           # Secondary markers
            marker_color = COLORS['text_secondary']
            size = 6
        
        if shape == 'triangle':
            points = [
                (marker_x, marker_y - size),
                (marker_x - size, marker_y + size),
                (marker_x + size, marker_y + size)
            ]
            pygame.draw.polygon(surface, marker_color, points)
        elif shape == 'square':
            rect = pygame.Rect(marker_x - size, marker_y - size, size*2, size*2)
            pygame.draw.rect(surface, marker_color, rect)
        elif shape == 'circle':
            pygame.draw.circle(surface, marker_color, (int(marker_x), int(marker_y)), size)
        elif shape == 'diamond':
            points = [
                (marker_x, marker_y - size),
                (marker_x - size, marker_y),
                (marker_x, marker_y + size),
                (marker_x + size, marker_y)
            ]
            pygame.draw.polygon(surface, marker_color, points)
    
    # Get time components
    hour = now.hour % 12
    minute = now.minute
    second = now.second
    
    # Draw hour hand
    hour_angle = radians((hour * 30) + (minute * 0.5) - 90)
    hour_length = radius * 0.5
    hour_end = (
        x + hour_length * cos(hour_angle),
        y + hour_length * sin(hour_angle)
    )
    pygame.draw.line(surface, COLORS['hour_hand_start'], center, hour_end, 8)
    
    # Draw minute hand
    minute_angle = radians((minute * 6) - 90)
    minute_length = radius * 0.7
    minute_end = (
        x + minute_length * cos(minute_angle),
        y + minute_length * sin(minute_angle)
    )
    pygame.draw.line(surface, COLORS['minute_hand_start'], center, minute_end, 5)
    
    # Draw second hand
    second_angle = radians((second * 6) - 90)
    second_length = radius * 0.8
    second_end = (
        x + second_length * cos(second_angle),
        y + second_length * sin(second_angle)
    )
    pygame.draw.line(surface, COLORS['second_hand'], center, second_end, 2)
    
    # Draw center cap
    pygame.draw.circle(surface, COLORS['ornament_gold'], center, 6)

def draw_digital_clock(surface, x, y):
    """Draw digital clock with date"""
    now = datetime.now()
    
    # Time string
    time_str = now.strftime("%H:%M:%S")
    time_font = pygame.font.Font(None, 48)
    time_text = time_font.render(time_str, True, COLORS['text_highlight'])
    time_rect = time_text.get_rect(center=(x, y))
    
    # Date string
    date_str = now.strftime("%A, %B %d, %Y")
    date_font = pygame.font.Font(None, 24)
    date_text = date_font.render(date_str, True, COLORS['text_secondary'])
    date_rect = date_text.get_rect(center=(x, y + 35))
    
    # Draw glow effect behind time
    glow_surface = pygame.Surface((time_rect.width + 20, time_rect.height + 10), pygame.SRCALPHA)
    pygame.draw.rect(glow_surface, (*COLORS['clock_face_start'], 100), 
                    glow_surface.get_rect(), border_radius=10)
    surface.blit(glow_surface, (time_rect.x - 10, time_rect.y - 5))
    
    surface.blit(time_text, time_rect)
    surface.blit(date_text, date_rect)
    
    # Countdown to New Year 2026
    if now.year < 2026:
        new_year = datetime(2026, 1, 1, 0, 0, 0)
        time_left = new_year - now
        
        if time_left.total_seconds() > 0:
            days = time_left.days
            hours = time_left.seconds // 3600
            minutes = (time_left.seconds % 3600) // 60
            
            countdown_text = f"Countdown to 2026: {days}d {hours}h {minutes}m"
            countdown_font = pygame.font.Font(None, 20)
            countdown = countdown_font.render(countdown_text, True, COLORS['text_warning'])
            countdown_rect = countdown.get_rect(center=(x, y + 70))
            surface.blit(countdown, countdown_rect)
            
            # Special effects for last day
            if days == 0 and hours < 24:
                pulse = abs(sin(time.time() * 4)) * 50
                pygame.draw.circle(surface, (*COLORS['firework_red'], 100),
                                 (x, y + 70), int(15 + pulse), 2)

# =============================================
# 📝 WISH MANAGEMENT SYSTEM
# =============================================
wishes_file = "wishes.json"
current_wish = ""
wish_mode = False
wish_history = []
firework_particles = []

def load_wishes():
    """Load saved wishes from JSON file"""
    global wish_history
    try:
        if os.path.exists(wishes_file):
            with open(wishes_file, 'r') as f:
                wish_history = json.load(f)
        else:
            wish_history = []
    except Exception as e:
        print(f"Error loading wishes: {e}")
        wish_history = []

def save_wish(wish_text):
    """Save wish to JSON file with timestamp"""
    global wish_history
    try:
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        wish_entry = {
            'text': wish_text,
            'timestamp': timestamp,
            'year': 2026
        }
        wish_history.append(wish_entry)
        
        # Keep only last 20 wishes
        if len(wish_history) > 20:
            wish_history = wish_history[-20:]
        
        with open(wishes_file, 'w') as f:
            json.dump(wish_history, f, indent=2)
        
        # Create celebration fireworks
        create_fireworks(WIDTH // 2, HEIGHT // 2, 25)
        return True
    except Exception as e:
        print(f"Error saving wish: {e}")
        return False

def create_fireworks(x, y, count=20):
    """Create firework explosion at position"""
    colors = [
        COLORS['firework_red'],
        COLORS['firework_blue'],
        COLORS['firework_green'],
        COLORS['firework_purple'],
        COLORS['ornament_gold']
    ]
    for _ in range(count):
        color = colors[randint(0, len(colors)-1)]
        firework_particles.append(FireworkParticle(x, y, color))

def update_fireworks():
    """Update all firework particles"""
    global firework_particles
    firework_particles = [p for p in firework_particles if p.update()]

# =============================================
# 🎮 GAME STATE MANAGEMENT
# =============================================
class GameState:
    """Manage game state and scores"""
    def __init__(self):
        self.ornaments = []
        self.clicked_count = 0
        self.total_ornaments = 15
        self.game_active = False
        self.game_start_time = None
        self.best_time = float('inf')
        self.current_time = 0.0
        self.tree_x = 0
        self.tree_base_y = 0
    
    def reset(self):
        """Reset game to initial state"""
        self.ornaments, self.tree_x, self.tree_base_y = create_christmas_tree()
        self.clicked_count = 0
        self.game_active = False
        self.game_start_time = None
        self.current_time = 0.0
        create_fireworks(self.tree_x, self.tree_base_y - 100, 15)
    
    def start(self):
        """Start the game timer"""
        if not self.game_active:
            self.game_active = True
            self.game_start_time = time.time()
    
    def check_ornament_click(self, pos):
        """Check if ornament was clicked and update state"""
        for ornament in self.ornaments:
            if not ornament.clicked and ornament.check_click(pos):
                self.clicked_count += 1
                
                # Start timer on first click
                if self.clicked_count == 1 and not self.game_active:
                    self.start()
                
                # Create mini fireworks on click
                create_fireworks(ornament.x, ornament.y, 8)
                
                # Check if game is complete
                if self.clicked_count >= self.total_ornaments and self.game_active:
                    self.current_time = time.time() - self.game_start_time
                    if self.current_time < self.best_time:
                        self.best_time = self.current_time
                    self.game_active = False
                    create_fireworks(self.tree_x, self.tree_base_y - 150, 30)
                
                return True
        return False
    
    def update(self):
        """Update game state"""
        if self.game_active and self.game_start_time:
            self.current_time = time.time() - self.game_start_time
        
        for ornament in self.ornaments:
            ornament.update()

# =============================================
# 🎨 UI COMPONENTS
# =============================================
def draw_panel(surface, rect, title, alpha=220):
    """Draw a styled panel with title"""
    # Panel background
    panel_surface = pygame.Surface((rect.width, rect.height), pygame.SRCALPHA)
    pygame.draw.rect(panel_surface, (*COLORS['panel_bg'][:3], alpha), 
                    panel_surface.get_rect(), border_radius=15)
    pygame.draw.rect(panel_surface, COLORS['panel_border'], 
                    panel_surface.get_rect(), 3, border_radius=15)
    surface.blit(panel_surface, rect)
    
    # Title
    title_font = pygame.font.Font(None, 28)
    title_text = title_font.render(title, True, COLORS['text_highlight'])
    title_rect = title_text.get_rect(center=(rect.centerx, rect.top + 25))
    surface.blit(title_text, title_rect)
    
    return rect

def draw_wish_interface(surface, current_wish, active, game_state):
    """Draw wish input interface"""
    panel_rect = pygame.Rect(WIDTH - 350, 20, 330, 220)
    draw_panel(surface, panel_rect, "✨ Your Wish for 2026")
    
    font = pygame.font.Font(None, 28)
    
    # Input field
    input_rect = pygame.Rect(panel_rect.x + 20, panel_rect.y + 60, 290, 40)
    pygame.draw.rect(surface, COLORS['input_bg'], input_rect, border_radius=8)
    pygame.draw.rect(surface, COLORS['input_border'], input_rect, 2, border_radius=8)
    
    # Display current wish with cursor
    display_text = current_wish
    if active:
        display_text += "|" if int(time.time() * 2) % 2 == 0 else ""
    
    if display_text:
        text_surface = font.render(display_text, True, COLORS['text_primary'])
        surface.blit(text_surface, (input_rect.x + 10, input_rect.y + 10))
    
    # Character counter
    counter_text = f"{len(current_wish)}/50"
    counter_surface = font.render(counter_text, True, COLORS['text_secondary'])
    surface.blit(counter_surface, (input_rect.right - 50, input_rect.y + 10))
    
    # Instructions
    instructions = [
        "Type your wish and press ENTER",
        "Press ESC to cancel",
        f"Ornaments lit: {game_state.clicked_count}/{game_state.total_ornaments}"
    ]
    
    for i, instruction in enumerate(instructions):
        inst_text = font.render(instruction, True, COLORS['text_secondary'])
        surface.blit(inst_text, (panel_rect.x + 20, panel_rect.y + 120 + i*30))

def draw_score_panel(surface, game_state):
    """Draw game score and timer panel"""
    panel_rect = pygame.Rect(20, 20, 300, 180)
    draw_panel(surface, panel_rect, "🎮 Tree Decorating Game")
    
    font = pygame.font.Font(None, 28)
    small_font = pygame.font.Font(None, 24)
    
    # Current time
    time_text = f"⏱️ Current: {game_state.current_time:.1f}s"
    time_surface = font.render(time_text, True, COLORS['text_primary'])
    surface.blit(time_surface, (panel_rect.x + 20, panel_rect.y + 60))
    
    # Best time
    if game_state.best_time < float('inf'):
        best_text = f"🏆 Best: {game_state.best_time:.1f}s"
        best_surface = font.render(best_text, True, COLORS['text_highlight'])
        surface.blit(best_surface, (panel_rect.x + 20, panel_rect.y + 100))
    
    # Progress bar
    progress = game_state.clicked_count / game_state.total_ornaments
    bar_rect = pygame.Rect(panel_rect.x + 20, panel_rect.y + 140, 260, 20)
    pygame.draw.rect(surface, (50, 50, 70), bar_rect, border_radius=10)
    
    if progress > 0:
        fill_rect = pygame.Rect(bar_rect.x, bar_rect.y, int(bar_rect.width * progress), bar_rect.height)
        color = (
            int(255 * (1 - progress) + 100 * progress),
            int(100 * (1 - progress) + 255 * progress),
            100
        )
        pygame.draw.rect(surface, color, fill_rect, border_radius=10)
    
    # Progress text
    progress_text = f"{game_state.clicked_count}/{game_state.total_ornaments}"
    progress_surface = small_font.render(progress_text, True, COLORS['text_primary'])
    progress_rect = progress_surface.get_rect(center=bar_rect.center)
    surface.blit(progress_surface, progress_rect)

def draw_recent_wishes(surface, wish_history):
    """Display recent wishes"""
    if not wish_history:
        return
    
    panel_rect = pygame.Rect(20, HEIGHT - 220, 400, 200)
    draw_panel(surface, panel_rect, "📝 Recent Wishes", alpha=180)
    
    font = pygame.font.Font(None, 22)
    
    # Show last 3 wishes
    for i, wish in enumerate(wish_history[-3:]):
        wish_text = f"• {wish['text'][:40]}{'...' if len(wish['text']) > 40 else ''}"
        time_text = wish['timestamp'][:16]
        
        wish_surface = font.render(wish_text, True, COLORS['text_secondary'])
        time_surface = font.render(time_text, True, COLORS['text_secondary'])
        
        surface.blit(wish_surface, (panel_rect.x + 20, panel_rect.y + 50 + i*50))
        surface.blit(time_surface, (panel_rect.x + 20, panel_rect.y + 70 + i*50))

# =============================================
# 🎯 MAIN APPLICATION
# =============================================
def main():
    global current_wish, wish_mode, firework_particles, wish_history
    
    # Initialize components
    load_wishes()
    game_state = GameState()
    game_state.reset()
    
    # Create background stars
    stars = [Star() for _ in range(100)]
    
    # Load fonts
    try:
        title_font = pygame.font.Font(None, 42)
    except:
        title_font = pygame.font.SysFont('Arial', 42, bold=True)
    
    # Main loop variables
    clock = pygame.time.Clock()
    running = True
    last_wish_save = 0
    
    print("=" * 60)
    print("Interactive Christmas Tree Wish Clock 2026")
    print("=" * 60)
    print("Controls:")
    print("  Mouse: Click ornaments to light them")
    print("  W: Enter wish mode")
    print("  S: Start game timer")
    print("  R: Reset game")
    print("  H: Display last wish")
    print("  ESC: Exit program")
    print("=" * 60)
    
    while running:
        current_time = time.time()
        
        # Event handling
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            
            elif event.type == pygame.KEYDOWN:
                if wish_mode:
                    if event.key == pygame.K_RETURN:
                        if current_wish.strip():
                            if save_wish(current_wish):
                                last_wish_save = current_time
                            current_wish = ""
                            wish_mode = False
                    elif event.key == pygame.K_ESCAPE:
                        current_wish = ""
                        wish_mode = False
                    elif event.key == pygame.K_BACKSPACE:
                        current_wish = current_wish[:-1]
                    elif event.unicode.isprintable():
                        if len(current_wish) < 50:
                            current_wish += event.unicode
                else:
                    if event.key == pygame.K_w:
                        wish_mode = True
                        current_wish = ""
                    elif event.key == pygame.K_s:
                        game_state.start()
                    elif event.key == pygame.K_r:
                        game_state.reset()
                    elif event.key == pygame.K_h:
                        if wish_history:
                            current_wish = wish_history[-1]['text']
                    elif event.key == pygame.K_ESCAPE:
                        running = False
            
            elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                mouse_pos = pygame.mouse.get_pos()
                if not wish_mode:
                    game_state.check_ornament_click(mouse_pos)
        
        # Update game state
        game_state.update()
        
        # Update particles
        for star in stars:
            star.update()
        update_fireworks()
        
        # Auto fireworks on New Year's Eve
        now = datetime.now()
        if now.month == 12 and now.day == 31:
            if now.hour == 23 and now.minute >= 55:
                if randint(0, 20) > 18:
                    create_fireworks(randint(100, WIDTH-100), randint(100, HEIGHT-100), 10)
        
        # =============================================
        # 🎨 DRAWING SECTION
        # =============================================
        
        # Draw gradient background
        for y in range(HEIGHT):
            ratio = y / HEIGHT
            color = (
                int(COLORS['bg_dark_blue'][0] * (1 - ratio) + COLORS['bg_navy'][0] * ratio),
                int(COLORS['bg_dark_blue'][1] * (1 - ratio) + COLORS['bg_navy'][1] * ratio),
                int(COLORS['bg_dark_blue'][2] * (1 - ratio) + COLORS['bg_navy'][2] * ratio)
            )
            pygame.draw.line(screen, color, (0, y), (WIDTH, y))
        
        # Draw stars
        for star in stars:
            star.draw(screen)
        
        # Draw fireworks
        for particle in firework_particles:
            particle.draw(screen)
        
        # Draw Christmas tree
        draw_christmas_tree(screen, game_state.tree_x, game_state.tree_base_y, game_state.ornaments)
        
        # Draw analog clock (left side)
        draw_analog_clock(screen, 200, HEIGHT // 4, 120)
        
        # Draw digital clock (right side)
        draw_digital_clock(screen, WIDTH - 200, HEIGHT // 4)
        
        # Draw UI panels
        draw_score_panel(screen, game_state)
        draw_wish_interface(screen, current_wish, wish_mode, game_state)
        draw_recent_wishes(screen, wish_history)
        
        # Draw title
        title_text = title_font.render("🎄 Interactive Christmas Tree Wish Clock 2026 ⏰", 
                                      True, COLORS['text_highlight'])
        screen.blit(title_text, (WIDTH//2 - title_text.get_width()//2, 10))
        
        # Show game completion message
        if game_state.clicked_count >= game_state.total_ornaments:
            completion_font = pygame.font.Font(None, 36)
            if game_state.current_time > 0:
                message = f"🎉 Tree Decorated in {game_state.current_time:.1f}s! 🎉"
                message_surface = completion_font.render(message, True, COLORS['text_success'])
                screen.blit(message_surface, (WIDTH//2 - message_surface.get_width()//2, HEIGHT//2))
        
        # Show wish save confirmation
        if current_time - last_wish_save < 2:
            save_font = pygame.font.Font(None, 32)
            save_text = save_font.render("✅ Wish saved successfully!", True, COLORS['text_success'])
            screen.blit(save_text, (WIDTH//2 - save_text.get_width()//2, HEIGHT - 50))
        
        # Show start instructions
        if game_state.clicked_count == 0 and not game_state.game_active:
            start_font = pygame.font.Font(None, 28)
            start_text = start_font.render("Click an ornament to start the game!", 
                                          True, COLORS['text_highlight'])
            screen.blit(start_text, (WIDTH//2 - start_text.get_width()//2, HEIGHT//2 + 50))
        
        # Update display
        pygame.display.flip()
        clock.tick(60)
    
    # Cleanup
    pygame.quit()
    print("\nApplication closed.")
    print(f"Wishes saved to: {wishes_file}")
    if game_state.best_time < float('inf'):
        print(f"Best decoration time: {game_state.best_time:.2f} seconds")

# =============================================
# 🚀 ENTRY POINT
# =============================================
if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\n\nApplication interrupted by user.")
        pygame.quit()
    except Exception as e:
        print(f"\n\nError: {e}")
        pygame.quit()

Credits

Aula Jazmati 💡🕊️
68 projects • 234 followers
Electronic Engineering

Comments