Как сделать счетчик очков в python

Обновлено: 04.07.2024

Quiz - викторина или тест, программа с тестовыми вопросами.

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

Для выполнения цели: нужно уметь разбивать задачу на шаги и выполнять их в дальнейшем.

Будем придумывать, записывать и выполнять шаги по мере решения.😉

Создадим окно приветствия.

Для подключения библиотек tkinter используем рекомендованный формат подключения:

Для вызова функции поле создания окна и фрейма в вызовем функцию командой:

При вызове этой функции происходит следующее:

Результат выполнения, после нажатия кнопки:

Какие виждеты используют для вывода текста с возможностью его редактирования? Which widgets using to display text with the ability to edit it?

Внесем изменения в функцию def ask_question(el): Для вопроса будем использовать виджет метку - Label():

А для вариантов ответа используем флажки - Checkbutton(), и у каждого флажка должна быть своя переменная. Выберем для неё тип переменной BooleanVar(), и хранить её будем в массиве answer. Чтобы добавить переменную в массив будем использовать метод .append(…) с параметром задающим тип переменной BooleanVar():

Остальные варианты ответа добавьте самостоятельно, используя копировать-вставить (copy-paste). Привязка новых элементов обязательно должна быть к передаваемому фрейм элементу el. Результат выполнения:

Пример результата вывода в консоль, который только отражает какие флажки были выбраны:

Переменная t будет перебирать список true_answers, переменная a - answers. После преобразования bool(int(t)) в булевый тип, пример вывода в консоль:

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

Пример вывода в консоль:

Для вывода результата в окно продолжим изменять функцию check(), она должна принимать параметр, а именно фрейм в который выводится результат:

При запуске программы все должно работать как и ранее.

Теперь вместо строки print(points) пишем код очищающий фрейм:

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

Добавьте переменную с вопросом и ответами, с использованием дополнительного символа перед строкой:

Добавьте этот вопрос в форму, с последующим отображением и подсчетом общего результата.


Для своих игр в ASCII-арте я написал библиотеку bear_hug с очередью событий, коллекцией виджетов, поддержкой ECS и прочими полезными мелочами. В этой статье мы посмотрим, как с её помощью сделать минимальную работающую игру.

  • Я единственный разработчик библиотеки, поэтому могу быть предвзят.
  • bear_hug – по сути обёртка вокруг bearlibterminal, поэтому относительно низкоуровневых операций с глифами не будет.
  • Похожая функциональность есть в clubsandwich, но я им не пользовался и сравнивать не могу.

Под капотом у bear_hug – bearlibterminal, библиотека на SDL для создания псевдо-консольного окна. То есть в чистом TTY, как какой-нибудь ncurses, она работать не будет. Но зато картинка получается одинаковой что на Linux, что на Windows, и не зависит от настроек пользовательского терминала. Это важно, особенно для игр, потому что при смене шрифта ASCII-арт вполне может превратиться Бог весть во что:



Один и тот же рисунок в оригинальном виде и после копипаста в разные программы

Разумеется, библиотека писалась для относительно масштабных проектов. Но, чтобы не отвлекаться на геймдизайн и архитектуру, в этой статье мы будем создавать что-нибудь простенькое. Проект на один вечер, в котором есть на чём показать основные функции библиотеки. А именно – упрощённый клон тех самых танчиков с Денди (они же Battle City). Будут танк игрока, танки противников, разрушаемые стены, звук и подсчёт очков. А вот главного меню, уровней и подбираемых бонусов не будет. Не потому, что их было бы невозможно добавить, а потому что этот проект – не более чем хэлловорлд.



Геймовер будет, а победы не будет. Потому что жизнь — боль.

Весь использованный в статье материал есть на гитхабе; сама библиотека – там же и на PyPI (под лицензией MIT).

Прежде всего нам понадобятся ассеты. Для рисования ASCII-арта я использую REXpaint от Джоша Ге (он же Kyzrati), разработчика научно-фантастического рогалика Cogmind. Редактор бесплатный, хоть и не опенсорсный; официальная версия есть только для Windows, но всё прекрасно работает под wine. Интерфейс довольно понятный и удобный:


Сохраняем в местный двоичный формат .xp и копируем из /path/to/rexpaint/images в папку с будущей игрой. В принципе поддерживается и загрузка изображений из .txt-файлов, но в текстовом файле очевидно невозможно сохранить цвета отдельных символов. Да и редактировать ASCII-арт в блокноте лично мне неудобно. Чтобы не хардкодить координаты и размер каждого элемента – эти данные хранятся в отдельном JSON-файле:

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

Терминал – это собственно окно игры. На него можно помещать виджеты, и он же по мере необходимости кидает события ввода. В качестве ключей при создании терминала можно использовать все опции терминала bearlibterminal; в данном случае мы задали шрифт, размер окна (в символах), заголовок окна и интересующие нас методы ввода.

Что касается очереди событий, то у неё очень простой интерфейс: dispatcher.add_event(event) добавляет событие в очередь, а dispatcher.register_listener(listener, event_types) позволяет на неё подписаться. У подписывающегося (например, виджета или компонента) должен быть коллбэк on_event, который принимает событие в качестве единственного аргумента и либо ничего не возвращает, либо возвращает другое событие или набор событий. Само событие состоит из типа и значения; тип тут не в смысле str или int, а в смысле “разновидность”, например ‘key_down’ или ‘tick’. Очередь принимает только события известных ей типов (встроенных или созданных пользователем) и отдаёт их в on_event всех подписавшихся на данный тип. Значения она никак не проверяет, но в пределах библиотеки есть соглашения по поводу того, что является валидным значением для каждого типа событий.

Сперва мы подписываем на очередь пару Listeners. Это базовый класс для объектов, которые могут подписываться на события, но не являются не виджетами, ни компонентами. В принципе использовать его не обязательно, лишь бы у подписывающегося был метод on_event.

Полный список встроенных типов событий есть в документации. Несложно заметить, что там есть события для создания и уничтожения сущностей, но нет для урона. Поскольку у нас будут объекты, которые не разваливаются с одного выстрела (стены и танк игрока), мы его создадим:

Договоримся, что в качестве значения у этого события будет кортеж из ID той сущности, которой нанесён урон, и значение урона. LoggingListener – просто инструмент для дебага, который печатает все полученные события куда скажут, в данном случае в stderr. В данном случае я хотел убедиться, что урон проходит корректно, а звук запрашивается всегда, когда должен.

Раз у нас теперь есть, на чём размещать внутриигровые объекты, можно начать создавать сущности. Весь код сущностей и компонентов вынесен в отдельный файл. Простейшая из них – разрушаемая кирпичная стена. Она умеет находиться на определённом месте, отображать свой виджет, служить объектом коллизии и получать урон. После достаточного количества урона стена исчезает.

Прежде всего, создаётся сам объект сущности. Он содержит только имя (которое должно быть уникальным) и набор компонентов. Их можно либо передать все сразу при создании, либо, как здесь, добавлять по одному. Потом создаются все нужные компоненты. В качестве виджета используется SwitchWidget, который содержит несколько рисунков одинакового размера и может их менять по команде. Кстати, рисунки загружаются из атласа при создании виджета. И, наконец, в очередь уходят объявление о создании сущности и приказ её отрисовать на нужных координатах.

Из не-встроенных компонентов тут только здоровье. Я создал базовый класс “Компонент здоровья” и унаследовал от него “Компонент здоровья, меняющий виджет” (чтобы показывать стену целой и на нескольких стадиях разрушения).

При создании компонента в super().__init__ передаётся ключ ‘name’. Когда компонент будет добавлен к какой-нибудь сущности, под названием из этого ключа он будет добавлен в __dict__ сущности и к нему можно будет обращаться через entity_object.health. Помимо удобства интерфейса, такой подход хорош тем, что запрещает выдавать сущности несколько однородных компонентов. А то, что он хардкодится внутри компонента, не позволяет по ошибке засунуть, например, WidgetComponent в слот компонента здоровья. Сразу при создании компонент подписывается на интересующие его классы событий, в данном случае ac_damage. Получив такое событие, метод on_event проверит, не о его ли хозяине часом речь. Если да – он вычтет нужное значение из хитпойнтов и дёрнет коллбэк на изменение здоровья, у базового класса абстрактный. Ещё есть метод __repr__, который используется для сериализации в JSON (например, для сохранений). Его добавлять не обязательно, но у всех встроенных компонентов и большинства встроенных виджетов он есть.

Наследующий от базового компонента здоровья VisualDamageHealthComponent переопределяет коллбэк на изменение здоровья:

Пока здоровье выше 0, он просит компонент, ответственный за виджет, отрисовывать стену в нужном состоянии. Тут как раз используется описанное выше обращение через атрибут объекта сущности. Как только хитпойнты кончатся, таким же образом будет вызван компонент, ответственный за корректное уничтожение сущности и всех компонентов.

Для остальных сущностей всё аналогично, только набор компонентов другой. Танкам добавляются контроллеры (ввод для игрока, ИИ для противников) и поворачивающиеся виджеты, снарядам – компонент коллизии, наносящий урон тем, в кого они попали. Разбирать каждый из них я не буду, потому что это громоздко и довольно тривиально; посмотрим только на коллайдер снаряда. У него есть метод collided_into, вызываемый, когда сущность-хозяин во что-то врезалась:

Чтобы убедиться, что в жертву действительно можно попасть (что может быть не так для, например, элементов фона), снаряд использует EntityTracker(). Это синглетон, который отслеживает все создаваемые и уничтожаемые сущности; через него можно получить объект сущности по имени и что-нибудь сделать с его компонентами. В данном случае проверяется, что entity.collision (обработчик коллизий жертвы) вообще существует.

Теперь в основном файле игры мы просто вызываем все необходимые функции создания сущностей:

Счётчики очков и хитпойнтов не являются сущностями и не находятся на поле боя. Поэтому они добавляются не на ECSLayout, а прямо на терминал справа от карты. Соответствующие виджеты наследуют от Label (виджет текстового вывода) и имеют метод on_event, чтобы узнавать о том, что их интересует. В отличие от Layout, терминал не обновляет виджеты автоматически каждый тик, поэтому после изменения текста виджеты приказывают ему это сделать:

Генератор врагов и объект, ответственный за вывод “GAME OVER”, на экране не представлены вовсе, поэтому они наследуют от Listener. Принцип тот же: объекты слушают очередь, дожидаются нужного момента, а потом создают сущность или виджет.

Теперь мы создали всё, что нужно, и можем запускать игру.

Виджеты добавляются на экран только после его запуска. Сущности можно было добавлять на карту и раньше – события создания (в которых хранится вся сущность, включая виджет) просто копятся в очереди и разрешаются на первом тике. А вот терминал умеет добавлять виджеты только после того, как для него будет успешно создано окно.

К этому моменту у нас есть работающий прототип, можно выпускаться в Early Access за двадцать баксов добавлять фичи и полировать геймплей. Но это уже выходит за рамки хэллоуворлда, а значит – и статьи. Добавлю только, что не зависящий от системного питона билд можно собрать с помощью pyinstaller.

У меня было задание: "Создайте игру, в которой компьютер загадывает имя одной из семи птиц, доступных с первой версии игры Angry Birds, а игрок должен его угадать."
Я сделала рабочую программу (СМ ФОТО №1)
Но теперь у меня следующее задание: Разработайте систему начисления очков для вашей задачи, в соответствии с которой игрок получал бы большее количество баллов за меньшее количество попыток.
Нужно изменить программу, чтобы она работала как на фото №2.
Помогите, я не знаю КАК:)

Прежде чем мы начнём программировать что-то полезное на Python, давайте закодим что-нибудь интересное. Например, свою игру, где нужно не дать шарику упасть, типа Арканоида. Вы, скорее всего, играли в детстве во что-то подобное, поэтому освоиться будет просто.

Логика игры

Есть игровое поле — простой прямоугольник с твёрдыми границами. Когда шарик касается стенки или потолка, он отскакивает в другую сторону. Если он упадёт на пол — вы проиграли. Чтобы этого не случилось, внизу вдоль пола летает платформа, а вы ей управляете с помощью стрелок. Ваша задача — подставлять платформу под шарик как можно дольше. За каждое удачное спасение шарика вы получаете одно очко.

Алгоритм

Чтобы реализовать такую логику игры, нужно предусмотреть такие сценарии поведения:

Хитрость в том, что всё это происходит параллельно и независимо друг от друга. То есть пока шарик летает, мы вполне можем двигать платформу, а можем и оставить её на месте. И когда шарик отскакивает от стен, это тоже не мешает другим объектам двигаться и взаимодействовать между собой.

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

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

По коням, пишем на Python

Для этого проекта вам потребуется установить и запустить среду Python. Как это сделать — читайте в нашей статье.

Начало программы

Чтобы у нас появилась графика в игре, используем библиотеку Tkinter. Она входит в набор стандартных библиотек Python и позволяет рисовать простейшие объекты — линии, прямоугольники, круги и красить их в разные цвета. Такой простой Paint, только для Python.

Чтобы создать окно, где будет видна графика, используют класс Tk(). Он просто делает окно, но без содержимого. Чтобы появилось содержимое, создают холст — видимую часть окна. Именно на нём мы будем рисовать нашу игру. За холст отвечает класс Canvas(), поэтому нам нужно будет создать свой объект из этого класса и дальше уже работать с этим объектом.

Если мы принудительно не ограничим скорость платформы, то она будет перемещаться мгновенно, ведь компьютер считает очень быстро и моментально передвинет её к другому краю. Поэтому мы будем искусственно ограничивать время движения, а для этого нам понадобится модуль Time — он тоже стандартный.

Последнее, что нам глобально нужно, — задавать случайным образом начальное положение шарика и платформы, чтобы было интереснее играть. За это отвечает модуль Random — он помогает генерировать случайные числа и перемешивать данные.

Запишем всё это в виде кода на Python:

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

Шарик

Сначала проговорим словами, что нам нужно от шарика. Он должен уметь:

  • задавать своё начальное положение и направление движение;
  • понимать, когда он коснулся платформы;
  • рисовать сам себя и понимать, когда нужно отрисовать себя в новом положении (например, после отскока от стены).


Этого достаточно, чтобы шарик жил своей жизнью и умел взаимодействовать с окружающей средой. При этом нужно не забыть о том, что каждый класс должен содержать конструктор — код, который отвечает за создание нового объекта. Без этого сделать шарик не получится. Запишем это на Python:

Платформа

Сделаем то же самое для платформы — сначала опишем её поведение словами, а потом переведём в код. Итак, вот что должна уметь платформа:

  • двигаться влево или вправо в зависимости от нажатой стрелки;
  • понимать, когда игра началась и можно двигаться.

А вот как это будет в виде кода:

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

От счёта нам нужно только одно (кроме конструктора) — чтобы он правильно реагировал на касание платформы, увеличивал число очков и выводил их на экран:

У нас всё готово для того, чтобы написать саму игру. Мы уже провели необходимую подготовку всех элементов, и нам остаётся только создать конкретные объекты шарика, платформы и счёта и сказать им, в каком порядке мы будем что делать.

Посмотрите, как лаконично выглядит код непосредственно самой игры:

Что дальше

На основе этого кода вы можете сделать свою модификацию игры:

  • добавить второй шарик;
  • раскрасить элементы в другой цвет;
  • поменять размеры шарика; поменять скорость платформы;
  • сделать всё это сразу;
  • поменять логику программы на свою.

Любишь Python? Зарабатывай на нём!
Любишь Python? Зарабатывай на нём!
Любишь Python? Зарабатывай на нём!
Любишь Python? Зарабатывай на нём!

Получите ИТ-профессию
Получите ИТ-профессию
Получите ИТ-профессию
Получите ИТ-профессию

Что означает ошибка TypeError:

Неочевидная ошибка при организации цикла в Python.


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

Читайте также: