Hardware components | ||||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
| × | 1 | ||||
An interactive digital clock that combines time display with wish-making for the new year, featuring a fun Christmas tree decoration mini-game. Perfect for New Year's Eve celebrations on Raspberry Pi!
- Raspberry Pi (any model)
- HDMI display or monitor
- Keyboard and mouse
- Optional: Speakers for sound effects
- Raspberry Pi OS
- Python 3Pygame library
# Update system
sudo apt update
sudo apt upgrade
# Install pygame
sudo apt install python3-pip python3-pygame
pip3 install pygame
# Clone or create project directory
mkdir InteractiveWishClock
cd InteractiveWishClockPhase 1: Clock DevelopmentInitially created a standalone digital clock application featuring:
- Dual analog/digital time display
- Wish input system with JSON storage
- Animated background with twinkling stars
- Countdown timer for New Year 2026
- Fireworks effects for celebrations
Key Features of Clock Version:
- Creative hour markers using shapes instead of numbers
- Real-time countdown display
- Wish archiving with timestamps
🧠 How It Works
-Press 'W': Enter wish mode
-Type your wish and press ENTER to save
-Press 'H': Display last wish
-Press 'R': Generate random wish
-Press ESC: Exit programThe clock combines analog and digital displays with animated elements:
- Analog Clock: Hour markers are drawn as shapes (triangles, squares, circles) symbolizing creativity
- Digital Display: Shows current time and date with glowing effects
- Wish Interface: Modal window for typing wishes with character counter
- Visual Effects: Stars twinkle in background, fireworks celebrate saved wishes
- Countdown Timer: Automatically activates on December 31st
- Data Persistence: Wishes are saved to JSON file and persist between sessions
Separately developed an interactive tree decoration game featuring:
- Clickable ornaments with visual feedback
- Timer-based scoring system
- Progress tracking and best time recording
🎯 Game Rules & Scoring
Tree Decoration Game
- Click ornaments to light them up
- Timer starts on first click
- Complete all ornaments to finish
- Try to beat your best time!
Excellent 🏆: < 15 seconds
Great 🌟: 15-25 seconds
Good 👍: 25-35 seconds
Keep practicing 💪: > 35 secondsSuccessfully merged both applications into a single cohesive experience:
- Technical Integration Achievements:
- Unified Event Loop - Single Pygame main loop handling both clock and game events
- Shared State Management - Combined game state and wish management in one structure
- Coordinated Visual Effects - Stars, fireworks, and sparkles work harmoniously
- Consistent UI System - Unified panel design and color palette
Innovative Features of the Merged Application:
- Dual Interaction Modes: Simultaneous clock viewing and game playing
- Shared Celebration System: Fireworks trigger for both wish saving and game completion
- Unified Data Storage: Wishes and game scores stored together
- Coordinated Visual Theme: Consistent holiday aesthetic throughout
- Multi-Input Support: Mouse for game, keyboard for wishes, both work concurrently
📝 Data Storage
- Wishes are saved to wishes.json in JSON format
- Each wish includes text, timestamp, and year
- Game best times are stored in memory during session
📊 How It Works
❄️ Clock Display: Shows current time in both analog and digital formats
❄️ Tree Game: Click ornaments to light them up and decorate the tree
❄️ Wish Input: Type your wishes for 2026 and save them
❄️Countdown: Automatic countdown to New Year 2026
❄️Data Storage: All wishes and scores are saved locally
Happy New Year 2026! ❄️🎄🎊✨
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()
# -*- 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()
# =============================================
# 📋 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()













Comments