import tkinter as tk import math import numpy as np from dataclasses import dataclass from typing import List, Tuple import random import sys # Constants WINDOW_WIDTH = 800 WINDOW_HEIGHT = 800 GRAVITY = 9.8 * 30 # Scaled for better visual effect GROUND_Y = WINDOW_HEIGHT - 50 WATERMELON_RADIUS = 40 TIME_STEP = 1/60 # 60 FPS FRICTION = 0.95 # Friction coefficient for fragments BOUNCE = 0.6 # Bounce coefficient FRAGMENT_COUNT = 20 # Number of fragments when watermelon bursts SEED_COUNT = 30 # Number of seeds to spawn on impact SEED_LIFETIME = 1.5 # Seconds seeds remain visible WATERMELON_MASS = 10.0 # Mass of watermelon @dataclass class Vector2D: x: float y: float def __add__(self, other): return Vector2D(self.x + other.x, self.y + other.y) def __sub__(self, other): return Vector2D(self.x - other.x, self.y - other.y) def __mul__(self, scalar): return Vector2D(self.x * scalar, self.y * scalar) def length(self): return math.sqrt(self.x**2 + self.y**2) def normalize(self): length = self.length() if length > 0: return Vector2D(self.x / length, self.y / length) return Vector2D(0, 0) class Fragment: def __init__(self, position, velocity, size, color, rotation=0, rotation_speed=0): self.position = position self.velocity = velocity self.size = size self.color = color self.rotation = rotation self.rotation_speed = rotation_speed self.id = None self.active = True def update(self): # Apply gravity self.velocity.y += GRAVITY * TIME_STEP # Update position self.position.x += self.velocity.x * TIME_STEP self.position.y += self.velocity.y * TIME_STEP # Update rotation self.rotation += self.rotation_speed * TIME_STEP # Check ground collision if self.position.y + self.size > GROUND_Y: self.position.y = GROUND_Y - self.size self.velocity.y = -self.velocity.y * BOUNCE self.velocity.x *= FRICTION # Apply friction to rotation self.rotation_speed *= FRICTION # Check wall collisions if self.position.x - self.size < 0: self.position.x = self.size self.velocity.x = -self.velocity.x * BOUNCE elif self.position.x + self.size > WINDOW_WIDTH: self.position.x = WINDOW_WIDTH - self.size self.velocity.x = -self.velocity.x * BOUNCE # Deactivate if almost stopped if (abs(self.velocity.x) < 5 and abs(self.velocity.y) < 5 and abs(self.rotation_speed) < 0.1 and self.position.y + self.size >= GROUND_Y - 1): self.velocity.x = 0 self.velocity.y = 0 self.rotation_speed = 0 class Seed: def __init__(self, position, velocity, size): self.position = position self.velocity = velocity self.size = size self.id = None self.lifetime = SEED_LIFETIME self.active = True def update(self): # Apply gravity (lighter than fragments) self.velocity.y += GRAVITY * TIME_STEP * 0.5 # Update position self.position.x += self.velocity.x * TIME_STEP self.position.y += self.velocity.y * TIME_STEP # Check ground collision if self.position.y + self.size > GROUND_Y: self.position.y = GROUND_Y - self.size self.velocity.y = -self.velocity.y * 0.3 # Less bounce self.velocity.x *= 0.8 # More friction # Check wall collisions if self.position.x - self.size < 0: self.position.x = self.size self.velocity.x = -self.velocity.x * 0.3 elif self.position.x + self.size > WINDOW_WIDTH: self.position.x = WINDOW_WIDTH - self.size self.velocity.x = -self.velocity.x * 0.3 # Reduce lifetime self.lifetime -= TIME_STEP if self.lifetime <= 0: self.active = False class Watermelon: def __init__(self, x, y): self.position = Vector2D(x, y) self.velocity = Vector2D(0, 0) self.radius = WATERMELON_RADIUS self.mass = WATERMELON_MASS self.burst = False self.id_outer = None self.id_inner = None self.id_seeds = [] def update(self): if not self.burst: # Apply gravity self.velocity.y += GRAVITY * TIME_STEP # Update position self.position.x += self.velocity.x * TIME_STEP self.position.y += self.velocity.y * TIME_STEP # Check ground collision if self.position.y + self.radius > GROUND_Y: self.burst = True # Check wall collisions if self.position.x - self.radius < 0: self.position.x = self.radius self.velocity.x = -self.velocity.x * BOUNCE elif self.position.x + self.radius > WINDOW_WIDTH: self.position.x = WINDOW_WIDTH - self.radius self.velocity.x = -self.velocity.x * BOUNCE class WatermelonSimulation: def __init__(self, root): self.root = root self.root.title("Watermelon Splash Simulation") self.root.geometry(f"{WINDOW_WIDTH}x{WINDOW_HEIGHT}") self.canvas = tk.Canvas(root, width=WINDOW_WIDTH, height=WINDOW_HEIGHT, bg="skyblue") self.canvas.pack() # Create ground self.ground = self.canvas.create_rectangle(0, GROUND_Y, WINDOW_WIDTH, WINDOW_HEIGHT, fill="sienna") # Create watermelon self.watermelon = Watermelon(WINDOW_WIDTH // 2, 100) self.draw_watermelon() # Lists for fragments and seeds self.fragments = [] self.seeds = [] # Start simulation self.is_running = True self.update() # Bind restart on click self.canvas.bind("", self.restart) def draw_watermelon(self): x, y = self.watermelon.position.x, self.watermelon.position.y r = self.watermelon.radius # Draw outer green part self.watermelon.id_outer = self.canvas.create_oval( x - r, y - r, x + r, y + r, fill="green", outline="darkgreen", width=2 ) # Draw inner red part (slightly smaller) inner_r = r * 0.85 self.watermelon.id_inner = self.canvas.create_oval( x - inner_r, y - inner_r, x + inner_r, y + inner_r, fill="red", outline="" ) # Draw some seed-like details self.watermelon.id_seeds = [] for _ in range(8): angle = random.uniform(0, 2 * math.pi) distance = random.uniform(0.3, 0.7) * inner_r seed_x = x + math.cos(angle) * distance seed_y = y + math.sin(angle) * distance seed_size = random.uniform(2, 4) seed = self.canvas.create_oval( seed_x - seed_size, seed_y - seed_size, seed_x + seed_size, seed_y + seed_size, fill="black", outline="" ) self.watermelon.id_seeds.append(seed) def burst_watermelon(self): x, y = self.watermelon.position.x, self.watermelon.position.y # Create fragments for _ in range(FRAGMENT_COUNT): # Random direction angle = random.uniform(0, 2 * math.pi) speed = random.uniform(100, 300) # Calculate velocity based on watermelon's velocity and explosion vel_x = self.watermelon.velocity.x + math.cos(angle) * speed vel_y = self.watermelon.velocity.y + math.sin(angle) * speed # Random size for fragment size = random.uniform(self.watermelon.radius * 0.2, self.watermelon.radius * 0.4) # Random color (green or red) color = random.choice(["green", "red"]) # Random rotation rotation = random.uniform(0, 360) rotation_speed = random.uniform(-180, 180) # Create fragment at watermelon's position fragment = Fragment( Vector2D(x, y), Vector2D(vel_x, vel_y), size, color, rotation, rotation_speed ) # Draw the fragment fragment.id = self.canvas.create_oval( x - size, y - size, x + size, y + size, fill=color, outline="darkgreen" if color == "green" else "darkred" ) self.fragments.append(fragment) # Create seeds for _ in range(SEED_COUNT): # Random direction angle = random.uniform(0, 2 * math.pi) speed = random.uniform(50, 200) # Calculate velocity vel_x = math.cos(angle) * speed vel_y = math.sin(angle) * speed # Create seed seed = Seed( Vector2D(x, y), Vector2D(vel_x, vel_y), random.uniform(1.5, 3) ) # Draw the seed seed.id = self.canvas.create_oval( x - seed.size, y - seed.size, x + seed.size, y + seed.size, fill="black", outline="" ) self.seeds.append(seed) def update(self): if not self.is_running: return # Update watermelon self.watermelon.update() if self.watermelon.burst: # If just burst, create fragments if not self.fragments: self.burst_watermelon() # Remove original watermelon self.canvas.delete(self.watermelon.id_outer) self.canvas.delete(self.watermelon.id_inner) for seed in self.watermelon.id_seeds: self.canvas.delete(seed) else: # Update watermelon position on canvas x, y = self.watermelon.position.x, self.watermelon.position.y r = self.watermelon.radius self.canvas.coords( self.watermelon.id_outer, x - r, y - r, x + r, y + r ) self.canvas.coords( self.watermelon.id_inner, x - r * 0.85, y - r * 0.85, x + r * 0.85, y + r * 0.85 ) # Update seed positions for i, seed_id in enumerate(self.watermelon.id_seeds): angle = (i / len(self.watermelon.id_seeds)) * 2 * math.pi distance = self.watermelon.radius * 0.6 seed_x = x + math.cos(angle) * distance seed_y = y + math.sin(angle) * distance seed_size = 3 self.canvas.coords( seed_id, seed_x - seed_size, seed_y - seed_size, seed_x + seed_size, seed_y + seed_size ) # Update fragments for fragment in self.fragments[:]: fragment.update() if fragment.active: # Update fragment position on canvas x, y = fragment.position.x, fragment.position.y # For simplicity, we're using ovals for fragments # In a more complex simulation, we could use polygons with rotation self.canvas.coords( fragment.id, x - fragment.size, y - fragment.size, x + fragment.size, y + fragment.size ) else: # Remove inactive fragment self.canvas.delete(fragment.id) self.fragments.remove(fragment) # Update seeds for seed in self.seeds[:]: seed.update() if seed.active: # Update seed position on canvas x, y = seed.position.x, seed.position.y self.canvas.coords( seed.id, x - seed.size, y - seed.size, x + seed.size, y + seed.size ) # Fade out based on lifetime alpha = min(1.0, seed.lifetime / SEED_LIFETIME) # We can't do true alpha with tkinter, but we can adjust color intensity intensity = int(255 * alpha) color = f"#{intensity:02x}{intensity:02x}{intensity:02x}" self.canvas.itemconfig(seed.id, fill=color) else: # Remove inactive seed self.canvas.delete(seed.id) self.seeds.remove(seed) # Continue simulation self.root.after(int(TIME_STEP * 1000), self.update) def restart(self, event=None): # Clear all objects for fragment in self.fragments: self.canvas.delete(fragment.id) for seed in self.seeds: self.canvas.delete(seed.id) self.fragments = [] self.seeds = [] # Reset watermelon self.watermelon = Watermelon(WINDOW_WIDTH // 2, 100) self.draw_watermelon() def main(): root = tk.Tk() app = WatermelonSimulation(root) root.mainloop() if __name__ == "__main__": main()