Каппес Даниил Олегович

ПРАКТИКУМ PYTHON · ЗАНЯТИЕ 🎮

Три проекта

Числовая угадайка, магический шар и виселица — программы, в которые можно играть.

Про эти проекты

Три программы, которыми заканчивается курс. Это уже не задача на пять строк: у каждой есть меню, диалог с человеком и защита от неверного ввода.

Ничего нового изучать для них не нужно — всё собрано из пройденного: ввод-вывод, ветвления, циклы, списки, свои функции и модуль random. Если что-то из этого подзабылось, справочник по random и занятия открыты рядом.

У каждого проекта ниже — техническое задание по шагам, две подсказки, разбор и готовая программа. Открывать готовое стоит после своей попытки: проект тем и хорош, что его собирают сами.

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

1

Числовая угадайка

Программа загадывает случайное число от 1 до 100 и просит человека его угадать. Если названное число больше загаданного — программа сообщает об этом, если меньше — тоже. Когда число угадано, программа поздравляет игрока.

Из чего собирается: целые числа, переменные, input() и print(), if/elif/else, цикл while, бесконечный цикл, break и continue, модуль random.

Техническое задание.

  1. Подключите модуль random, загадайте число от 1 до 100 и поприветствуйте игрока: «Добро пожаловать в числовую угадайку».
  2. Напишите функцию is_valid(), которая принимает строку и возвращает True, если это целое число от 1 до 100, и False иначе.
  3. Организуйте цикл, который запрашивает у игрока число.
  4. Если введено не то — напечатайте «А может быть все-таки введем целое число от 1 до 100?» и перейдите к следующему шагу цикла.
  5. Если число меньше загаданного — «Ваше число меньше загаданного, попробуйте еще разок», если больше — «Ваше число больше загаданного, попробуйте еще разок», если совпало — «Вы угадали, поздравляем!».
  6. После выхода из цикла попрощайтесь: «Спасибо, что играли в числовую угадайку. Еще увидимся...».

Что можно улучшить: считать число попыток и показывать его в конце; предлагать сыграть ещё раз; спрашивать верхнюю границу диапазона.

Про стратегию. Чтобы наверняка угадать число от 1 до 100, хватит семи попыток. Называйте середину промежутка: если она мала — двигайте левую границу, если велика — правую. Каждый раз отбрасывается половина чисел, поэтому попыток нужно примерно log₂n — при ста числах это 7.

Подсказка 1

Сколько будет попыток, заранее неизвестно — значит, бесконечный цикл while True и выход по break, когда число угадано.

Подсказка 2

Проверку ввода удобно делать до преобразования в число: int('привет') остановит программу, а 'привет'.isdigit() спокойно вернёт False.

Разбор
  1. Загадываем число: random.randint(1, 100).
  2. Функция is_valid() сначала проверяет, что в строке одни цифры, а потом — что число попадает в диапазон.
  3. В цикле читаем ответ. Неверный ввод — печатаем замечание и делаем continue, чтобы попытка не засчиталась.
  4. Верный ввод — увеличиваем счётчик попыток и сравниваем с загаданным числом каскадом if/elif/else.
  5. Совпало — печатаем поздравление с числом попыток и выходим break.
  6. После цикла — прощальное сообщение.
Решение
import random


def is_valid(text):
    """Годится ли введённое: целое число от 1 до 100."""
    if not text.isdigit():          # буквы, пробелы, минус — сразу мимо
        return False
    return 1 <= int(text) <= 100


secret = random.randint(1, 100)     # загаданное число
tries = 0                           # сколько попыток сделано

print('Добро пожаловать в числовую угадайку')

while True:
    answer = input('Введите число от 1 до 100: ')

    if not is_valid(answer):
        print('А может быть все-таки введем целое число от 1 до 100?')
        continue                    # попытку не засчитываем

    number = int(answer)
    tries += 1

    if number < secret:
        print('Ваше число меньше загаданного, попробуйте еще разок')
    elif number > secret:
        print('Ваше число больше загаданного, попробуйте еще разок')
    else:
        print(f'Вы угадали за {tries} попыток, поздравляем!')
        break                       # число найдено — выходим из цикла

print('Спасибо, что играли в числовую угадайку. Еще увидимся...')

Обратите внимание на continue в проверке ввода: без него ошибочный ввод засчитывался бы как попытка. И на порядок в is_valid() — сначала isdigit(), потом сравнение: если проверить наоборот, программа упадёт на первом же слове вместо числа.

2

Магический шар 8

Магический шар (шар судьбы) — шуточный способ предсказывать будущее. Внутри настоящего шара плавает фигура с двадцатью гранями, и на каждой написан ответ. Программа спрашивает у человека вопрос и отвечает случайной фразой.

Из чего собирается: переменные, input() и print(), if/elif/else, цикл while, break и continue, списки, модуль random.

Техническое задание.

  1. Подключите модуль random и создайте список answers из двадцати ответов.
  2. Поприветствуйте игрока: «Привет Мир, я магический шар, и я знаю ответ на любой твой вопрос.»
  3. Спросите, как его зовут, и поздоровайтесь по имени.
  4. В цикле просите задать вопрос и печатайте случайный ответ функцией choice().
  5. Спрашивайте, будут ли ещё вопросы. Если нет — напечатайте «Возвращайся если возникнут вопросы!» и закончите работу.

Двадцать ответов настоящего шара делятся на четыре группы: десять положительных («Бесспорно», «Определённо да»), пять уклончивых («Спроси позже»), пять отрицательных («Мой ответ — нет»). Свой список можно составить и по-своему — лишь бы ответов было двадцать.

Подсказка 1

Сам вопрос программе не нужен: она отвечает случайно. Но прочитать его надо — иначе диалога не получится.

Подсказка 2

Ответ «да» может быть набран как угодно: приведите его к нижнему регистру методом lower(), прежде чем сравнивать.

Разбор
  1. Складываем двадцать ответов в список.
  2. Печатаем приветствие, читаем имя и здороваемся — здесь удобна f-строка.
  3. В бесконечном цикле читаем вопрос (результат никуда не сохраняем) и печатаем random.choice(answers).
  4. Спрашиваем про следующий вопрос, приводим ответ к нижнему регистру.
  5. Если ответ не «да» — прощаемся и выходим break.
Решение
import random

# двадцать граней настоящего шара судьбы
answers = ['Бесспорно',
           'Предрешено',
           'Никаких сомнений',
           'Определённо да',
           'Можешь быть уверен в этом',
           'Мне кажется — да',
           'Вероятнее всего',
           'Хорошие перспективы',
           'Знаки говорят — да',
           'Да',
           'Пока неясно, попробуй снова',
           'Спроси позже',
           'Лучше не рассказывать',
           'Сейчас нельзя предсказать',
           'Сконцентрируйся и спроси опять',
           'Даже не думай',
           'Мой ответ — нет',
           'По моим данным — нет',
           'Перспективы не очень хорошие',
           'Весьма сомнительно']

print('Привет Мир, я магический шар, и я знаю ответ на любой твой вопрос.')

name = input('Как тебя зовут? ')
print(f'Привет, {name}')

while True:
    input('Задай мне любой вопрос: ')     # сам вопрос шару не нужен
    print(random.choice(answers))

    again = input('Хочешь задать ещё вопрос? (да/нет) ').lower()
    if again != 'да':
        print('Возвращайся если возникнут вопросы!')
        break

Строка input('Задай мне любой вопрос: ') стоит без присваивания — и это не ошибка: программе нужен сам факт вопроса, а не его текст. Так тоже пишут, когда прочитанное заведомо не нужно.

3

Угадайка слов (виселица)

Программа загадывает слово, а игрок его отгадывает. Сначала все буквы скрыты, а рядом нарисована пустая виселица. Игрок называет букву: если она есть в слове, программа открывает её везде, где она встречается. Если буквы нет — к виселице добавляется часть тела повешенного. Частей шесть: голова, туловище, две руки и две ноги.

Из чего собирается: переменные, input() и print(), if/elif/else, циклы, break и continue, списки, свои функции, списочные выражения, модуль random.

Техническое задание.

  1. Создайте глобальный список word_list со словами для игры.
  2. Напишите функцию get_word(), которая возвращает случайное слово из списка в верхнем регистре.
  3. Напишите функцию display_hangman(tries), которая по числу оставшихся попыток возвращает рисунок виселицы: 6 — пустая, 0 — полностью нарисованный повешенный.
  4. Напишите функцию play(word) с основной логикой игры. Заведите в ней word_completion (открытые буквы), guessed_letters (названные буквы) и tries = 6.
  5. В начале партии напечатайте «Давайте играть в угадайку слов!», покажите пустую виселицу и слово из подчёркиваний.
  6. Обрабатывайте и одиночные буквы, и слово целиком. Предусмотрите защиту от дурака: если введена не буква — скажите об этом и не тратьте попытку.
  7. Если игрок называет уже названную букву — сообщите ему и попытку тоже не тратьте.
  8. Когда слово открыто целиком — «Поздравляем, вы угадали слово! Вы победили!». Когда попытки кончились — покажите загаданное слово.
  9. В основной программе организуйте партию за партией: пока игрок хочет играть, генерируйте новое слово и вызывайте play().

Что можно улучшить: по просьбе игрока открывать первую и последнюю букву слова.

Чтобы напечатать обратную косую черту, её пишут дважды: \\. Одиночная считается управляющим символом — как в \n.

Подсказка 1

Рисунки виселицы удобно сложить в список из семи строк: тогда stages[tries] сразу даёт нужный кадр, и никаких развилок не нужно.

Подсказка 2

Открыть букву в слове — значит собрать word_completion заново: там, где в слове стоит названная буква, ставим её, в остальных местах оставляем как было.

Разбор
  1. get_word() — одна строка: random.choice(word_list).upper().
  2. display_hangman(tries) хранит семь рисунков списком и возвращает stages[tries]. Индекс совпадает с числом оставшихся попыток: 6 — пустая виселица, 0 — проигрыш.
  3. В play() цикл идёт, пока есть попытки и слово не открыто целиком.
  4. Ввод длиннее одного символа — попытка назвать слово целиком: совпало — открываем всё, не совпало — минус попытка.
  5. Одиночный символ проверяем: буква ли это и не называли ли её раньше. В обоих случаях делаем continue.
  6. Если буква есть в слове — пересобираем word_completion посимвольно, иначе тратим попытку.
  7. После цикла разбираем, чем всё кончилось: слово открыто — победа, иначе показываем загаданное слово.
Решение
import random

word_list = ['партия', 'кольцо', 'радио', 'яйцо', 'правда', 'информатика']


def get_word():
    """Случайное слово из списка, заглавными буквами."""
    return random.choice(word_list).upper()


def display_hangman(tries):
    """Виселица по числу оставшихся попыток: 6 — пусто, 0 — проигрыш."""
    stages = [
        # 0 попыток: голова, туловище, обе руки, обе ноги
        """
        --------
        |      |
        |      O
        |     \\|/
        |      |
        |     / \\
        """,
        # 1 попытка
        """
        --------
        |      |
        |      O
        |     \\|/
        |      |
        |     /
        """,
        # 2 попытки
        """
        --------
        |      |
        |      O
        |     \\|/
        |      |
        |
        """,
        # 3 попытки
        """
        --------
        |      |
        |      O
        |     \\|
        |      |
        |
        """,
        # 4 попытки
        """
        --------
        |      |
        |      O
        |      |
        |      |
        |
        """,
        # 5 попыток
        """
        --------
        |      |
        |      O
        |
        |
        |
        """,
        # 6 попыток: пустая виселица
        """
        --------
        |      |
        |
        |
        |
        |
        """]
    return stages[tries]


def play(word):
    """Одна партия: угадываем слово word."""
    word_completion = '_' * len(word)   # что уже открыто
    guessed_letters = []                # какие буквы называли
    tries = 6                           # сколько промахов осталось

    print('Давайте играть в угадайку слов!')
    print(display_hangman(tries))
    print(word_completion)

    while tries > 0 and word_completion != word:
        guess = input('Введите букву или слово целиком: ').upper()

        if len(guess) > 1:                   # назвали слово целиком
            if guess == word:
                word_completion = word
            else:
                tries -= 1
        elif not guess.isalpha():            # защита от дурака
            print('Это не буква, попробуйте ещё раз')
            continue                         # попытку не тратим
        elif guess in guessed_letters:
            print('Эту букву уже называли')
            continue
        else:
            guessed_letters.append(guess)

            if guess in word:
                # открываем все места, где стоит эта буква
                opened = ''
                for i in range(len(word)):
                    if word[i] == guess:
                        opened += guess
                    else:
                        opened += word_completion[i]
                word_completion = opened
            else:
                tries -= 1

        print(display_hangman(tries))
        print(word_completion)

    if word_completion == word:
        print('Поздравляем, вы угадали слово! Вы победили!')
    else:
        print('Попытки кончились. Загаданное слово:', word)


# основная программа: партия за партией, пока игроку не надоест
while True:
    play(get_word())
    if input('Сыграем ещё? (да/нет) ').upper() != 'ДА':
        break

Список guessed_words из задания в этой версии не заведён: слово целиком проверяется сразу, и хранить названные слова незачем. Если решите предупреждать и о повторном слове — добавьте его так же, как guessed_letters. А цикл while tries > 0 and word_completion != word держит оба условия окончания в одном месте: и проигрыш, и победу.

Итог занятия

Что дальше. Все три программы работают в консоли, и это не предел: тот же код можно перенести в оконное приложение или в телеграм-бота. Но сначала стоит довести до конца эти — с проверками ввода, повторной игрой и понятными сообщениями. Программа, которую не стыдно дать в руки другому человеку, отличается от «работает у меня» именно мелочами.