2025, Nov 25 00:02

Отладка while–else в Python: баг if в игре в кости и исправление

Почему while–else в Python не при чём, когда if «молчит»: в примере игры в кости условие сравнивает значение само с собой. Даем исправление и советы по отладке.

Отладка while–else в Python, которая «пропускает» if: разбор простой игры в кости

Что пошло не так

Вы делаете игру в кости для двух игроков, запускаете цикл на выбранное число раундов и затем определяете победителя. Всё выполняется, но финальный блок if после while так и не печатает ожидаемый результат. Под подозрение попадает while–else. На деле проблема проще: в последнем условии значение сравнивают само с собой, а такое сравнение никогда не будет истинным.

Проблемный пример

Ниже — фрагмент, который повторяет исходную логику и поток управления, включая баг, из‑за которого победитель не объявляется. Имена переменных изменены для наглядности, но поведение то же.

import random
# Предположим, это было подготовлено заранее из файла и т. п.
seen_count = 0  # сколько раз ранее встречалось слово "game"
token = "game"
next_game = seen_count + 1
p1_points = 0
p2_points = 0
def play_round():
    global p1_points, p2_points
    p1_roll = random.randint(0, 20)
    p2_roll = random.randint(0, 20)
    print("player 1 rolled", p1_roll)
    print("player 2 rolled", p2_roll)
    if p1_roll > p2_roll:
        print("Player 1 Wins!!" + "\n")
        p1_points += 1
    elif p1_roll < p2_roll:
        print("Player 2 Wins!!" + "\n")
        p2_points += 1
    else:
        print("It's a Tie!!" + "\n")
def run_match():
    global p1_points, p2_points
    total_rounds = int(input('How many rounds do you want?: '))
    limit = total_rounds
    idx = 0
    while idx < limit:
        play_round()
        idx += 1
    else:
        # ОШИБКА: сравнение p1_points само с собой
        if p1_points > p1_points:
            print("Player 1 Wins Game", seen_count)
        elif p1_points < p1_points:
            print("Player 2 Wins Game", seen_count)

Почему if никогда не срабатывает

Суть проблемы — в финальном сравнении. В условии p1_points проверяют относительно p1_points, а не p2_points. Число не может быть больше самого себя и не может быть меньше самого себя. Оба сравнения всегда ложные, поэтому ничего не выводится. Конструкция while–else здесь ни при чём: else у while выполняется, когда цикл завершился без break. Поскольку break в коде вообще нет, else запускается всегда, но сравнения внутри него просто не могут выполниться.

Есть и второй недочёт: номер игры для вывода берётся из seen_count, который хранит количество прошлых вхождений слова «game» в файле, а по смыслу нужен номер следующей игры. Следует использовать seen_count + 1.

Исправление

Сравнивайте суммарные очки игроков друг с другом, а не значение с самим собой, и выводите номер следующей игры. Остальной поток можно оставить прежним.

def run_match():
    global p1_points, p2_points
    total_rounds = int(input('How many rounds do you want?: '))
    limit = total_rounds
    idx = 0
    while idx < limit:
        play_round()
        idx += 1
    else:
        if p1_points > p2_points:
            print("Player 1 Wins Game", seen_count + 1)
        elif p1_points < p2_points:
            print("Player 2 Wins Game", seen_count + 1)
        else:
            print("It's a Draw!")

Если сохранить форму while–else, эта версия делает именно то, что вы ожидаете: завершает раунды, входит в else и корректно сообщает исход. Поскольку в цикле нет break, ветка else по сути избыточна — её можно заменить обычным кодом сразу после цикла; поведение останется тем же.

Почему это важно

Это классический пример, когда логическая ошибка маскируется под проблему управления потоком. Легко обвинить while–else, когда последняя ветка будто игнорируется, но истинная причина — самосравнение, которое не может стать истинным. Находя такие вещи заранее, вы не переделываете рабочие конструкции управления и сосредотачиваетесь на реальных проверках условий, определяющих результат игры или бизнес‑правила.

Что стоит запомнить

Если кажется, что if «отказывается выполняться», сначала проверьте операнды. Убедитесь, что сравниваете нужные переменные — особенно после копирования похожих строк. Для кода, завершающего цикл, помните, как работает while–else в Python: без break ветка else всегда будет выполняться. Если вы ведёте счётчики из внешних источников, вроде файла с таблицей лидеров, чётко разводите исторический счёт и следующий номер, который хотите показать. И небольшая стилистическая подсказка: глобальные имена нередко пишут в CONSTANT_CASING — так общий стейт легче заметить при ревью.

Держите поток управления простым, перепроверьте сравнения и валидируйте пользовательские числа вроде индексов игры на соответствие задуманному. Обычно этого достаточно, чтобы итоговые сводки после цикла вели себя предсказуемо.