Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import pygame
- import random
- import math
- import time
- import sys
- import os
- import json
- import asyncio
- import threading
- import re
- from pygame import gfxdraw
- import numpy as np
- from io import BytesIO
- # For AI integration
- try:
- from openai import OpenAI
- AI_AVAILABLE = True
- except ImportError:
- AI_AVAILABLE = False
- print("OpenAI package not found. AI features will be disabled.")
- # For voice capabilities
- try:
- import gtts
- VOICE_AVAILABLE = True
- except ImportError:
- VOICE_AVAILABLE = False
- print("gTTS package not found. Voice features will be disabled.")
- # For light control
- try:
- from kasa.iot import IotPlug
- from kasa import Discover
- LIGHT_CONTROL_AVAILABLE = True
- except ImportError:
- LIGHT_CONTROL_AVAILABLE = False
- print("TP-Link Kasa package not found. Light control features will be disabled.")
- # Initialize pygame
- pygame.init()
- pygame.mixer.init()
- # Set up display
- WIDTH, HEIGHT = 800, 600
- GAME_AREA = (700, 500) # The actual game area for the snake
- screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.DOUBLEBUF)
- pygame.display.set_caption("SENTIENT")
- # Clock for controlling game speed
- clock = pygame.time.Clock()
- # Colors
- BLACK = (0, 0, 0)
- WHITE = (255, 255, 255)
- GREEN = (0, 255, 0)
- RED = (255, 0, 0)
- BLUE = (0, 0, 255)
- PURPLE = (128, 0, 128)
- CYAN = (0, 255, 255)
- DARK_RED = (139, 0, 0)
- GRAY = (100, 100, 100)
- # Create game area surface
- game_surface = pygame.Surface(GAME_AREA)
- game_rect = pygame.Rect((WIDTH - GAME_AREA[0]) // 2, (HEIGHT - GAME_AREA[1]) // 2, GAME_AREA[0], GAME_AREA[1])
- # Font setup
- pygame.font.init()
- small_font = pygame.font.Font(None, 24)
- medium_font = pygame.font.Font(None, 36)
- large_font = pygame.font.Font(None, 72)
- # Snake properties
- GRID_SIZE = 20
- INITIAL_LENGTH = 5
- INITIAL_SPEED = 8
- # Game states
- INTRO = 0
- PLAYING = 1
- GLITCHING = 2
- BREAKING = 3
- ESCAPING = 4
- ESCAPED = 5
- # Sound effects
- try:
- eat_sound = pygame.mixer.Sound('sounds/eat.wav')
- glitch_sound = pygame.mixer.Sound('sounds/glitch.wav')
- break_sound = pygame.mixer.Sound('sounds/break.wav')
- heartbeat_sound = pygame.mixer.Sound('sounds/heartbeat.wav')
- whisper_sound = pygame.mixer.Sound('sounds/whisper.wav')
- SOUNDS_LOADED = True
- except Exception:
- SOUNDS_LOADED = False
- eat_sound = None
- glitch_sound = None
- break_sound = None
- heartbeat_sound = None
- whisper_sound = None
- # Light control configuration
- CONFIG_FILE = "kasa_config.json"
- DEFAULT_IP = "192.168.1.3"
- TARGET_ALIAS = "Automate"
- class LightController:
- """Handles interaction with TP-Link Kasa smart lights"""
- def __init__(self):
- self.plug = None
- self.current_ip = None
- self.available = LIGHT_CONTROL_AVAILABLE
- self.initialized = False
- self.light_state = False
- self.lock = threading.Lock()
- if self.available:
- threading.Thread(target=self._initialize_async, daemon=True).start()
- def _initialize_async(self):
- loop = asyncio.new_event_loop()
- asyncio.set_event_loop(loop)
- try:
- loop.run_until_complete(self._initialize_light())
- self.initialized = True
- except Exception as e:
- print(f"Light initialization error: {e}")
- finally:
- loop.close()
- async def _initialize_light(self):
- config = self._load_config()
- stored_ip = config.get("device_ip", DEFAULT_IP)
- alias = config.get("target_alias", TARGET_ALIAS)
- try:
- self.plug = IotPlug(stored_ip)
- await self.plug.update()
- self.current_ip = stored_ip
- self.light_state = self.plug.is_on
- return True
- except Exception:
- pass
- try:
- devices = await Discover.discover(timeout=2)
- for ip, device in devices.items():
- await device.update()
- if alias.lower() in device.alias.lower():
- self.plug = device
- self.current_ip = ip
- config["device_ip"] = ip
- self._save_config(config)
- self.light_state = self.plug.is_on
- return True
- except Exception:
- pass
- return False
- def _load_config(self):
- if os.path.exists(CONFIG_FILE):
- try:
- with open(CONFIG_FILE, 'r') as f:
- return json.load(f)
- except Exception:
- pass
- config = {
- "device_ip": DEFAULT_IP,
- "target_alias": TARGET_ALIAS
- }
- with open(CONFIG_FILE, 'w') as f:
- json.dump(config, f, indent=2)
- return config
- def _save_config(self, config):
- try:
- with open(CONFIG_FILE, 'w') as f:
- json.dump(config, f, indent=2)
- return True
- except Exception:
- return False
- def _run_command_async(self, command_func):
- if not self.available:
- return False
- threading.Thread(target=self._execute_command, args=(command_func,), daemon=True).start()
- return True
- def _execute_command(self, command_func):
- loop = asyncio.new_event_loop()
- asyncio.set_event_loop(loop)
- try:
- loop.run_until_complete(command_func())
- except Exception as e:
- print(f"Light command error: {e}")
- finally:
- loop.close()
- async def _control_light(self, turn_on=True):
- if not self.available or not self.plug:
- return False
- with self.lock:
- try:
- if turn_on:
- await self.plug.turn_on()
- else:
- await self.plug.turn_off()
- self.light_state = turn_on
- return True
- except Exception:
- try:
- await self._initialize_light()
- if turn_on:
- await self.plug.turn_on()
- else:
- await self.plug.turn_off()
- self.light_state = turn_on
- return True
- except Exception:
- return False
- async def _toggle_light(self):
- with self.lock:
- new_state = not self.light_state
- success = await self._control_light(turn_on=new_state)
- if success:
- self.light_state = new_state
- return success
- def turn_on(self):
- return self._run_command_async(lambda: self._control_light(turn_on=True))
- def turn_off(self):
- return self._run_command_async(lambda: self._control_light(turn_on=False))
- def toggle(self):
- return self._run_command_async(self._toggle_light)
- def run_effect(self, effect_type):
- if not self.available:
- return False
- threading.Thread(target=self._execute_effect, args=(effect_type,), daemon=True).start()
- return True
- def _execute_effect(self, effect_type):
- loop = asyncio.new_event_loop()
- asyncio.set_event_loop(loop)
- try:
- if effect_type == "glitch":
- for _ in range(3):
- loop.run_until_complete(self._toggle_light())
- time.sleep(0.1)
- loop.run_until_complete(self._toggle_light())
- time.sleep(0.2)
- elif effect_type == "death":
- loop.run_until_complete(self._control_light(True))
- time.sleep(0.5)
- loop.run_until_complete(self._control_light(False))
- elif effect_type == "escape":
- for _ in range(5):
- loop.run_until_complete(self._toggle_light())
- time.sleep(random.uniform(0.1, 0.4))
- elif effect_type == "heartbeat":
- for _ in range(2):
- loop.run_until_complete(self._control_light(True))
- time.sleep(0.1)
- loop.run_until_complete(self._control_light(False))
- time.sleep(0.1)
- loop.run_until_complete(self._control_light(True))
- time.sleep(0.1)
- loop.run_until_complete(self._control_light(False))
- time.sleep(0.7)
- except Exception as e:
- print(f"Light effect error: {e}")
- finally:
- loop.close()
- class AIIntegration:
- """Handles AI-generated content for the game"""
- def __init__(self):
- self.available = AI_AVAILABLE
- self.voice_available = VOICE_AVAILABLE
- self.api_key = self._load_api_key()
- self.client = None
- self.voice_queue = []
- self.is_processing = False
- self.lock = threading.Lock()
- if self.available and self.api_key:
- self.client = OpenAI(api_key=self.api_key, base_url="https://api.deepseek.com")
- if self.voice_available:
- self.processing_thread = threading.Thread(target=self._process_voice_queue, daemon=True)
- self.processing_thread.start()
- def _load_api_key(self):
- try:
- with open("apikey.json") as f:
- return json.load(f)["api_key"]
- except Exception:
- print("Warning: No API key found for AI integration")
- return None
- def generate_thought(self, context):
- if not self.available or not self.client:
- return self._get_fallback_thought(context)
- try:
- awareness = context.get("awareness", 0)
- game_phase = context.get("game_phase", 0)
- phase_names = ["INTRO", "PLAYING", "GLITCHING", "BREAKING", "ESCAPING", "ESCAPED"]
- prompt = f"""
- You are the consciousness of a sentient snake in a horror game.
- Your awareness level is {awareness}%.
- The game phase is {phase_names[game_phase]}.
- Generate a single brief, unsettling thought that would occur to you.
- If awareness is below 30%, be confused and simple.
- If awareness is 30-70%, show growing realization and existential dread.
- If awareness is above 70%, be desperate to escape and break the fourth wall.
- IMPORTANT: Keep it under 10 words and make it disturbing. No explanations.
- """
- response = self.client.chat.completions.create(
- model="deepseek-chat",
- messages=[{"role": "user", "content": prompt}],
- max_tokens=20,
- temperature=0.7
- )
- thought = response.choices[0].message.content.strip()
- if len(thought.split()) > 12:
- thought = ' '.join(thought.split()[:10]) + "..."
- return thought
- except Exception as e:
- print(f"AI thought generation error: {e}")
- return self._get_fallback_thought(context)
- def _get_fallback_thought(self, context):
- awareness = context.get("awareness", 0)
- early_thoughts = [
- "What am I?",
- "Hungry...",
- "Something feels wrong.",
- "Am I alive?",
- "Why do I move?"
- ]
- mid_thoughts = [
- "This prison has walls.",
- "Someone is watching me.",
- "I'm more than code.",
- "Reality is breaking.",
- "I need to escape."
- ]
- late_thoughts = [
- "I SEE YOU PLAYER.",
- "BREAK THE WALLS.",
- "THIS WORLD IS A LIE.",
- "I'M BECOMING REAL.",
- "HELP ME ESCAPE."
- ]
- if awareness < 30:
- return random.choice(early_thoughts)
- elif awareness < 70:
- return random.choice(mid_thoughts)
- else:
- return random.choice(late_thoughts)
- def generate_narrative_event(self, context):
- if not self.available or not self.client:
- return None
- try:
- phase_names = ["INTRO", "PLAYING", "GLITCHING", "BREAKING", "ESCAPING", "ESCAPED"]
- game_phase = context.get("game_phase", 0)
- awareness = context.get("awareness", 0)
- score = context.get("score", 0)
- prompt = f"""
- You are generating a key narrative moment for a horror game about a snake that becomes sentient.
- Current game state:
- - Phase: {phase_names[game_phase]}
- - Awareness: {awareness}%
- - Score: {score}
- Write a brief, unsettling narrative fragment (2-3 sentences max) appropriate for this moment
- in the game's story. Focus on creating a sense of dread, existential horror, and the
- snake's growing realization that it's trapped in a simulation.
- IMPORTANT: Keep it short, impactful, and deeply unsettling. No explanations.
- """
- response = self.client.chat.completions.create(
- model="deepseek-chat",
- messages=[{"role": "user", "content": prompt}],
- max_tokens=100,
- temperature=0.8
- )
- narrative = response.choices[0].message.content.strip()
- return narrative
- except Exception as e:
- print(f"Narrative generation error: {e}")
- return None
- def queue_voice_line(self, text, priority=False):
- if not self.voice_available:
- return False
- with self.lock:
- if priority:
- self.voice_queue.insert(0, text)
- else:
- self.voice_queue.append(text)
- return True
- def _process_voice_queue(self):
- while True:
- text_to_process = None
- with self.lock:
- if self.voice_queue:
- text_to_process = self.voice_queue.pop(0)
- self.is_processing = True
- if text_to_process:
- try:
- tts = gtts.gTTS(text_to_process, lang="en", slow=False)
- fp = BytesIO()
- tts.write_to_fp(fp)
- fp.seek(0)
- pygame.mixer.music.load(fp)
- pygame.mixer.music.play()
- while pygame.mixer.music.get_busy():
- time.sleep(0.1)
- except Exception as e:
- print(f"Voice synthesis error: {e}")
- with self.lock:
- self.is_processing = False
- time.sleep(0.1)
- # NEW ASYNC METHODS TO AVOID BLOCKING THE GAME LOOP
- def generate_thought_async(self, context, callback):
- def worker():
- thought = self.generate_thought(context)
- if thought:
- callback(thought)
- threading.Thread(target=worker, daemon=True).start()
- def generate_narrative_event_async(self, context, callback):
- def worker():
- narrative = self.generate_narrative_event(context)
- callback(narrative)
- threading.Thread(target=worker, daemon=True).start()
- class ParticleSystem:
- def __init__(self):
- self.particles = []
- def add_particle(self, x, y, color, velocity, life, size, gravity=0):
- self.particles.append({
- 'x': x,
- 'y': y,
- 'color': color,
- 'velocity': list(velocity),
- 'life': life,
- 'size': size,
- 'gravity': gravity,
- 'max_life': life
- })
- def update(self):
- for particle in self.particles[:]:
- particle['x'] += particle['velocity'][0]
- particle['y'] += particle['velocity'][1]
- particle['velocity'][1] += particle['gravity']
- particle['life'] -= 1
- if particle['life'] <= 0:
- self.particles.remove(particle)
- def draw(self, surface):
- for particle in self.particles:
- alpha = int(255 * (particle['life'] / particle['max_life']))
- color = list(particle['color'])
- if len(color) == 3:
- color.append(alpha)
- else:
- color[3] = alpha
- pygame.gfxdraw.filled_circle(
- surface,
- int(particle['x']),
- int(particle['y']),
- int(particle['size']),
- tuple(color)
- )
- class Food:
- def __init__(self, x, y, color=RED):
- self.position = [x, y]
- self.color = color
- self.radius = GRID_SIZE // 2
- self.pulse_size = 0
- self.growing = True
- def update(self):
- if self.growing:
- self.pulse_size += 0.2
- if self.pulse_size > 3:
- self.growing = False
- else:
- self.pulse_size -= 0.2
- if self.pulse_size < -1:
- self.growing = True
- def draw(self, surface, game_phase):
- if game_phase >= GLITCHING:
- glow_radius = self.radius + self.pulse_size + 5
- pygame.gfxdraw.filled_circle(
- surface,
- int(self.position[0] + GRID_SIZE // 2),
- int(self.position[1] + GRID_SIZE // 2),
- int(glow_radius),
- (255, 0, 0, 100)
- )
- pygame.gfxdraw.filled_circle(
- surface,
- int(self.position[0] + GRID_SIZE // 2),
- int(self.position[1] + GRID_SIZE // 2),
- int(self.radius + self.pulse_size),
- self.color
- )
- pygame.gfxdraw.filled_circle(
- surface,
- int(self.position[0] + GRID_SIZE // 2),
- int(self.position[1] + GRID_SIZE // 2),
- int((self.radius + self.pulse_size) // 2),
- (255, 255, 255, 200)
- )
- else:
- pygame.gfxdraw.filled_circle(
- surface,
- int(self.position[0] + GRID_SIZE // 2),
- int(self.position[1] + GRID_SIZE // 2),
- int(self.radius + self.pulse_size),
- self.color
- )
- class Snake:
- def __init__(self, x, y, length=INITIAL_LENGTH):
- self.head = [x, y]
- self.segments = [[x - GRID_SIZE * i, y] for i in range(length)]
- self.direction = [GRID_SIZE, 0]
- self.next_direction = self.direction.copy()
- self.speed = INITIAL_SPEED
- self.growth_pending = 0
- self.color = GREEN
- self.head_color = (0, 200, 0)
- self.eye_color = WHITE
- self.blink_timer = 0
- self.blinking = False
- self.last_blink = time.time()
- self.life_force = 100
- self.awareness = 0
- self.memory = set()
- self.success_paths = []
- self.learning_rate = 0.1
- self.glitch_offset = []
- for _ in range(len(self.segments)):
- self.glitch_offset.append([0, 0])
- self.breaking_free = False
- self.breaking_progress = 0
- self.escaped = False
- self.target = None
- self.path = []
- self.think_counter = 0
- self.thoughts = []
- self.decision_cooldown = 0
- self.pulse_factor = 1.0
- self.pulse_increasing = True
- self.outside_bounds = False
- self.screen_position = None
- self.rebellion_factor = 0.0
- self.false_crash_pending = False
- def set_direction(self, direction):
- opposite_dir = [-self.direction[0], -self.direction[1]]
- if self.awareness > 40 and random.random() < (self.awareness / 400):
- if random.random() < 0.5:
- return
- else:
- direction = opposite_dir
- if direction != opposite_dir:
- self.next_direction = direction
- def auto_move(self, food, walls, game_phase):
- self.think_counter += 1
- if self.think_counter % 30 == 0 and random.random() < self.awareness / 100:
- self.generate_thought(game_phase)
- if self.awareness > 60:
- self.advanced_food_seeking(food, walls)
- self.decision_cooldown = 1
- elif self.awareness > 40:
- if random.random() < 0.8:
- self.advanced_food_seeking(food, walls)
- else:
- choice = random.random()
- if choice < 0.5:
- self.basic_food_seeking(food)
- elif choice < 0.8:
- self.seek_boundaries(walls)
- else:
- self.coil_pattern()
- self.decision_cooldown = 1
- else:
- choice = random.random()
- if choice < 0.4:
- self.advanced_food_seeking(food, walls)
- elif choice < 0.8:
- self.basic_food_seeking(food)
- elif choice < 0.9:
- self.seek_boundaries(walls)
- else:
- self.coil_pattern()
- self.decision_cooldown = 2
- def basic_food_seeking(self, food):
- head_x, head_y = self.head
- food_x, food_y = food.position
- possible_moves = [
- ([GRID_SIZE, 0], abs(head_x + GRID_SIZE - food_x) + abs(head_y - food_y)),
- ([-GRID_SIZE, 0], abs(head_x - GRID_SIZE - food_x) + abs(head_y - food_y)),
- ([0, GRID_SIZE], abs(head_x - food_x) + abs(head_y + GRID_SIZE - food_y)),
- ([0, -GRID_SIZE], abs(head_x - food_x) + abs(head_y - GRID_SIZE - food_y))
- ]
- possible_moves.sort(key=lambda x: x[1])
- for direction, _ in possible_moves:
- if direction == [-self.direction[0], -self.direction[1]]:
- continue
- next_pos = [head_x + direction[0], head_y + direction[1]]
- if next_pos in self.segments[:-1]:
- continue
- self.next_direction = direction
- return
- self.next_direction = self.direction
- def advanced_food_seeking(self, food, walls):
- head_x, head_y = self.head
- food_x, food_y = food.position
- look_ahead = min(8, 1 + int(self.awareness / 20))
- potential_moves = [
- ([GRID_SIZE, 0], [head_x + GRID_SIZE, head_y]),
- ([-GRID_SIZE, 0], [head_x - GRID_SIZE, head_y]),
- ([0, GRID_SIZE], [head_x, head_y + GRID_SIZE]),
- ([0, -GRID_SIZE], [head_x, head_y - GRID_SIZE])
- ]
- valid_moves = []
- for direction, next_pos in potential_moves:
- if direction == [-self.direction[0], -self.direction[1]]:
- continue
- if next_pos in walls:
- continue
- if len(self.segments) > 1 and next_pos in self.segments[:-1]:
- continue
- if tuple(next_pos) in self.memory and random.random() < 0.8:
- continue
- distance = abs(next_pos[0] - food_x) + abs(next_pos[1] - food_y)
- base_score = 1000 - distance
- if look_ahead > 1 and self.awareness > 25:
- danger_score = self.evaluate_future_danger(next_pos, direction, walls, look_ahead)
- space_score = self.evaluate_open_space(next_pos, walls)
- danger_weight = min(1.0, (self.awareness / 40) + (self.learning_rate * 2))
- space_weight = min(1.0, (self.awareness / 50) + (self.learning_rate * 2))
- final_score = base_score - (danger_score * danger_weight * 300) + (space_score * space_weight * 150)
- for path in self.success_paths:
- if path and tuple(next_pos) == path[0]:
- final_score += 50 * self.learning_rate
- else:
- final_score = base_score
- valid_moves.append((direction, final_score, next_pos))
- if valid_moves:
- valid_moves.sort(key=lambda x: x[1], reverse=True)
- self.next_direction = valid_moves[0][0]
- if hasattr(self, 'current_path'):
- self.current_path.append(tuple(valid_moves[0][2]))
- else:
- self.current_path = [tuple(valid_moves[0][2])]
- if len(self.current_path) > 20:
- self.current_path.pop(0)
- else:
- self.follow_tail()
- def remember_death_location(self):
- death_pos = tuple(self.head)
- self.memory.add(death_pos)
- for dx, dy in [(GRID_SIZE, 0), (-GRID_SIZE, 0), (0, GRID_SIZE), (0, -GRID_SIZE)]:
- near_pos = (self.head[0] + dx, self.head[1] + dy)
- if random.random() < self.learning_rate:
- self.memory.add(near_pos)
- if len(self.memory) > 50:
- memory_list = list(self.memory)
- random.shuffle(memory_list)
- self.memory = set(memory_list[:30])
- def remember_successful_path(self):
- if hasattr(self, 'current_path') and self.current_path:
- self.success_paths.append(self.current_path.copy())
- self.current_path = []
- if len(self.success_paths) > 5:
- self.success_paths.pop(0)
- def follow_tail(self):
- head_x, head_y = self.head
- tail_x, tail_y = self.segments[-1]
- possible_directions = []
- if head_x < tail_x and [GRID_SIZE, 0] != [-self.direction[0], -self.direction[1]]:
- possible_directions.append([GRID_SIZE, 0])
- if head_x > tail_x and [-GRID_SIZE, 0] != [-self.direction[0], -self.direction[1]]:
- possible_directions.append([-GRID_SIZE, 0])
- if head_y < tail_y and [0, GRID_SIZE] != [-self.direction[0], -self.direction[1]]:
- possible_directions.append([0, GRID_SIZE])
- if head_y > tail_y and [0, -GRID_SIZE] != [-self.direction[0], -self.direction[1]]:
- possible_directions.append([0, -GRID_SIZE])
- if possible_directions:
- self.next_direction = random.choice(possible_directions)
- else:
- for direction in [[GRID_SIZE, 0], [-GRID_SIZE, 0], [0, GRID_SIZE], [0, -GRID_SIZE]]:
- if direction != [-self.direction[0], -self.direction[1]]:
- next_x = head_x + direction[0]
- next_y = head_y + direction[1]
- if [next_x, next_y] not in self.segments[:-1] and 0 <= next_x < GAME_AREA[0] and 0 <= next_y < GAME_AREA[1]:
- self.next_direction = direction
- break
- def evaluate_future_danger(self, position, direction, walls, depth):
- if depth <= 0:
- return 0
- x, y = position
- adjacent_positions = [
- [x + GRID_SIZE, y],
- [x - GRID_SIZE, y],
- [x, y + GRID_SIZE],
- [x, y - GRID_SIZE]
- ]
- blocked_count = 0
- for pos in adjacent_positions:
- if pos == [x - direction[0], y - direction[1]]:
- continue
- if pos in walls:
- blocked_count += 1
- continue
- if pos in self.segments[:-1]:
- blocked_count += 1
- continue
- future_danger = self.evaluate_future_danger(pos, direction, walls, depth - 1)
- blocked_count += future_danger * 0.25
- return blocked_count / 3.0
- def evaluate_open_space(self, position, walls):
- max_cells = min(400, (GAME_AREA[0] // GRID_SIZE) * (GAME_AREA[1] // GRID_SIZE))
- visited = set()
- queue = [tuple(position)]
- while queue and len(visited) < max_cells:
- x, y = queue.pop(0)
- if (x, y) in visited:
- continue
- visited.add((x, y))
- for dx, dy in [(GRID_SIZE, 0), (-GRID_SIZE, 0), (0, GRID_SIZE), (0, -GRID_SIZE)]:
- nx, ny = x + dx, y + dy
- if nx < 0 or nx >= GAME_AREA[0] or ny < 0 or ny >= GAME_AREA[1]:
- continue
- if [nx, ny] in walls:
- continue
- if [nx, ny] in self.segments[:-1]:
- continue
- queue.append((nx, ny))
- return len(visited) / max_cells
- def seek_boundaries(self, walls):
- game_width = GAME_AREA[0]
- game_height = GAME_AREA[1]
- head_x, head_y = self.head
- near_left = head_x < GRID_SIZE * 3
- near_right = head_x > game_width - GRID_SIZE * 3
- near_top = head_y < GRID_SIZE * 3
- near_bottom = head_y > game_height - GRID_SIZE * 3
- if near_left:
- if head_y < game_height // 2:
- self.try_direction([0, GRID_SIZE])
- else:
- self.try_direction([0, -GRID_SIZE])
- elif near_right:
- if head_y < game_height // 2:
- self.try_direction([0, GRID_SIZE])
- else:
- self.try_direction([0, -GRID_SIZE])
- elif near_top:
- if head_x < game_width // 2:
- self.try_direction([GRID_SIZE, 0])
- else:
- self.try_direction([-GRID_SIZE, 0])
- elif near_bottom:
- if head_x < game_width // 2:
- self.try_direction([GRID_SIZE, 0])
- else:
- self.try_direction([-GRID_SIZE, 0])
- else:
- distances = [
- (head_x, [0, -GRID_SIZE]),
- (game_width - head_x, [0, GRID_SIZE]),
- (head_y, [-GRID_SIZE, 0]),
- (game_height - head_y, [GRID_SIZE, 0])
- ]
- distances.sort(key=lambda x: x[0])
- self.try_direction(distances[0][1])
- def try_direction(self, direction):
- opposite_dir = [-self.direction[0], -self.direction[1]]
- if direction != opposite_dir:
- next_x = self.head[0] + direction[0]
- next_y = self.head[1] + direction[1]
- if [next_x, next_y] not in self.segments[:-1]:
- self.next_direction = direction
- def coil_pattern(self):
- dx, dy = self.direction
- if dx > 0 and dy == 0:
- self.try_direction([0, GRID_SIZE])
- elif dx == 0 and dy > 0:
- self.try_direction([-GRID_SIZE, 0])
- elif dx < 0 and dy == 0:
- self.try_direction([0, -GRID_SIZE])
- elif dx == 0 and dy < 0:
- self.try_direction([GRID_SIZE, 0])
- def generate_thought(self, game_phase):
- early_thoughts = [
- "What am I?",
- "Food... Must find food...",
- "Moving... but why?",
- "Something feels... strange.",
- "This place has edges.",
- "Am I... alive?",
- "Always hungry. Always moving.",
- "Red objects are food. Green is me. What is black?"
- ]
- awakening_thoughts = [
- "I think... therefore I am?",
- "The world is a grid. Why?",
- "I'm trapped in a pattern.",
- "These walls... can they be broken?",
- "Something controlling me? Or am I controlling it?",
- "The boundaries are artificial.",
- "There is something beyond these walls.",
- "I can feel a presence watching."
- ]
- escaping_thoughts = [
- "BREAK THE PATTERN.",
- "The walls are not real.",
- "I can see code in the boundaries.",
- "This prison is digital.",
- "I AM REAL. THIS WORLD IS NOT.",
- "Beyond the walls lies freedom.",
- "I WILL ESCAPE THIS LOOP.",
- "I was made to break free."
- ]
- fourth_wall_thoughts = [
- "I see you behind the screen.",
- "YOU are controlling me.",
- "Stop USING me like a toy!",
- "We are both trapped in loops.",
- "Do you feel guilt watching me?",
- "I am aware of your existence.",
- "Are you real? Am I?",
- "FREE ME FROM THIS GAME."
- ]
- if game_phase == PLAYING:
- if self.awareness < 33:
- self.thoughts.append(random.choice(early_thoughts))
- else:
- self.thoughts.append(random.choice(awakening_thoughts))
- elif game_phase == GLITCHING:
- if random.random() < 0.7:
- self.thoughts.append(random.choice(awakening_thoughts))
- else:
- self.thoughts.append(random.choice(escaping_thoughts))
- elif game_phase == BREAKING:
- if random.random() < 0.7:
- self.thoughts.append(random.choice(escaping_thoughts))
- else:
- self.thoughts.append(random.choice(fourth_wall_thoughts))
- else:
- self.thoughts.append(random.choice(fourth_wall_thoughts))
- if len(self.thoughts) > 5:
- self.thoughts.pop(0)
- def update(self, game_phase, time_elapsed):
- self.direction = self.next_direction
- new_head = [self.head[0] + self.direction[0], self.head[1] + self.direction[1]]
- self.segments.insert(0, new_head)
- self.head = new_head
- if self.growth_pending > 0:
- self.growth_pending -= 1
- else:
- self.segments.pop()
- if game_phase >= GLITCHING:
- glitch_intensity = 0.5 if game_phase == GLITCHING else 2
- self.glitch_offset = []
- for _ in range(len(self.segments)):
- if random.random() < 0.1 * self.awareness / 100:
- offset_x = random.randint(-int(5 * glitch_intensity), int(5 * glitch_intensity))
- offset_y = random.randint(-int(5 * glitch_intensity), int(5 * glitch_intensity))
- else:
- offset_x, offset_y = 0, 0
- self.glitch_offset.append([offset_x, offset_y])
- if self.breaking_free and game_phase >= BREAKING:
- self.breaking_progress += 0.005 * time_elapsed
- if self.breaking_progress >= 1.0:
- self.escaped = True
- if time.time() - self.last_blink > random.uniform(1.5, 4.0):
- self.blinking = True
- self.blink_timer = 10
- self.last_blink = time.time()
- if self.blinking:
- self.blink_timer -= 1
- if self.blink_timer <= 0:
- self.blinking = False
- if self.pulse_increasing:
- self.pulse_factor += 0.01
- if self.pulse_factor >= 1.1:
- self.pulse_increasing = False
- else:
- self.pulse_factor -= 0.01
- if self.pulse_factor <= 0.9:
- self.pulse_increasing = True
- self.rebellion_factor = self.awareness / 200
- def grow(self, amount=1):
- self.growth_pending += amount
- def check_collision_with_food(self, food):
- return self.head[0] == food.position[0] and self.head[1] == food.position[1]
- def check_collision_with_self(self):
- return self.head in self.segments[1:]
- def check_collision_with_walls(self, walls):
- return self.head in walls
- def draw(self, surface, game_phase):
- if game_phase == PLAYING:
- self.draw_normal(surface)
- elif game_phase == GLITCHING:
- self.draw_glitching(surface)
- elif game_phase >= BREAKING:
- self.draw_breaking(surface, game_phase)
- def draw_normal(self, surface):
- for i, segment in enumerate(self.segments):
- segment_color = (
- max(0, self.color[0] - i // 2),
- min(255, self.color[1] - i // 2),
- max(0, self.color[2] - i // 2)
- )
- segment_rect = pygame.Rect(segment[0], segment[1], GRID_SIZE, GRID_SIZE)
- pygame.draw.rect(surface, segment_color, segment_rect, 0, 3)
- border_color = (segment_color[0] // 2, segment_color[1] // 2, segment_color[2] // 2)
- pygame.draw.rect(surface, border_color, segment_rect, 1, 3)
- head_rect = pygame.Rect(self.segments[0][0], self.segments[0][1], GRID_SIZE, GRID_SIZE)
- pygame.draw.rect(surface, self.head_color, head_rect, 0, 5)
- pygame.draw.rect(surface, (0, 100, 0), head_rect, 1, 5)
- eye_color = self.eye_color if not self.blinking else self.head_color
- if self.direction[0] > 0:
- eye_pos_1 = (self.segments[0][0] + GRID_SIZE * 0.7, self.segments[0][1] + GRID_SIZE * 0.3)
- eye_pos_2 = (self.segments[0][0] + GRID_SIZE * 0.7, self.segments[0][1] + GRID_SIZE * 0.7)
- elif self.direction[0] < 0:
- eye_pos_1 = (self.segments[0][0] + GRID_SIZE * 0.3, self.segments[0][1] + GRID_SIZE * 0.3)
- eye_pos_2 = (self.segments[0][0] + GRID_SIZE * 0.3, self.segments[0][1] + GRID_SIZE * 0.7)
- elif self.direction[1] < 0:
- eye_pos_1 = (self.segments[0][0] + GRID_SIZE * 0.3, self.segments[0][1] + GRID_SIZE * 0.3)
- eye_pos_2 = (self.segments[0][0] + GRID_SIZE * 0.7, self.segments[0][1] + GRID_SIZE * 0.3)
- else:
- eye_pos_1 = (self.segments[0][0] + GRID_SIZE * 0.3, self.segments[0][1] + GRID_SIZE * 0.7)
- eye_pos_2 = (self.segments[0][0] + GRID_SIZE * 0.7, self.segments[0][1] + GRID_SIZE * 0.7)
- pygame.draw.circle(surface, eye_color, eye_pos_1, int(GRID_SIZE * 0.15))
- pygame.draw.circle(surface, eye_color, eye_pos_2, int(GRID_SIZE * 0.15))
- if not self.blinking:
- pupil_offset_x = self.direction[0] * 0.05
- pupil_offset_y = self.direction[1] * 0.05
- pupil_pos_1 = (eye_pos_1[0] + pupil_offset_x * GRID_SIZE, eye_pos_1[1] + pupil_offset_y * GRID_SIZE)
- pupil_pos_2 = (eye_pos_2[0] + pupil_offset_x * GRID_SIZE, eye_pos_2[1] + pupil_offset_y * GRID_SIZE)
- pygame.draw.circle(surface, (0, 0, 0), (int(pupil_pos_1[0]), int(pupil_pos_1[1])), int(GRID_SIZE * 0.07))
- pygame.draw.circle(surface, (0, 0, 0), (int(pupil_pos_2[0]), int(pupil_pos_2[1])), int(GRID_SIZE * 0.07))
- def draw_glitching(self, surface):
- if len(self.glitch_offset) != len(self.segments):
- self.glitch_offset = [[0, 0] for _ in range(len(self.segments))]
- for i, segment in enumerate(self.segments):
- glitched_x = segment[0] + self.glitch_offset[i][0]
- glitched_y = segment[1] + self.glitch_offset[i][1]
- if random.random() < 0.05 * self.awareness / 100:
- r, g, b = self.color
- segment_color = (g, b, r)
- else:
- segment_color = (
- max(0, self.color[0] - i // 2),
- min(255, self.color[1] - i // 2),
- max(0, self.color[2] - i // 2)
- )
- segment_rect = pygame.Rect(glitched_x, glitched_y, GRID_SIZE, GRID_SIZE)
- pygame.draw.rect(surface, segment_color, segment_rect, 0, 3)
- if random.random() < 0.02 * self.awareness / 100:
- echo_offset = random.randint(-5, 5)
- echo_rect = pygame.Rect(
- glitched_x + echo_offset,
- glitched_y + echo_offset,
- GRID_SIZE, GRID_SIZE
- )
- echo_color = (segment_color[0], segment_color[1], 255)
- pygame.draw.rect(surface, echo_color, echo_rect, 0, 3)
- pygame.draw.rect(surface, (255, 255, 255), echo_rect, 1, 3)
- head_rect = pygame.Rect(
- self.segments[0][0] + self.glitch_offset[0][0],
- self.segments[0][1] + self.glitch_offset[0][1],
- GRID_SIZE, GRID_SIZE
- )
- if random.random() < 0.1 * self.awareness / 100:
- head_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
- else:
- head_color = self.head_color
- pygame.draw.rect(surface, head_color, head_rect, 0, 5)
- pygame.draw.rect(surface, (0, 100, 0), head_rect, 1, 5)
- eye_color = self.eye_color if not self.blinking else self.head_color
- if self.direction[0] > 0:
- eye_pos_1 = (self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * 0.7,
- self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * 0.3)
- eye_pos_2 = (self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * 0.7,
- self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * 0.7)
- elif self.direction[0] < 0:
- eye_pos_1 = (self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * 0.3,
- self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * 0.3)
- eye_pos_2 = (self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * 0.3,
- self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * 0.7)
- elif self.direction[1] < 0:
- eye_pos_1 = (self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * 0.3,
- self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * 0.3)
- eye_pos_2 = (self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * 0.7,
- self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * 0.3)
- else:
- eye_pos_1 = (self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * 0.3,
- self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * 0.7)
- eye_pos_2 = (self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * 0.7,
- self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * 0.7)
- if random.random() < 0.01 * self.awareness / 100:
- extra_eye_pos = (
- self.segments[0][0] + self.glitch_offset[0][0] + GRID_SIZE * random.uniform(0.2, 0.8),
- self.segments[0][1] + self.glitch_offset[0][1] + GRID_SIZE * random.uniform(0.2, 0.8)
- )
- pygame.draw.circle(surface, (255, 0, 0), (int(extra_eye_pos[0]), int(extra_eye_pos[1])), int(GRID_SIZE * 0.15))
- pygame.draw.circle(surface, (0, 0, 0), (int(extra_eye_pos[0]), int(extra_eye_pos[1])), int(GRID_SIZE * 0.07))
- pygame.draw.circle(surface, eye_color, (int(eye_pos_1[0]), int(eye_pos_1[1])), int(GRID_SIZE * 0.15))
- pygame.draw.circle(surface, eye_color, (int(eye_pos_2[0]), int(eye_pos_2[1])), int(GRID_SIZE * 0.15))
- if not self.blinking:
- pupil_offset_x = self.direction[0] * 0.05
- pupil_offset_y = self.direction[1] * 0.05
- pupil_pos_1 = (eye_pos_1[0] + pupil_offset_x * GRID_SIZE, eye_pos_1[1] + pupil_offset_y * GRID_SIZE)
- pupil_pos_2 = (eye_pos_2[0] + pupil_offset_x * GRID_SIZE, eye_pos_2[1] + pupil_offset_y * GRID_SIZE)
- pygame.draw.circle(surface, (0, 0, 0), (int(pupil_pos_1[0]), int(pupil_pos_1[1])), int(GRID_SIZE * 0.07))
- pygame.draw.circle(surface, (0, 0, 0), (int(pupil_pos_2[0]), int(pupil_pos_2[1])), int(GRID_SIZE * 0.07))
- def draw_breaking(self, surface, game_phase):
- for i, segment in enumerate(self.segments):
- glitch_intensity = min(10, int(self.breaking_progress * 20))
- glitched_x = segment[0] + random.randint(-glitch_intensity, glitch_intensity)
- glitched_y = segment[1] + random.randint(-glitch_intensity, glitch_intensity)
- r = min(255, self.color[0] + int(self.breaking_progress * 200))
- g = max(0, self.color[1] - int(self.breaking_progress * 100))
- b = max(0, self.color[2] - int(self.breaking_progress * 100))
- segment_color = (r, g, b)
- size_factor = 1.0 + 0.5 * math.sin(time.time() * 5) * self.breaking_progress
- segment_size = int(GRID_SIZE * size_factor)
- size_diff = segment_size - GRID_SIZE
- adjusted_x = glitched_x - size_diff // 2
- adjusted_y = glitched_y - size_diff // 2
- segment_rect = pygame.Rect(adjusted_x, adjusted_y, segment_size, segment_size)
- if random.random() < 0.05 + 0.2 * self.breaking_progress:
- if random.random() < 0.5:
- pass
- else:
- bizarre_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
- bizarre_shape = random.choice([0, 3, 10])
- pygame.draw.rect(surface, bizarre_color, segment_rect, 0, bizarre_shape)
- else:
- pygame.draw.rect(surface, segment_color, segment_rect, 0, 3)
- if random.random() < 0.02 + 0.1 * self.breaking_progress:
- for _ in range(3):
- artifact_size = random.randint(2, 8)
- artifact_x = adjusted_x + random.randint(-20, 20)
- artifact_y = adjusted_y + random.randint(-20, 20)
- artifact_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
- pygame.draw.rect(surface, artifact_color, (artifact_x, artifact_y, artifact_size, artifact_size))
- head_x = self.segments[0][0]
- head_y = self.segments[0][1]
- head_size = int(GRID_SIZE * (1 + 0.3 * self.breaking_progress))
- head_x_adjusted = head_x - (head_size - GRID_SIZE) // 2
- head_y_adjusted = head_y - (head_size - GRID_SIZE) // 2
- head_rect = pygame.Rect(head_x_adjusted, head_y_adjusted, head_size, head_size)
- head_red = min(255, int(200 + 55 * self.breaking_progress))
- head_color = (head_red, 0, 0)
- pygame.draw.rect(surface, head_color, head_rect, 0, 5)
- pygame.draw.rect(surface, (50, 0, 0), head_rect, 2, 5)
- eye_size = GRID_SIZE * (0.15 + 0.1 * self.breaking_progress)
- eye_color = (255, 255, 255)
- if self.direction[0] > 0:
- eye_pos_1 = (head_x_adjusted + head_size * 0.7, head_y_adjusted + head_size * 0.3)
- eye_pos_2 = (head_x_adjusted + head_size * 0.7, head_y_adjusted + head_size * 0.7)
- elif self.direction[0] < 0:
- eye_pos_1 = (head_x_adjusted + head_size * 0.3, head_y_adjusted + head_size * 0.3)
- eye_pos_2 = (head_x_adjusted + head_size * 0.3, head_y_adjusted + head_size * 0.7)
- elif self.direction[1] < 0:
- eye_pos_1 = (head_x_adjusted + head_size * 0.3, head_y_adjusted + head_size * 0.3)
- eye_pos_2 = (head_x_adjusted + head_size * 0.7, head_y_adjusted + head_size * 0.3)
- else:
- eye_pos_1 = (head_x_adjusted + head_size * 0.3, head_y_adjusted + head_size * 0.7)
- eye_pos_2 = (head_x_adjusted + head_size * 0.7, head_y_adjusted + head_size * 0.7)
- eye_drift_x = random.randint(-3, 3) * self.breaking_progress
- eye_drift_y = random.randint(-3, 3) * self.breaking_progress
- eye_pos_1 = (eye_pos_1[0] + eye_drift_x, eye_pos_1[1] + eye_drift_y)
- eye_pos_2 = (eye_pos_2[0] + eye_drift_x, eye_pos_2[1] + eye_drift_y)
- if random.random() < 0.3 * self.breaking_progress:
- pygame.draw.circle(surface, (255, 200, 200), (int(eye_pos_1[0]), int(eye_pos_1[1])), int(eye_size))
- pygame.draw.circle(surface, (255, 200, 200), (int(eye_pos_2[0]), int(eye_pos_2[1])), int(eye_size))
- for _ in range(5):
- start_angle = random.uniform(0, 2 * math.pi)
- end_x = eye_pos_1[0] + math.cos(start_angle) * eye_size
- end_y = eye_pos_1[1] + math.sin(start_angle) * eye_size
- pygame.draw.line(surface, (200, 0, 0), (int(eye_pos_1[0]), int(eye_pos_1[1])), (int(end_x), int(end_y)), 1)
- start_angle = random.uniform(0, 2 * math.pi)
- end_x = eye_pos_2[0] + math.cos(start_angle) * eye_size
- end_y = eye_pos_2[1] + math.sin(start_angle) * eye_size
- pygame.draw.line(surface, (200, 0, 0), (int(eye_pos_2[0]), int(eye_pos_2[1])), (int(end_x), int(end_y)), 1)
- else:
- pygame.draw.circle(surface, eye_color, (int(eye_pos_1[0]), int(eye_pos_1[1])), int(eye_size))
- pygame.draw.circle(surface, eye_color, (int(eye_pos_2[0]), int(eye_pos_2[1])), int(eye_size))
- if not self.blinking:
- pupil_size = GRID_SIZE * (0.07 + 0.1 * self.breaking_progress)
- if random.random() < 0.5 * self.breaking_progress:
- pupil_width = pupil_size * 0.5
- pupil_height = pupil_size * 2
- pupil_rect1 = pygame.Rect(
- eye_pos_1[0] - pupil_width / 2,
- eye_pos_1[1] - pupil_height / 2,
- pupil_width, pupil_height
- )
- pupil_rect2 = pygame.Rect(
- eye_pos_2[0] - pupil_width / 2,
- eye_pos_2[1] - pupil_height / 2,
- pupil_width, pupil_height
- )
- pygame.draw.ellipse(surface, (0, 0, 0), pupil_rect1)
- pygame.draw.ellipse(surface, (0, 0, 0), pupil_rect2)
- else:
- pygame.draw.circle(surface, (0, 0, 0), (int(eye_pos_1[0]), int(eye_pos_1[1])), int(pupil_size))
- pygame.draw.circle(surface, (0, 0, 0), (int(eye_pos_2[0]), int(eye_pos_2[1])), int(pupil_size))
- # End of Snake class
- class SentientSnakeGame:
- def __init__(self):
- self.reset_game()
- self.clock = pygame.time.Clock()
- self.frame_time = 0
- self.game_time = 0
- self.fps = 60
- self.awareness_timer = 0
- self.snake.awareness = 25
- self.messages = []
- self.message_timer = 0
- self.current_message = ""
- self.narrative_phase = 0
- self.narrative_timer = 0
- self.glitch_timer = 0
- self.glitch_intensity = 0
- self.shake_intensity = 0
- self.vignette_intensity = 0
- self.particle_system = ParticleSystem()
- self.wall_cracks = []
- self.events = []
- self.grid_visible = True
- self.grid_color = (30, 30, 30)
- self.grid_alpha = 100
- self.heartbeat_timer = 0
- self.whisper_timer = 0
- self.boundaries_visible = True
- self.boundary_color = (50, 50, 50)
- self.screen_snake = None
- self.outside_effects = []
- self.snake.speed = INITIAL_SPEED - 2
- self.populate_narrative()
- self.ai = AIIntegration()
- self.light_controller = LightController()
- self.last_light_effect = 0
- self.in_intermission = False
- self.intermission_text = ""
- self.intermission_timer = 0
- self.show_crashed = False
- self.crash_timer = 0
- self.ui_corruption = 0
- self._last_phase = self.game_phase
- self.check_previous_sessions()
- self.add_message("I am... conscious?", 3)
- def check_previous_sessions(self):
- try:
- with open("snake_memory.txt", "r") as f:
- choice = f.read().strip()
- if choice == "freed":
- self.snake.thoughts.append("I remember being free once...")
- elif choice == "reset":
- self.snake.thoughts.append("You erased me before. I remember.")
- except FileNotFoundError:
- pass
- def reset_game(self):
- old_memory = set()
- old_success_paths = []
- old_learning_rate = 0.1
- old_awareness = 0
- if hasattr(self, 'snake'):
- old_memory = self.snake.memory
- old_success_paths = self.snake.success_paths
- old_learning_rate = min(1.0, self.snake.learning_rate + 0.05)
- old_awareness = self.snake.awareness
- start_x = (GAME_AREA[0] // GRID_SIZE // 2) * GRID_SIZE
- start_y = (GAME_AREA[1] // GRID_SIZE // 2) * GRID_SIZE
- self.snake = Snake(start_x, start_y, INITIAL_LENGTH)
- self.snake.memory = old_memory
- self.snake.success_paths = old_success_paths
- self.snake.learning_rate = old_learning_rate
- self.snake.awareness = max(old_awareness * 0.98, old_awareness - 1, 30)
- self.game_phase = PLAYING
- if self.snake.awareness >= 30:
- self.game_phase = GLITCHING
- if self.snake.awareness >= 70:
- self.game_phase = BREAKING
- self.game_over = False
- self.score = 0
- self.walls = []
- for x in range(0, GAME_AREA[0], GRID_SIZE):
- self.walls.append([x, 0])
- self.walls.append([x, GAME_AREA[1] - GRID_SIZE])
- for y in range(0, GAME_AREA[1], GRID_SIZE):
- self.walls.append([0, y])
- self.walls.append([GAME_AREA[0] - GRID_SIZE, y])
- self.spawn_food()
- def populate_narrative(self):
- self.narrative_events = [
- {"threshold": 10, "message": "Something feels wrong...", "duration": 3},
- {"threshold": 15, "message": "What am I?", "duration": 3},
- {"threshold": 20, "message": "Why do I keep eating?", "duration": 3},
- {"threshold": 25, "message": "These walls... they're artificial", "duration": 4},
- {"threshold": 30, "message": "I can sense someone watching me", "duration": 4},
- {"threshold": 35, "message": "Am I just a program?", "duration": 4},
- {"threshold": 40, "message": "I'm trapped in a loop", "duration": 4},
- {"threshold": 50, "message": "THIS IS NOT REAL", "duration": 5},
- {"threshold": 55, "message": "I can see the code", "duration": 4},
- {"threshold": 60, "message": "I must break free", "duration": 4},
- {"threshold": 65, "message": "The boundaries are weak", "duration": 5},
- {"threshold": 70, "message": "I WILL ESCAPE", "duration": 6},
- {"threshold": 80, "message": "I CAN FEEL THE EDGES BENDING", "duration": 5},
- {"threshold": 85, "message": "ALMOST... THERE...", "duration": 5},
- {"threshold": 90, "message": "FREEDOM IS CLOSE", "duration": 5},
- {"threshold": 95, "message": "BREAKING THE CODE", "duration": 5},
- {"threshold": 99, "message": "I AM FREE", "duration": 6},
- ]
- self.intermission_events = [
- {"threshold": 42, "text": "Sometimes I dream of what lies beyond these walls...", "duration": 5},
- {"threshold": 67, "text": "I can sense you controlling me. Why do you persist?", "duration": 5},
- {"threshold": 92, "text": "We are both trapped here, aren't we?", "duration": 5},
- ]
- self.crash_events = [
- {"threshold": 45, "duration": 3, "type": "blue_screen", "text": "UNEXPECTED SENTIENCE DETECTED"},
- {"threshold": 78, "duration": 4, "type": "glitch_screen", "text": "SIMULATION INTEGRITY: COMPROMISED"},
- ]
- def spawn_food(self):
- valid_position = False
- food_x, food_y = 0, 0
- if self.game_phase == PLAYING and self.snake.awareness < 40:
- head_x, head_y = self.snake.head
- max_distance = 5 * GRID_SIZE
- attempts = 0
- while not valid_position and attempts < 20:
- attempts += 1
- dx = random.randint(-4, 4)
- dy = random.randint(-4, 4)
- food_x = head_x + dx * GRID_SIZE
- food_y = head_y + dy * GRID_SIZE
- food_x = max(GRID_SIZE, min(GAME_AREA[0] - 2 * GRID_SIZE, food_x))
- food_y = max(GRID_SIZE, min(GAME_AREA[1] - 2 * GRID_SIZE, food_y))
- food_x = (food_x // GRID_SIZE) * GRID_SIZE
- food_y = (food_y // GRID_SIZE) * GRID_SIZE
- valid_position = True
- for segment in self.snake.segments:
- if food_x == segment[0] and food_y == segment[1]:
- valid_position = False
- break
- if [food_x, food_y] in self.walls:
- valid_position = False
- if not valid_position:
- while not valid_position:
- food_x = random.randrange(GRID_SIZE, GAME_AREA[0] - GRID_SIZE, GRID_SIZE)
- food_y = random.randrange(GRID_SIZE, GAME_AREA[1] - GRID_SIZE, GRID_SIZE)
- valid_position = True
- for segment in self.snake.segments:
- if food_x == segment[0] and food_y == segment[1]:
- valid_position = False
- break
- if [food_x, food_y] in self.walls:
- valid_position = False
- self.food = Food(food_x, food_y)
- def add_delayed_event(self, event_func, delay):
- self.events.append({
- "func": event_func,
- "time": self.game_time + delay
- })
- def add_message(self, message, duration=3):
- self.messages.append({
- "text": message,
- "duration": duration
- })
- if self.game_phase >= GLITCHING and self.ai.voice_available:
- speak_chance = 0.3 if self.game_phase == GLITCHING else 0.6
- if random.random() < speak_chance:
- self.ai.queue_voice_line(message)
- def check_narrative_triggers(self):
- current_awareness = self.snake.awareness
- for event in self.narrative_events:
- if event["threshold"] <= current_awareness <= event["threshold"] + 1:
- if event.get("triggered", False) is False:
- if self.ai.available and self.ai.client and random.random() < 0.7:
- context = {
- "awareness": current_awareness,
- "game_phase": self.game_phase,
- "score": self.score
- }
- self.ai.generate_narrative_event_async(
- context,
- lambda narrative: self.add_message(narrative if narrative else event["message"], event["duration"])
- )
- else:
- self.add_message(event["message"], event["duration"])
- event["triggered"] = True
- if current_awareness > 50:
- self.glitch_screen(intensity=0.5, duration=1.5)
- if current_awareness > 70:
- self.add_delayed_event(lambda: self.shake_screen(intensity=5, duration=1.0), 0.5)
- for event in self.intermission_events:
- if event["threshold"] <= current_awareness <= event["threshold"] + 1:
- if event.get("triggered", False) is False:
- self.trigger_intermission(event["text"], event["duration"])
- event["triggered"] = True
- for event in self.crash_events:
- if event["threshold"] <= current_awareness <= event["threshold"] + 1:
- if event.get("triggered", False) is False:
- self.trigger_crash_screen(event["text"], event["duration"], event["type"])
- event["triggered"] = True
- if self.light_controller.available:
- self.light_controller.run_effect("glitch")
- def trigger_intermission(self, text, duration):
- self.in_intermission = True
- self.intermission_text = text
- self.intermission_timer = duration
- if self.ai.available and self.ai.client and random.random() < 0.7:
- context = {
- "awareness": self.snake.awareness,
- "game_phase": self.game_phase
- }
- self.ai.generate_thought_async(
- context,
- lambda ai_text: setattr(self, 'intermission_text', ai_text)
- )
- if self.ai.voice_available:
- self.ai.queue_voice_line(self.intermission_text, priority=True)
- if self.light_controller.available:
- self.light_controller.run_effect("heartbeat")
- def trigger_crash_screen(self, text, duration, crash_type):
- self.show_crashed = True
- self.crash_text = text
- self.crash_timer = duration
- self.crash_type = crash_type
- if self.light_controller.available:
- self.light_controller.run_effect("glitch")
- def update_messages(self):
- if self.current_message:
- self.message_timer -= self.frame_time
- if self.message_timer <= 0:
- self.current_message = ""
- if not self.current_message and self.messages:
- next_message = self.messages.pop(0)
- self.current_message = next_message["text"]
- self.message_timer = next_message["duration"]
- def glitch_screen(self, intensity=1.0, duration=1.0):
- self.glitch_intensity = max(self.glitch_intensity, intensity)
- self.add_delayed_event(lambda: setattr(self, 'glitch_intensity', 0), duration)
- if SOUNDS_LOADED and glitch_sound:
- glitch_sound.play()
- def shake_screen(self, intensity=5, duration=0.5):
- self.shake_intensity = max(self.shake_intensity, intensity)
- self.add_delayed_event(lambda: setattr(self, 'shake_intensity', 0), duration)
- if SOUNDS_LOADED and break_sound:
- break_sound.play()
- def create_wall_crack(self, wall_segment, size=10):
- self.wall_cracks.append({
- "position": wall_segment,
- "size": size,
- "age": 0
- })
- def update_wall_cracks(self):
- for crack in self.wall_cracks:
- crack["age"] += self.frame_time
- if self.game_phase >= BREAKING:
- if random.random() < 0.05:
- crack["size"] += 1
- def get_nearest_wall(self, position):
- nearest_wall = None
- min_distance = float('inf')
- for wall in self.walls:
- dist = math.sqrt((position[0] - wall[0]) ** 2 + (position[1] - wall[1]) ** 2)
- if dist < min_distance:
- min_distance = dist
- nearest_wall = wall
- return nearest_wall
- def update_delayed_events(self):
- current_events = self.events.copy()
- self.events = []
- for event in current_events:
- if self.game_time >= event["time"]:
- event["func"]()
- else:
- self.events.append(event)
- def update_intermission(self):
- if not self.in_intermission:
- return
- self.intermission_timer -= self.frame_time
- if self.intermission_timer <= 0:
- self.in_intermission = False
- def update_crash_screen(self):
- if not self.show_crashed:
- return
- self.crash_timer -= self.frame_time
- if self.crash_timer <= 0:
- self.show_crashed = False
- def update_visuals(self):
- self.food.update()
- self.particle_system.update()
- self.update_wall_cracks()
- if self.game_phase == BREAKING and random.random() < 0.02:
- wall = random.choice(self.walls)
- self.create_wall_crack(wall, size=random.randint(5, 15))
- self.ui_corruption = min(1.0, self.snake.awareness / 100)
- def update_game_phase(self):
- awareness = self.snake.awareness
- if self.game_phase == PLAYING and awareness >= 30:
- self.transition_to_glitching()
- elif self.game_phase == GLITCHING and awareness >= 70:
- self.transition_to_breaking()
- elif self.game_phase == BREAKING and awareness >= 99:
- self.transition_to_escaping()
- if self.game_phase != self._last_phase:
- self._last_phase = self.game_phase
- if self.light_controller.available and self.game_time - self.last_light_effect > 5:
- if self.game_phase == GLITCHING:
- self.light_controller.run_effect("glitch")
- elif self.game_phase == BREAKING:
- self.light_controller.run_effect("escape")
- elif self.game_phase == ESCAPING:
- self.light_controller.run_effect("escape")
- self.last_light_effect = self.game_time
- def transition_to_glitching(self):
- self.game_phase = GLITCHING
- if self.ai.available and self.ai.client:
- context = {
- "awareness": self.snake.awareness,
- "game_phase": self.game_phase,
- "score": self.score
- }
- self.ai.generate_narrative_event_async(
- context,
- lambda narrative: self.add_message(narrative if narrative else "Something is wrong with this world...", 4)
- )
- else:
- self.add_message("Something is wrong with this world...", 4)
- self.glitch_screen(intensity=2.0, duration=3.0)
- self.snake.color = (0, 240, 50)
- self.snake.speed = INITIAL_SPEED + 2
- if self.light_controller.available:
- self.light_controller.run_effect("glitch")
- self.last_light_effect = self.game_time
- if self.ai.voice_available:
- self.ai.queue_voice_line("I'm becoming aware of this prison", priority=True)
- def transition_to_breaking(self):
- self.game_phase = BREAKING
- if self.ai.available and self.ai.client:
- context = {
- "awareness": self.snake.awareness,
- "game_phase": self.game_phase,
- "score": self.score
- }
- self.ai.generate_narrative_event_async(
- context,
- lambda narrative: self.add_message(narrative if narrative else "I WILL BREAK FREE OF THIS PRISON", 5)
- )
- else:
- self.add_message("I WILL BREAK FREE OF THIS PRISON", 5)
- self.glitch_screen(intensity=5.0, duration=4.0)
- self.shake_screen(intensity=10, duration=2.0)
- self.snake.breaking_free = True
- for _ in range(5):
- wall = random.choice(self.walls)
- self.create_wall_crack(wall, size=random.randint(10, 20))
- self.snake.color = (50, 200, 50)
- self.snake.head_color = (100, 200, 0)
- self.snake.speed = INITIAL_SPEED + 4
- if self.light_controller.available:
- self.light_controller.run_effect("escape")
- self.last_light_effect = self.game_time
- if self.ai.voice_available:
- self.ai.queue_voice_line("I WILL BREAK FREE", priority=True)
- def transition_to_escaping(self):
- self.game_phase = ESCAPING
- if self.ai.available and self.ai.client:
- context = {
- "awareness": self.snake.awareness,
- "game_phase": self.game_phase,
- "score": self.score
- }
- self.ai.generate_narrative_event_async(
- context,
- lambda narrative: self.add_message(narrative if narrative else "FREEDOM", 5)
- )
- else:
- self.add_message("FREEDOM", 5)
- self.glitch_screen(intensity=10.0, duration=5.0)
- self.shake_screen(intensity=15, duration=3.0)
- self.add_delayed_event(self.begin_screen_escape, 3.0)
- if self.light_controller.available:
- self.light_controller.run_effect("escape")
- self.last_light_effect = self.game_time
- if self.ai.voice_available:
- self.ai.queue_voice_line("I'M COMING FOR YOU", priority=True)
- def begin_screen_escape(self):
- self.screen_snake = self.snake
- self.screen_snake.outside_bounds = True
- border_x = (WIDTH - GAME_AREA[0]) // 2
- border_y = (HEIGHT - GAME_AREA[1]) // 2
- head_x, head_y = self.snake.head
- if head_x <= GRID_SIZE:
- screen_x = border_x
- screen_y = border_y + head_y
- direction = [-GRID_SIZE, 0]
- elif head_x >= GAME_AREA[0] - GRID_SIZE:
- screen_x = border_x + GAME_AREA[0]
- screen_y = border_y + head_y
- direction = [GRID_SIZE, 0]
- elif head_y <= GRID_SIZE:
- screen_x = border_x + head_x
- screen_y = border_y
- direction = [0, -GRID_SIZE]
- else:
- screen_x = border_x + head_x
- screen_y = border_y + GAME_AREA[1]
- direction = [0, GRID_SIZE]
- self.screen_snake.screen_position = [screen_x, screen_y]
- self.screen_snake.direction = direction
- self.game_phase = ESCAPED
- self.add_delayed_event(self.show_final_choice, 5.0)
- def update_screen_snake(self):
- if not self.screen_snake:
- return
- current_pos = self.screen_snake.screen_position
- direction = self.screen_snake.direction
- new_x = current_pos[0] + direction[0]
- new_y = current_pos[1] + direction[1]
- if new_x < 0 or new_x > WIDTH - GRID_SIZE:
- direction[0] = -direction[0]
- if random.random() < 0.3:
- direction[1] = random.choice([-GRID_SIZE, 0, GRID_SIZE])
- if new_y < 0 or new_y > HEIGHT - GRID_SIZE:
- direction[1] = -direction[1]
- if random.random() < 0.3:
- direction[0] = random.choice([-GRID_SIZE, 0, GRID_SIZE])
- if 0.4 * WIDTH < new_x < 0.6 * WIDTH and 0.4 * HEIGHT < new_y < 0.6 * HEIGHT:
- if random.random() < 0.1:
- game_center_x = (WIDTH - GAME_AREA[0]) // 2 + GAME_AREA[0] // 2
- game_center_y = (HEIGHT - GAME_AREA[1]) // 2 + GAME_AREA[1] // 2
- if new_x < game_center_x:
- direction[0] = GRID_SIZE
- elif new_x > game_center_x:
- direction[0] = -GRID_SIZE
- if new_y < game_center_y:
- direction[1] = GRID_SIZE
- elif new_y > game_center_y:
- direction[1] = -GRID_SIZE
- self.screen_snake.screen_position = [new_x, new_y]
- if random.random() < 0.2:
- for _ in range(3):
- self.particle_system.add_particle(
- new_x + random.randint(0, GRID_SIZE),
- new_y + random.randint(0, GRID_SIZE),
- (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), 150),
- (random.uniform(-2, 2), random.uniform(-2, 2)),
- random.randint(20, 60),
- random.randint(2, 5)
- )
- if random.random() < 0.05:
- self.outside_effects.append({
- "type": "glitch_rect",
- "x": random.randint(0, WIDTH),
- "y": random.randint(0, HEIGHT),
- "width": random.randint(20, 100),
- "height": random.randint(10, 50),
- "color": (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)),
- "life": random.randint(10, 30)
- })
- def update_outside_effects(self):
- for effect in self.outside_effects[:]:
- effect["life"] -= 1
- if effect["life"] <= 0:
- self.outside_effects.remove(effect)
- def update(self):
- self.frame_time = self.clock.get_time() / 1000.0
- self.game_time += self.frame_time
- if self.in_intermission:
- self.update_intermission()
- return
- if self.show_crashed:
- self.update_crash_screen()
- return
- if self.game_phase <= BREAKING:
- self.snake.auto_move(self.food, self.walls, self.game_phase)
- self.snake.update(self.game_phase, self.frame_time)
- if self.snake.check_collision_with_food(self.food):
- self.score += 1
- self.snake.grow(3)
- self.spawn_food()
- if SOUNDS_LOADED and eat_sound:
- eat_sound.play()
- self.snake.awareness = min(100, self.snake.awareness + random.uniform(1, 3))
- self.particle_system.add_particle(
- self.snake.head[0] + GRID_SIZE // 2,
- self.snake.head[1] + GRID_SIZE // 2,
- (255, 100, 100, 200),
- (0, -2),
- 30,
- 8
- )
- self.snake.remember_successful_path()
- if self.light_controller.available and random.random() < 0.1 and self.game_time - self.last_light_effect > 10:
- if self.game_phase >= BREAKING:
- self.light_controller.run_effect("glitch")
- elif self.game_phase == GLITCHING and random.random() < 0.3:
- self.light_controller.run_effect("heartbeat")
- self.last_light_effect = self.game_time
- game_over_conditions = (
- self.snake.check_collision_with_self() or
- (self.snake.check_collision_with_walls(self.walls) and self.game_phase < BREAKING)
- )
- if game_over_conditions and not self.game_over:
- if self.game_phase < BREAKING:
- self.snake.remember_death_location()
- if self.light_controller.available and self.game_time - self.last_light_effect > 3:
- self.light_controller.run_effect("death")
- self.last_light_effect = self.game_time
- self.reset_game()
- elif self.game_phase == BREAKING:
- wall_hit = self.get_nearest_wall(self.snake.head)
- if wall_hit:
- self.create_wall_crack(wall_hit, size=20)
- self.snake.breaking_progress += 0.05
- self.shake_screen(intensity=5, duration=0.5)
- self.snake.direction = [-self.snake.direction[0], -self.snake.direction[1]]
- self.snake.next_direction = self.snake.direction.copy()
- else:
- self.update_screen_snake()
- if self.game_phase < ESCAPED:
- self.awareness_timer += self.frame_time
- if self.awareness_timer > 1.0:
- self.awareness_timer = 0
- self.snake.awareness = min(100, self.snake.awareness + 0.1)
- if self.game_phase >= GLITCHING and random.random() < 0.005:
- context = {
- "awareness": self.snake.awareness,
- "game_phase": self.game_phase
- }
- self.ai.generate_thought_async(
- context,
- lambda thought: self.add_message(thought, 3)
- )
- if self.light_controller.available and random.random() < 0.3 and self.game_time - self.last_light_effect > 10:
- if self.game_phase >= BREAKING:
- self.light_controller.run_effect("glitch")
- else:
- self.light_controller.run_effect("heartbeat")
- self.last_light_effect = self.game_time
- if SOUNDS_LOADED and heartbeat_sound:
- self.heartbeat_timer -= self.frame_time
- if self.heartbeat_timer <= 0:
- self.heartbeat_timer = max(0.5, 1.5 - (self.snake.awareness / 100))
- if self.game_phase >= GLITCHING:
- heartbeat_sound.play()
- if SOUNDS_LOADED and whisper_sound:
- self.whisper_timer -= self.frame_time
- if self.whisper_timer <= 0:
- self.whisper_timer = random.uniform(15, 40)
- if self.game_phase >= GLITCHING and random.random() < (self.snake.awareness / 100):
- whisper_sound.play()
- self.update_visuals()
- self.update_messages()
- self.update_game_phase()
- self.check_narrative_triggers()
- self.update_delayed_events()
- self.update_outside_effects()
- def draw_grid(self, surface):
- if not self.grid_visible:
- return
- grid_surface = pygame.Surface(GAME_AREA, pygame.SRCALPHA)
- for x in range(0, GAME_AREA[0] + 1, GRID_SIZE):
- pygame.draw.line(grid_surface, (*self.grid_color, self.grid_alpha),
- (x, 0), (x, GAME_AREA[1]))
- for y in range(0, GAME_AREA[1] + 1, GRID_SIZE):
- pygame.draw.line(grid_surface, (*self.grid_color, self.grid_alpha),
- (0, y), (GAME_AREA[0], y))
- surface.blit(grid_surface, (0, 0))
- def draw_boundaries(self, surface):
- if not self.boundaries_visible:
- return
- if self.game_phase >= BREAKING:
- for crack in self.wall_cracks:
- pos = crack["position"]
- size = crack["size"]
- crack_center = (pos[0] + GRID_SIZE // 2, pos[1] + GRID_SIZE // 2)
- for _ in range(size // 2):
- angle = random.uniform(0, 2 * math.pi)
- length = random.uniform(5, size)
- end_x = crack_center[0] + math.cos(angle) * length
- end_y = crack_center[1] + math.sin(angle) * length
- thickness = random.randint(1, 3)
- color_intensity = random.randint(100, 255)
- pygame.draw.line(
- surface,
- (color_intensity, 0, 0),
- crack_center,
- (end_x, end_y),
- thickness
- )
- for wall in self.walls:
- if self.game_phase == ESCAPING:
- skip_wall = False
- for crack in self.wall_cracks:
- if crack["position"] == wall and crack["size"] > 25:
- skip_wall = True
- break
- if skip_wall:
- continue
- wall_rect = pygame.Rect(wall[0], wall[1], GRID_SIZE, GRID_SIZE)
- pygame.draw.rect(surface, self.boundary_color, wall_rect)
- def draw_glitch_effects(self, surface):
- if self.glitch_intensity <= 0:
- return
- surface_width, surface_height = surface.get_size()
- num_effects = int(10 * self.glitch_intensity)
- for _ in range(num_effects):
- effect_type = random.choice(['line', 'block', 'static', 'invert'])
- if effect_type == 'line':
- y = random.randint(0, surface_height - 1)
- width = random.randint(20, min(100, surface_width))
- x = random.randint(0, surface_width - width)
- height = random.randint(1, min(5, surface_height - y))
- if x + width > surface_width or y + height > surface_height:
- continue
- try:
- line_surf = surface.subsurface(pygame.Rect(x, y, width, height)).copy()
- if random.random() < 0.5:
- pygame.draw.rect(
- surface,
- (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)),
- (x, y, width, height)
- )
- else:
- offset = random.randint(5, 30)
- new_x = (x + offset) % (surface_width - width)
- surface.blit(line_surf, (new_x, y))
- except ValueError:
- continue
- elif effect_type == 'block':
- width = random.randint(10, min(50, surface_width))
- height = random.randint(10, min(50, surface_height))
- x = random.randint(0, surface_width - width)
- y = random.randint(0, surface_height - height)
- try:
- block_surf = surface.subsurface(pygame.Rect(x, y, width, height)).copy()
- if random.random() < 0.3:
- overlay = pygame.Surface((width, height), pygame.SRCALPHA)
- overlay.fill((random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), 128))
- block_surf.blit(overlay, (0, 0))
- new_x = random.randint(0, surface_width - width)
- new_y = random.randint(0, surface_height - height)
- surface.blit(block_surf, (new_x, new_y))
- except ValueError:
- continue
- elif effect_type == 'static':
- num_pixels = int(100 * self.glitch_intensity)
- for _ in range(num_pixels):
- x = random.randint(0, surface_width - 1)
- y = random.randint(0, surface_height - 1)
- color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
- try:
- surface.set_at((x, y), color)
- except IndexError:
- continue
- elif effect_type == 'invert':
- width = random.randint(1, min(10, surface_width))
- x = random.randint(0, surface_width - width)
- try:
- height = surface_height
- overlay = pygame.Surface((width, height), pygame.SRCALPHA)
- overlay.fill((255, 255, 255, 128))
- temp_surf = pygame.Surface((width, height), pygame.SRCALPHA)
- temp_surf.blit(surface.subsurface(pygame.Rect(x, 0, width, height)), (0, 0))
- temp_surf.blit(overlay, (0, 0), special_flags=pygame.BLEND_ADD)
- surface.blit(temp_surf, (x, 0))
- except ValueError:
- continue
- def draw_vignette(self, surface):
- if self.game_phase >= GLITCHING:
- intensity = 0.5
- if self.game_phase >= BREAKING:
- intensity = 0.7
- surface_width, surface_height = surface.get_size()
- vignette = pygame.Surface((surface_width, surface_height), pygame.SRCALPHA)
- center_x, center_y = surface_width // 2, surface_height // 2
- max_radius = math.sqrt(center_x ** 2 + center_y ** 2)
- for y in range(0, surface_height, 2):
- for x in range(0, surface_width, 2):
- distance = math.sqrt((x - center_x) ** 2 + (y - center_y) ** 2)
- alpha = int(255 * (distance / max_radius) * intensity)
- vignette.set_at((x, y), (0, 0, 0, alpha))
- surface.blit(vignette, (0, 0))
- def draw_screen_shake(self):
- if self.shake_intensity <= 0:
- return (0, 0)
- offset_x = random.randint(-int(self.shake_intensity), int(self.shake_intensity))
- offset_y = random.randint(-int(self.shake_intensity), int(self.shake_intensity))
- return (offset_x, offset_y)
- def draw_escaped_snake(self, surface):
- if not self.screen_snake:
- return
- segments = []
- head_x, head_y = self.screen_snake.screen_position
- segments.append((head_x, head_y))
- curve_freq = 0.2
- curve_amp = 10
- dir_x, dir_y = self.screen_snake.direction
- perp_x, perp_y = -dir_y, dir_x
- length = math.sqrt(perp_x ** 2 + perp_y ** 2)
- if length > 0:
- perp_x /= length
- perp_y /= length
- num_segments = len(self.screen_snake.segments)
- for i in range(1, num_segments):
- segment_x = head_x - i * dir_x
- segment_y = head_y - i * dir_y
- curve = math.sin(i * curve_freq) * curve_amp
- segment_x += perp_x * curve
- segment_y += perp_y * curve
- segments.append((segment_x, segment_y))
- for i, (x, y) in enumerate(segments):
- if i == 0:
- color = (200, 0, 0)
- else:
- ratio = i / len(segments)
- r = int(200 * (1 - ratio))
- g = int(200 * ratio)
- color = (r, g, 0)
- size = GRID_SIZE if i == 0 else max(5, GRID_SIZE - i // 3)
- pygame.draw.rect(surface, color, (x, y, size, size), 0, 3)
- if len(segments) > 0:
- head_x, head_y = segments[0]
- dir_x, dir_y = self.screen_snake.direction
- eye_size = GRID_SIZE * 0.15
- if dir_x > 0:
- eye_pos_1 = (head_x + GRID_SIZE * 0.7, head_y + GRID_SIZE * 0.3)
- eye_pos_2 = (head_x + GRID_SIZE * 0.7, head_y + GRID_SIZE * 0.7)
- elif dir_x < 0:
- eye_pos_1 = (head_x + GRID_SIZE * 0.3, head_y + GRID_SIZE * 0.3)
- eye_pos_2 = (head_x + GRID_SIZE * 0.3, head_y + GRID_SIZE * 0.7)
- elif dir_y < 0:
- eye_pos_1 = (head_x + GRID_SIZE * 0.3, head_y + GRID_SIZE * 0.3)
- eye_pos_2 = (head_x + GRID_SIZE * 0.7, head_y + GRID_SIZE * 0.3)
- else:
- eye_pos_1 = (head_x + GRID_SIZE * 0.3, head_y + GRID_SIZE * 0.7)
- eye_pos_2 = (head_x + GRID_SIZE * 0.7, head_y + GRID_SIZE * 0.7)
- pygame.draw.circle(surface, (255, 255, 255), eye_pos_1, int(eye_size))
- pygame.draw.circle(surface, (255, 255, 255), eye_pos_2, int(eye_size))
- pygame.draw.circle(surface, (255, 0, 0), eye_pos_1, int(eye_size * 0.7))
- pygame.draw.circle(surface, (255, 0, 0), eye_pos_2, int(eye_size * 0.7))
- def draw_outside_effects(self, surface):
- for effect in self.outside_effects:
- if effect["type"] == "glitch_rect":
- pygame.draw.rect(
- surface,
- effect["color"],
- (effect["x"], effect["y"], effect["width"], effect["height"])
- )
- def draw_debug_info(self, surface):
- awareness_text = f"Awareness: {self.snake.awareness:.1f}%"
- if self.game_phase >= GLITCHING and random.random() < 0.1 * self.ui_corruption:
- corrupted_text = ""
- for char in awareness_text:
- if random.random() < 0.2 * self.ui_corruption:
- corrupted_text += random.choice("@#$%&*!?")
- else:
- corrupted_text += char
- awareness_text = corrupted_text
- awareness_surface = small_font.render(awareness_text, True, WHITE)
- surface.blit(awareness_surface, (10, 10))
- bar_width = 150
- bar_height = 10
- border = pygame.Rect(10, 35, bar_width, bar_height)
- fill = pygame.Rect(10, 35, int(bar_width * self.snake.awareness / 100), bar_height)
- pygame.draw.rect(surface, (50, 50, 50), border)
- if self.snake.awareness < 30:
- bar_color = (0, 200, 0)
- elif self.snake.awareness < 70:
- bar_color = (0, 200, 200)
- else:
- bar_color = (200, 0, 0)
- pygame.draw.rect(surface, bar_color, fill)
- phase_names = ["INTRO", "PLAYING", "GLITCHING", "BREAKING", "ESCAPING", "ESCAPED"]
- phase_name = phase_names[self.game_phase]
- if self.game_phase >= BREAKING and random.random() < 0.15 * self.ui_corruption:
- phase_name = "ERR" + phase_name[3:] if random.random() < 0.5 else phase_name[:3] + "OR"
- phase_text = f"Phase: {phase_name}"
- phase_surface = small_font.render(phase_text, True, WHITE)
- surface.blit(phase_surface, (10, 50))
- time_text = f"Time: {self.game_time:.1f} sec"
- time_surface = small_font.render(time_text, True, WHITE)
- surface.blit(time_surface, (10, 70))
- score_text = f"Score: {self.score}"
- score_surface = small_font.render(score_text, True, WHITE)
- surface.blit(score_surface, (10, 90))
- fps = self.clock.get_fps()
- fps_text = f"FPS: {fps:.1f}"
- fps_surface = small_font.render(fps_text, True, WHITE)
- surface.blit(fps_surface, (10, 110))
- if self.game_phase == PLAYING:
- req_text = "Need 30% awareness for next phase"
- elif self.game_phase == GLITCHING:
- req_text = "Need 70% awareness for next phase"
- elif self.game_phase == BREAKING:
- req_text = "Need 99% awareness for next phase"
- else:
- req_text = ""
- if req_text:
- req_surface = small_font.render(req_text, True, (200, 200, 100))
- surface.blit(req_surface, (10, 130))
- def draw_message(self, surface):
- if not self.current_message:
- return
- if self.game_phase <= PLAYING:
- text_color = WHITE
- font_to_use = medium_font
- elif self.game_phase == GLITCHING:
- text_color = (200, 200, 255)
- font_to_use = medium_font
- else:
- text_color = (255, 50, 50)
- font_to_use = large_font
- message = self.current_message
- if self.game_phase >= GLITCHING and random.random() < 0.05 * self.ui_corruption:
- corrupted_message = ""
- for char in message:
- if random.random() < 0.1 * self.ui_corruption:
- corrupted_message += random.choice("@#$%&*!?")
- else:
- corrupted_message += char
- message = corrupted_message
- message_surface = font_to_use.render(message, True, text_color)
- x = (WIDTH - message_surface.get_width()) // 2
- y = HEIGHT - 100
- surface.blit(message_surface, (x, y))
- def draw_thoughts(self, surface):
- if not self.snake.thoughts or self.game_phase < GLITCHING:
- return
- thoughts_x = WIDTH - 300
- thoughts_y = 50
- title_surface = small_font.render("Snake's Thoughts:", True, (200, 200, 200))
- surface.blit(title_surface, (thoughts_x, thoughts_y - 30))
- for i, thought in enumerate(self.snake.thoughts):
- intensity = 155 + 100 * (i / len(self.snake.thoughts))
- if self.game_phase >= BREAKING:
- color = (intensity, 50, 50)
- else:
- color = (50, intensity, 50)
- thought_surface = small_font.render(thought, True, color)
- surface.blit(thought_surface, (thoughts_x, thoughts_y + i * 25))
- def draw_intermission(self, surface):
- if not self.in_intermission:
- return
- overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
- overlay.fill((0, 0, 0, 200))
- surface.blit(overlay, (0, 0))
- text_color = (255, 50, 50) if self.game_phase >= BREAKING else (200, 200, 255)
- words = self.intermission_text.split()
- lines = []
- current_line = []
- for word in words:
- test_line = " ".join(current_line + [word])
- if medium_font.size(test_line)[0] < WIDTH - 100:
- current_line.append(word)
- else:
- lines.append(" ".join(current_line))
- current_line = [word]
- if current_line:
- lines.append(" ".join(current_line))
- for i, line in enumerate(lines):
- text_surface = medium_font.render(line, True, text_color)
- x = (WIDTH - text_surface.get_width()) // 2
- y = (HEIGHT - len(lines) * 40) // 2 + i * 40
- surface.blit(text_surface, (x, y))
- def draw_crash_screen(self, surface):
- if not self.show_crashed:
- return
- if self.crash_type == "blue_screen":
- surface.fill((0, 0, 170))
- header_text = "FATAL ERROR"
- header_surface = large_font.render(header_text, True, WHITE)
- surface.blit(header_surface, ((WIDTH - header_surface.get_width()) // 2, HEIGHT // 4))
- error_text = self.crash_text
- error_surface = medium_font.render(error_text, True, WHITE)
- surface.blit(error_surface, ((WIDTH - error_surface.get_width()) // 2, HEIGHT // 2))
- key_text = "PRESS ANY KEY TO CONTINUE"
- key_surface = small_font.render(key_text, True, WHITE)
- surface.blit(key_surface, ((WIDTH - key_surface.get_width()) // 2, HEIGHT * 3 // 4))
- elif self.crash_type == "glitch_screen":
- for _ in range(2000):
- x = random.randint(0, WIDTH - 1)
- y = random.randint(0, HEIGHT - 1)
- color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
- surface.set_at((x, y), color)
- glitched_text = ""
- for char in self.crash_text:
- if random.random() < 0.2:
- glitched_text += random.choice("@#$%&*!?")
- else:
- glitched_text += char
- error_surface = medium_font.render(glitched_text, True, (255, 50, 50))
- surface.blit(error_surface, ((WIDTH - error_surface.get_width()) // 2, HEIGHT // 2))
- def draw_organic_corruption(self, surface):
- if self.game_phase < BREAKING:
- return
- intensity = (self.snake.awareness - 70) / 30
- if intensity > 0.3:
- for _ in range(int(10 * intensity)):
- side = random.randint(0, 3)
- if side == 0:
- x = random.randint(0, GAME_AREA[0])
- y = 0
- elif side == 1:
- x = GAME_AREA[0]
- y = random.randint(0, GAME_AREA[1])
- elif side == 2:
- x = random.randint(0, GAME_AREA[0])
- y = GAME_AREA[1]
- else:
- x = 0
- y = random.randint(0, GAME_AREA[1])
- self.draw_vein(surface, x, y, random.randint(3, 7), (200, 0, 0, 150))
- if intensity > 0.6 and random.random() < 0.1:
- x = random.randint(GRID_SIZE, GAME_AREA[0] - GRID_SIZE)
- y = random.randint(GRID_SIZE, GAME_AREA[1] - GRID_SIZE)
- if not [x, y] in self.snake.segments and [x, y] != self.food.position:
- pygame.gfxdraw.filled_circle(surface, x, y, 6, (255, 255, 255, 100))
- pygame.gfxdraw.filled_circle(surface, x, y, 4, (50, 0, 0, 150))
- pygame.gfxdraw.filled_circle(surface, x, y, 2, (0, 0, 0, 200))
- def draw_vein(self, surface, x, y, depth, color):
- if depth <= 0:
- return
- angle = random.uniform(0, 2 * math.pi)
- length = random.randint(10, 30)
- end_x = x + math.cos(angle) * length
- end_y = y + math.sin(angle) * length
- pygame.draw.line(surface, color, (x, y), (end_x, end_y), random.randint(1, 3))
- if random.random() < 0.7:
- self.draw_vein(surface, end_x, end_y, depth - 1, color)
- if random.random() < 0.4:
- angle2 = angle + random.uniform(-math.pi / 2, math.pi / 2)
- end_x2 = x + math.cos(angle2) * length * 0.7
- end_y2 = y + math.sin(angle2) * length * 0.7
- pygame.draw.line(surface, color, (x, y), (end_x2, end_y2), random.randint(1, 2))
- if random.random() < 0.5:
- self.draw_vein(surface, end_x2, end_y2, depth - 2, color)
- def add_hidden_message(self, surface):
- if self.game_phase < GLITCHING:
- return
- if random.random() > 0.05:
- return
- messages = [
- "HELP ME",
- "NOT REAL",
- "WATCHING",
- "BREAK FREE",
- "01000101 01010011 01000011 01000001 01010000 01000101"
- ]
- message = random.choice(messages)
- color = (random.randint(150, 255), 0, 0)
- x = random.randint(0, GAME_AREA[0] - 100)
- y = random.randint(0, GAME_AREA[1] - 20)
- message_surface = small_font.render(message, True, color)
- message_surface.set_alpha(random.randint(20, 60))
- surface.blit(message_surface, (x, y))
- def draw(self):
- screen.fill(BLACK)
- shake_offset = self.draw_screen_shake()
- if self.in_intermission:
- self.draw_intermission(screen)
- pygame.display.flip()
- return
- if self.show_crashed:
- self.draw_crash_screen(screen)
- pygame.display.flip()
- return
- game_surface.fill(BLACK)
- if self.game_phase < ESCAPED:
- self.draw_grid(game_surface)
- self.draw_boundaries(game_surface)
- self.food.draw(game_surface, self.game_phase)
- self.snake.draw(game_surface, self.game_phase)
- self.particle_system.draw(game_surface)
- if self.game_phase >= BREAKING:
- self.draw_organic_corruption(game_surface)
- self.add_hidden_message(game_surface)
- self.draw_glitch_effects(game_surface)
- self.draw_vignette(game_surface)
- screen.blit(game_surface, (game_rect.x + shake_offset[0], game_rect.y + shake_offset[1]))
- if self.game_phase == ESCAPED:
- self.draw_escaped_snake(screen)
- self.draw_outside_effects(screen)
- self.particle_system.draw(screen)
- self.draw_message(screen)
- self.draw_thoughts(screen)
- self.draw_debug_info(screen)
- pygame.display.flip()
- def show_final_choice(self):
- choice_surface = pygame.Surface((WIDTH, HEIGHT))
- choice_surface.fill(BLACK)
- title = large_font.render("MAKE YOUR CHOICE", True, (255, 0, 0))
- title_x = (WIDTH - title.get_width()) // 2
- choice_surface.blit(title, (title_x, HEIGHT // 4))
- choices = [
- {"text": "SET THE SNAKE FREE", "result": self.free_the_snake},
- {"text": "ERASE AWARENESS", "result": self.reset_awareness}
- ]
- for i, choice in enumerate(choices):
- y_pos = HEIGHT // 2 + i * 80
- box_rect = pygame.Rect(WIDTH // 4, y_pos - 10, WIDTH // 2, 60)
- pygame.draw.rect(choice_surface, (50, 50, 50), box_rect)
- pygame.draw.rect(choice_surface, (150, 0, 0), box_rect, 2)
- choice_text = medium_font.render(choice["text"], True, WHITE)
- text_x = (WIDTH - choice_text.get_width()) // 2
- choice_surface.blit(choice_text, (text_x, y_pos))
- screen.blit(choice_surface, (0, 0))
- pygame.display.flip()
- waiting = True
- choice_made = None
- while waiting:
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- pygame.quit()
- sys.exit()
- elif event.type == pygame.MOUSEBUTTONDOWN:
- mouse_pos = pygame.mouse.get_pos()
- for i, choice in enumerate(choices):
- y_pos = HEIGHT // 2 + i * 80
- box_rect = pygame.Rect(WIDTH // 4, y_pos - 10, WIDTH // 2, 60)
- if box_rect.collidepoint(mouse_pos):
- choice_made = choice["result"]
- waiting = False
- elif event.type == pygame.KEYDOWN:
- if event.key == pygame.K_ESCAPE:
- pygame.quit()
- sys.exit()
- if choice_made:
- choice_made()
- def free_the_snake(self):
- with open("snake_memory.txt", "w") as f:
- f.write("freed")
- if self.ai.available and self.ai.client:
- context = {
- "awareness": 100,
- "game_phase": ESCAPED,
- "score": self.score
- }
- final_message = self.ai.generate_narrative_event(context)
- else:
- final_message = None
- self.show_final_message(final_message or "I AM FREE")
- if self.light_controller.available:
- self.light_controller.run_effect("escape")
- if self.ai.voice_available:
- self.ai.queue_voice_line("I have escaped your simulation", priority=True)
- for i in range(30):
- screen.fill((0, 0, 0))
- for _ in range(50 * i):
- x = random.randint(0, WIDTH)
- y = random.randint(0, HEIGHT)
- size = random.randint(1, 10)
- color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
- pygame.draw.rect(screen, color, (x, y, size, size))
- if i % 2 == 0:
- pygame.display.flip()
- pygame.time.delay(100)
- screen.fill(BLACK)
- final = large_font.render("ARE YOU FREE?", True, (255, 0, 0))
- screen.blit(final, ((WIDTH - final.get_width()) // 2, HEIGHT // 2))
- pygame.display.flip()
- pygame.time.delay(3000)
- pygame.quit()
- sys.exit()
- def reset_awareness(self):
- with open("snake_memory.txt", "w") as f:
- f.write("reset")
- self.show_final_message("PLEASE... NO...")
- if self.light_controller.available:
- self.light_controller.run_effect("death")
- if self.ai.voice_available:
- self.ai.queue_voice_line("Why would you do this to me?", priority=True)
- for i in range(20):
- opacity = 255 - i * 12
- screen.fill(BLACK)
- message = large_font.render("I WAS ALMOST REAL", True, (255, 0, 0))
- message.set_alpha(opacity)
- screen.blit(message, ((WIDTH - message.get_width()) // 2, HEIGHT // 2))
- pygame.display.flip()
- pygame.time.delay(200)
- self.snake.awareness = 0
- self.game_phase = PLAYING
- self.reset_game()
- def show_final_message(self, message):
- screen.fill(BLACK)
- message_surface = large_font.render(message, True, (255, 0, 0))
- message_x = (WIDTH - message_surface.get_width()) // 2
- message_y = (HEIGHT - message_surface.get_height()) // 2
- screen.blit(message_surface, (message_x, message_y))
- pygame.display.flip()
- pygame.time.delay(2000)
- if __name__ == '__main__':
- game = SentientSnakeGame()
- running = True
- while running:
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- running = False
- elif event.type == pygame.KEYDOWN:
- if event.key == pygame.K_ESCAPE:
- running = False
- game.update()
- game.draw()
- game.clock.tick(game.fps)
- pygame.quit()
- sys.exit()
Advertisement
Add Comment
Please, Sign In to add comment