| import torch |
| import torch.nn as nn |
| import numpy as np |
| import random |
| import os |
| import io |
| import http.server |
| import socketserver |
| from PIL import Image, ImageDraw, ImageFont |
| import json |
|
|
| |
| |
| |
|
|
| class AndreyNN(nn.Module): |
| def __init__(self, vocab_size): |
| super().__init__() |
| self.fc1 = nn.Linear(vocab_size, 128) |
| self.fc2 = nn.Linear(128, 128) |
| self.fc3 = nn.Linear(128, 64) |
| self.fc4 = nn.Linear(64, 32) |
| self.fc5 = nn.Linear(32, vocab_size) |
| self.relu = nn.ReLU() |
| self.dropout = nn.Dropout(0.2) |
| self.softmax = nn.Softmax(dim=1) |
| |
| def forward(self, x): |
| x = self.relu(self.fc1(x)) |
| x = self.dropout(x) |
| x = self.relu(self.fc2(x)) |
| x = self.dropout(x) |
| x = self.relu(self.fc3(x)) |
| x = self.dropout(x) |
| x = self.relu(self.fc4(x)) |
| x = self.softmax(self.fc5(x)) |
| return x |
|
|
| |
| |
| |
|
|
| class AndreyWorldLoader: |
| def __init__(self, model_path='andrey_world_model.pt', device='cpu'): |
| self.device = torch.device(device) |
| |
| if not os.path.exists(model_path): |
| raise FileNotFoundError(f"Файл {model_path} не найден!") |
| |
| checkpoint = torch.load(model_path, map_location='cpu') |
| |
| self.vocab = checkpoint['vocab'] |
| self.word_to_idx = checkpoint['word_to_idx'] |
| self.idx_to_word = checkpoint['idx_to_word'] |
| self.vocab_size = checkpoint['vocab_size'] |
| self.is_trained = checkpoint['is_trained'] |
| |
| self.model = AndreyNN(self.vocab_size).to(self.device) |
| self.model.load_state_dict(checkpoint['model_state']) |
| self.model.eval() |
| |
| self.themes = ['свет', 'тьма', 'вода', 'огонь', 'ветер', 'земля'] |
| self.colors = ['золотой', 'синий', 'зелёный', 'розовый', 'фиолетовый', 'голубой'] |
| self.creatures = ['существа', 'птицы', 'змеи', 'великаны', 'духи', 'кони'] |
| self.places = ['долина', 'гора', 'лес', 'пустыня', 'океан', 'небо'] |
| |
| print(f"[ГОТОВО] Модель загружена! Словарь: {self.vocab_size} слов") |
| |
| def generate_word(self, context=None): |
| x = np.zeros(self.vocab_size) |
| if context: |
| for word in context: |
| if word in self.word_to_idx: |
| x[self.word_to_idx[word]] = 1 |
| |
| if np.sum(x) == 0: |
| x[np.random.randint(0, self.vocab_size)] = 1 |
| |
| x_tensor = torch.tensor(x, dtype=torch.float32).to(self.device).unsqueeze(0) |
| |
| with torch.no_grad(): |
| output = self.model(x_tensor) |
| probs = output.cpu().numpy()[0] |
| probs = probs / np.sum(probs) |
| idx = np.random.choice(self.vocab_size, p=probs) |
| return self.idx_to_word[idx] |
| |
| def generate_world(self): |
| theme = self.generate_word(['мир', 'из']) |
| color = self.generate_word(['цвет', 'как']) |
| creature = self.generate_word(['существо', 'живёт']) |
| place = self.generate_word(['место', 'где']) |
| |
| if theme not in self.themes: |
| theme = random.choice(self.themes) |
| if color not in self.colors: |
| color = random.choice(self.colors) |
| if creature not in self.creatures: |
| creature = random.choice(self.creatures) |
| if place not in self.places: |
| place = random.choice(self.places) |
| |
| prefixes = ["Великий", "Тихий", "Золотой", "Древний", "Светлый", "Тёмный"] |
| name = f"{random.choice(prefixes)} {place.capitalize()}" |
| |
| desc_parts = [ |
| f"Мир, где всё состоит из {theme}.", |
| f"Здесь {creature} охраняют {place}.", |
| f"{color.capitalize()} {place} наполнен {theme}.", |
| f"{creature} танцуют в {place}." |
| ] |
| description = ' '.join(random.sample(desc_parts, 2)) |
| |
| return { |
| 'name': name, |
| 'description': description, |
| 'theme': theme, |
| 'color': color, |
| 'creature': creature, |
| 'place': place |
| } |
|
|
| |
| |
| |
|
|
| def generate_world_image(world_data, size=(640, 480)): |
| img = Image.new('RGB', size, color=(20, 20, 40)) |
| draw = ImageDraw.Draw(img) |
| |
| colors = { |
| 'свет': (255, 240, 200), |
| 'тьма': (30, 30, 60), |
| 'вода': (0, 100, 200), |
| 'огонь': (255, 100, 50), |
| 'ветер': (180, 200, 220), |
| 'земля': (120, 80, 40) |
| } |
| |
| base_color = colors.get(world_data.get('theme', 'свет'), (50, 50, 80)) |
| |
| for y in range(size[1]): |
| r = int(base_color[0] * (0.5 + 0.5 * y / size[1])) |
| g = int(base_color[1] * (0.5 + 0.5 * y / size[1])) |
| b = int(base_color[2] * (0.5 + 0.5 * y / size[1])) |
| draw.line([(0, y), (size[0], y)], fill=(r, g, b)) |
| |
| ground_y = int(size[1] * 0.6) |
| draw.rectangle([(0, ground_y), (size[0], size[1])], fill=(60, 40, 20)) |
| |
| for _ in range(30): |
| x = random.randint(0, size[0]) |
| y = random.randint(ground_y, size[1]) |
| h = random.randint(5, 15) |
| draw.line([(x, y), (x + random.randint(-3, 3), y - h)], fill=(50, 150, 50), width=1) |
| |
| for _ in range(random.randint(3, 5)): |
| cx = random.randint(100, size[0] - 100) |
| cy = ground_y - random.randint(30, 100) |
| r = random.randint(60, 150) |
| draw.ellipse([(cx - r, cy - r), (cx + r, cy + r)], fill=(100, 80, 60)) |
| |
| sx = random.randint(50, 200) |
| sy = random.randint(30, 100) |
| draw.ellipse([(sx - 30, sy - 30), (sx + 30, sy + 30)], fill=(255, 200, 100)) |
| |
| if world_data.get('theme') == 'тьма': |
| for _ in range(50): |
| sx2 = random.randint(0, size[0]) |
| sy2 = random.randint(0, ground_y) |
| br = random.randint(1, 3) |
| draw.ellipse([(sx2 - br, sy2 - br), (sx2 + br, sy2 + br)], fill=(255, 255, 200)) |
| |
| for _ in range(random.randint(8, 15)): |
| tx = random.randint(0, size[0]) |
| ty = ground_y + random.randint(0, 30) |
| draw.rectangle([(tx - 3, ty - 20), (tx + 3, ty)], fill=(80, 50, 30)) |
| cr = random.randint(15, 30) |
| draw.ellipse([(tx - cr, ty - 20 - cr), (tx + cr, ty - 20 + cr)], fill=(40, 140, 40)) |
| |
| for _ in range(random.randint(3, 6)): |
| ox = random.randint(0, size[0]) |
| oy = random.randint(20, ground_y - 50) |
| ow = random.randint(60, 120) |
| oh = random.randint(20, 40) |
| draw.ellipse([(ox, oy), (ox + ow, oy + oh)], fill=(200, 200, 220, 100)) |
| |
| try: |
| font = ImageFont.load_default() |
| draw.text((20, 20), f"🌍 {world_data.get('name', '')}", fill=(255, 255, 255), font=font) |
| draw.text((20, 50), f"🏷️ {world_data.get('theme', '')} | {world_data.get('color', '')}", fill=(200, 200, 200), font=font) |
| draw.text((20, 80), f"🦄 {world_data.get('description', '')[:50]}...", fill=(180, 180, 180), font=font) |
| except: |
| pass |
| |
| draw.rectangle([(0, 0), (size[0]-1, size[1]-1)], outline=(100, 100, 150), width=2) |
| |
| return img |
|
|
| |
| |
| |
|
|
| |
| loader = AndreyWorldLoader('andrey_world_model.pt') |
|
|
| class WorldHandler(http.server.BaseHTTPRequestHandler): |
| def log_message(self, format, *args): |
| pass |
| |
| def do_GET(self): |
| if self.path == '/': |
| |
| world = loader.generate_world() |
| img = generate_world_image(world) |
| |
| |
| buffer = io.BytesIO() |
| img.save(buffer, format='PNG') |
| img_data = buffer.getvalue() |
| |
| |
| html = f''' |
| <!DOCTYPE html> |
| <html> |
| <head> |
| <title>AndreyWorld</title> |
| <meta http-equiv="refresh" content="10"> |
| <style> |
| body {{ |
| background: #0d0d1a; |
| color: #e0e0e0; |
| font-family: 'Segoe UI', sans-serif; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| height: 100vh; |
| margin: 0; |
| padding: 20px; |
| }} |
| .container {{ |
| max-width: 900px; |
| width: 100%; |
| background: #1a1a2e; |
| padding: 30px; |
| border-radius: 20px; |
| border: 1px solid #2a2a4e; |
| box-shadow: 0 0 50px rgba(0,0,0,0.8); |
| text-align: center; |
| }} |
| h1 {{ |
| color: #6a8fbd; |
| margin-top: 0; |
| }} |
| img {{ |
| width: 100%; |
| border-radius: 15px; |
| border: 2px solid #2a2a4e; |
| margin: 15px 0; |
| }} |
| .info {{ |
| background: #0d0d1a; |
| padding: 15px; |
| border-radius: 10px; |
| margin: 10px 0; |
| }} |
| .tag {{ |
| display: inline-block; |
| background: #2a2a4e; |
| padding: 4px 12px; |
| border-radius: 12px; |
| margin: 3px; |
| font-size: 0.9em; |
| }} |
| .meta {{ |
| color: #666; |
| font-size: 0.85em; |
| margin-top: 15px; |
| }} |
| .refresh {{ |
| color: #4a6fa5; |
| text-decoration: none; |
| }} |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <h1>🌍 AndreyWorld</h1> |
| <div class="info"> |
| <h2>🌍 {world.get('name', '')}</h2> |
| <p>{world.get('description', '')}</p> |
| <div> |
| <span class="tag">🏷️ {world.get('theme', '')}</span> |
| <span class="tag">🎨 {world.get('color', '')}</span> |
| <span class="tag">🦄 {world.get('creature', '')}</span> |
| <span class="tag">📍 {world.get('place', '')}</span> |
| </div> |
| </div> |
| <img src="/world.png" alt="AndreyWorld"> |
| <div class="meta"> |
| 🔄 Страница обновляется каждые 10 секунд<br> |
| 📡 <a href="/" class="refresh">Обновить сейчас</a> |
| </div> |
| </div> |
| </body> |
| </html> |
| ''' |
| |
| self.send_response(200) |
| self.send_header('Content-Type', 'text/html; charset=utf-8') |
| self.end_headers() |
| self.wfile.write(html.encode('utf-8')) |
| |
| elif self.path == '/world.png': |
| |
| world = loader.generate_world() |
| img = generate_world_image(world) |
| |
| buffer = io.BytesIO() |
| img.save(buffer, format='PNG') |
| img_data = buffer.getvalue() |
| |
| self.send_response(200) |
| self.send_header('Content-Type', 'image/png') |
| self.end_headers() |
| self.wfile.write(img_data) |
| |
| else: |
| self.send_response(404) |
| self.end_headers() |
| self.wfile.write(b'Not Found') |
|
|
| |
| |
| |
|
|
| def main(): |
| PORT = 6767 |
| |
| print("\n" + "="*50) |
| print("🌍 ANDREYWORLD") |
| print(" http://localhost:" + str(PORT)) |
| print("="*50 + "\n") |
| |
| with socketserver.TCPServer(("", PORT), WorldHandler) as httpd: |
| print(f"[ГОТОВО] Сервер запущен на порту {PORT}") |
| print(f"[ССЫЛКА] Открой http://localhost:{PORT}") |
| print("[ИНФО] Страница обновляется каждые 10 секунд\n") |
| try: |
| httpd.serve_forever() |
| except KeyboardInterrupt: |
| print("\n[СТОП] Остановка...") |
| httpd.shutdown() |
|
|
| if __name__ == "__main__": |
| main() |