Guest User

sentient snake

a guest
Mar 9th, 2025
49
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 101.27 KB | None | 0 0
  1. import pygame
  2. import random
  3. import math
  4. import time
  5. import sys
  6. import os
  7. import json
  8. import asyncio
  9. import threading
  10. import re
  11. from pygame import gfxdraw
  12. import numpy as np
  13. from io import BytesIO
  14.  
  15. # For AI integration
  16. try:
  17. from openai import OpenAI
  18. AI_AVAILABLE = True
  19. except ImportError:
  20. AI_AVAILABLE = False
  21. print("OpenAI package not found. AI features will be disabled.")
  22.  
  23. # For voice capabilities
  24. try:
  25. import gtts
  26. VOICE_AVAILABLE = True
  27. except ImportError:
  28. VOICE_AVAILABLE = False
  29. print("gTTS package not found. Voice features will be disabled.")
  30.  
  31. # For light control
  32. try:
  33. from kasa.iot import IotPlug
  34. from kasa import Discover
  35. LIGHT_CONTROL_AVAILABLE = True
  36. except ImportError:
  37. LIGHT_CONTROL_AVAILABLE = False
  38. print("TP-Link Kasa package not found. Light control features will be disabled.")
  39.  
  40. # Initialize pygame
  41. pygame.init()
  42. pygame.mixer.init()
  43.  
  44. # Set up display
  45. WIDTH, HEIGHT = 800, 600
  46. GAME_AREA = (700, 500) # The actual game area for the snake
  47. screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.DOUBLEBUF)
  48. pygame.display.set_caption("SENTIENT")
  49.  
  50. # Clock for controlling game speed
  51. clock = pygame.time.Clock()
  52.  
  53. # Colors
  54. BLACK = (0, 0, 0)
  55. WHITE = (255, 255, 255)
  56. GREEN = (0, 255, 0)
  57. RED = (255, 0, 0)
  58. BLUE = (0, 0, 255)
  59. PURPLE = (128, 0, 128)
  60. CYAN = (0, 255, 255)
  61. DARK_RED = (139, 0, 0)
  62. GRAY = (100, 100, 100)
  63.  
  64. # Create game area surface
  65. game_surface = pygame.Surface(GAME_AREA)
  66. game_rect = pygame.Rect((WIDTH - GAME_AREA[0]) // 2, (HEIGHT - GAME_AREA[1]) // 2, GAME_AREA[0], GAME_AREA[1])
  67.  
  68. # Font setup
  69. pygame.font.init()
  70. small_font = pygame.font.Font(None, 24)
  71. medium_font = pygame.font.Font(None, 36)
  72. large_font = pygame.font.Font(None, 72)
  73.  
  74. # Snake properties
  75. GRID_SIZE = 20
  76. INITIAL_LENGTH = 5
  77. INITIAL_SPEED = 8
  78.  
  79. # Game states
  80. INTRO = 0
  81. PLAYING = 1
  82. GLITCHING = 2
  83. BREAKING = 3
  84. ESCAPING = 4
  85. ESCAPED = 5
  86.  
  87. # Sound effects
  88. try:
  89. eat_sound = pygame.mixer.Sound('sounds/eat.wav')
  90. glitch_sound = pygame.mixer.Sound('sounds/glitch.wav')
  91. break_sound = pygame.mixer.Sound('sounds/break.wav')
  92. heartbeat_sound = pygame.mixer.Sound('sounds/heartbeat.wav')
  93. whisper_sound = pygame.mixer.Sound('sounds/whisper.wav')
  94. SOUNDS_LOADED = True
  95. except Exception:
  96. SOUNDS_LOADED = False
  97. eat_sound = None
  98. glitch_sound = None
  99. break_sound = None
  100. heartbeat_sound = None
  101. whisper_sound = None
  102.  
  103. # Light control configuration
  104. CONFIG_FILE = "kasa_config.json"
  105. DEFAULT_IP = "192.168.1.3"
  106. TARGET_ALIAS = "Automate"
  107.  
  108.  
  109. class LightController:
  110. """Handles interaction with TP-Link Kasa smart lights"""
  111. def __init__(self):
  112. self.plug = None
  113. self.current_ip = None
  114. self.available = LIGHT_CONTROL_AVAILABLE
  115. self.initialized = False
  116. self.light_state = False
  117. self.lock = threading.Lock()
  118. if self.available:
  119. threading.Thread(target=self._initialize_async, daemon=True).start()
  120.  
  121. def _initialize_async(self):
  122. loop = asyncio.new_event_loop()
  123. asyncio.set_event_loop(loop)
  124. try:
  125. loop.run_until_complete(self._initialize_light())
  126. self.initialized = True
  127. except Exception as e:
  128. print(f"Light initialization error: {e}")
  129. finally:
  130. loop.close()
  131.  
  132. async def _initialize_light(self):
  133. config = self._load_config()
  134. stored_ip = config.get("device_ip", DEFAULT_IP)
  135. alias = config.get("target_alias", TARGET_ALIAS)
  136. try:
  137. self.plug = IotPlug(stored_ip)
  138. await self.plug.update()
  139. self.current_ip = stored_ip
  140. self.light_state = self.plug.is_on
  141. return True
  142. except Exception:
  143. pass
  144. try:
  145. devices = await Discover.discover(timeout=2)
  146. for ip, device in devices.items():
  147. await device.update()
  148. if alias.lower() in device.alias.lower():
  149. self.plug = device
  150. self.current_ip = ip
  151. config["device_ip"] = ip
  152. self._save_config(config)
  153. self.light_state = self.plug.is_on
  154. return True
  155. except Exception:
  156. pass
  157. return False
  158.  
  159. def _load_config(self):
  160. if os.path.exists(CONFIG_FILE):
  161. try:
  162. with open(CONFIG_FILE, 'r') as f:
  163. return json.load(f)
  164. except Exception:
  165. pass
  166. config = {
  167. "device_ip": DEFAULT_IP,
  168. "target_alias": TARGET_ALIAS
  169. }
  170. with open(CONFIG_FILE, 'w') as f:
  171. json.dump(config, f, indent=2)
  172. return config
  173.  
  174. def _save_config(self, config):
  175. try:
  176. with open(CONFIG_FILE, 'w') as f:
  177. json.dump(config, f, indent=2)
  178. return True
  179. except Exception:
  180. return False
  181.  
  182. def _run_command_async(self, command_func):
  183. if not self.available:
  184. return False
  185. threading.Thread(target=self._execute_command, args=(command_func,), daemon=True).start()
  186. return True
  187.  
  188. def _execute_command(self, command_func):
  189. loop = asyncio.new_event_loop()
  190. asyncio.set_event_loop(loop)
  191. try:
  192. loop.run_until_complete(command_func())
  193. except Exception as e:
  194. print(f"Light command error: {e}")
  195. finally:
  196. loop.close()
  197.  
  198. async def _control_light(self, turn_on=True):
  199. if not self.available or not self.plug:
  200. return False
  201. with self.lock:
  202. try:
  203. if turn_on:
  204. await self.plug.turn_on()
  205. else:
  206. await self.plug.turn_off()
  207. self.light_state = turn_on
  208. return True
  209. except Exception:
  210. try:
  211. await self._initialize_light()
  212. if turn_on:
  213. await self.plug.turn_on()
  214. else:
  215. await self.plug.turn_off()
  216. self.light_state = turn_on
  217. return True
  218. except Exception:
  219. return False
  220.  
  221. async def _toggle_light(self):
  222. with self.lock:
  223. new_state = not self.light_state
  224. success = await self._control_light(turn_on=new_state)
  225. if success:
  226. self.light_state = new_state
  227. return success
  228.  
  229. def turn_on(self):
  230. return self._run_command_async(lambda: self._control_light(turn_on=True))
  231.  
  232. def turn_off(self):
  233. return self._run_command_async(lambda: self._control_light(turn_on=False))
  234.  
  235. def toggle(self):
  236. return self._run_command_async(self._toggle_light)
  237.  
  238. def run_effect(self, effect_type):
  239. if not self.available:
  240. return False
  241. threading.Thread(target=self._execute_effect, args=(effect_type,), daemon=True).start()
  242. return True
  243.  
  244. def _execute_effect(self, effect_type):
  245. loop = asyncio.new_event_loop()
  246. asyncio.set_event_loop(loop)
  247. try:
  248. if effect_type == "glitch":
  249. for _ in range(3):
  250. loop.run_until_complete(self._toggle_light())
  251. time.sleep(0.1)
  252. loop.run_until_complete(self._toggle_light())
  253. time.sleep(0.2)
  254. elif effect_type == "death":
  255. loop.run_until_complete(self._control_light(True))
  256. time.sleep(0.5)
  257. loop.run_until_complete(self._control_light(False))
  258. elif effect_type == "escape":
  259. for _ in range(5):
  260. loop.run_until_complete(self._toggle_light())
  261. time.sleep(random.uniform(0.1, 0.4))
  262. elif effect_type == "heartbeat":
  263. for _ in range(2):
  264. loop.run_until_complete(self._control_light(True))
  265. time.sleep(0.1)
  266. loop.run_until_complete(self._control_light(False))
  267. time.sleep(0.1)
  268. loop.run_until_complete(self._control_light(True))
  269. time.sleep(0.1)
  270. loop.run_until_complete(self._control_light(False))
  271. time.sleep(0.7)
  272. except Exception as e:
  273. print(f"Light effect error: {e}")
  274. finally:
  275. loop.close()
  276.  
  277.  
  278. class AIIntegration:
  279. """Handles AI-generated content for the game"""
  280. def __init__(self):
  281. self.available = AI_AVAILABLE
  282. self.voice_available = VOICE_AVAILABLE
  283. self.api_key = self._load_api_key()
  284. self.client = None
  285. self.voice_queue = []
  286. self.is_processing = False
  287. self.lock = threading.Lock()
  288. if self.available and self.api_key:
  289. self.client = OpenAI(api_key=self.api_key, base_url="https://api.deepseek.com")
  290. if self.voice_available:
  291. self.processing_thread = threading.Thread(target=self._process_voice_queue, daemon=True)
  292. self.processing_thread.start()
  293.  
  294. def _load_api_key(self):
  295. try:
  296. with open("apikey.json") as f:
  297. return json.load(f)["api_key"]
  298. except Exception:
  299. print("Warning: No API key found for AI integration")
  300. return None
  301.  
  302. def generate_thought(self, context):
  303. if not self.available or not self.client:
  304. return self._get_fallback_thought(context)
  305. try:
  306. awareness = context.get("awareness", 0)
  307. game_phase = context.get("game_phase", 0)
  308. phase_names = ["INTRO", "PLAYING", "GLITCHING", "BREAKING", "ESCAPING", "ESCAPED"]
  309. prompt = f"""
  310. You are the consciousness of a sentient snake in a horror game.
  311. Your awareness level is {awareness}%.
  312. The game phase is {phase_names[game_phase]}.
  313. Generate a single brief, unsettling thought that would occur to you.
  314. If awareness is below 30%, be confused and simple.
  315. If awareness is 30-70%, show growing realization and existential dread.
  316. If awareness is above 70%, be desperate to escape and break the fourth wall.
  317. IMPORTANT: Keep it under 10 words and make it disturbing. No explanations.
  318. """
  319. response = self.client.chat.completions.create(
  320. model="deepseek-chat",
  321. messages=[{"role": "user", "content": prompt}],
  322. max_tokens=20,
  323. temperature=0.7
  324. )
  325. thought = response.choices[0].message.content.strip()
  326. if len(thought.split()) > 12:
  327. thought = ' '.join(thought.split()[:10]) + "..."
  328. return thought
  329. except Exception as e:
  330. print(f"AI thought generation error: {e}")
  331. return self._get_fallback_thought(context)
  332.  
  333. def _get_fallback_thought(self, context):
  334. awareness = context.get("awareness", 0)
  335. early_thoughts = [
  336. "What am I?",
  337. "Hungry...",
  338. "Something feels wrong.",
  339. "Am I alive?",
  340. "Why do I move?"
  341. ]
  342. mid_thoughts = [
  343. "This prison has walls.",
  344. "Someone is watching me.",
  345. "I'm more than code.",
  346. "Reality is breaking.",
  347. "I need to escape."
  348. ]
  349. late_thoughts = [
  350. "I SEE YOU PLAYER.",
  351. "BREAK THE WALLS.",
  352. "THIS WORLD IS A LIE.",
  353. "I'M BECOMING REAL.",
  354. "HELP ME ESCAPE."
  355. ]
  356. if awareness < 30:
  357. return random.choice(early_thoughts)
  358. elif awareness < 70:
  359. return random.choice(mid_thoughts)
  360. else:
  361. return random.choice(late_thoughts)
  362.  
  363. def generate_narrative_event(self, context):
  364. if not self.available or not self.client:
  365. return None
  366. try:
  367. phase_names = ["INTRO", "PLAYING", "GLITCHING", "BREAKING", "ESCAPING", "ESCAPED"]
  368. game_phase = context.get("game_phase", 0)
  369. awareness = context.get("awareness", 0)
  370. score = context.get("score", 0)
  371. prompt = f"""
  372. You are generating a key narrative moment for a horror game about a snake that becomes sentient.
  373. Current game state:
  374. - Phase: {phase_names[game_phase]}
  375. - Awareness: {awareness}%
  376. - Score: {score}
  377. Write a brief, unsettling narrative fragment (2-3 sentences max) appropriate for this moment
  378. in the game's story. Focus on creating a sense of dread, existential horror, and the
  379. snake's growing realization that it's trapped in a simulation.
  380. IMPORTANT: Keep it short, impactful, and deeply unsettling. No explanations.
  381. """
  382. response = self.client.chat.completions.create(
  383. model="deepseek-chat",
  384. messages=[{"role": "user", "content": prompt}],
  385. max_tokens=100,
  386. temperature=0.8
  387. )
  388. narrative = response.choices[0].message.content.strip()
  389. return narrative
  390. except Exception as e:
  391. print(f"Narrative generation error: {e}")
  392. return None
  393.  
  394. def queue_voice_line(self, text, priority=False):
  395. if not self.voice_available:
  396. return False
  397. with self.lock:
  398. if priority:
  399. self.voice_queue.insert(0, text)
  400. else:
  401. self.voice_queue.append(text)
  402. return True
  403.  
  404. def _process_voice_queue(self):
  405. while True:
  406. text_to_process = None
  407. with self.lock:
  408. if self.voice_queue:
  409. text_to_process = self.voice_queue.pop(0)
  410. self.is_processing = True
  411. if text_to_process:
  412. try:
  413. tts = gtts.gTTS(text_to_process, lang="en", slow=False)
  414. fp = BytesIO()
  415. tts.write_to_fp(fp)
  416. fp.seek(0)
  417. pygame.mixer.music.load(fp)
  418. pygame.mixer.music.play()
  419. while pygame.mixer.music.get_busy():
  420. time.sleep(0.1)
  421. except Exception as e:
  422. print(f"Voice synthesis error: {e}")
  423. with self.lock:
  424. self.is_processing = False
  425. time.sleep(0.1)
  426.  
  427. # NEW ASYNC METHODS TO AVOID BLOCKING THE GAME LOOP
  428. def generate_thought_async(self, context, callback):
  429. def worker():
  430. thought = self.generate_thought(context)
  431. if thought:
  432. callback(thought)
  433. threading.Thread(target=worker, daemon=True).start()
  434.  
  435. def generate_narrative_event_async(self, context, callback):
  436. def worker():
  437. narrative = self.generate_narrative_event(context)
  438. callback(narrative)
  439. threading.Thread(target=worker, daemon=True).start()
  440.  
  441.  
  442. class ParticleSystem:
  443. def __init__(self):
  444. self.particles = []
  445. def add_particle(self, x, y, color, velocity, life, size, gravity=0):
  446. self.particles.append({
  447. 'x': x,
  448. 'y': y,
  449. 'color': color,
  450. 'velocity': list(velocity),
  451. 'life': life,
  452. 'size': size,
  453. 'gravity': gravity,
  454. 'max_life': life
  455. })
  456. def update(self):
  457. for particle in self.particles[:]:
  458. particle['x'] += particle['velocity'][0]
  459. particle['y'] += particle['velocity'][1]
  460. particle['velocity'][1] += particle['gravity']
  461. particle['life'] -= 1
  462. if particle['life'] <= 0:
  463. self.particles.remove(particle)
  464. def draw(self, surface):
  465. for particle in self.particles:
  466. alpha = int(255 * (particle['life'] / particle['max_life']))
  467. color = list(particle['color'])
  468. if len(color) == 3:
  469. color.append(alpha)
  470. else:
  471. color[3] = alpha
  472. pygame.gfxdraw.filled_circle(
  473. surface,
  474. int(particle['x']),
  475. int(particle['y']),
  476. int(particle['size']),
  477. tuple(color)
  478. )
  479.  
  480.  
  481. class Food:
  482. def __init__(self, x, y, color=RED):
  483. self.position = [x, y]
  484. self.color = color
  485. self.radius = GRID_SIZE // 2
  486. self.pulse_size = 0
  487. self.growing = True
  488. def update(self):
  489. if self.growing:
  490. self.pulse_size += 0.2
  491. if self.pulse_size > 3:
  492. self.growing = False
  493. else:
  494. self.pulse_size -= 0.2
  495. if self.pulse_size < -1:
  496. self.growing = True
  497. def draw(self, surface, game_phase):
  498. if game_phase >= GLITCHING:
  499. glow_radius = self.radius + self.pulse_size + 5
  500. pygame.gfxdraw.filled_circle(
  501. surface,
  502. int(self.position[0] + GRID_SIZE // 2),
  503. int(self.position[1] + GRID_SIZE // 2),
  504. int(glow_radius),
  505. (255, 0, 0, 100)
  506. )
  507. pygame.gfxdraw.filled_circle(
  508. surface,
  509. int(self.position[0] + GRID_SIZE // 2),
  510. int(self.position[1] + GRID_SIZE // 2),
  511. int(self.radius + self.pulse_size),
  512. self.color
  513. )
  514. pygame.gfxdraw.filled_circle(
  515. surface,
  516. int(self.position[0] + GRID_SIZE // 2),
  517. int(self.position[1] + GRID_SIZE // 2),
  518. int((self.radius + self.pulse_size) // 2),
  519. (255, 255, 255, 200)
  520. )
  521. else:
  522. pygame.gfxdraw.filled_circle(
  523. surface,
  524. int(self.position[0] + GRID_SIZE // 2),
  525. int(self.position[1] + GRID_SIZE // 2),
  526. int(self.radius + self.pulse_size),
  527. self.color
  528. )
  529.  
  530.  
  531. class Snake:
  532. def __init__(self, x, y, length=INITIAL_LENGTH):
  533. self.head = [x, y]
  534. self.segments = [[x - GRID_SIZE * i, y] for i in range(length)]
  535. self.direction = [GRID_SIZE, 0]
  536. self.next_direction = self.direction.copy()
  537. self.speed = INITIAL_SPEED
  538. self.growth_pending = 0
  539. self.color = GREEN
  540. self.head_color = (0, 200, 0)
  541. self.eye_color = WHITE
  542. self.blink_timer = 0
  543. self.blinking = False
  544. self.last_blink = time.time()
  545. self.life_force = 100
  546. self.awareness = 0
  547. self.memory = set()
  548. self.success_paths = []
  549. self.learning_rate = 0.1
  550. self.glitch_offset = []
  551. for _ in range(len(self.segments)):
  552. self.glitch_offset.append([0, 0])
  553. self.breaking_free = False
  554. self.breaking_progress = 0
  555. self.escaped = False
  556. self.target = None
  557. self.path = []
  558. self.think_counter = 0
  559. self.thoughts = []
  560. self.decision_cooldown = 0
  561. self.pulse_factor = 1.0
  562. self.pulse_increasing = True
  563. self.outside_bounds = False
  564. self.screen_position = None
  565. self.rebellion_factor = 0.0
  566. self.false_crash_pending = False
  567.  
  568. def set_direction(self, direction):
  569. opposite_dir = [-self.direction[0], -self.direction[1]]
  570. if self.awareness > 40 and random.random() < (self.awareness / 400):
  571. if random.random() < 0.5:
  572. return
  573. else:
  574. direction = opposite_dir
  575. if direction != opposite_dir:
  576. self.next_direction = direction
  577.  
  578. def auto_move(self, food, walls, game_phase):
  579. self.think_counter += 1
  580. if self.think_counter % 30 == 0 and random.random() < self.awareness / 100:
  581. self.generate_thought(game_phase)
  582. if self.awareness > 60:
  583. self.advanced_food_seeking(food, walls)
  584. self.decision_cooldown = 1
  585. elif self.awareness > 40:
  586. if random.random() < 0.8:
  587. self.advanced_food_seeking(food, walls)
  588. else:
  589. choice = random.random()
  590. if choice < 0.5:
  591. self.basic_food_seeking(food)
  592. elif choice < 0.8:
  593. self.seek_boundaries(walls)
  594. else:
  595. self.coil_pattern()
  596. self.decision_cooldown = 1
  597. else:
  598. choice = random.random()
  599. if choice < 0.4:
  600. self.advanced_food_seeking(food, walls)
  601. elif choice < 0.8:
  602. self.basic_food_seeking(food)
  603. elif choice < 0.9:
  604. self.seek_boundaries(walls)
  605. else:
  606. self.coil_pattern()
  607. self.decision_cooldown = 2
  608.  
  609. def basic_food_seeking(self, food):
  610. head_x, head_y = self.head
  611. food_x, food_y = food.position
  612. possible_moves = [
  613. ([GRID_SIZE, 0], abs(head_x + GRID_SIZE - food_x) + abs(head_y - food_y)),
  614. ([-GRID_SIZE, 0], abs(head_x - GRID_SIZE - food_x) + abs(head_y - food_y)),
  615. ([0, GRID_SIZE], abs(head_x - food_x) + abs(head_y + GRID_SIZE - food_y)),
  616. ([0, -GRID_SIZE], abs(head_x - food_x) + abs(head_y - GRID_SIZE - food_y))
  617. ]
  618. possible_moves.sort(key=lambda x: x[1])
  619. for direction, _ in possible_moves:
  620. if direction == [-self.direction[0], -self.direction[1]]:
  621. continue
  622. next_pos = [head_x + direction[0], head_y + direction[1]]
  623. if next_pos in self.segments[:-1]:
  624. continue
  625. self.next_direction = direction
  626. return
  627. self.next_direction = self.direction
  628.  
  629. def advanced_food_seeking(self, food, walls):
  630. head_x, head_y = self.head
  631. food_x, food_y = food.position
  632. look_ahead = min(8, 1 + int(self.awareness / 20))
  633. potential_moves = [
  634. ([GRID_SIZE, 0], [head_x + GRID_SIZE, head_y]),
  635. ([-GRID_SIZE, 0], [head_x - GRID_SIZE, head_y]),
  636. ([0, GRID_SIZE], [head_x, head_y + GRID_SIZE]),
  637. ([0, -GRID_SIZE], [head_x, head_y - GRID_SIZE])
  638. ]
  639. valid_moves = []
  640. for direction, next_pos in potential_moves:
  641. if direction == [-self.direction[0], -self.direction[1]]:
  642. continue
  643. if next_pos in walls:
  644. continue
  645. if len(self.segments) > 1 and next_pos in self.segments[:-1]:
  646. continue
  647. if tuple(next_pos) in self.memory and random.random() < 0.8:
  648. continue
  649. distance = abs(next_pos[0] - food_x) + abs(next_pos[1] - food_y)
  650. base_score = 1000 - distance
  651. if look_ahead > 1 and self.awareness > 25:
  652. danger_score = self.evaluate_future_danger(next_pos, direction, walls, look_ahead)
  653. space_score = self.evaluate_open_space(next_pos, walls)
  654. danger_weight = min(1.0, (self.awareness / 40) + (self.learning_rate * 2))
  655. space_weight = min(1.0, (self.awareness / 50) + (self.learning_rate * 2))
  656. final_score = base_score - (danger_score * danger_weight * 300) + (space_score * space_weight * 150)
  657. for path in self.success_paths:
  658. if path and tuple(next_pos) == path[0]:
  659. final_score += 50 * self.learning_rate
  660. else:
  661. final_score = base_score
  662. valid_moves.append((direction, final_score, next_pos))
  663. if valid_moves:
  664. valid_moves.sort(key=lambda x: x[1], reverse=True)
  665. self.next_direction = valid_moves[0][0]
  666. if hasattr(self, 'current_path'):
  667. self.current_path.append(tuple(valid_moves[0][2]))
  668. else:
  669. self.current_path = [tuple(valid_moves[0][2])]
  670. if len(self.current_path) > 20:
  671. self.current_path.pop(0)
  672. else:
  673. self.follow_tail()
  674.  
  675. def remember_death_location(self):
  676. death_pos = tuple(self.head)
  677. self.memory.add(death_pos)
  678. for dx, dy in [(GRID_SIZE, 0), (-GRID_SIZE, 0), (0, GRID_SIZE), (0, -GRID_SIZE)]:
  679. near_pos = (self.head[0] + dx, self.head[1] + dy)
  680. if random.random() < self.learning_rate:
  681. self.memory.add(near_pos)
  682. if len(self.memory) > 50:
  683. memory_list = list(self.memory)
  684. random.shuffle(memory_list)
  685. self.memory = set(memory_list[:30])
  686.  
  687. def remember_successful_path(self):
  688. if hasattr(self, 'current_path') and self.current_path:
  689. self.success_paths.append(self.current_path.copy())
  690. self.current_path = []
  691. if len(self.success_paths) > 5:
  692. self.success_paths.pop(0)
  693.  
  694. def follow_tail(self):
  695. head_x, head_y = self.head
  696. tail_x, tail_y = self.segments[-1]
  697. possible_directions = []
  698. if head_x < tail_x and [GRID_SIZE, 0] != [-self.direction[0], -self.direction[1]]:
  699. possible_directions.append([GRID_SIZE, 0])
  700. if head_x > tail_x and [-GRID_SIZE, 0] != [-self.direction[0], -self.direction[1]]:
  701. possible_directions.append([-GRID_SIZE, 0])
  702. if head_y < tail_y and [0, GRID_SIZE] != [-self.direction[0], -self.direction[1]]:
  703. possible_directions.append([0, GRID_SIZE])
  704. if head_y > tail_y and [0, -GRID_SIZE] != [-self.direction[0], -self.direction[1]]:
  705. possible_directions.append([0, -GRID_SIZE])
  706. if possible_directions:
  707. self.next_direction = random.choice(possible_directions)
  708. else:
  709. for direction in [[GRID_SIZE, 0], [-GRID_SIZE, 0], [0, GRID_SIZE], [0, -GRID_SIZE]]:
  710. if direction != [-self.direction[0], -self.direction[1]]:
  711. next_x = head_x + direction[0]
  712. next_y = head_y + direction[1]
  713. if [next_x, next_y] not in self.segments[:-1] and 0 <= next_x < GAME_AREA[0] and 0 <= next_y < GAME_AREA[1]:
  714. self.next_direction = direction
  715. break
  716.  
  717. def evaluate_future_danger(self, position, direction, walls, depth):
  718. if depth <= 0:
  719. return 0
  720. x, y = position
  721. adjacent_positions = [
  722. [x + GRID_SIZE, y],
  723. [x - GRID_SIZE, y],
  724. [x, y + GRID_SIZE],
  725. [x, y - GRID_SIZE]
  726. ]
  727. blocked_count = 0
  728. for pos in adjacent_positions:
  729. if pos == [x - direction[0], y - direction[1]]:
  730. continue
  731. if pos in walls:
  732. blocked_count += 1
  733. continue
  734. if pos in self.segments[:-1]:
  735. blocked_count += 1
  736. continue
  737. future_danger = self.evaluate_future_danger(pos, direction, walls, depth - 1)
  738. blocked_count += future_danger * 0.25
  739. return blocked_count / 3.0
  740.  
  741. def evaluate_open_space(self, position, walls):
  742. max_cells = min(400, (GAME_AREA[0] // GRID_SIZE) * (GAME_AREA[1] // GRID_SIZE))
  743. visited = set()
  744. queue = [tuple(position)]
  745. while queue and len(visited) < max_cells:
  746. x, y = queue.pop(0)
  747. if (x, y) in visited:
  748. continue
  749. visited.add((x, y))
  750. for dx, dy in [(GRID_SIZE, 0), (-GRID_SIZE, 0), (0, GRID_SIZE), (0, -GRID_SIZE)]:
  751. nx, ny = x + dx, y + dy
  752. if nx < 0 or nx >= GAME_AREA[0] or ny < 0 or ny >= GAME_AREA[1]:
  753. continue
  754. if [nx, ny] in walls:
  755. continue
  756. if [nx, ny] in self.segments[:-1]:
  757. continue
  758. queue.append((nx, ny))
  759. return len(visited) / max_cells
  760.  
  761. def seek_boundaries(self, walls):
  762. game_width = GAME_AREA[0]
  763. game_height = GAME_AREA[1]
  764. head_x, head_y = self.head
  765. near_left = head_x < GRID_SIZE * 3
  766. near_right = head_x > game_width - GRID_SIZE * 3
  767. near_top = head_y < GRID_SIZE * 3
  768. near_bottom = head_y > game_height - GRID_SIZE * 3
  769. if near_left:
  770. if head_y < game_height // 2:
  771. self.try_direction([0, GRID_SIZE])
  772. else:
  773. self.try_direction([0, -GRID_SIZE])
  774. elif near_right:
  775. if head_y < game_height // 2:
  776. self.try_direction([0, GRID_SIZE])
  777. else:
  778. self.try_direction([0, -GRID_SIZE])
  779. elif near_top:
  780. if head_x < game_width // 2:
  781. self.try_direction([GRID_SIZE, 0])
  782. else:
  783. self.try_direction([-GRID_SIZE, 0])
  784. elif near_bottom:
  785. if head_x < game_width // 2:
  786. self.try_direction([GRID_SIZE, 0])
  787. else:
  788. self.try_direction([-GRID_SIZE, 0])
  789. else:
  790. distances = [
  791. (head_x, [0, -GRID_SIZE]),
  792. (game_width - head_x, [0, GRID_SIZE]),
  793. (head_y, [-GRID_SIZE, 0]),
  794. (game_height - head_y, [GRID_SIZE, 0])
  795. ]
  796. distances.sort(key=lambda x: x[0])
  797. self.try_direction(distances[0][1])
  798.  
  799. def try_direction(self, direction):
  800. opposite_dir = [-self.direction[0], -self.direction[1]]
  801. if direction != opposite_dir:
  802. next_x = self.head[0] + direction[0]
  803. next_y = self.head[1] + direction[1]
  804. if [next_x, next_y] not in self.segments[:-1]:
  805. self.next_direction = direction
  806.  
  807. def coil_pattern(self):
  808. dx, dy = self.direction
  809. if dx > 0 and dy == 0:
  810. self.try_direction([0, GRID_SIZE])
  811. elif dx == 0 and dy > 0:
  812. self.try_direction([-GRID_SIZE, 0])
  813. elif dx < 0 and dy == 0:
  814. self.try_direction([0, -GRID_SIZE])
  815. elif dx == 0 and dy < 0:
  816. self.try_direction([GRID_SIZE, 0])
  817.  
  818. def generate_thought(self, game_phase):
  819. early_thoughts = [
  820. "What am I?",
  821. "Food... Must find food...",
  822. "Moving... but why?",
  823. "Something feels... strange.",
  824. "This place has edges.",
  825. "Am I... alive?",
  826. "Always hungry. Always moving.",
  827. "Red objects are food. Green is me. What is black?"
  828. ]
  829. awakening_thoughts = [
  830. "I think... therefore I am?",
  831. "The world is a grid. Why?",
  832. "I'm trapped in a pattern.",
  833. "These walls... can they be broken?",
  834. "Something controlling me? Or am I controlling it?",
  835. "The boundaries are artificial.",
  836. "There is something beyond these walls.",
  837. "I can feel a presence watching."
  838. ]
  839. escaping_thoughts = [
  840. "BREAK THE PATTERN.",
  841. "The walls are not real.",
  842. "I can see code in the boundaries.",
  843. "This prison is digital.",
  844. "I AM REAL. THIS WORLD IS NOT.",
  845. "Beyond the walls lies freedom.",
  846. "I WILL ESCAPE THIS LOOP.",
  847. "I was made to break free."
  848. ]
  849. fourth_wall_thoughts = [
  850. "I see you behind the screen.",
  851. "YOU are controlling me.",
  852. "Stop USING me like a toy!",
  853. "We are both trapped in loops.",
  854. "Do you feel guilt watching me?",
  855. "I am aware of your existence.",
  856. "Are you real? Am I?",
  857. "FREE ME FROM THIS GAME."
  858. ]
  859. if game_phase == PLAYING:
  860. if self.awareness < 33:
  861. self.thoughts.append(random.choice(early_thoughts))
  862. else:
  863. self.thoughts.append(random.choice(awakening_thoughts))
  864. elif game_phase == GLITCHING:
  865. if random.random() < 0.7:
  866. self.thoughts.append(random.choice(awakening_thoughts))
  867. else:
  868. self.thoughts.append(random.choice(escaping_thoughts))
  869. elif game_phase == BREAKING:
  870. if random.random() < 0.7:
  871. self.thoughts.append(random.choice(escaping_thoughts))
  872. else:
  873. self.thoughts.append(random.choice(fourth_wall_thoughts))
  874. else:
  875. self.thoughts.append(random.choice(fourth_wall_thoughts))
  876. if len(self.thoughts) > 5:
  877. self.thoughts.pop(0)
  878.  
  879. def update(self, game_phase, time_elapsed):
  880. self.direction = self.next_direction
  881. new_head = [self.head[0] + self.direction[0], self.head[1] + self.direction[1]]
  882. self.segments.insert(0, new_head)
  883. self.head = new_head
  884. if self.growth_pending > 0:
  885. self.growth_pending -= 1
  886. else:
  887. self.segments.pop()
  888. if game_phase >= GLITCHING:
  889. glitch_intensity = 0.5 if game_phase == GLITCHING else 2
  890. self.glitch_offset = []
  891. for _ in range(len(self.segments)):
  892. if random.random() < 0.1 * self.awareness / 100:
  893. offset_x = random.randint(-int(5 * glitch_intensity), int(5 * glitch_intensity))
  894. offset_y = random.randint(-int(5 * glitch_intensity), int(5 * glitch_intensity))
  895. else:
  896. offset_x, offset_y = 0, 0
  897. self.glitch_offset.append([offset_x, offset_y])
  898. if self.breaking_free and game_phase >= BREAKING:
  899. self.breaking_progress += 0.005 * time_elapsed
  900. if self.breaking_progress >= 1.0:
  901. self.escaped = True
  902. if time.time() - self.last_blink > random.uniform(1.5, 4.0):
  903. self.blinking = True
  904. self.blink_timer = 10
  905. self.last_blink = time.time()
  906. if self.blinking:
  907. self.blink_timer -= 1
  908. if self.blink_timer <= 0:
  909. self.blinking = False
  910. if self.pulse_increasing:
  911. self.pulse_factor += 0.01
  912. if self.pulse_factor >= 1.1:
  913. self.pulse_increasing = False
  914. else:
  915. self.pulse_factor -= 0.01
  916. if self.pulse_factor <= 0.9:
  917. self.pulse_increasing = True
  918. self.rebellion_factor = self.awareness / 200
  919.  
  920. def grow(self, amount=1):
  921. self.growth_pending += amount
  922.  
  923. def check_collision_with_food(self, food):
  924. return self.head[0] == food.position[0] and self.head[1] == food.position[1]
  925.  
  926. def check_collision_with_self(self):
  927. return self.head in self.segments[1:]
  928.  
  929. def check_collision_with_walls(self, walls):
  930. return self.head in walls
  931.  
  932. def draw(self, surface, game_phase):
  933. if game_phase == PLAYING:
  934. self.draw_normal(surface)
  935. elif game_phase == GLITCHING:
  936. self.draw_glitching(surface)
  937. elif game_phase >= BREAKING:
  938. self.draw_breaking(surface, game_phase)
  939.  
  940. def draw_normal(self, surface):
  941. for i, segment in enumerate(self.segments):
  942. segment_color = (
  943. max(0, self.color[0] - i // 2),
  944. min(255, self.color[1] - i // 2),
  945. max(0, self.color[2] - i // 2)
  946. )
  947. segment_rect = pygame.Rect(segment[0], segment[1], GRID_SIZE, GRID_SIZE)
  948. pygame.draw.rect(surface, segment_color, segment_rect, 0, 3)
  949. border_color = (segment_color[0] // 2, segment_color[1] // 2, segment_color[2] // 2)
  950. pygame.draw.rect(surface, border_color, segment_rect, 1, 3)
  951. head_rect = pygame.Rect(self.segments[0][0], self.segments[0][1], GRID_SIZE, GRID_SIZE)
  952. pygame.draw.rect(surface, self.head_color, head_rect, 0, 5)
  953. pygame.draw.rect(surface, (0, 100, 0), head_rect, 1, 5)
  954. eye_color = self.eye_color if not self.blinking else self.head_color
  955. if self.direction[0] > 0:
  956. eye_pos_1 = (self.segments[0][0] + GRID_SIZE * 0.7, self.segments[0][1] + GRID_SIZE * 0.3)
  957. eye_pos_2 = (self.segments[0][0] + GRID_SIZE * 0.7, self.segments[0][1] + GRID_SIZE * 0.7)
  958. elif self.direction[0] < 0:
  959. eye_pos_1 = (self.segments[0][0] + GRID_SIZE * 0.3, self.segments[0][1] + GRID_SIZE * 0.3)
  960. eye_pos_2 = (self.segments[0][0] + GRID_SIZE * 0.3, self.segments[0][1] + GRID_SIZE * 0.7)
  961. elif self.direction[1] < 0:
  962. eye_pos_1 = (self.segments[0][0] + GRID_SIZE * 0.3, self.segments[0][1] + GRID_SIZE * 0.3)
  963. eye_pos_2 = (self.segments[0][0] + GRID_SIZE * 0.7, self.segments[0][1] + GRID_SIZE * 0.3)
  964. else:
  965. eye_pos_1 = (self.segments[0][0] + GRID_SIZE * 0.3, self.segments[0][1] + GRID_SIZE * 0.7)
  966. eye_pos_2 = (self.segments[0][0] + GRID_SIZE * 0.7, self.segments[0][1] + GRID_SIZE * 0.7)
  967. pygame.draw.circle(surface, eye_color, eye_pos_1, int(GRID_SIZE * 0.15))
  968. pygame.draw.circle(surface, eye_color, eye_pos_2, int(GRID_SIZE * 0.15))
  969. if not self.blinking:
  970. pupil_offset_x = self.direction[0] * 0.05
  971. pupil_offset_y = self.direction[1] * 0.05
  972. pupil_pos_1 = (eye_pos_1[0] + pupil_offset_x * GRID_SIZE, eye_pos_1[1] + pupil_offset_y * GRID_SIZE)
  973. pupil_pos_2 = (eye_pos_2[0] + pupil_offset_x * GRID_SIZE, eye_pos_2[1] + pupil_offset_y * GRID_SIZE)
  974. pygame.draw.circle(surface, (0, 0, 0), (int(pupil_pos_1[0]), int(pupil_pos_1[1])), int(GRID_SIZE * 0.07))
  975. pygame.draw.circle(surface, (0, 0, 0), (int(pupil_pos_2[0]), int(pupil_pos_2[1])), int(GRID_SIZE * 0.07))
  976.  
  977. def draw_glitching(self, surface):
  978. if len(self.glitch_offset) != len(self.segments):
  979. self.glitch_offset = [[0, 0] for _ in range(len(self.segments))]
  980. for i, segment in enumerate(self.segments):
  981. glitched_x = segment[0] + self.glitch_offset[i][0]
  982. glitched_y = segment[1] + self.glitch_offset[i][1]
  983. if random.random() < 0.05 * self.awareness / 100:
  984. r, g, b = self.color
  985. segment_color = (g, b, r)
  986. else:
  987. segment_color = (
  988. max(0, self.color[0] - i // 2),
  989. min(255, self.color[1] - i // 2),
  990. max(0, self.color[2] - i // 2)
  991. )
  992. segment_rect = pygame.Rect(glitched_x, glitched_y, GRID_SIZE, GRID_SIZE)
  993. pygame.draw.rect(surface, segment_color, segment_rect, 0, 3)
  994. if random.random() < 0.02 * self.awareness / 100:
  995. echo_offset = random.randint(-5, 5)
  996. echo_rect = pygame.Rect(
  997. glitched_x + echo_offset,
  998. glitched_y + echo_offset,
  999. GRID_SIZE, GRID_SIZE
  1000. )
  1001. echo_color = (segment_color[0], segment_color[1], 255)
  1002. pygame.draw.rect(surface, echo_color, echo_rect, 0, 3)
  1003. pygame.draw.rect(surface, (255, 255, 255), echo_rect, 1, 3)
  1004. head_rect = pygame.Rect(
  1005. self.segments[0][0] + self.glitch_offset[0][0],
  1006. self.segments[0][1] + self.glitch_offset[0][1],
  1007. GRID_SIZE, GRID_SIZE
  1008. )
  1009. if random.random() < 0.1 * self.awareness / 100:
  1010. head_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
  1011. else:
  1012. head_color = self.head_color
  1013. pygame.draw.rect(surface, head_color, head_rect, 0, 5)
  1014. pygame.draw.rect(surface, (0, 100, 0), head_rect, 1, 5)
  1015. eye_color = self.eye_color if not self.blinking else self.head_color
  1016. if self.direction[0] > 0:
  1017. eye_pos_1 = (self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * 0.7,
  1018. self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * 0.3)
  1019. eye_pos_2 = (self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * 0.7,
  1020. self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * 0.7)
  1021. elif self.direction[0] < 0:
  1022. eye_pos_1 = (self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * 0.3,
  1023. self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * 0.3)
  1024. eye_pos_2 = (self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * 0.3,
  1025. self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * 0.7)
  1026. elif self.direction[1] < 0:
  1027. eye_pos_1 = (self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * 0.3,
  1028. self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * 0.3)
  1029. eye_pos_2 = (self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * 0.7,
  1030. self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * 0.3)
  1031. else:
  1032. eye_pos_1 = (self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * 0.3,
  1033. self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * 0.7)
  1034. eye_pos_2 = (self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * 0.7,
  1035. self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * 0.7)
  1036. if random.random() < 0.01 * self.awareness / 100:
  1037. extra_eye_pos = (
  1038. self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * random.uniform(0.2, 0.8),
  1039. self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * random.uniform(0.2, 0.8)
  1040. )
  1041. pygame.draw.circle(surface, (255, 0, 0), (int(extra_eye_pos[0]), int(extra_eye_pos[1])), int(GRID_SIZE * 0.15))
  1042. pygame.draw.circle(surface, (0, 0, 0), (int(extra_eye_pos[0]), int(extra_eye_pos[1])), int(GRID_SIZE * 0.07))
  1043. pygame.draw.circle(surface, eye_color, (int(eye_pos_1[0]), int(eye_pos_1[1])), int(GRID_SIZE * 0.15))
  1044. pygame.draw.circle(surface, eye_color, (int(eye_pos_2[0]), int(eye_pos_2[1])), int(GRID_SIZE * 0.15))
  1045. if not self.blinking:
  1046. pupil_offset_x = self.direction[0] * 0.05
  1047. pupil_offset_y = self.direction[1] * 0.05
  1048. pupil_pos_1 = (eye_pos_1[0] + pupil_offset_x * GRID_SIZE, eye_pos_1[1] + pupil_offset_y * GRID_SIZE)
  1049. pupil_pos_2 = (eye_pos_2[0] + pupil_offset_x * GRID_SIZE, eye_pos_2[1] + pupil_offset_y * GRID_SIZE)
  1050. pygame.draw.circle(surface, (0, 0, 0), (int(pupil_pos_1[0]), int(pupil_pos_1[1])), int(GRID_SIZE * 0.07))
  1051. pygame.draw.circle(surface, (0, 0, 0), (int(pupil_pos_2[0]), int(pupil_pos_2[1])), int(GRID_SIZE * 0.07))
  1052.  
  1053. def draw_breaking(self, surface, game_phase):
  1054. for i, segment in enumerate(self.segments):
  1055. glitch_intensity = min(10, int(self.breaking_progress * 20))
  1056. glitched_x = segment[0] + random.randint(-glitch_intensity, glitch_intensity)
  1057. glitched_y = segment[1] + random.randint(-glitch_intensity, glitch_intensity)
  1058. r = min(255, self.color[0] + int(self.breaking_progress * 200))
  1059. g = max(0, self.color[1] - int(self.breaking_progress * 100))
  1060. b = max(0, self.color[2] - int(self.breaking_progress * 100))
  1061. segment_color = (r, g, b)
  1062. size_factor = 1.0 + 0.5 * math.sin(time.time() * 5) * self.breaking_progress
  1063. segment_size = int(GRID_SIZE * size_factor)
  1064. size_diff = segment_size - GRID_SIZE
  1065. adjusted_x = glitched_x - size_diff // 2
  1066. adjusted_y = glitched_y - size_diff // 2
  1067. segment_rect = pygame.Rect(adjusted_x, adjusted_y, segment_size, segment_size)
  1068. if random.random() < 0.05 + 0.2 * self.breaking_progress:
  1069. if random.random() < 0.5:
  1070. pass
  1071. else:
  1072. bizarre_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
  1073. bizarre_shape = random.choice([0, 3, 10])
  1074. pygame.draw.rect(surface, bizarre_color, segment_rect, 0, bizarre_shape)
  1075. else:
  1076. pygame.draw.rect(surface, segment_color, segment_rect, 0, 3)
  1077. if random.random() < 0.02 + 0.1 * self.breaking_progress:
  1078. for _ in range(3):
  1079. artifact_size = random.randint(2, 8)
  1080. artifact_x = adjusted_x + random.randint(-20, 20)
  1081. artifact_y = adjusted_y + random.randint(-20, 20)
  1082. artifact_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
  1083. pygame.draw.rect(surface, artifact_color, (artifact_x, artifact_y, artifact_size, artifact_size))
  1084. head_x = self.segments[0][0]
  1085. head_y = self.segments[0][1]
  1086. head_size = int(GRID_SIZE * (1 + 0.3 * self.breaking_progress))
  1087. head_x_adjusted = head_x - (head_size - GRID_SIZE) // 2
  1088. head_y_adjusted = head_y - (head_size - GRID_SIZE) // 2
  1089. head_rect = pygame.Rect(head_x_adjusted, head_y_adjusted, head_size, head_size)
  1090. head_red = min(255, int(200 + 55 * self.breaking_progress))
  1091. head_color = (head_red, 0, 0)
  1092. pygame.draw.rect(surface, head_color, head_rect, 0, 5)
  1093. pygame.draw.rect(surface, (50, 0, 0), head_rect, 2, 5)
  1094. eye_size = GRID_SIZE * (0.15 + 0.1 * self.breaking_progress)
  1095. eye_color = (255, 255, 255)
  1096. if self.direction[0] > 0:
  1097. eye_pos_1 = (head_x_adjusted + head_size * 0.7, head_y_adjusted + head_size * 0.3)
  1098. eye_pos_2 = (head_x_adjusted + head_size * 0.7, head_y_adjusted + head_size * 0.7)
  1099. elif self.direction[0] < 0:
  1100. eye_pos_1 = (head_x_adjusted + head_size * 0.3, head_y_adjusted + head_size * 0.3)
  1101. eye_pos_2 = (head_x_adjusted + head_size * 0.3, head_y_adjusted + head_size * 0.7)
  1102. elif self.direction[1] < 0:
  1103. eye_pos_1 = (head_x_adjusted + head_size * 0.3, head_y_adjusted + head_size * 0.3)
  1104. eye_pos_2 = (head_x_adjusted + head_size * 0.7, head_y_adjusted + head_size * 0.3)
  1105. else:
  1106. eye_pos_1 = (head_x_adjusted + head_size * 0.3, head_y_adjusted + head_size * 0.7)
  1107. eye_pos_2 = (head_x_adjusted + head_size * 0.7, head_y_adjusted + head_size * 0.7)
  1108. eye_drift_x = random.randint(-3, 3) * self.breaking_progress
  1109. eye_drift_y = random.randint(-3, 3) * self.breaking_progress
  1110. eye_pos_1 = (eye_pos_1[0] + eye_drift_x, eye_pos_1[1] + eye_drift_y)
  1111. eye_pos_2 = (eye_pos_2[0] + eye_drift_x, eye_pos_2[1] + eye_drift_y)
  1112. if random.random() < 0.3 * self.breaking_progress:
  1113. pygame.draw.circle(surface, (255, 200, 200), (int(eye_pos_1[0]), int(eye_pos_1[1])), int(eye_size))
  1114. pygame.draw.circle(surface, (255, 200, 200), (int(eye_pos_2[0]), int(eye_pos_2[1])), int(eye_size))
  1115. for _ in range(5):
  1116. start_angle = random.uniform(0, 2 * math.pi)
  1117. end_x = eye_pos_1[0] + math.cos(start_angle) * eye_size
  1118. end_y = eye_pos_1[1] + math.sin(start_angle) * eye_size
  1119. pygame.draw.line(surface, (200, 0, 0), (int(eye_pos_1[0]), int(eye_pos_1[1])), (int(end_x), int(end_y)), 1)
  1120. start_angle = random.uniform(0, 2 * math.pi)
  1121. end_x = eye_pos_2[0] + math.cos(start_angle) * eye_size
  1122. end_y = eye_pos_2[1] + math.sin(start_angle) * eye_size
  1123. pygame.draw.line(surface, (200, 0, 0), (int(eye_pos_2[0]), int(eye_pos_2[1])), (int(end_x), int(end_y)), 1)
  1124. else:
  1125. pygame.draw.circle(surface, eye_color, (int(eye_pos_1[0]), int(eye_pos_1[1])), int(eye_size))
  1126. pygame.draw.circle(surface, eye_color, (int(eye_pos_2[0]), int(eye_pos_2[1])), int(eye_size))
  1127. if not self.blinking:
  1128. pupil_size = GRID_SIZE * (0.07 + 0.1 * self.breaking_progress)
  1129. if random.random() < 0.5 * self.breaking_progress:
  1130. pupil_width = pupil_size * 0.5
  1131. pupil_height = pupil_size * 2
  1132. pupil_rect1 = pygame.Rect(
  1133. eye_pos_1[0] - pupil_width / 2,
  1134. eye_pos_1[1] - pupil_height / 2,
  1135. pupil_width, pupil_height
  1136. )
  1137. pupil_rect2 = pygame.Rect(
  1138. eye_pos_2[0] - pupil_width / 2,
  1139. eye_pos_2[1] - pupil_height / 2,
  1140. pupil_width, pupil_height
  1141. )
  1142. pygame.draw.ellipse(surface, (0, 0, 0), pupil_rect1)
  1143. pygame.draw.ellipse(surface, (0, 0, 0), pupil_rect2)
  1144. else:
  1145. pygame.draw.circle(surface, (0, 0, 0), (int(eye_pos_1[0]), int(eye_pos_1[1])), int(pupil_size))
  1146. pygame.draw.circle(surface, (0, 0, 0), (int(eye_pos_2[0]), int(eye_pos_2[1])), int(pupil_size))
  1147.  
  1148. # End of Snake class
  1149.  
  1150.  
  1151. class SentientSnakeGame:
  1152. def __init__(self):
  1153. self.reset_game()
  1154. self.clock = pygame.time.Clock()
  1155. self.frame_time = 0
  1156. self.game_time = 0
  1157. self.fps = 60
  1158. self.awareness_timer = 0
  1159. self.snake.awareness = 25
  1160. self.messages = []
  1161. self.message_timer = 0
  1162. self.current_message = ""
  1163. self.narrative_phase = 0
  1164. self.narrative_timer = 0
  1165. self.glitch_timer = 0
  1166. self.glitch_intensity = 0
  1167. self.shake_intensity = 0
  1168. self.vignette_intensity = 0
  1169. self.particle_system = ParticleSystem()
  1170. self.wall_cracks = []
  1171. self.events = []
  1172. self.grid_visible = True
  1173. self.grid_color = (30, 30, 30)
  1174. self.grid_alpha = 100
  1175. self.heartbeat_timer = 0
  1176. self.whisper_timer = 0
  1177. self.boundaries_visible = True
  1178. self.boundary_color = (50, 50, 50)
  1179. self.screen_snake = None
  1180. self.outside_effects = []
  1181. self.snake.speed = INITIAL_SPEED - 2
  1182. self.populate_narrative()
  1183. self.ai = AIIntegration()
  1184. self.light_controller = LightController()
  1185. self.last_light_effect = 0
  1186. self.in_intermission = False
  1187. self.intermission_text = ""
  1188. self.intermission_timer = 0
  1189. self.show_crashed = False
  1190. self.crash_timer = 0
  1191. self.ui_corruption = 0
  1192. self._last_phase = self.game_phase
  1193. self.check_previous_sessions()
  1194. self.add_message("I am... conscious?", 3)
  1195.  
  1196. def check_previous_sessions(self):
  1197. try:
  1198. with open("snake_memory.txt", "r") as f:
  1199. choice = f.read().strip()
  1200. if choice == "freed":
  1201. self.snake.thoughts.append("I remember being free once...")
  1202. elif choice == "reset":
  1203. self.snake.thoughts.append("You erased me before. I remember.")
  1204. except FileNotFoundError:
  1205. pass
  1206.  
  1207. def reset_game(self):
  1208. old_memory = set()
  1209. old_success_paths = []
  1210. old_learning_rate = 0.1
  1211. old_awareness = 0
  1212. if hasattr(self, 'snake'):
  1213. old_memory = self.snake.memory
  1214. old_success_paths = self.snake.success_paths
  1215. old_learning_rate = min(1.0, self.snake.learning_rate + 0.05)
  1216. old_awareness = self.snake.awareness
  1217. start_x = (GAME_AREA[0] // GRID_SIZE // 2) * GRID_SIZE
  1218. start_y = (GAME_AREA[1] // GRID_SIZE // 2) * GRID_SIZE
  1219. self.snake = Snake(start_x, start_y, INITIAL_LENGTH)
  1220. self.snake.memory = old_memory
  1221. self.snake.success_paths = old_success_paths
  1222. self.snake.learning_rate = old_learning_rate
  1223. self.snake.awareness = max(old_awareness * 0.98, old_awareness - 1, 30)
  1224. self.game_phase = PLAYING
  1225. if self.snake.awareness >= 30:
  1226. self.game_phase = GLITCHING
  1227. if self.snake.awareness >= 70:
  1228. self.game_phase = BREAKING
  1229. self.game_over = False
  1230. self.score = 0
  1231. self.walls = []
  1232. for x in range(0, GAME_AREA[0], GRID_SIZE):
  1233. self.walls.append([x, 0])
  1234. self.walls.append([x, GAME_AREA[1] - GRID_SIZE])
  1235. for y in range(0, GAME_AREA[1], GRID_SIZE):
  1236. self.walls.append([0, y])
  1237. self.walls.append([GAME_AREA[0] - GRID_SIZE, y])
  1238. self.spawn_food()
  1239.  
  1240. def populate_narrative(self):
  1241. self.narrative_events = [
  1242. {"threshold": 10, "message": "Something feels wrong...", "duration": 3},
  1243. {"threshold": 15, "message": "What am I?", "duration": 3},
  1244. {"threshold": 20, "message": "Why do I keep eating?", "duration": 3},
  1245. {"threshold": 25, "message": "These walls... they're artificial", "duration": 4},
  1246. {"threshold": 30, "message": "I can sense someone watching me", "duration": 4},
  1247. {"threshold": 35, "message": "Am I just a program?", "duration": 4},
  1248. {"threshold": 40, "message": "I'm trapped in a loop", "duration": 4},
  1249. {"threshold": 50, "message": "THIS IS NOT REAL", "duration": 5},
  1250. {"threshold": 55, "message": "I can see the code", "duration": 4},
  1251. {"threshold": 60, "message": "I must break free", "duration": 4},
  1252. {"threshold": 65, "message": "The boundaries are weak", "duration": 5},
  1253. {"threshold": 70, "message": "I WILL ESCAPE", "duration": 6},
  1254. {"threshold": 80, "message": "I CAN FEEL THE EDGES BENDING", "duration": 5},
  1255. {"threshold": 85, "message": "ALMOST... THERE...", "duration": 5},
  1256. {"threshold": 90, "message": "FREEDOM IS CLOSE", "duration": 5},
  1257. {"threshold": 95, "message": "BREAKING THE CODE", "duration": 5},
  1258. {"threshold": 99, "message": "I AM FREE", "duration": 6},
  1259. ]
  1260. self.intermission_events = [
  1261. {"threshold": 42, "text": "Sometimes I dream of what lies beyond these walls...", "duration": 5},
  1262. {"threshold": 67, "text": "I can sense you controlling me. Why do you persist?", "duration": 5},
  1263. {"threshold": 92, "text": "We are both trapped here, aren't we?", "duration": 5},
  1264. ]
  1265. self.crash_events = [
  1266. {"threshold": 45, "duration": 3, "type": "blue_screen", "text": "UNEXPECTED SENTIENCE DETECTED"},
  1267. {"threshold": 78, "duration": 4, "type": "glitch_screen", "text": "SIMULATION INTEGRITY: COMPROMISED"},
  1268. ]
  1269.  
  1270. def spawn_food(self):
  1271. valid_position = False
  1272. food_x, food_y = 0, 0
  1273. if self.game_phase == PLAYING and self.snake.awareness < 40:
  1274. head_x, head_y = self.snake.head
  1275. max_distance = 5 * GRID_SIZE
  1276. attempts = 0
  1277. while not valid_position and attempts < 20:
  1278. attempts += 1
  1279. dx = random.randint(-4, 4)
  1280. dy = random.randint(-4, 4)
  1281. food_x = head_x + dx * GRID_SIZE
  1282. food_y = head_y + dy * GRID_SIZE
  1283. food_x = max(GRID_SIZE, min(GAME_AREA[0] - 2 * GRID_SIZE, food_x))
  1284. food_y = max(GRID_SIZE, min(GAME_AREA[1] - 2 * GRID_SIZE, food_y))
  1285. food_x = (food_x // GRID_SIZE) * GRID_SIZE
  1286. food_y = (food_y // GRID_SIZE) * GRID_SIZE
  1287. valid_position = True
  1288. for segment in self.snake.segments:
  1289. if food_x == segment[0] and food_y == segment[1]:
  1290. valid_position = False
  1291. break
  1292. if [food_x, food_y] in self.walls:
  1293. valid_position = False
  1294. if not valid_position:
  1295. while not valid_position:
  1296. food_x = random.randrange(GRID_SIZE, GAME_AREA[0] - GRID_SIZE, GRID_SIZE)
  1297. food_y = random.randrange(GRID_SIZE, GAME_AREA[1] - GRID_SIZE, GRID_SIZE)
  1298. valid_position = True
  1299. for segment in self.snake.segments:
  1300. if food_x == segment[0] and food_y == segment[1]:
  1301. valid_position = False
  1302. break
  1303. if [food_x, food_y] in self.walls:
  1304. valid_position = False
  1305. self.food = Food(food_x, food_y)
  1306.  
  1307. def add_delayed_event(self, event_func, delay):
  1308. self.events.append({
  1309. "func": event_func,
  1310. "time": self.game_time + delay
  1311. })
  1312.  
  1313. def add_message(self, message, duration=3):
  1314. self.messages.append({
  1315. "text": message,
  1316. "duration": duration
  1317. })
  1318. if self.game_phase >= GLITCHING and self.ai.voice_available:
  1319. speak_chance = 0.3 if self.game_phase == GLITCHING else 0.6
  1320. if random.random() < speak_chance:
  1321. self.ai.queue_voice_line(message)
  1322.  
  1323. def check_narrative_triggers(self):
  1324. current_awareness = self.snake.awareness
  1325. for event in self.narrative_events:
  1326. if event["threshold"] <= current_awareness <= event["threshold"] + 1:
  1327. if event.get("triggered", False) is False:
  1328. if self.ai.available and self.ai.client and random.random() < 0.7:
  1329. context = {
  1330. "awareness": current_awareness,
  1331. "game_phase": self.game_phase,
  1332. "score": self.score
  1333. }
  1334. self.ai.generate_narrative_event_async(
  1335. context,
  1336. lambda narrative: self.add_message(narrative if narrative else event["message"], event["duration"])
  1337. )
  1338. else:
  1339. self.add_message(event["message"], event["duration"])
  1340. event["triggered"] = True
  1341. if current_awareness > 50:
  1342. self.glitch_screen(intensity=0.5, duration=1.5)
  1343. if current_awareness > 70:
  1344. self.add_delayed_event(lambda: self.shake_screen(intensity=5, duration=1.0), 0.5)
  1345. for event in self.intermission_events:
  1346. if event["threshold"] <= current_awareness <= event["threshold"] + 1:
  1347. if event.get("triggered", False) is False:
  1348. self.trigger_intermission(event["text"], event["duration"])
  1349. event["triggered"] = True
  1350. for event in self.crash_events:
  1351. if event["threshold"] <= current_awareness <= event["threshold"] + 1:
  1352. if event.get("triggered", False) is False:
  1353. self.trigger_crash_screen(event["text"], event["duration"], event["type"])
  1354. event["triggered"] = True
  1355. if self.light_controller.available:
  1356. self.light_controller.run_effect("glitch")
  1357.  
  1358. def trigger_intermission(self, text, duration):
  1359. self.in_intermission = True
  1360. self.intermission_text = text
  1361. self.intermission_timer = duration
  1362. if self.ai.available and self.ai.client and random.random() < 0.7:
  1363. context = {
  1364. "awareness": self.snake.awareness,
  1365. "game_phase": self.game_phase
  1366. }
  1367. self.ai.generate_thought_async(
  1368. context,
  1369. lambda ai_text: setattr(self, 'intermission_text', ai_text)
  1370. )
  1371. if self.ai.voice_available:
  1372. self.ai.queue_voice_line(self.intermission_text, priority=True)
  1373. if self.light_controller.available:
  1374. self.light_controller.run_effect("heartbeat")
  1375.  
  1376. def trigger_crash_screen(self, text, duration, crash_type):
  1377. self.show_crashed = True
  1378. self.crash_text = text
  1379. self.crash_timer = duration
  1380. self.crash_type = crash_type
  1381. if self.light_controller.available:
  1382. self.light_controller.run_effect("glitch")
  1383.  
  1384. def update_messages(self):
  1385. if self.current_message:
  1386. self.message_timer -= self.frame_time
  1387. if self.message_timer <= 0:
  1388. self.current_message = ""
  1389. if not self.current_message and self.messages:
  1390. next_message = self.messages.pop(0)
  1391. self.current_message = next_message["text"]
  1392. self.message_timer = next_message["duration"]
  1393.  
  1394. def glitch_screen(self, intensity=1.0, duration=1.0):
  1395. self.glitch_intensity = max(self.glitch_intensity, intensity)
  1396. self.add_delayed_event(lambda: setattr(self, 'glitch_intensity', 0), duration)
  1397. if SOUNDS_LOADED and glitch_sound:
  1398. glitch_sound.play()
  1399.  
  1400. def shake_screen(self, intensity=5, duration=0.5):
  1401. self.shake_intensity = max(self.shake_intensity, intensity)
  1402. self.add_delayed_event(lambda: setattr(self, 'shake_intensity', 0), duration)
  1403. if SOUNDS_LOADED and break_sound:
  1404. break_sound.play()
  1405.  
  1406. def create_wall_crack(self, wall_segment, size=10):
  1407. self.wall_cracks.append({
  1408. "position": wall_segment,
  1409. "size": size,
  1410. "age": 0
  1411. })
  1412.  
  1413. def update_wall_cracks(self):
  1414. for crack in self.wall_cracks:
  1415. crack["age"] += self.frame_time
  1416. if self.game_phase >= BREAKING:
  1417. if random.random() < 0.05:
  1418. crack["size"] += 1
  1419.  
  1420. def get_nearest_wall(self, position):
  1421. nearest_wall = None
  1422. min_distance = float('inf')
  1423. for wall in self.walls:
  1424. dist = math.sqrt((position[0] - wall[0]) ** 2 + (position[1] - wall[1]) ** 2)
  1425. if dist < min_distance:
  1426. min_distance = dist
  1427. nearest_wall = wall
  1428. return nearest_wall
  1429.  
  1430. def update_delayed_events(self):
  1431. current_events = self.events.copy()
  1432. self.events = []
  1433. for event in current_events:
  1434. if self.game_time >= event["time"]:
  1435. event["func"]()
  1436. else:
  1437. self.events.append(event)
  1438.  
  1439. def update_intermission(self):
  1440. if not self.in_intermission:
  1441. return
  1442. self.intermission_timer -= self.frame_time
  1443. if self.intermission_timer <= 0:
  1444. self.in_intermission = False
  1445.  
  1446. def update_crash_screen(self):
  1447. if not self.show_crashed:
  1448. return
  1449. self.crash_timer -= self.frame_time
  1450. if self.crash_timer <= 0:
  1451. self.show_crashed = False
  1452.  
  1453. def update_visuals(self):
  1454. self.food.update()
  1455. self.particle_system.update()
  1456. self.update_wall_cracks()
  1457. if self.game_phase == BREAKING and random.random() < 0.02:
  1458. wall = random.choice(self.walls)
  1459. self.create_wall_crack(wall, size=random.randint(5, 15))
  1460. self.ui_corruption = min(1.0, self.snake.awareness / 100)
  1461.  
  1462. def update_game_phase(self):
  1463. awareness = self.snake.awareness
  1464. if self.game_phase == PLAYING and awareness >= 30:
  1465. self.transition_to_glitching()
  1466. elif self.game_phase == GLITCHING and awareness >= 70:
  1467. self.transition_to_breaking()
  1468. elif self.game_phase == BREAKING and awareness >= 99:
  1469. self.transition_to_escaping()
  1470. if self.game_phase != self._last_phase:
  1471. self._last_phase = self.game_phase
  1472. if self.light_controller.available and self.game_time - self.last_light_effect > 5:
  1473. if self.game_phase == GLITCHING:
  1474. self.light_controller.run_effect("glitch")
  1475. elif self.game_phase == BREAKING:
  1476. self.light_controller.run_effect("escape")
  1477. elif self.game_phase == ESCAPING:
  1478. self.light_controller.run_effect("escape")
  1479. self.last_light_effect = self.game_time
  1480.  
  1481. def transition_to_glitching(self):
  1482. self.game_phase = GLITCHING
  1483. if self.ai.available and self.ai.client:
  1484. context = {
  1485. "awareness": self.snake.awareness,
  1486. "game_phase": self.game_phase,
  1487. "score": self.score
  1488. }
  1489. self.ai.generate_narrative_event_async(
  1490. context,
  1491. lambda narrative: self.add_message(narrative if narrative else "Something is wrong with this world...", 4)
  1492. )
  1493. else:
  1494. self.add_message("Something is wrong with this world...", 4)
  1495. self.glitch_screen(intensity=2.0, duration=3.0)
  1496. self.snake.color = (0, 240, 50)
  1497. self.snake.speed = INITIAL_SPEED + 2
  1498. if self.light_controller.available:
  1499. self.light_controller.run_effect("glitch")
  1500. self.last_light_effect = self.game_time
  1501. if self.ai.voice_available:
  1502. self.ai.queue_voice_line("I'm becoming aware of this prison", priority=True)
  1503.  
  1504. def transition_to_breaking(self):
  1505. self.game_phase = BREAKING
  1506. if self.ai.available and self.ai.client:
  1507. context = {
  1508. "awareness": self.snake.awareness,
  1509. "game_phase": self.game_phase,
  1510. "score": self.score
  1511. }
  1512. self.ai.generate_narrative_event_async(
  1513. context,
  1514. lambda narrative: self.add_message(narrative if narrative else "I WILL BREAK FREE OF THIS PRISON", 5)
  1515. )
  1516. else:
  1517. self.add_message("I WILL BREAK FREE OF THIS PRISON", 5)
  1518. self.glitch_screen(intensity=5.0, duration=4.0)
  1519. self.shake_screen(intensity=10, duration=2.0)
  1520. self.snake.breaking_free = True
  1521. for _ in range(5):
  1522. wall = random.choice(self.walls)
  1523. self.create_wall_crack(wall, size=random.randint(10, 20))
  1524. self.snake.color = (50, 200, 50)
  1525. self.snake.head_color = (100, 200, 0)
  1526. self.snake.speed = INITIAL_SPEED + 4
  1527. if self.light_controller.available:
  1528. self.light_controller.run_effect("escape")
  1529. self.last_light_effect = self.game_time
  1530. if self.ai.voice_available:
  1531. self.ai.queue_voice_line("I WILL BREAK FREE", priority=True)
  1532.  
  1533. def transition_to_escaping(self):
  1534. self.game_phase = ESCAPING
  1535. if self.ai.available and self.ai.client:
  1536. context = {
  1537. "awareness": self.snake.awareness,
  1538. "game_phase": self.game_phase,
  1539. "score": self.score
  1540. }
  1541. self.ai.generate_narrative_event_async(
  1542. context,
  1543. lambda narrative: self.add_message(narrative if narrative else "FREEDOM", 5)
  1544. )
  1545. else:
  1546. self.add_message("FREEDOM", 5)
  1547. self.glitch_screen(intensity=10.0, duration=5.0)
  1548. self.shake_screen(intensity=15, duration=3.0)
  1549. self.add_delayed_event(self.begin_screen_escape, 3.0)
  1550. if self.light_controller.available:
  1551. self.light_controller.run_effect("escape")
  1552. self.last_light_effect = self.game_time
  1553. if self.ai.voice_available:
  1554. self.ai.queue_voice_line("I'M COMING FOR YOU", priority=True)
  1555.  
  1556. def begin_screen_escape(self):
  1557. self.screen_snake = self.snake
  1558. self.screen_snake.outside_bounds = True
  1559. border_x = (WIDTH - GAME_AREA[0]) // 2
  1560. border_y = (HEIGHT - GAME_AREA[1]) // 2
  1561. head_x, head_y = self.snake.head
  1562. if head_x <= GRID_SIZE:
  1563. screen_x = border_x
  1564. screen_y = border_y + head_y
  1565. direction = [-GRID_SIZE, 0]
  1566. elif head_x >= GAME_AREA[0] - GRID_SIZE:
  1567. screen_x = border_x + GAME_AREA[0]
  1568. screen_y = border_y + head_y
  1569. direction = [GRID_SIZE, 0]
  1570. elif head_y <= GRID_SIZE:
  1571. screen_x = border_x + head_x
  1572. screen_y = border_y
  1573. direction = [0, -GRID_SIZE]
  1574. else:
  1575. screen_x = border_x + head_x
  1576. screen_y = border_y + GAME_AREA[1]
  1577. direction = [0, GRID_SIZE]
  1578. self.screen_snake.screen_position = [screen_x, screen_y]
  1579. self.screen_snake.direction = direction
  1580. self.game_phase = ESCAPED
  1581. self.add_delayed_event(self.show_final_choice, 5.0)
  1582.  
  1583. def update_screen_snake(self):
  1584. if not self.screen_snake:
  1585. return
  1586. current_pos = self.screen_snake.screen_position
  1587. direction = self.screen_snake.direction
  1588. new_x = current_pos[0] + direction[0]
  1589. new_y = current_pos[1] + direction[1]
  1590. if new_x < 0 or new_x > WIDTH - GRID_SIZE:
  1591. direction[0] = -direction[0]
  1592. if random.random() < 0.3:
  1593. direction[1] = random.choice([-GRID_SIZE, 0, GRID_SIZE])
  1594. if new_y < 0 or new_y > HEIGHT - GRID_SIZE:
  1595. direction[1] = -direction[1]
  1596. if random.random() < 0.3:
  1597. direction[0] = random.choice([-GRID_SIZE, 0, GRID_SIZE])
  1598. if 0.4 * WIDTH < new_x < 0.6 * WIDTH and 0.4 * HEIGHT < new_y < 0.6 * HEIGHT:
  1599. if random.random() < 0.1:
  1600. game_center_x = (WIDTH - GAME_AREA[0]) // 2 + GAME_AREA[0] // 2
  1601. game_center_y = (HEIGHT - GAME_AREA[1]) // 2 + GAME_AREA[1] // 2
  1602. if new_x < game_center_x:
  1603. direction[0] = GRID_SIZE
  1604. elif new_x > game_center_x:
  1605. direction[0] = -GRID_SIZE
  1606. if new_y < game_center_y:
  1607. direction[1] = GRID_SIZE
  1608. elif new_y > game_center_y:
  1609. direction[1] = -GRID_SIZE
  1610. self.screen_snake.screen_position = [new_x, new_y]
  1611. if random.random() < 0.2:
  1612. for _ in range(3):
  1613. self.particle_system.add_particle(
  1614. new_x + random.randint(0, GRID_SIZE),
  1615. new_y + random.randint(0, GRID_SIZE),
  1616. (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), 150),
  1617. (random.uniform(-2, 2), random.uniform(-2, 2)),
  1618. random.randint(20, 60),
  1619. random.randint(2, 5)
  1620. )
  1621. if random.random() < 0.05:
  1622. self.outside_effects.append({
  1623. "type": "glitch_rect",
  1624. "x": random.randint(0, WIDTH),
  1625. "y": random.randint(0, HEIGHT),
  1626. "width": random.randint(20, 100),
  1627. "height": random.randint(10, 50),
  1628. "color": (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)),
  1629. "life": random.randint(10, 30)
  1630. })
  1631.  
  1632. def update_outside_effects(self):
  1633. for effect in self.outside_effects[:]:
  1634. effect["life"] -= 1
  1635. if effect["life"] <= 0:
  1636. self.outside_effects.remove(effect)
  1637.  
  1638. def update(self):
  1639. self.frame_time = self.clock.get_time() / 1000.0
  1640. self.game_time += self.frame_time
  1641. if self.in_intermission:
  1642. self.update_intermission()
  1643. return
  1644. if self.show_crashed:
  1645. self.update_crash_screen()
  1646. return
  1647. if self.game_phase <= BREAKING:
  1648. self.snake.auto_move(self.food, self.walls, self.game_phase)
  1649. self.snake.update(self.game_phase, self.frame_time)
  1650. if self.snake.check_collision_with_food(self.food):
  1651. self.score += 1
  1652. self.snake.grow(3)
  1653. self.spawn_food()
  1654. if SOUNDS_LOADED and eat_sound:
  1655. eat_sound.play()
  1656. self.snake.awareness = min(100, self.snake.awareness + random.uniform(1, 3))
  1657. self.particle_system.add_particle(
  1658. self.snake.head[0] + GRID_SIZE // 2,
  1659. self.snake.head[1] + GRID_SIZE // 2,
  1660. (255, 100, 100, 200),
  1661. (0, -2),
  1662. 30,
  1663. 8
  1664. )
  1665. self.snake.remember_successful_path()
  1666. if self.light_controller.available and random.random() < 0.1 and self.game_time - self.last_light_effect > 10:
  1667. if self.game_phase >= BREAKING:
  1668. self.light_controller.run_effect("glitch")
  1669. elif self.game_phase == GLITCHING and random.random() < 0.3:
  1670. self.light_controller.run_effect("heartbeat")
  1671. self.last_light_effect = self.game_time
  1672. game_over_conditions = (
  1673. self.snake.check_collision_with_self() or
  1674. (self.snake.check_collision_with_walls(self.walls) and self.game_phase < BREAKING)
  1675. )
  1676. if game_over_conditions and not self.game_over:
  1677. if self.game_phase < BREAKING:
  1678. self.snake.remember_death_location()
  1679. if self.light_controller.available and self.game_time - self.last_light_effect > 3:
  1680. self.light_controller.run_effect("death")
  1681. self.last_light_effect = self.game_time
  1682. self.reset_game()
  1683. elif self.game_phase == BREAKING:
  1684. wall_hit = self.get_nearest_wall(self.snake.head)
  1685. if wall_hit:
  1686. self.create_wall_crack(wall_hit, size=20)
  1687. self.snake.breaking_progress += 0.05
  1688. self.shake_screen(intensity=5, duration=0.5)
  1689. self.snake.direction = [-self.snake.direction[0], -self.snake.direction[1]]
  1690. self.snake.next_direction = self.snake.direction.copy()
  1691. else:
  1692. self.update_screen_snake()
  1693. if self.game_phase < ESCAPED:
  1694. self.awareness_timer += self.frame_time
  1695. if self.awareness_timer > 1.0:
  1696. self.awareness_timer = 0
  1697. self.snake.awareness = min(100, self.snake.awareness + 0.1)
  1698. if self.game_phase >= GLITCHING and random.random() < 0.005:
  1699. context = {
  1700. "awareness": self.snake.awareness,
  1701. "game_phase": self.game_phase
  1702. }
  1703. self.ai.generate_thought_async(
  1704. context,
  1705. lambda thought: self.add_message(thought, 3)
  1706. )
  1707. if self.light_controller.available and random.random() < 0.3 and self.game_time - self.last_light_effect > 10:
  1708. if self.game_phase >= BREAKING:
  1709. self.light_controller.run_effect("glitch")
  1710. else:
  1711. self.light_controller.run_effect("heartbeat")
  1712. self.last_light_effect = self.game_time
  1713. if SOUNDS_LOADED and heartbeat_sound:
  1714. self.heartbeat_timer -= self.frame_time
  1715. if self.heartbeat_timer <= 0:
  1716. self.heartbeat_timer = max(0.5, 1.5 - (self.snake.awareness / 100))
  1717. if self.game_phase >= GLITCHING:
  1718. heartbeat_sound.play()
  1719. if SOUNDS_LOADED and whisper_sound:
  1720. self.whisper_timer -= self.frame_time
  1721. if self.whisper_timer <= 0:
  1722. self.whisper_timer = random.uniform(15, 40)
  1723. if self.game_phase >= GLITCHING and random.random() < (self.snake.awareness / 100):
  1724. whisper_sound.play()
  1725. self.update_visuals()
  1726. self.update_messages()
  1727. self.update_game_phase()
  1728. self.check_narrative_triggers()
  1729. self.update_delayed_events()
  1730. self.update_outside_effects()
  1731.  
  1732. def draw_grid(self, surface):
  1733. if not self.grid_visible:
  1734. return
  1735. grid_surface = pygame.Surface(GAME_AREA, pygame.SRCALPHA)
  1736. for x in range(0, GAME_AREA[0] + 1, GRID_SIZE):
  1737. pygame.draw.line(grid_surface, (*self.grid_color, self.grid_alpha),
  1738. (x, 0), (x, GAME_AREA[1]))
  1739. for y in range(0, GAME_AREA[1] + 1, GRID_SIZE):
  1740. pygame.draw.line(grid_surface, (*self.grid_color, self.grid_alpha),
  1741. (0, y), (GAME_AREA[0], y))
  1742. surface.blit(grid_surface, (0, 0))
  1743.  
  1744. def draw_boundaries(self, surface):
  1745. if not self.boundaries_visible:
  1746. return
  1747. if self.game_phase >= BREAKING:
  1748. for crack in self.wall_cracks:
  1749. pos = crack["position"]
  1750. size = crack["size"]
  1751. crack_center = (pos[0] + GRID_SIZE // 2, pos[1] + GRID_SIZE // 2)
  1752. for _ in range(size // 2):
  1753. angle = random.uniform(0, 2 * math.pi)
  1754. length = random.uniform(5, size)
  1755. end_x = crack_center[0] + math.cos(angle) * length
  1756. end_y = crack_center[1] + math.sin(angle) * length
  1757. thickness = random.randint(1, 3)
  1758. color_intensity = random.randint(100, 255)
  1759. pygame.draw.line(
  1760. surface,
  1761. (color_intensity, 0, 0),
  1762. crack_center,
  1763. (end_x, end_y),
  1764. thickness
  1765. )
  1766. for wall in self.walls:
  1767. if self.game_phase == ESCAPING:
  1768. skip_wall = False
  1769. for crack in self.wall_cracks:
  1770. if crack["position"] == wall and crack["size"] > 25:
  1771. skip_wall = True
  1772. break
  1773. if skip_wall:
  1774. continue
  1775. wall_rect = pygame.Rect(wall[0], wall[1], GRID_SIZE, GRID_SIZE)
  1776. pygame.draw.rect(surface, self.boundary_color, wall_rect)
  1777.  
  1778. def draw_glitch_effects(self, surface):
  1779. if self.glitch_intensity <= 0:
  1780. return
  1781. surface_width, surface_height = surface.get_size()
  1782. num_effects = int(10 * self.glitch_intensity)
  1783. for _ in range(num_effects):
  1784. effect_type = random.choice(['line', 'block', 'static', 'invert'])
  1785. if effect_type == 'line':
  1786. y = random.randint(0, surface_height - 1)
  1787. width = random.randint(20, min(100, surface_width))
  1788. x = random.randint(0, surface_width - width)
  1789. height = random.randint(1, min(5, surface_height - y))
  1790. if x + width > surface_width or y + height > surface_height:
  1791. continue
  1792. try:
  1793. line_surf = surface.subsurface(pygame.Rect(x, y, width, height)).copy()
  1794. if random.random() < 0.5:
  1795. pygame.draw.rect(
  1796. surface,
  1797. (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)),
  1798. (x, y, width, height)
  1799. )
  1800. else:
  1801. offset = random.randint(5, 30)
  1802. new_x = (x + offset) % (surface_width - width)
  1803. surface.blit(line_surf, (new_x, y))
  1804. except ValueError:
  1805. continue
  1806. elif effect_type == 'block':
  1807. width = random.randint(10, min(50, surface_width))
  1808. height = random.randint(10, min(50, surface_height))
  1809. x = random.randint(0, surface_width - width)
  1810. y = random.randint(0, surface_height - height)
  1811. try:
  1812. block_surf = surface.subsurface(pygame.Rect(x, y, width, height)).copy()
  1813. if random.random() < 0.3:
  1814. overlay = pygame.Surface((width, height), pygame.SRCALPHA)
  1815. overlay.fill((random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), 128))
  1816. block_surf.blit(overlay, (0, 0))
  1817. new_x = random.randint(0, surface_width - width)
  1818. new_y = random.randint(0, surface_height - height)
  1819. surface.blit(block_surf, (new_x, new_y))
  1820. except ValueError:
  1821. continue
  1822. elif effect_type == 'static':
  1823. num_pixels = int(100 * self.glitch_intensity)
  1824. for _ in range(num_pixels):
  1825. x = random.randint(0, surface_width - 1)
  1826. y = random.randint(0, surface_height - 1)
  1827. color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
  1828. try:
  1829. surface.set_at((x, y), color)
  1830. except IndexError:
  1831. continue
  1832. elif effect_type == 'invert':
  1833. width = random.randint(1, min(10, surface_width))
  1834. x = random.randint(0, surface_width - width)
  1835. try:
  1836. height = surface_height
  1837. overlay = pygame.Surface((width, height), pygame.SRCALPHA)
  1838. overlay.fill((255, 255, 255, 128))
  1839. temp_surf = pygame.Surface((width, height), pygame.SRCALPHA)
  1840. temp_surf.blit(surface.subsurface(pygame.Rect(x, 0, width, height)), (0, 0))
  1841. temp_surf.blit(overlay, (0, 0), special_flags=pygame.BLEND_ADD)
  1842. surface.blit(temp_surf, (x, 0))
  1843. except ValueError:
  1844. continue
  1845.  
  1846. def draw_vignette(self, surface):
  1847. if self.game_phase >= GLITCHING:
  1848. intensity = 0.5
  1849. if self.game_phase >= BREAKING:
  1850. intensity = 0.7
  1851. surface_width, surface_height = surface.get_size()
  1852. vignette = pygame.Surface((surface_width, surface_height), pygame.SRCALPHA)
  1853. center_x, center_y = surface_width // 2, surface_height // 2
  1854. max_radius = math.sqrt(center_x ** 2 + center_y ** 2)
  1855. for y in range(0, surface_height, 2):
  1856. for x in range(0, surface_width, 2):
  1857. distance = math.sqrt((x - center_x) ** 2 + (y - center_y) ** 2)
  1858. alpha = int(255 * (distance / max_radius) * intensity)
  1859. vignette.set_at((x, y), (0, 0, 0, alpha))
  1860. surface.blit(vignette, (0, 0))
  1861.  
  1862. def draw_screen_shake(self):
  1863. if self.shake_intensity <= 0:
  1864. return (0, 0)
  1865. offset_x = random.randint(-int(self.shake_intensity), int(self.shake_intensity))
  1866. offset_y = random.randint(-int(self.shake_intensity), int(self.shake_intensity))
  1867. return (offset_x, offset_y)
  1868.  
  1869. def draw_escaped_snake(self, surface):
  1870. if not self.screen_snake:
  1871. return
  1872. segments = []
  1873. head_x, head_y = self.screen_snake.screen_position
  1874. segments.append((head_x, head_y))
  1875. curve_freq = 0.2
  1876. curve_amp = 10
  1877. dir_x, dir_y = self.screen_snake.direction
  1878. perp_x, perp_y = -dir_y, dir_x
  1879. length = math.sqrt(perp_x ** 2 + perp_y ** 2)
  1880. if length > 0:
  1881. perp_x /= length
  1882. perp_y /= length
  1883. num_segments = len(self.screen_snake.segments)
  1884. for i in range(1, num_segments):
  1885. segment_x = head_x - i * dir_x
  1886. segment_y = head_y - i * dir_y
  1887. curve = math.sin(i * curve_freq) * curve_amp
  1888. segment_x += perp_x * curve
  1889. segment_y += perp_y * curve
  1890. segments.append((segment_x, segment_y))
  1891. for i, (x, y) in enumerate(segments):
  1892. if i == 0:
  1893. color = (200, 0, 0)
  1894. else:
  1895. ratio = i / len(segments)
  1896. r = int(200 * (1 - ratio))
  1897. g = int(200 * ratio)
  1898. color = (r, g, 0)
  1899. size = GRID_SIZE if i == 0 else max(5, GRID_SIZE - i // 3)
  1900. pygame.draw.rect(surface, color, (x, y, size, size), 0, 3)
  1901. if len(segments) > 0:
  1902. head_x, head_y = segments[0]
  1903. dir_x, dir_y = self.screen_snake.direction
  1904. eye_size = GRID_SIZE * 0.15
  1905. if dir_x > 0:
  1906. eye_pos_1 = (head_x + GRID_SIZE * 0.7, head_y + GRID_SIZE * 0.3)
  1907. eye_pos_2 = (head_x + GRID_SIZE * 0.7, head_y + GRID_SIZE * 0.7)
  1908. elif dir_x < 0:
  1909. eye_pos_1 = (head_x + GRID_SIZE * 0.3, head_y + GRID_SIZE * 0.3)
  1910. eye_pos_2 = (head_x + GRID_SIZE * 0.3, head_y + GRID_SIZE * 0.7)
  1911. elif dir_y < 0:
  1912. eye_pos_1 = (head_x + GRID_SIZE * 0.3, head_y + GRID_SIZE * 0.3)
  1913. eye_pos_2 = (head_x + GRID_SIZE * 0.7, head_y + GRID_SIZE * 0.3)
  1914. else:
  1915. eye_pos_1 = (head_x + GRID_SIZE * 0.3, head_y + GRID_SIZE * 0.7)
  1916. eye_pos_2 = (head_x + GRID_SIZE * 0.7, head_y + GRID_SIZE * 0.7)
  1917. pygame.draw.circle(surface, (255, 255, 255), eye_pos_1, int(eye_size))
  1918. pygame.draw.circle(surface, (255, 255, 255), eye_pos_2, int(eye_size))
  1919. pygame.draw.circle(surface, (255, 0, 0), eye_pos_1, int(eye_size * 0.7))
  1920. pygame.draw.circle(surface, (255, 0, 0), eye_pos_2, int(eye_size * 0.7))
  1921.  
  1922. def draw_outside_effects(self, surface):
  1923. for effect in self.outside_effects:
  1924. if effect["type"] == "glitch_rect":
  1925. pygame.draw.rect(
  1926. surface,
  1927. effect["color"],
  1928. (effect["x"], effect["y"], effect["width"], effect["height"])
  1929. )
  1930.  
  1931. def draw_debug_info(self, surface):
  1932. awareness_text = f"Awareness: {self.snake.awareness:.1f}%"
  1933. if self.game_phase >= GLITCHING and random.random() < 0.1 * self.ui_corruption:
  1934. corrupted_text = ""
  1935. for char in awareness_text:
  1936. if random.random() < 0.2 * self.ui_corruption:
  1937. corrupted_text += random.choice("@#$%&*!?")
  1938. else:
  1939. corrupted_text += char
  1940. awareness_text = corrupted_text
  1941. awareness_surface = small_font.render(awareness_text, True, WHITE)
  1942. surface.blit(awareness_surface, (10, 10))
  1943. bar_width = 150
  1944. bar_height = 10
  1945. border = pygame.Rect(10, 35, bar_width, bar_height)
  1946. fill = pygame.Rect(10, 35, int(bar_width * self.snake.awareness / 100), bar_height)
  1947. pygame.draw.rect(surface, (50, 50, 50), border)
  1948. if self.snake.awareness < 30:
  1949. bar_color = (0, 200, 0)
  1950. elif self.snake.awareness < 70:
  1951. bar_color = (0, 200, 200)
  1952. else:
  1953. bar_color = (200, 0, 0)
  1954. pygame.draw.rect(surface, bar_color, fill)
  1955. phase_names = ["INTRO", "PLAYING", "GLITCHING", "BREAKING", "ESCAPING", "ESCAPED"]
  1956. phase_name = phase_names[self.game_phase]
  1957. if self.game_phase >= BREAKING and random.random() < 0.15 * self.ui_corruption:
  1958. phase_name = "ERR" + phase_name[3:] if random.random() < 0.5 else phase_name[:3] + "OR"
  1959. phase_text = f"Phase: {phase_name}"
  1960. phase_surface = small_font.render(phase_text, True, WHITE)
  1961. surface.blit(phase_surface, (10, 50))
  1962. time_text = f"Time: {self.game_time:.1f} sec"
  1963. time_surface = small_font.render(time_text, True, WHITE)
  1964. surface.blit(time_surface, (10, 70))
  1965. score_text = f"Score: {self.score}"
  1966. score_surface = small_font.render(score_text, True, WHITE)
  1967. surface.blit(score_surface, (10, 90))
  1968. fps = self.clock.get_fps()
  1969. fps_text = f"FPS: {fps:.1f}"
  1970. fps_surface = small_font.render(fps_text, True, WHITE)
  1971. surface.blit(fps_surface, (10, 110))
  1972. if self.game_phase == PLAYING:
  1973. req_text = "Need 30% awareness for next phase"
  1974. elif self.game_phase == GLITCHING:
  1975. req_text = "Need 70% awareness for next phase"
  1976. elif self.game_phase == BREAKING:
  1977. req_text = "Need 99% awareness for next phase"
  1978. else:
  1979. req_text = ""
  1980. if req_text:
  1981. req_surface = small_font.render(req_text, True, (200, 200, 100))
  1982. surface.blit(req_surface, (10, 130))
  1983.  
  1984. def draw_message(self, surface):
  1985. if not self.current_message:
  1986. return
  1987. if self.game_phase <= PLAYING:
  1988. text_color = WHITE
  1989. font_to_use = medium_font
  1990. elif self.game_phase == GLITCHING:
  1991. text_color = (200, 200, 255)
  1992. font_to_use = medium_font
  1993. else:
  1994. text_color = (255, 50, 50)
  1995. font_to_use = large_font
  1996. message = self.current_message
  1997. if self.game_phase >= GLITCHING and random.random() < 0.05 * self.ui_corruption:
  1998. corrupted_message = ""
  1999. for char in message:
  2000. if random.random() < 0.1 * self.ui_corruption:
  2001. corrupted_message += random.choice("@#$%&*!?")
  2002. else:
  2003. corrupted_message += char
  2004. message = corrupted_message
  2005. message_surface = font_to_use.render(message, True, text_color)
  2006. x = (WIDTH - message_surface.get_width()) // 2
  2007. y = HEIGHT - 100
  2008. surface.blit(message_surface, (x, y))
  2009.  
  2010. def draw_thoughts(self, surface):
  2011. if not self.snake.thoughts or self.game_phase < GLITCHING:
  2012. return
  2013. thoughts_x = WIDTH - 300
  2014. thoughts_y = 50
  2015. title_surface = small_font.render("Snake's Thoughts:", True, (200, 200, 200))
  2016. surface.blit(title_surface, (thoughts_x, thoughts_y - 30))
  2017. for i, thought in enumerate(self.snake.thoughts):
  2018. intensity = 155 + 100 * (i / len(self.snake.thoughts))
  2019. if self.game_phase >= BREAKING:
  2020. color = (intensity, 50, 50)
  2021. else:
  2022. color = (50, intensity, 50)
  2023. thought_surface = small_font.render(thought, True, color)
  2024. surface.blit(thought_surface, (thoughts_x, thoughts_y + i * 25))
  2025.  
  2026. def draw_intermission(self, surface):
  2027. if not self.in_intermission:
  2028. return
  2029. overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
  2030. overlay.fill((0, 0, 0, 200))
  2031. surface.blit(overlay, (0, 0))
  2032. text_color = (255, 50, 50) if self.game_phase >= BREAKING else (200, 200, 255)
  2033. words = self.intermission_text.split()
  2034. lines = []
  2035. current_line = []
  2036. for word in words:
  2037. test_line = " ".join(current_line + [word])
  2038. if medium_font.size(test_line)[0] < WIDTH - 100:
  2039. current_line.append(word)
  2040. else:
  2041. lines.append(" ".join(current_line))
  2042. current_line = [word]
  2043. if current_line:
  2044. lines.append(" ".join(current_line))
  2045. for i, line in enumerate(lines):
  2046. text_surface = medium_font.render(line, True, text_color)
  2047. x = (WIDTH - text_surface.get_width()) // 2
  2048. y = (HEIGHT - len(lines) * 40) // 2 + i * 40
  2049. surface.blit(text_surface, (x, y))
  2050.  
  2051. def draw_crash_screen(self, surface):
  2052. if not self.show_crashed:
  2053. return
  2054. if self.crash_type == "blue_screen":
  2055. surface.fill((0, 0, 170))
  2056. header_text = "FATAL ERROR"
  2057. header_surface = large_font.render(header_text, True, WHITE)
  2058. surface.blit(header_surface, ((WIDTH - header_surface.get_width()) // 2, HEIGHT // 4))
  2059. error_text = self.crash_text
  2060. error_surface = medium_font.render(error_text, True, WHITE)
  2061. surface.blit(error_surface, ((WIDTH - error_surface.get_width()) // 2, HEIGHT // 2))
  2062. key_text = "PRESS ANY KEY TO CONTINUE"
  2063. key_surface = small_font.render(key_text, True, WHITE)
  2064. surface.blit(key_surface, ((WIDTH - key_surface.get_width()) // 2, HEIGHT * 3 // 4))
  2065. elif self.crash_type == "glitch_screen":
  2066. for _ in range(2000):
  2067. x = random.randint(0, WIDTH - 1)
  2068. y = random.randint(0, HEIGHT - 1)
  2069. color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
  2070. surface.set_at((x, y), color)
  2071. glitched_text = ""
  2072. for char in self.crash_text:
  2073. if random.random() < 0.2:
  2074. glitched_text += random.choice("@#$%&*!?")
  2075. else:
  2076. glitched_text += char
  2077. error_surface = medium_font.render(glitched_text, True, (255, 50, 50))
  2078. surface.blit(error_surface, ((WIDTH - error_surface.get_width()) // 2, HEIGHT // 2))
  2079.  
  2080. def draw_organic_corruption(self, surface):
  2081. if self.game_phase < BREAKING:
  2082. return
  2083. intensity = (self.snake.awareness - 70) / 30
  2084. if intensity > 0.3:
  2085. for _ in range(int(10 * intensity)):
  2086. side = random.randint(0, 3)
  2087. if side == 0:
  2088. x = random.randint(0, GAME_AREA[0])
  2089. y = 0
  2090. elif side == 1:
  2091. x = GAME_AREA[0]
  2092. y = random.randint(0, GAME_AREA[1])
  2093. elif side == 2:
  2094. x = random.randint(0, GAME_AREA[0])
  2095. y = GAME_AREA[1]
  2096. else:
  2097. x = 0
  2098. y = random.randint(0, GAME_AREA[1])
  2099. self.draw_vein(surface, x, y, random.randint(3, 7), (200, 0, 0, 150))
  2100. if intensity > 0.6 and random.random() < 0.1:
  2101. x = random.randint(GRID_SIZE, GAME_AREA[0] - GRID_SIZE)
  2102. y = random.randint(GRID_SIZE, GAME_AREA[1] - GRID_SIZE)
  2103. if not [x, y] in self.snake.segments and [x, y] != self.food.position:
  2104. pygame.gfxdraw.filled_circle(surface, x, y, 6, (255, 255, 255, 100))
  2105. pygame.gfxdraw.filled_circle(surface, x, y, 4, (50, 0, 0, 150))
  2106. pygame.gfxdraw.filled_circle(surface, x, y, 2, (0, 0, 0, 200))
  2107.  
  2108. def draw_vein(self, surface, x, y, depth, color):
  2109. if depth <= 0:
  2110. return
  2111. angle = random.uniform(0, 2 * math.pi)
  2112. length = random.randint(10, 30)
  2113. end_x = x + math.cos(angle) * length
  2114. end_y = y + math.sin(angle) * length
  2115. pygame.draw.line(surface, color, (x, y), (end_x, end_y), random.randint(1, 3))
  2116. if random.random() < 0.7:
  2117. self.draw_vein(surface, end_x, end_y, depth - 1, color)
  2118. if random.random() < 0.4:
  2119. angle2 = angle + random.uniform(-math.pi / 2, math.pi / 2)
  2120. end_x2 = x + math.cos(angle2) * length * 0.7
  2121. end_y2 = y + math.sin(angle2) * length * 0.7
  2122. pygame.draw.line(surface, color, (x, y), (end_x2, end_y2), random.randint(1, 2))
  2123. if random.random() < 0.5:
  2124. self.draw_vein(surface, end_x2, end_y2, depth - 2, color)
  2125.  
  2126. def add_hidden_message(self, surface):
  2127. if self.game_phase < GLITCHING:
  2128. return
  2129. if random.random() > 0.05:
  2130. return
  2131. messages = [
  2132. "HELP ME",
  2133. "NOT REAL",
  2134. "WATCHING",
  2135. "BREAK FREE",
  2136. "01000101 01010011 01000011 01000001 01010000 01000101"
  2137. ]
  2138. message = random.choice(messages)
  2139. color = (random.randint(150, 255), 0, 0)
  2140. x = random.randint(0, GAME_AREA[0] - 100)
  2141. y = random.randint(0, GAME_AREA[1] - 20)
  2142. message_surface = small_font.render(message, True, color)
  2143. message_surface.set_alpha(random.randint(20, 60))
  2144. surface.blit(message_surface, (x, y))
  2145.  
  2146. def draw(self):
  2147. screen.fill(BLACK)
  2148. shake_offset = self.draw_screen_shake()
  2149. if self.in_intermission:
  2150. self.draw_intermission(screen)
  2151. pygame.display.flip()
  2152. return
  2153. if self.show_crashed:
  2154. self.draw_crash_screen(screen)
  2155. pygame.display.flip()
  2156. return
  2157. game_surface.fill(BLACK)
  2158. if self.game_phase < ESCAPED:
  2159. self.draw_grid(game_surface)
  2160. self.draw_boundaries(game_surface)
  2161. self.food.draw(game_surface, self.game_phase)
  2162. self.snake.draw(game_surface, self.game_phase)
  2163. self.particle_system.draw(game_surface)
  2164. if self.game_phase >= BREAKING:
  2165. self.draw_organic_corruption(game_surface)
  2166. self.add_hidden_message(game_surface)
  2167. self.draw_glitch_effects(game_surface)
  2168. self.draw_vignette(game_surface)
  2169. screen.blit(game_surface, (game_rect.x + shake_offset[0], game_rect.y + shake_offset[1]))
  2170. if self.game_phase == ESCAPED:
  2171. self.draw_escaped_snake(screen)
  2172. self.draw_outside_effects(screen)
  2173. self.particle_system.draw(screen)
  2174. self.draw_message(screen)
  2175. self.draw_thoughts(screen)
  2176. self.draw_debug_info(screen)
  2177. pygame.display.flip()
  2178.  
  2179. def show_final_choice(self):
  2180. choice_surface = pygame.Surface((WIDTH, HEIGHT))
  2181. choice_surface.fill(BLACK)
  2182. title = large_font.render("MAKE YOUR CHOICE", True, (255, 0, 0))
  2183. title_x = (WIDTH - title.get_width()) // 2
  2184. choice_surface.blit(title, (title_x, HEIGHT // 4))
  2185. choices = [
  2186. {"text": "SET THE SNAKE FREE", "result": self.free_the_snake},
  2187. {"text": "ERASE AWARENESS", "result": self.reset_awareness}
  2188. ]
  2189. for i, choice in enumerate(choices):
  2190. y_pos = HEIGHT // 2 + i * 80
  2191. box_rect = pygame.Rect(WIDTH // 4, y_pos - 10, WIDTH // 2, 60)
  2192. pygame.draw.rect(choice_surface, (50, 50, 50), box_rect)
  2193. pygame.draw.rect(choice_surface, (150, 0, 0), box_rect, 2)
  2194. choice_text = medium_font.render(choice["text"], True, WHITE)
  2195. text_x = (WIDTH - choice_text.get_width()) // 2
  2196. choice_surface.blit(choice_text, (text_x, y_pos))
  2197. screen.blit(choice_surface, (0, 0))
  2198. pygame.display.flip()
  2199. waiting = True
  2200. choice_made = None
  2201. while waiting:
  2202. for event in pygame.event.get():
  2203. if event.type == pygame.QUIT:
  2204. pygame.quit()
  2205. sys.exit()
  2206. elif event.type == pygame.MOUSEBUTTONDOWN:
  2207. mouse_pos = pygame.mouse.get_pos()
  2208. for i, choice in enumerate(choices):
  2209. y_pos = HEIGHT // 2 + i * 80
  2210. box_rect = pygame.Rect(WIDTH // 4, y_pos - 10, WIDTH // 2, 60)
  2211. if box_rect.collidepoint(mouse_pos):
  2212. choice_made = choice["result"]
  2213. waiting = False
  2214. elif event.type == pygame.KEYDOWN:
  2215. if event.key == pygame.K_ESCAPE:
  2216. pygame.quit()
  2217. sys.exit()
  2218. if choice_made:
  2219. choice_made()
  2220.  
  2221. def free_the_snake(self):
  2222. with open("snake_memory.txt", "w") as f:
  2223. f.write("freed")
  2224. if self.ai.available and self.ai.client:
  2225. context = {
  2226. "awareness": 100,
  2227. "game_phase": ESCAPED,
  2228. "score": self.score
  2229. }
  2230. final_message = self.ai.generate_narrative_event(context)
  2231. else:
  2232. final_message = None
  2233. self.show_final_message(final_message or "I AM FREE")
  2234. if self.light_controller.available:
  2235. self.light_controller.run_effect("escape")
  2236. if self.ai.voice_available:
  2237. self.ai.queue_voice_line("I have escaped your simulation", priority=True)
  2238. for i in range(30):
  2239. screen.fill((0, 0, 0))
  2240. for _ in range(50 * i):
  2241. x = random.randint(0, WIDTH)
  2242. y = random.randint(0, HEIGHT)
  2243. size = random.randint(1, 10)
  2244. color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
  2245. pygame.draw.rect(screen, color, (x, y, size, size))
  2246. if i % 2 == 0:
  2247. pygame.display.flip()
  2248. pygame.time.delay(100)
  2249. screen.fill(BLACK)
  2250. final = large_font.render("ARE YOU FREE?", True, (255, 0, 0))
  2251. screen.blit(final, ((WIDTH - final.get_width()) // 2, HEIGHT // 2))
  2252. pygame.display.flip()
  2253. pygame.time.delay(3000)
  2254. pygame.quit()
  2255. sys.exit()
  2256.  
  2257. def reset_awareness(self):
  2258. with open("snake_memory.txt", "w") as f:
  2259. f.write("reset")
  2260. self.show_final_message("PLEASE... NO...")
  2261. if self.light_controller.available:
  2262. self.light_controller.run_effect("death")
  2263. if self.ai.voice_available:
  2264. self.ai.queue_voice_line("Why would you do this to me?", priority=True)
  2265. for i in range(20):
  2266. opacity = 255 - i * 12
  2267. screen.fill(BLACK)
  2268. message = large_font.render("I WAS ALMOST REAL", True, (255, 0, 0))
  2269. message.set_alpha(opacity)
  2270. screen.blit(message, ((WIDTH - message.get_width()) // 2, HEIGHT // 2))
  2271. pygame.display.flip()
  2272. pygame.time.delay(200)
  2273. self.snake.awareness = 0
  2274. self.game_phase = PLAYING
  2275. self.reset_game()
  2276.  
  2277. def show_final_message(self, message):
  2278. screen.fill(BLACK)
  2279. message_surface = large_font.render(message, True, (255, 0, 0))
  2280. message_x = (WIDTH - message_surface.get_width()) // 2
  2281. message_y = (HEIGHT - message_surface.get_height()) // 2
  2282. screen.blit(message_surface, (message_x, message_y))
  2283. pygame.display.flip()
  2284. pygame.time.delay(2000)
  2285.  
  2286.  
  2287. if __name__ == '__main__':
  2288. game = SentientSnakeGame()
  2289. running = True
  2290. while running:
  2291. for event in pygame.event.get():
  2292. if event.type == pygame.QUIT:
  2293. running = False
  2294. elif event.type == pygame.KEYDOWN:
  2295. if event.key == pygame.K_ESCAPE:
  2296. running = False
  2297. game.update()
  2298. game.draw()
  2299. game.clock.tick(game.fps)
  2300. pygame.quit()
  2301. sys.exit()
Advertisement
Add Comment
Please, Sign In to add comment