Я пишу текстовую игру на Python: элемент случайности
В новом выпуске блога о программировании я добавляю в текстовую игру LAM-40 на Python неожиданность — теперь каждое действие с разной вероятностью может обернуться успехом или провалом.
Предыдущий выпуск
В прошлый раз мы довели до ума первый прототип игры, поменяв структуру кода и улучшив интерфейс. В этот раз мы временно забудем об интерфейсе и продолжим улучшать структуру кода, добавим новый класс и полностью переделаем ключевую механику игры, введя в неё элемент случайности. Кроме того, мы расширим набор возможных действий до 18: в играх всегда есть проблемы с глаголами, а потому я решил, что ни один лишним не будет. Конечно, это временное решение: в финальной версии количество действий будет увеличиваться по мере прохождения.
ПРИМЕЧАНИЕ: весь код приводится для Python версии 3.0 и старше, а потому может не работать на более старых версиях.
Начнём с нового файла character.py, в котором я описал класс Character — главного героя игры, то есть игрока.
Строки 7–11: все возможные действия игрока я перенёс сюда — это логично. Также обратите внимание, что действий стало намного больше: аж 18. Да, я искал глаголы, которые начинаются на разные буквы алфавита, в специальном словаре.
Строки 13–21: новая версия LAM-40 использует простенькую вероятностную модель, придуманную мной. Из 18 действий исключаем ожидание и выход: их выполнение обязательно и не зависит от удачи игрока. Оставшиеся 16 действий делятся на два вида: те, которые будут выполнены с высокой вероятностью, и те, которые будут выполнены с низкой вероятностью. Действия с низкой вероятностью выполнения более рисковы, но в будущем мы сделаем так, чтобы награда за риск была достойной. Первая функция описывает именно нерисковые действия. Каждая характеристика бюрократа, которая положительно сказывается на вероятности выполнения действия, повышает её в 2 раза, а каждая отрицательная характеристика понижает вероятность в 1,5 раза. В случае с рисковыми действиями всё наоборот: вероятность выполнения растёт в зависимости от характеристик бюрократа в 1,5 раза и падает в 2 раза. Это, конечно, пока всё несерьёзно — с числами ещё придётся играться, тестировать и подстраивать всё друг под друга.
Строки 23–41: 16 действий делятся ещё по двум параметрам. Есть действия, вероятность которых тем выше, чем выше ранг или лучше настроение бюрократа, но есть и действия, вероятность которых снижается, когда ранг растёт или настроение улучшается. Всего получаются четыре сочетания, на каждое из которых приходится по функции. Переменные asc_rank и asc_mood логического типа данных говорят нам о том, растёт или падает вероятность выполнения действия при росте ранга и улучшении настроения бюрократа. Их значение может быть либо True (да, растёт), либо False (нет, падает).
Строки 15–16, 25–26, 31, 35: обратите внимание, что отдельные части кода я теперь сопровождаю не только dosstring'ами, но и полноценными комментариями. По стандарту PEP 8 для комментария внутри строки нужно сделать отступ в два пробела от конца строки, ввести символ решётки # и ещё один пробел, после чего начать набирать комментарий. Объясняйте и себе и другим, что вы делаете, — это правда помогает (правда, подозреваю, не все мои объяснения сейчас выглядят осмысленно, но это мы ещё успеем исправить).
В классе Bureaucrat, по сути, одно большое изменение, разбитое на два маленьких, но они достойны внимания.
Строка 23: функция greet(), автоматическое приветствие, которое делает каждый новый бюрократ, переехала внутрь инициализации класса. Это логично, потому что это две связанных друг с другом вещи: приветствия без появления нового бюрократа не бывает. Тем самым мы облегчили основной цикл игры — скоро вы в этом сможете убедиться.
Строка 42: новый бюрократ теперь генерируется не в основном цикле игры с условием и загадочной характеристикой negative, а в случае успешного выполнения действия.
Наконец, главное — основной файл игры game.py, который стал заметно больше.
Строки 19, 21–22: мы создаём образец класса Character (в отличие от бюрократов, главный герой у игры всего один, и образец надо создавать один раз, а потому он создаётся до бесконечного цикла while True, который теперь устроен намного проще: внутри блока всего одна строка).
Строки 31–39: в основной функции act() несколько изменилась часть после ввода первой буквы нужного действия. 'Q' всё так же позволяет выйти из игры (кстати, замечу, что функция sys.exit() — самый адекватный способ насильного прерывания работы программы из всех существующих в Python). Первая ветка elif — это действие ожидания, которое приводит к тому, что появляется новый бюрократ и цикл продолжается. Следующая ветка elif относится к остальным 16 действиям и переводит нас к другим функциям, которые мы скоро разберём по порядку, а ветка else относится ко всем прочим возможным символам, которые не предусмотрены нашей игрой, — если ввести неправильную команду, выскочит сообщение об ошибке, выводимое функцией print(). Обратите внимание на ключевое слово in и набор символов в одном string'е. Дело в том, что интерпретатор воспринимает каждый string длиной больше одного символа как набор множества символов. Иначе говоря, 'abcdefhijklmpsty' — это по смыслу кортеж ('a', 'b', 'c', ... , 't', 'y'). Поэтому, когда мы употребляем ключевое слово in, мы имеем в виду, что в этом гипотетическом кортеже есть символ, присвоенный переменной action.
Строки 41–65: как я уже упоминал, 16 действий сгруппированы по нескольким видам — уникально разве что «спросить». В зависимости от того, какое это действие, образец класса Character обзаводится набором характеристик из класса Character, которые вступают в код в следующей функции. Ветка else относится к символу 'w', то есть команде ожидания. Ключевое слово pass говорит интерпретатору о том, что ему следует продолжать выполнение программы.
Строки 67–94: вычисляем вероятность выполнения выбранного нами действия, исходя из полученных в предыдущей функции характеристик. Для этого создаём переменную odds и присваиваем ей значение целого числа от 25 до 75, используя знакомый нам модуль random и стандартную функцию range(). Функция range() создаёт список из указанного количества элементов или промежутка чисел. Указывая range(25, 76), мы создаём список (25, 26, 27, ... , 74, 75), указывая range(25) — список (0, 1, 2, ... , 23, 24). Да, не забывайте, что отсчёт в программировании всегда начинается с нуля, а также то, что к каждой функции, которую вы добавляете в программу, желательно читать описание. Затем мы начинаем изменять переменную odds в зависимости от характеристик бюрократа и главного героя. Средний ранг и среднее настроение не влияют на вероятность — она остаётся точно такой же случайно сгенерированной.
Строки 96–104: в последней на сегодня новой функции мы создаём переменную luck, придаём ей значение integer от 0 до 100 и сравниваем с полученным в результате выполнения предыдущей функции переменной odds. Если odds оказывается меньше luck, то действие оказывается неудачным, в противном случае оно выполняется — и мы переходим к новому этапу бесконечного цикла, то есть к новому бюрократу.
Внешне игра с прошлого раза изменилась несильно — разве что доступных действий стало намного больше. Но теперь вы никогда не можете заранее знать, сработает какое-то действие или нет. Впрочем, работы над балансировкой вероятности предстоит ещё полно.
Я уже говорил о том, что пришла пора делать дизайн-документ — и если сегодня без него можно было обойтись, то к следующему разу мне всё-таки придётся его написать, потому что мы начнём работать над sense of progression в игре. Кроме того, по-прежнему не меняется text.py с репликами бюрократов (а потенциально и других людей) — это пора исправлять. Да и над интерфейсом надо работать постоянно: хотя бы сделать так, чтобы все действия выводились вровень, а не как сейчас, и научить программу показывать текст постепенно, а не резким обновлением экрана.
Если вам что-то непонятно, пишите комментарии под материалом и в социальных сетях — буду рад и любым другим отзывам. Если вы более опытный в программировании человек, чем я, то с удовольствием выслушаю содержательную критику. Спасибо и до следующего раза!
Смотрите также:
Документация модуля random
Полные курсы Python на Codeacademy и Treehouse*
* — платные курсы, но есть пробный период
Развитие программистского мышления на Udacity
Учебники по Python на LearnPython.org и Python Course
Комментарии
Подписаться