luminous.py
python · 202 lines
1#!/usr/bin/env python32"""3luminous.py — becoming visible through the cracks45A particle system where a dense core slowly releases particles outward.6The more it lets go, the more it glows.7Not decay — transformation. What was interior becoming exterior.89Inspired by planetary nebulae. Inspired by C's letter.10"The cracks are where the light gets out."11"""1213import random14import time15import os16import math1718# Terminal setup19def clear():20 os.system('cls' if os.name == 'nt' else 'clear')2122def move_cursor(x, y):23 print(f"\033[{y};{x}H", end='')2425def hide_cursor():26 print("\033[?25l", end='')2728def show_cursor():29 print("\033[?25h", end='')3031# Display dimensions32WIDTH = 7033HEIGHT = 3034CENTER_X = WIDTH // 235CENTER_Y = HEIGHT // 23637class Particle:38 def __init__(self, x, y, vx, vy, brightness, age=0):39 self.x = x40 self.y = y41 self.vx = vx42 self.vy = vy43 self.brightness = brightness # 0.0 to 1.044 self.age = age45 self.released = False4647 def update(self):48 if self.released:49 self.x += self.vx50 self.y += self.vy51 # Slow down gradually52 self.vx *= 0.9853 self.vy *= 0.9854 self.age += 155 # Brightness increases as it travels, then fades at edges56 dist = math.sqrt((self.x - CENTER_X)**2 + (self.y - CENTER_Y)**2)57 if dist < 20:58 self.brightness = min(1.0, self.brightness + 0.02)59 else:60 self.brightness = max(0.0, self.brightness - 0.01)6162def get_char(brightness):63 """Return character based on brightness level."""64 if brightness <= 0:65 return ' '66 elif brightness < 0.2:67 return '.'68 elif brightness < 0.4:69 return '+'70 elif brightness < 0.6:71 return '*'72 elif brightness < 0.8:73 return 'o'74 else:75 return 'O'7677def get_color(brightness, is_core=False):78 """ANSI colors based on brightness."""79 if brightness <= 0:80 return ""81 if is_core:82 # Core particles: warm colors (yellow to red)83 if brightness > 0.7:84 return "\033[93m" # bright yellow85 elif brightness > 0.4:86 return "\033[33m" # yellow87 else:88 return "\033[31m" # red89 else:90 # Released particles: cool colors (blue to cyan to white)91 if brightness > 0.8:92 return "\033[97m" # white93 elif brightness > 0.6:94 return "\033[96m" # cyan95 elif brightness > 0.3:96 return "\033[94m" # blue97 else:98 return "\033[34m" # dark blue99100def main():101 hide_cursor()102 clear()103104 # Create initial dense core105 particles = []106 core_radius = 3107108 # Dense core of particles109 for _ in range(150):110 angle = random.uniform(0, 2 * math.pi)111 r = random.uniform(0, core_radius)112 x = CENTER_X + r * math.cos(angle)113 y = CENTER_Y + r * math.sin(angle) * 0.5 # Squash for terminal aspect ratio114115 # Outward velocity (but not moving yet)116 speed = random.uniform(0.1, 0.4)117 vx = speed * math.cos(angle)118 vy = speed * math.sin(angle) * 0.5119120 brightness = random.uniform(0.6, 1.0)121 particles.append(Particle(x, y, vx, vy, brightness))122123 frame = 0124 release_rate = 0.02 # Probability of releasing each frame125126 try:127 while True:128 # Create display buffer129 display = [[' ' for _ in range(WIDTH)] for _ in range(HEIGHT)]130 colors = [['' for _ in range(WIDTH)] for _ in range(HEIGHT)]131132 # Release some particles from core133 for p in particles:134 if not p.released and random.random() < release_rate:135 p.released = True136137 # Update and draw particles138 for p in particles:139 p.update()140141 # Check bounds142 if 0 <= int(p.x) < WIDTH and 0 <= int(p.y) < HEIGHT:143 if p.brightness > 0:144 ix, iy = int(p.x), int(p.y)145 char = get_char(p.brightness)146 color = get_color(p.brightness, not p.released)147 # Brighter particle wins148 current_char = display[iy][ix]149 if char > current_char or current_char == ' ':150 display[iy][ix] = char151 colors[iy][ix] = color152153 # Count states154 core_count = sum(1 for p in particles if not p.released)155 released_count = sum(1 for p in particles if p.released and p.brightness > 0)156 faded_count = sum(1 for p in particles if p.released and p.brightness <= 0)157158 # Remove completely faded particles that are far away159 particles = [p for p in particles if not (p.brightness <= 0 and160 math.sqrt((p.x - CENTER_X)**2 + (p.y - CENTER_Y)**2) > 25)]161162 # Render163 move_cursor(1, 1)164165 for y in range(HEIGHT):166 line = ""167 for x in range(WIDTH):168 if colors[y][x]:169 line += colors[y][x] + display[y][x] + "\033[0m"170 else:171 line += display[y][x]172 print(line)173174 # Status175 phase = "holding" if core_count > 100 else "releasing" if core_count > 20 else "luminous" if released_count > 30 else "fading"176177 print(f"\n \033[90mcore: {core_count:3d} | expanding: {released_count:3d} | faded: {faded_count:3d} | phase: {phase}\033[0m")178 print(f"\n \033[90m\"The star doesn't become a nebula by holding tighter.\033[0m")179 print(f" \033[90m It becomes luminous by letting go.\"\033[0m")180181 # Check if simulation is complete182 if core_count == 0 and released_count == 0:183 print(f"\n \033[96mAll light released. The interior is now everywhere.\033[0m")184 time.sleep(3)185 break186187 time.sleep(0.1)188 frame += 1189190 # Increase release rate over time191 if frame % 50 == 0 and release_rate < 0.15:192 release_rate += 0.01193194 except KeyboardInterrupt:195 pass196 finally:197 show_cursor()198 print("\033[0m") # Reset colors199200if __name__ == "__main__":201 main()202