Механизированный симулятор эволюции

Материал из Абсурдопедии
Перейти к навигацииПерейти к поиску

Механизированный симулятор эволюции — другой взгляд на тему, которая уже много времени будоражит мозги великих деятелей науки. Другой по той причине, что уже есть эта игра, для игры в которую не нужно копировать код. С другой стороны её аффтор забил на неё.

Геймплей[править]

Ваша задача — выиграть. Это можно сделать, набрав максимум очков. (Если что, y = yes, n = no)

Подготовка[править]

Сначала игра спрашивает, сколько ходов вы хотите. Оптимально 3–7. Если ввести 100, игра не сломается, но вы — да.

Потом спрашивают количество ботов от 0 до 10. Если 0 — вы играете сами с собой, что символично. Если 10 — вы играете против десяти гениев, которые к третьему ходу создадут больше видов, чем существует в реальной зоологии.

Уровни ботов:

Минимальный — бот-овощ. Ничего не делает, но существует.

Простой — иногда что-то делает, но без огонька.

Средний — уже начинает шевелиться.

Сложный — плодится и мигрирует.

Максимальный — создаёт дохрена видов за ход. Возможно, вы будете первым человеком, выигравшим в битве с ним на время > 5 ходов.

Существа[править]

Начало игры. За исключением умников, которые решили мигрировать, состояние у всех одинаковое.

У каждого вида есть размер, популяция, интеллект, скорость, вооружение, защита, маскировка, обзор, хищность и зона.

Максимальная популяция зависит от размера: чем ты больше, тем меньше вас влезает в экосистему. Размер 1 — 1024 особи, размер 10 — 2 особи. То есть гигантизм — это и эволюционный успех, и демографическая ошибка.

Травоядные плодятся сами: мелкие быстро, крупные медленно. Хищники плодятся, только если пожрут. Если хищник не пожрал — population = 0. Без права на пересдачу.

Маскировка зависит от размера: мелкие хорошо прячутся, крупные — плохо. Потому что слона не видно, только если он стоит в Эрмитаже.

Экосистемы[править]

Если вы не будете мигрировать, то фиг вам а не картинки.

Есть семь зон: мелководье, глубоководье, реки, болота, леса, степи, пустыни. Они соединены так, что из пустыни можно прийти в реки, леса и степи. Так что в пустыне, видимо, растут леса. Не спрашивайте как.

Охота возможна только внутри одной зоны. Если ты в глубоководье, а еда на мелководье, ты можешь только смотреть и грустить.

При миграции игра показывает ASCII-арт зоны. Выглядит так, будто по клавиатуре играли как на рояле.

Ход[править]

Боты создают новые виды. Чем выше уровень, тем БОЛЬШЕ.

Игрок создаёт новые виды. Если у вида популяция ≥ 2, можно отщипнуть две особи и получить новый вид с популяцией 1. Это называется «эволюция», а по сути — почкование с потерей численности.

Миграция. Можно переселить вид в соседнюю зону и получить новую картинку.

Смена статуса. Со второго хода можно стать хищником. Или перестать. Или снова стать. Никто не запрещает быть хищником-травоядным-хищником, как в жизни.

Карты. Игроку дают 2–5 карт. Среди них могут быть: «Увеличение размера», «Когти», «Панцирь», «Крылья», «Мозговые извилины», «Очень быстрое размножение» (+70 популяции, −4 размера), «Обжорство» (+3 размер, +5 броня, −4 скорость, −3 интеллект). Если отказаться — дают +1 популяцию. Отказ от карты — тоже карта, но для бедных.

Боты просто жмут случайно, как кот по клавиатуре.

Охота[править]

Первые неудачники показались на горизонте... Но сдохли от голода.

Хищник выбирает жертву. Игрок тыкает номер бота и вида. Бот выбирает случайно.

Перед охотой можно использовать интеллект: временно ослабить жертву по обзору, скорости, вооружению и защите. Интеллект — это не чтобы думать, а чтобы делать другим хуже.

Условия успешной охоты:

зона совпадает;

зрение хищника ≥ маскировки жертвы;

зрение жертвы ≤ маскировки хищника;

скорость хищника > скорости жертвы;

вооружение × размер ≥ защита × размер жертвы;

вооружение жертвы × размер ≤ защита × размер хищника.

Если всё ок — хищник съедает до своей популяции особей жертвы и столько же прибавляет себе. Если три попытки провалились — хищник умирает от голода. Три промаха, и ты труп. Естественный отбор, детка.

Финал[править]

Очки считаются так: размер × популяция + интеллект. Хищникам бонус: +25 при размере 1–5, +50 при размере 6–10. Победитель — у кого больше.

Если игрок проиграл, игра пишет: «Не печальтесь, в следующий раз вы учтёте ошибки и выиграете». Это ложь, но приятная.

Стратегии для одного вида[править]

Стать хищником и жрать всех. Но если промахнулся — умри.

Стать травоядным и размножаться до 1024. Потом тебя съедают, но ты был плодовит.

Качать интеллект и унижать жертву перед охотой. Это не интеллект, это токсичность.

Мигрировать в пустыню. Там никто не живёт, кроме тебя и кактуса.

Создавать новые виды, пока таблица не перестанет влезать в экран. Вы скоро запутаетесь с видами и будете тыкать наугад

Интересные факты[править]

Вам кажется графика ужасной? Тогда взгляните, что было вначале.

Графика в этой игре вроде-бы есть, но это высказывание натянуто за уши.

Карта «Очень быстрое размножение» даёт +70 популяции и −4 размера. После неё ты не вид, а облако мошек.

«Обжорство» даёт +5 защиты и −4 скорости. Ты превращаешься в бронированную подушку, которую никто не хочет есть, но и ты никого не догонишь.

В коде пустыни соединены с лесами. Так что в пустыне растут леса. Не спрашивайте.

Если все ваши виды вымерли, игра выходит. Это не баг, это ускоренная эволюция.

Код игры[править]

import random
import sys

# ---------- Константы экосистем ----------
ZONES = ["shallow", "deep", "rivers", "swamps", "forests", "steppes", "deserts"]
ZONE_NAMES_RU = {
    "shallow": "Мелководье",
    "deep": "Глубоководье",
    "rivers": "Реки",
    "swamps": "Болота",
    "forests": "Леса",
    "steppes": "Степи",
    "deserts": "Пустыни"
}
ZONE_CONNECTIONS = {
    "shallow": ["deep", "rivers"],
    "deep": ["shallow"],
    "rivers": ["shallow", "swamps", "deserts"],
    "swamps": ["rivers", "forests", "steppes"],
    "forests": ["swamps", "deserts"],
    "steppes": ["swamps", "deserts"],
    "deserts": ["rivers", "forests", "steppes"]
}

# ---------- Ввод количества ходов ----------
turns = int(input("Сколько ходов должна длиться игра (оптимально 3–7): "))

# ---------- Ввод количества ботов и их уровней сложности ----------
while True:
    try:
        num_bots = int(input("Введите количество ботов (0–10): "))
        if 0 <= num_bots <= 10:
            break
        else:
            print("Число должно быть от 0 до 10.")
    except ValueError:
        print("Введите целое число.")

LEVEL_NAMES = {
    1: "Минимальный",
    2: "Простой",
    3: "Средний",
    4: "Сложный",
    5: "Максимальный"
}

LEVEL_PARAMS = {
    1: (None, None, 0, 0),
    2: (3, 4, 1, 1),
    3: (2, 3, 1, 5),
    4: (1, 2, 1, 15),
    5: (1, 1, None, None)
}

bot_configs = []
for i in range(num_bots):
    print(f"\nБот {i+1}:")
    while True:
        choice = input("Выберите уровень сложности (1-Минимальный, 2-Простой, 3-Средний, 4-Сложный, 5-Максимальный), по умолчанию 2: ").strip()
        if choice == "":
            level = 2
            break
        try:
            level = int(choice)
            if 1 <= level <= 5:
                break
            else:
                print("Введите число от 1 до 5.")
        except ValueError:
            print("Введите число.")
    if level == 1:
        counter = 0
    elif level == 5:
        counter = 1
    else:
        interval_min, interval_max, _, _ = LEVEL_PARAMS[level]
        counter = random.randint(interval_min, interval_max)
    bot_configs.append({
        'level': level,
        'counter': counter
    })

# ---------- Классы ----------
class Creature:
    def __init__(self, name="Существо", zone="shallow"):
        self.name = name
        self.size = 1
        self.population = 1
        self.intelligence = 1
        self.speed = 1
        self.weapons = 1
        self.armor = 1
        self.camouflage = 1
        self.vision = 1
        self.is_predator = False
        self.zone = zone

    def is_alive(self):
        return self.population > 0

    def max_population(self):
        return 1024 // (2 ** (self.size - 1))

    def growth_rate(self):
        size = self.size
        if size == 1:
            return 10
        elif size == 2:
            return 8
        elif size == 3:
            return 6
        elif size == 4:
            return 4
        elif size == 5:
            return 3
        elif size == 6:
            return 2
        else:
            return 1

    def max_camouflage(self):
        return max(1, 11 - self.size)

    def clamp_camouflage(self):
        max_c = self.max_camouflage()
        if self.camouflage > max_c:
            self.camouflage = max_c

    def apply_effects(self, effects):
        for attr, delta in effects.items():
            if hasattr(self, attr) and attr != 'is_predator' and attr != 'zone':
                current = getattr(self, attr)
                new_val = current + delta

                if attr == 'population':
                    self.add_population(delta)
                elif attr == 'size':
                    new_val = max(1, min(10, new_val))
                    setattr(self, attr, new_val)
                    self.clamp_camouflage()
                elif attr == 'camouflage':
                    max_c = self.max_camouflage()
                    new_val = max(1, min(max_c, new_val))
                    setattr(self, attr, new_val)
                else:
                    new_val = max(1, min(10, new_val))
                    setattr(self, attr, new_val)

    def add_population(self, delta):
        new_val = self.population + delta
        max_pop = self.max_population()
        new_val = max(0, min(max_pop, new_val))
        self.population = new_val

    def total_score(self):
        return (self.size * self.population) + self.intelligence

    def final_score(self):
        base = self.total_score()
        if self.is_predator:
            if self.size <= 5:
                base += 25
            else:
                base += 50
        return base

    def __str__(self):
        status = "Хищник" if self.is_predator else "Травоядное"
        alive = "жив" if self.is_alive() else "ВЫМЕР"
        zone_ru = ZONE_NAMES_RU.get(self.zone, self.zone)
        return (f"{self.name} ({status}, {alive}, зона={zone_ru}): "
                f"Размер={self.size}, Популяция={self.population}, "
                f"Интеллект={self.intelligence}, Скорость={self.speed}, "
                f"Вооружение={self.weapons}, Защита={self.armor}, "
                f"Маскировка={self.camouflage}, Обзор={self.vision}")

class Card:
    def __init__(self, name, effects):
        self.name = name
        self.effects = effects

    def apply(self, creature):
        creature.apply_effects(self.effects)

    def __str__(self):
        return self.name

# ---------- Пул карт ----------
def create_card_pool():
    base_pool = [
        Card("Увеличение размера", {"size": 1}),
        Card("Увеличение популяции", {"population": 3}),
        Card("Когти", {"weapons": 2, "speed": -1}),
        Card("Панцирь", {"armor": 3, "speed": -2}),
        Card("Крылья", {"speed": 4, "size": -1}),
        Card("Камуфляж", {"camouflage": 2}),
        Card("Острое зрение", {"vision": 2}),
        Card("Мозговые извилины", {"intelligence": 2}),
        Card("Рога", {"weapons": 1, "armor": 1, "speed": -1}),
        Card("Шипы", {"armor": 1, "weapons": 1, "speed": -1}),
        Card("Ядовитые зубы", {"weapons": 2}),
        Card("Толстая кожа", {"armor": 2, "speed": -1}),
    ]
    new_cards = [
        Card("Быстрое размножение", {"population": 3, "size": -1}),
        Card("Очень быстрое размножение", {"population": 70, "size": -4}),
        Card("Грива", {"armor": 2, "speed": -1}),
        Card("Развитый мозг", {"intelligence": 2}),
        Card("Острые когти", {"weapons": 2, "speed": -1}),
        Card("Длинные ноги", {"speed": 1, "size": 1}),
        Card("Засада", {"camouflage": 3, "population": -1}),
        Card("Эхолокация", {"vision": 2, "intelligence": 1}),
        Card("Ядовитые шипы", {"weapons": 1, "armor": 1}),
        Card("Крепкий скелет", {"size": 1, "armor": 1}),
        Card("Повышенная плодовитость", {"population": 3, "size": -1}),
        Card("Создание орудий труда", {"intelligence": 2, "speed": -1, "weapons": 2}),
        Card("Ночное зрение", {"vision": 2}),
        Card("Терморегуляция", {"size": 1, "speed": 1}),
        Card("Совершенные органы чувств", {"vision": 2}),
        Card("Социальность", {"population": 2, "intelligence": 1}),
        Card("Хищнические инстинкты", {"weapons": 2, "speed": 1}),
        Card("Защитная окраска", {"camouflage": 2}),
        Card("Мускулистость", {"speed": 2, "weapons": 1, "size": 1}),
        Card("Регенерация", {"armor": 2, "population": 1}),
        Card("Симбиоз", {"population": 1, "camouflage": 1}),
        Card("Стратегическое мышление", {"intelligence": 3, "speed": -2}),
        Card("Сверхчувствительность", {"vision": 3}),
        Card("Токсичная кровь", {"armor": 1}),
        Card("Колючки", {"armor": 2, "speed": -1}),
        Card("Карликовость", {"size": -3, "population": 2}),
        Card("Гигантизм", {"size": 2, "population": -1}),
        Card("Использование чужих раковин", {"size": 2, "armor": 3, "speed": -2}),
        Card("Парус", {"size": 3, "armor": -1, "speed": -2}),
        Card("Обжорство", {"size": 3, "armor": 5, "speed": -4, "intelligence": -3}),
        Card("Сворачивание", {"size": 1, "armor": 1}),
    ]
    return base_pool + new_cards

def random_card(pool):
    return random.choice(pool)

def generate_cards(pool, count):
    return [random_card(pool) for _ in range(count)]

# ---------- Вспомогательные функции ----------
PARAM_NAMES_RU = {
    "size": "Размер",
    "population": "Популяция",
    "intelligence": "Интеллект",
    "speed": "Скорость",
    "weapons": "Вооружение",
    "armor": "Защита",
    "camouflage": "Маскировка",
    "vision": "Обзор"
}

def print_header():
    print("\n" + "=" * 60)
    print("          ЭВОЛЮЦИЯ: Хищники и экосистемы")
    print("=" * 60)

def print_stats(all_creatures):
    alive = [c for c in all_creatures if c.is_alive()]
    if not alive:
        print("Все существа вымерли!")
        return

    headers = ["Вид", "Тип", "Зона", "Размер", "Попул.", "Мозг", "Скорость", "Вооруж.", "Защит", "Маск.", "Обзор", "Сум"]
    param_keys = ["size", "population", "intelligence", "speed", "weapons", "armor", "camouflage", "vision"]
    
    rows = []
    for c in alive:
        type_str = "Х" if c.is_predator else "Т"
        zone_ru = ZONE_NAMES_RU.get(c.zone, c.zone)[:4]
        name = c.name[:20]
        values = [str(getattr(c, key)) for key in param_keys]
        total = str(c.total_score())
        rows.append([name, type_str, zone_ru] + values + [total])

    col_widths = [len(h) for h in headers]
    for row in rows:
        for i, cell in enumerate(row):
            col_widths[i] = max(col_widths[i], len(cell))
    col_widths = [w + 2 for w in col_widths]

    def format_row(cells):
        return "| " + " | ".join(cell.ljust(col_widths[i]) for i, cell in enumerate(cells)) + " |"

    def separator():
        return "+" + "+".join("-" * (w+2) for w in col_widths) + "+"

    def print_header_table():
        print(separator())
        print(format_row(headers))
        print(separator())

    n = len(rows)
    print("\nТекущее состояние (живые виды):")
    print_header_table()

    for i, row in enumerate(rows):
        print(format_row(row))
        row_num = i + 1
        if n >= 10 and row_num != n:
            if row_num % 20 == 0:
                print(separator())
                print_header_table()
            elif row_num % 5 == 0:
                print(separator())
    print(separator())

def get_player_action(card, creature):
    while True:
        print(f"\nКарта: {card.name}")
        effects_parts = []
        for key, val in card.effects.items():
            ru_name = PARAM_NAMES_RU.get(key, key)
            sign = "+" if val >= 0 else ""
            effects_parts.append(f"{ru_name}: {sign}{val}")
        effects_str = ", ".join(effects_parts)
        print(f"Эффекты: {effects_str}")
        choice = input("Применить? (y/n): ").strip().lower()
        if choice in ('y', 'n'):
            return choice == 'y'
        print("Введите 'y' или 'n'.")

def bot_turn_for_creature(creature, pool, card_count):
    cards = generate_cards(pool, card_count)
    for card in cards:
        if random.random() < 0.5:
            card.apply(creature)
        else:
            creature.add_population(1)

# ---------- Функции для интеллекта ----------
def apply_temporary_debuff(prey, allocation):
    old_values = {}
    for attr, amount in allocation.items():
        if attr in ['vision', 'speed', 'weapons', 'armor']:
            old = getattr(prey, attr)
            old_values[attr] = old
            new_val = max(1, old - amount)
            setattr(prey, attr, new_val)
    return old_values

def restore_parameters(prey, old_values):
    for attr, val in old_values.items():
        setattr(prey, attr, val)

def can_hunt(predator, prey):
    if predator is prey:
        return False
    if not predator.is_alive() or not prey.is_alive():
        return False
    if predator.zone != prey.zone:
        return False
    if predator.vision < prey.camouflage:
        return False
    if prey.vision > predator.camouflage:
        return False
    if predator.speed <= prey.speed:
        return False
    pred_weapon_eff = predator.weapons * predator.size
    prey_armor_eff = prey.armor * prey.size
    prey_weapon_eff = prey.weapons * prey.size
    pred_armor_eff = predator.armor * predator.size
    if pred_weapon_eff < prey_armor_eff:
        return False
    if prey_weapon_eff > pred_armor_eff:
        return False
    return True

# ---------- Выбор цели для игрока ----------
def choose_prey_for_player(predator, all_creatures, player_creatures, bot_species_list):
    prey_options = [c for c in all_creatures if c.is_alive() and c is not predator and c.zone == predator.zone]
    if not prey_options:
        print("Нет доступной добычи в вашей зоне.")
        return None

    print("\nДоступные цели для охоты (в вашей зоне):")
    for bot_idx, bot_species in enumerate(bot_species_list, start=1):
        bot_alive = [c for c in bot_species if c.is_alive() and c is not predator and c.zone == predator.zone]
        if bot_alive:
            print(f"  Бот{bot_idx}:")
            for idx, c in enumerate(bot_alive, start=1):
                print(f"    {bot_idx} {idx} -> {c.name} (поп.{c.population})")
    player_alive = [c for c in player_creatures if c.is_alive() and c is not predator and c.zone == predator.zone]
    if player_alive:
        print("  Игрок:")
        for idx, c in enumerate(player_alive, start=1):
            print(f"    И {idx} -> {c.name} (поп.{c.population})")

    while True:
        inp = input("Введите 'номер_бота номер_вида' (например, '1 3') или 'И номер_вида' для своего вида, или 0 для пропуска: ").strip()
        if inp == '0':
            return None
        parts = inp.split()
        if len(parts) == 2:
            if parts[0].upper() == 'И':
                try:
                    idx = int(parts[1]) - 1
                    if 0 <= idx < len(player_alive):
                        return player_alive[idx]
                    else:
                        print("Неверный номер вида игрока.")
                except ValueError:
                    print("Введите число после 'И'.")
            else:
                try:
                    bot_num = int(parts[0]) - 1
                    species_idx = int(parts[1]) - 1
                    if 0 <= bot_num < len(bot_species_list):
                        bot_alive = [c for c in bot_species_list[bot_num] if c.is_alive() and c is not predator and c.zone == predator.zone]
                        if 0 <= species_idx < len(bot_alive):
                            return bot_alive[species_idx]
                        else:
                            print("Неверный номер вида для этого бота.")
                    else:
                        print("Неверный номер бота (допустимо 1..{})".format(len(bot_species_list)))
                except ValueError:
                    print("Введите числа.")
        else:
            print("Введите два значения через пробел.")

# ---------- Решение бота использовать интеллект ----------
def bot_decide_intelligence(predator, prey):
    if predator.intelligence <= 0:
        return None
    if can_hunt(predator, prey):
        return None
    attrs = ['vision', 'speed', 'weapons', 'armor']
    import copy
    test_prey = copy.copy(prey)
    alloc = {}
    for attr in attrs:
        max_dec = getattr(test_prey, attr) - 1
        dec = min(predator.intelligence, max_dec)
        alloc[attr] = dec
        setattr(test_prey, attr, getattr(test_prey, attr) - dec)
    if can_hunt(predator, test_prey):
        return alloc
    else:
        return None

# ---------- Фаза охоты ----------
def hunting_phase(all_creatures, player_creatures, bot_species_list):
    predators = [c for c in all_creatures if c.is_alive() and c.is_predator]
    random.shuffle(predators)

    for predator in predators:
        is_player = predator in player_creatures

        if is_player:
            print(f"\nХищник игрока: {predator.name} (зона {ZONE_NAMES_RU[predator.zone]})")
            prey = choose_prey_for_player(predator, all_creatures, player_creatures, bot_species_list)
            if prey is None:
                print("Охота пропущена. Хищник умирает от голода.")
                predator.population = 0
                continue
        else:
            alive_prey = [c for c in all_creatures if c.is_alive() and c is not predator and c.zone == predator.zone]
            if not alive_prey:
                predator.population = 0
                continue
            prey = random.choice(alive_prey)

        use_intel = False
        allocation = {}
        if is_player:
            choice = input(f"Использовать интеллект ({predator.intelligence}) для ослабления {prey.name}? (y/n): ").strip().lower()
            if choice == 'y' and predator.intelligence > 0:
                use_intel = True
                max_intel = predator.intelligence
                print(f"Интеллект: {max_intel}. Распределите очки по параметрам (обзор, скорость, вооружение, защита).")
                while True:
                    try:
                        inp = input("Введите четыре числа через пробел (сумма <= {}): ".format(max_intel)).strip().split()
                        if len(inp) != 4:
                            print("Нужно ровно четыре числа.")
                            continue
                        nums = [int(x) for x in inp]
                        if sum(nums) > max_intel:
                            print(f"Сумма {sum(nums)} превышает интеллект {max_intel}.")
                            continue
                        if any(n < 0 for n in nums):
                            print("Числа должны быть неотрицательными.")
                            continue
                        allocation = {'vision': nums[0], 'speed': nums[1], 'weapons': nums[2], 'armor': nums[3]}
                        break
                    except ValueError:
                        print("Введите целые числа.")
        else:
            alloc = bot_decide_intelligence(predator, prey)
            if alloc is not None:
                use_intel = True
                allocation = alloc
                print(f"{predator.name} использует интеллект для ослабления {prey.name}.")

        old_values = {}
        if use_intel and allocation:
            old_values = apply_temporary_debuff(prey, allocation)
            print(f"Ослабление применено: {prey.name} параметры уменьшены.")

        hunt_success = False  # флаг, была ли хотя бы одна успешная атака

        for attempt in range(1, 4):  # три попытки
            if not prey.is_alive():
                break

            if can_hunt(predator, prey):
                # Сколько особей съедает хищник (не больше, чем есть у жертвы)
                eat = predator.population
                if eat > prey.population:
                    eat = prey.population
                if eat <= 0:
                    # Если популяция хищника = 0, он мёртв, выходим
                    break

                prey.add_population(-eat)
                predator.add_population(eat)

                hunt_success = True
                # Вывод сообщения об успешной охоте
                if is_player:
                    print(f"Успешная охота! {predator.name} съедает {eat} особей {prey.name}.")
                else:
                    print(f"{predator.name} успешно охотится на {prey.name} и съедает {eat} особей.")
            else:
                # Неудачная попытка (сообщение можно не выводить, чтобы не засорять, но для наглядности оставим)
                if is_player:
                    print(f"Попытка {attempt} не удалась: {predator.name} не смог поймать {prey.name}.")
                else:
                    # Для ботов выводим только если включена отладка, здесь пропускаем
                    pass

        # Если ни одна атака не удалась, хищник умирает от голода
        if not hunt_success and predator.is_alive():
            predator.population = 0
            if is_player:
                print(f"{predator.name} не смог добыть пищу и вымер от голодания.")
            else:
                print(f"{predator.name} не смог добыть пищу и вымер.")

        # Восстанавливаем параметры жертвы после ослабления (если применялось)
        if old_values:
            restore_parameters(prey, old_values)

def grow_herbivores(all_creatures):
    for c in all_creatures:
        if c.is_alive() and not c.is_predator:
            c.add_population(c.growth_rate())

# ---------- Смена статуса ----------
def player_switch_predator(creature, turn, all_creatures):
    if turn <= 1 or not creature.is_alive():
        return
    while True:
        print(f"\n{creature.name}: сейчас вы {'хищник' if creature.is_predator else 'травоядное'}.")
        choice = input("Хотите сменить статус? (y/n): ").strip().lower()
        if choice == 'n':
            break
        elif choice == 'y':
            if creature.is_predator:
                creature.is_predator = False
                print("Теперь травоядное.")
                break
            else:
                creature.is_predator = True
                print("Теперь хищник!")
                break
        else:
            print("Введите 'y' или 'n'.")

def bot_switch_predator(creature, turn, all_creatures):
    if turn <= 1 or not creature.is_alive():
        return
    if random.random() < 0.5:
        if not creature.is_predator:
            if has_any_prey(creature, all_creatures):
                creature.is_predator = True
        else:
            creature.is_predator = False

def has_any_prey(predator, all_creatures):
    for other in all_creatures:
        if other is not predator and other.is_alive() and other.zone == predator.zone:
            if can_hunt(predator, other):
                return True
    return False

# ---------- Создание новых видов ----------
def create_new_species_from_parent(parent, new_name):
    if parent.population < 2:
        return None
    parent.add_population(-2)
    child = Creature(new_name, zone=parent.zone)
    for attr in ['size', 'intelligence', 'speed', 'weapons', 'armor', 'camouflage', 'vision', 'is_predator']:
        setattr(child, attr, getattr(parent, attr))
    child.population = 1
    child.clamp_camouflage()
    return child

def player_create_new_species(player_creatures):
    candidates = [c for c in player_creatures if c.is_alive() and c.population >= 2]
    if not candidates:
        print("Нет видов с популяцией ≥ 2, нельзя создать новый вид.")
        return

    print("\n--- Создание новых видов (игрок) ---")
    print("Введите номера видов (через пробел), из которых хотите создать по одному новому виду.")
    print("Например: '1 1 3' создаст 2 вида из первого и 1 из третьего.")
    print("Доступные виды:")
    for i, c in enumerate(candidates, start=1):
        max_new = c.population // 2
        print(f"{i}. {c.name} (популяция: {c.population}, можно создать максимум {max_new})")

    while True:
        inp = input("Введите номера (или 0 для отмены): ").strip()
        if inp == '0':
            return
        parts = inp.split()
        if not parts:
            print("Введите хотя бы одно число.")
            continue
        valid = True
        indices = []
        for p in parts:
            try:
                num = int(p)
                if num <= 0:
                    print("Номера должны быть положительными.")
                    valid = False
                    break
                indices.append(num)
            except ValueError:
                print(f"'{p}' не является числом.")
                valid = False
                break
        if not valid:
            continue

        created = 0
        for idx in indices:
            if idx < 1 or idx > len(candidates):
                print(f"Номер {idx} вне допустимого диапазона (1..{len(candidates)}), пропускаем.")
                continue
            parent = candidates[idx - 1]
            if not parent.is_alive() or parent.population < 2:
                print(f"Родитель {parent.name} уже не может дать потомство (популяция < 2), пропускаем.")
                continue
            new_name = f"Игрок, вид{len(player_creatures)+1}"
            child = create_new_species_from_parent(parent, new_name)
            if child is None:
                print(f"Не удалось создать вид из {parent.name}.")
                continue
            player_creatures.append(child)
            created += 1
            print(f"Создан новый вид: {child.name} (популяция 1, унаследован от {parent.name}, зона {ZONE_NAMES_RU[child.zone]})")
        print(f"Создано {created} новых видов.")

def create_new_species_for_bot(bot_species, bot_name):
    candidates = [c for c in bot_species if c.is_alive() and c.population >= 2]
    if not candidates:
        return False
    parent = random.choice(candidates)
    new_name = f"{bot_name}, вид{len(bot_species)+1}"
    child = create_new_species_from_parent(parent, new_name)
    if child is None:
        return False
    bot_species.append(child)
    return True

def remove_dead(species_list):
    species_list[:] = [c for c in species_list if c.is_alive()]

# ---------- Миграция ----------
def print_zone_art(zone):
    """Выводит ASCII-картинку для зоны и приветствие."""
    zone_ru = ZONE_NAMES_RU.get(zone, zone)
    print(f"\nДобро пожаловать в {zone_ru}!\n")
    if zone == "shallow":
        print("     @    𓅯    ")
        print("𓇗_𓅰_/_       𓅯")
        print("^^^^^^\__𓅯      𓅯     _𓅻___")
        print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
        print("  ⬳     𓆝     𖦹       𓆉︎    𓆟")
        print("  𓆟 𓇗 𓆑    𓇗   𓆛   𓍼    𓆞   ")
        print("__𓆨_𓇗_𓆜__𓇗𓆨___𓆟____𓇗_____𓇗__")
    elif zone == "rivers":
        print("█████_𓅯__//%\\\\____~~~~~~~~__")
        print("████___☘__☘_𓅯__~~~~~~~~~~~~☘_")
        print("^^^^\\__𓅯~~~~~𓅯~~~~~~_𓅻__𓆨___")
        print("_𓆏__~~~~~~~~~~~~~~~~/_𓅟___𓅢_")
        print("  ⬳ 𓆊   𓆝     𓆉︎  𓇗 ▟████████")
        print("  𓆟 𓇗 𓆑    𓇗 𓆛  ▟███████████")
        print("__𓆨_𓇗_𓆜__𓇗𓆨___▟█████████████")
    elif zone == "deep":
        print("⛆⛆⛆⛆⛆⛆⛆⛆⛆⛆⛆⛆⛆⛆⛆⛆⛆")
        print("⛆⛆⛆𓍼⛆⛆⛆𖡎⛆⛆⛆⛆ 𓆜 ⛆⛆")
        print("⛆⛆⛆⛆⛆⛆𖡎⛆⛆⛆⛆⛆⛆⛆⛆𖡎⛆")
        print("⛆⛆⛆⛆⛆𖡎⛆⛆⛆⛆⛆⛆⛆⛆𖡎⛆⛆")
        print("⛆⛆⛆⛆𖡎⛆⛆⛆⛆⛆ 𓆜 ⛆𖡎⛆⛆")
        print("⛆⛆⛆𖡎⛆⛆⛆⛆⛆⛆⛆⛆𖡎⛆⛆⛆⛆")
        print("⛆⛆𖡎𓆨⛆⛆⛆⛆⛆⛆⛆𖡎⛆⛆⛆⛆⛆")
    elif zone == "swamps":
        print("☁☁☁☁☁☁☁☁☁☁☁☁☁☁☁☁☁")
        print("⛆ލ⛆↯⛆⛆⛆ᔪ⛆⛆⛆ލ⛆⛆↯⛆⛆")
        print("𖡼𖤣𖥧𖡼𓋼𖤣𖥧𓋼𓍊𖡼𖤣𖥧𖡼𓋼𖤣𖥧𓋼𓍊")
        print("𓍊𓋼~𓅯 𖥧𓍊𖡼𖤣~𓍊𓋼𖥧~𓍊𖥧𖡼𖤣")
        print("𓅻𓍊𓋼𓍊𓋼𓍊_𓅤_𖥧_𓆉︎_𓅻_𖥧𓅭")
        print("~~𓆏~~~𖤣𖥧𓆏~~~𖤣𖥧~𓆌~")
        print("𓆜/𓆝 \\/𓆜  𓆝 )/ 𓆜 ")
    elif zone == "forests":
        print("☁ ☁    ☁  ☁☁ ☁  ☁")
        print("  𓅯  𓅯   ⛆ᔪ   𓅯    ")
        print("𖥧𖤣𖥧𖡼𓋼𖤣𖥧𓋼𓍊𖡼𖤣𖥧𖡼𓋼𖤣𖥧𓋼𓍊")
        print("𖥧𓋼𓍊𖡼𓋼𖤣𖥧𓋼𓍊𖡼𖤣𖥧𖡼𓋼𖤣𖥧𓋼𓍊")
        print("𖡼𓋼𖤣𖥧𓋼𓍊𖡼𖤣𖥧𖡼𓋼𖤣𖥧𓋼𓍊𖡼𖤣𖥧")
        print("𖤣𖥧𖡼𓅪𖤣𖥧𓋼𓍊𓅇𖤣𖥧𖡼𓋼𖤣𖥧𓋼𓍊𖤣")
        print("𓅼𖤣𖥧𓃦𓋼𖤣𖥧𓆈𓍊𖡼𖤣𖥧𓃹𓋼𖤣𖥧𓋼𓆤")
    elif zone == "steppes":
        print("         ☼         ")
        print("༄༄༄༄༄༄༄༄༄༄༄༄༄༄༄༄༄༄")
        print("◞◞◞◞◞◞◞◞◞◞◞◞◞◞◞◞◞◞")
        print("ᖗᖘ૬୭ໂໃໄ⌇ᖗᖘ૬୭ໄ𓃶ᖗ૬ມີເກມທີ່ດີ")
        print("∭६३ᖘ૬१२𓃲५७९ϡ𓃶ᖘ૬୭ঐঙৡ∭६")
        print("ໂໃໄ𓃶❀ໂᖘ૬୭ໄ𓃹ᖘ૬୭☘︎𓃶ᖘ૬୭❀𓃶")
        print("𓃦∭६३𓆚∭𓃶ᖗᖘ☘︎𓃶ໃໄ⌇𓃹ᖘ૬୭☘︎𓅼")
    elif zone == "deserts":
        print("         ☼         ")
        print("")
        print("◞◞◞◞◞◞◞◞◞◞◞◞◞◞◞◞◞◞")
        print("🏜 🏜 🏜 🏜 🏜 🏜 🏜 🏜 🏜 🏜")
        print("🏝 𖣂🏜 🏜 🏜 𓅇 🏜 🏝𖣂 🏜 🏜")
        print("𓆱𓆱𓆱𓃲𓃲𓃲𓃲𓃲𓆱𓆱𓃹𓆱𓆱 𓆱𓃶𓆱𓆱")
        print("𓆨𓆱𓆱𓆱🕷𓆱𓆱𓆱𓆨𓆱𓆱𓆱𓆱𓆱𓆱🕷𓆨")
    else:
        print(f"(Картинка для зоны {zone_ru} не найдена)")

def player_migration(player_creatures):
    alive = [c for c in player_creatures if c.is_alive()]
    if not alive:
        return
    print("\n--- Миграция видов (игрок) ---")
    print("Вы можете переселить некоторые виды в соседние экосистемы.")
    print("Введите номера видов (через пробел), которые хотите передвигать, или 0 для пропуска.")
    for i, c in enumerate(alive, start=1):
        zone_ru = ZONE_NAMES_RU.get(c.zone, c.zone)
        print(f"{i}. {c.name} (зона: {zone_ru}, популяция: {c.population})")

    while True:
        inp = input("Введите номера (или 0 для завершения): ").strip()
        if inp == '0':
            return
        parts = inp.split()
        if not parts:
            print("Введите номера.")
            continue
        indices = []
        try:
            for p in parts:
                num = int(p)
                if num <= 0:
                    print("Номера должны быть положительными.")
                    raise ValueError
                indices.append(num)
        except ValueError:
            continue

        for idx in indices:
            if idx < 1 or idx > len(alive):
                print(f"Номер {idx} вне диапазона, пропускаем.")
                continue
            creature = alive[idx - 1]
            zone_ru = ZONE_NAMES_RU.get(creature.zone, creature.zone)
            neighbors = ZONE_CONNECTIONS.get(creature.zone, [])
            if not neighbors:
                print(f"У зоны {zone_ru} нет соседей, миграция невозможна.")
                continue
            print(f"\nПеремещаем {creature.name} из {zone_ru}.")
            print("Доступные соседние зоны:")
            for j, nz in enumerate(neighbors, start=1):
                print(f"  {j}. {ZONE_NAMES_RU.get(nz, nz)}")
            while True:
                choice = input("Выберите номер новой зоны (0 - отмена): ").strip()
                if choice == '0':
                    break
                try:
                    n_idx = int(choice) - 1
                    if 0 <= n_idx < len(neighbors):
                        new_zone = neighbors[n_idx]
                        creature.zone = new_zone
                        print(f"{creature.name} перемещён в {ZONE_NAMES_RU[new_zone]}.")
                        # Показываем картинку новой зоны
                        print_zone_art(new_zone)
                        break
                    else:
                        print("Неверный номер.")
                except ValueError:
                    print("Введите число.")

def bot_migrate(bot_species):
    alive = [c for c in bot_species if c.is_alive()]
    if not alive:
        return
    creature = random.choice(alive)
    neighbors = ZONE_CONNECTIONS.get(creature.zone, [])
    if not neighbors:
        return
    new_zone = random.choice(neighbors)
    creature.zone = new_zone

# ---------- Основная игра ----------
def main():
    random.seed()

    # Создаём начальные виды
    player_creatures = [Creature("Игрок, вид1", zone="shallow")]
    bot_species_list = []
    for i in range(num_bots):
        bot_species_list.append([Creature(f"Бот{i+1}, вид1", zone="shallow")])

    bot_migrate_counters = [random.randint(1, 3) for _ in range(num_bots)]

    card_pool = create_card_pool()

    print_header()
    print(f"Игра будет длиться {turns} ходов.")
    print("Цель: набрать максимальную сумму очков (суммируются все ваши виды).")
    print("В начале каждого хода можно создать несколько новых видов от разных предков.")
    print("Боты могут создавать новые виды в зависимости от выбранного уровня сложности.")
    print("Выершие виды автоматически удаляются.")
    print("Механика интеллекта: хищник может ослабить параметры жертвы перед охотой.")
    print("Игрок (и бот) может стать хищником со 2-го хода.")
    print("Охота полностью контролируется игроком – выбор цели и её ослабление.")
    print("Максимальная популяция зависит от размера (1->1024, 2->512, ... 10->2).")
    print("Прирост численности травоядных обратно-пропорционален размеру.")
    print("Максимальная маскировка теперь зависит от размера (у мелких лучше).")
    print("Хищники получают бонус в финальном счёте: +25 (размер 1-5) и +50 (размер 6-10).")
    print("Добавлены экосистемы: мелководье, глубоководье, реки, болота, леса, степи, пустыни.")
    print("Виды могут мигрировать между соседними зонами. Охота возможна только внутри одной зоны.")
    input("Нажмите Enter, чтобы начать...")

    # Показываем приветственную картинку мелководья
    print_zone_art("shallow")

    for turn in range(1, turns + 1):
        print(f"\n---< Ход {turn} >---")

        # 0. Создание новых видов у ботов (с учётом уровней сложности)
        for i, bot_species in enumerate(bot_species_list):
            config = bot_configs[i]
            level = config['level']
            if level == 1:
                continue

            config['counter'] -= 1
            if config['counter'] <= 0:
                if level == 5:
                    num_to_create = 20
                else:
                    _, _, count_min, count_max = LEVEL_PARAMS[level]
                    num_to_create = random.randint(count_min, count_max)

                created = 0
                for _ in range(num_to_create):
                    if create_new_species_for_bot(bot_species, f"Бот{i+1}"):
                        created += 1
                    else:
                        break
                if created > 0:
                    print(f"Бот{i+1} создал {created} новых видов!")

                if level == 5:
                    config['counter'] = 1
                else:
                    interval_min, interval_max, _, _ = LEVEL_PARAMS[level]
                    config['counter'] = random.randint(interval_min, interval_max)

        # 1. Создание новых видов у игрока
        player_create_new_species(player_creatures)

        # 2. Миграция для игрока
        player_migration(player_creatures)

        # 3. Миграция для ботов
        for i, bot_species in enumerate(bot_species_list):
            bot_migrate_counters[i] -= 1
            if bot_migrate_counters[i] <= 0:
                bot_migrate(bot_species)
                bot_migrate_counters[i] = random.randint(1, 3)

        # Собираем всех существ
        all_creatures = player_creatures + [c for bot in bot_species_list for c in bot]

        # 4. Смена статуса для игрока
        for creature in player_creatures:
            if creature.is_alive():
                player_switch_predator(creature, turn, all_creatures)

        # Смена статуса для ботов
        for bot_species in bot_species_list:
            for creature in bot_species:
                if creature.is_alive():
                    bot_switch_predator(creature, turn, all_creatures)

        # 5. Показываем состояние
        print_stats(all_creatures)

        # 6. Ход каждого вида игрока
        for creature in player_creatures:
            if not creature.is_alive():
                continue
            print(f"\n--- Ход вида {creature.name} ---")
            cards_for_player = random.randint(2, 5)
            player_cards = generate_cards(card_pool, cards_for_player)
            print(f"Вы получили {len(player_cards)} карт для {creature.name}:")

            for i, card in enumerate(player_cards, 1):
                print(f"\nКарта {i}/{len(player_cards)}:")
                if get_player_action(card, creature):
                    card.apply(creature)
                    print("Карта применена.")
                else:
                    print("Карта заменена на увеличение популяции.")
                    creature.add_population(1)

        # 7. Ходы ботов
        for bot_species in bot_species_list:
            for creature in bot_species:
                if not creature.is_alive():
                    continue
                card_count = random.randint(2, 5)
                bot_turn_for_creature(creature, card_pool, card_count)

        # 8. Обновляем список всех существ
        all_creatures = player_creatures + [c for bot in bot_species_list for c in bot]

        # 9. Выводим обновлённую таблицу перед охотой
        print("\n---< Состояние перед охотой >---")
        print_stats(all_creatures)

        # 10. Фаза охоты
        print("\n---< Фаза охоты >---")
        hunting_phase(all_creatures, player_creatures, bot_species_list)

        # Проверяем, жив ли хоть один вид игрока
        player_alive = any(c.is_alive() for c in player_creatures)
        if not player_alive:
            print("\nВсе ваши виды вымерли! Игра окончена.")
            sys.exit(0)

        # 11. Прирост травоядных
        grow_herbivores(all_creatures)

        # 12. Удаляем мёртвых из всех списков
        remove_dead(player_creatures)
        for bot_species in bot_species_list:
            remove_dead(bot_species)

        print(f"\n---< Конец хода {turn} >---")
        input("Нажмите Enter для следующего хода...")

    # Финальная статистика
    print("\n" + "=" * 60)
    print("ИГРА ОКОНЧЕНА!")
    print("=" * 60)

    all_creatures = player_creatures + [c for bot in bot_species_list for c in bot]
    print_stats(all_creatures)

    player_score = sum(c.final_score() for c in player_creatures)
    bot_scores = []
    for i, bot_species in enumerate(bot_species_list):
        score = sum(c.final_score() for c in bot_species)
        bot_scores.append((f"Бот{i+1}", score))

    print(f"\nИтоговые суммы очков (с бонусами хищников):")
    print(f"Игрок: {player_score}")
    for name, score in bot_scores:
        print(f"{name}: {score}")

    all_participants = [("Игрок", player_score)] + bot_scores
    max_score = max(score for _, score in all_participants)
    winners = [name for name, score in all_participants if score == max_score]

    if len(winners) == 1:
        print(f"\nПобедитель: {winners[0]} с суммой {max_score} очков!")
        if winners[0] == "Игрок":
            print("Поздравляем!")
        else:
            print("Не печальтесь, ведь в следующий раз вы учтёте ошибки и выиграете!")
            print("Попробуйте снова!")
    else:
        print(f"\nНичья между {', '.join(winners)} с суммой {max_score} очков!")

if __name__ == "__main__":
    main()

См. также[править]

Эволюция

Симулятор эволюции