Слёрм Universe - Python на примере Minecraft
Модули random, time
Лекция 8.2
Как вы помните, прошлую лекцию мы закончили на определении чем же являются библиотеки в языке Python. А представляют они из себя готовый набор модулей и функций для решения различных задач. И ярким тому примером является одна из множества библиотек Python mcpi, позволяющая нам выполнять код в рамках Minecraft. В мире, где не существует ни одной готовой библиотеки, программисту пришлось бы писать очень много кода даже для решения простых задач. Давайте представим себя программистами такого жуткого мира. Б-р-р, страшно!
Итак, нам требуется написать программу, которая возводит арену при условии, что игрок оказался на золотом блоке. В настоящем мире уже существует библиотека mcpi с множеством встроенных функций, благодаря которым мы можем определить ID блока, под ногами нашего игрока и построить необходимое сооружение. Однако, в жутком мире без библиотек всего это нет и нам нужно потратить много времени, чтобы написать даже такой простенькой код. Писать код с нуля для решения типовых задач - занятие утомительное и сложное. Конечно, было время, когда библиотек ещё не было и любая задача занимала огромное количество сил и нервов программиста. Но зачем писать каждый раз то, что можно написать один раз и дать остальным? Разработчики пишут библиотеки и делятся с другими! Захотелось кому-то научить Python работать с Minecraft? Пожалуйста, с нами любезно поделились библиотекой mcpi.

Теперь, когда вы знаете назначение библиотек, есть новый повод похвалить язык, которым мы изучаем. Python славится своей стандартной библиотекой, имеющий очень богатый функционал. Давайте сначала попробуем разобраться в том, что представляет из себя стандартная библиотека в принципе. А представляет она из себя набор модулей, классов, объектов, констант, глобальных переменных, шаблонов, макросов, функций и процедур, доступных для вызова из любой программы, написанной на этом языке и присутствующих во всех реализациях языка. Если сказать проще, то это библиотека, которую нам не нужно дополнительно устанавливать, так как она является частью самого языка. А вот такие библиотеки, вроде легендарной для нас mcpi, являются внешними библиотеками. К слову, мы и сами можем создавать внешние библиотеки и делиться ими в Интернете.

Однако, сейчас нас интересует именно стандартная библиотека Python. И в рамках данной лекции мы познакомимся с двумя её популярными модулями - random и time. Ну что, поехали?!
Модуль time
Вы уже немного знакомы из практик с применением этих модулей. Но давайте глубже заглянем в них. И начнём мы с модуля time. А для начала произведём его импорт в нашу программу:
import time
Отлично, теперь мы имеем доступ ко встроенным функциям этого модуля. Начнём с простого и выведем на экран текущую дату и время:
import time

print(time.ctime())
...выполним код:
Sat Jun  5 16:51:58 2021
Отлично! Теперь вы знаете дату и время, когда я писал эту лекцию. А помогла нам в этом функция ctime() модуля time.

Давайте рассмотрим ещё одну очень интересную функцию time(). Она возвращает количество секунд, прошедших с начала эпохи. Звучит немного странно! О какой эпохе вдруг идёт речь?! Эпохой времени в Python принято называть точку, с которой начинается время.
Представьте, что вы купили обыкновенные наручные часы с цифровым механизмом. Настраивая на них календарь, вы обязательно столкнётесь с тем, что минимальное значение года, которое вы можете установить будет, к примеру 2000-й год, а максимальное - 2099-й год. То есть количество дат на таких часах ограничено, и минимальная дата, с которой вы можете начать отсчёт времени на них - 1 января 2000 год. В таком примере эта дата является началом эпохи электронных часов, которые мы купили.

В Python для разных операционных систем, например Windows, MacOS, Linux и т.д. тоже есть минимальное значение даты, с которой ведётся счёт времени. Например, в MacOS и Windows - это 1 января 1970 год. Теперь наконец попробуем узнать сколько секунд прошло с начала эпохи (с 1 января 1970 года):
import time

current_seconds = time.time()
print(current_seconds)

1622911836.461578

Как вы видите, мы получили вещественное число прошедших секунд с 1 января 1970 до текущего момента времени. Обратите внимание, что число после точки обозначает долю секунды. Таким образом мы получили максимально точное значение. Однако, вы можете спросить: "А для чего нам нужно знать сколько прошло секунд с начала эпохи?". И действительно, а зачем?! Дело в том, что при помощи этого значения мы можем с лёгкостью определить практически любую дату через заданное количество времени, либо, наоборот - заданное количество времени назад. К примеру, мы хотим узнать точную дату через 51 день и 13 часов от настоящего времени. Давайте разберёмся, как мы можем это сделать. Рассмотрим следующий код:
import time

current_seconds = time.time()        # Определяем количество секунд с начала эпохи
hour = 3600                          # Определяем количество секунд в одном часе
day = hour * 24                      # Определяем количество секунд в одних сутках
total_seconds = 51 * day + 13 * hour  # Определяем количество секунд в 51-ом дне и 13-ти часах

print(time.ctime())                  # Выводим текущую дату
print(time.ctime(current_seconds + total_seconds)) # Выводим дату через 51 день и 13 часов
  • В переменную current_seconds при помощи функции time() мы записываем сколько секунд прошло с начала эпохи;
  • В переменную hour мы записываем сколько секунд содержит один час, а именно - 3600;
  • В переменную day мы записываем сколько секунд содержат одни сутки, а именно умножаем количество секунд в одном часе на число 24;
  • В переменную total_seconds мы записываем сколько секунд содержит 51 день и 13 часов;
  • Далее, используя функцию ctime(), мы выводим текущую дату и время на экран;
  • А после мы вновь используем функцию ctime(), но теперь необычным способом. ctime() может принимать в качестве аргумента любое значение секунд, прошедших после 1 января 1970 года. Если в неё передать количество секунд, например, содержащихся в пяти сутках, тогда ctime() вернёт дату через 5 дней от 1 января 1970 года, а именно - 5 января 1970 года. Но в нашем случае мы передаём в ctime() сумму количества секунд с начала эпохи по текущее время и количества секунд, содержащихся в 51-ом дне и 13-ти часах.
Выполним код:
Sat Jun  5 19:03:14 2021
Tue Jul 27 08:03:14 2021
И мы получили две строчки
  1. Текущую дату и время;
  2. И наконец-то дату и время, которые будут через 51 день и 13 часов от текущего времени.

Давайте разберём ещё одну функцию модуля time - sleep(). С ней мы уже знакомы из практик, однако, давайте ещё раз повторим её применение. Она просто приостанавливает выполнение кода, следующего после неё на заданное количество секунд:
import time

print("Через 3 секунды после этого сообщения я попращаюсь с вами!")
time.sleep(3)
print("Пока-пока!")
Если мы запустим данный код, то вторая в коде функция print() со строкой "Пока-пока!" сработает через 3 секунды, после выполнения первой. Как вы помните, мы использовали sleep() для замедления скорости выполнения итераций бесконечного цикла в 0.25 секунды, что нам здорово помогало экономить ресурсы "железа".
Модуль random
Давайте теперь поработаем с функциями модуля random. На одной из практик мы уже встречались с ним. Для начала произведём его импорт в нашу программу и создадим список, например, с именами игроков Minecraft:
import random

players = ["Толстый Медведь", "Хмурый Барсук", "Лис"]
Давайте попробуем получить случайный элемент из нашего списка players. Для этого существует функция choice() модуля random, которая возвращает случайный элемент последовательности:
import random

players = ["Толстый Медведь", "Хмурый Барсук", "Лис"]
casual_player = random.choice(players)

print(casual_player)
В качестве аргумента функции choice() мы можем передать любую последовательность, будь то это список, кортеж или строка. Выполним код:
Лис
Отлично, мы получили случайное значение списка!

Помните какую именно функцию из модуля random мы использовали на практике по спискам? Правильно, shuffle()! Данная функция перемешивает элементы списка который в неё передаётся. Давайте попробуем перемешать наш список players:
import random

players = ["Толстый Медведь", "Хмурый Барсук", "Лис"]
random.shuffle(players)

print(players)
Стоит отметить, что в функцию shuffle() мы можем передать лишь изменяемую последовательность вроде списков. Строка и кортеж не являются изменяемым типом данных, поэтому перемешать их не выйдет. Итак, выполним код:
['Хмурый Барсук', 'Лис', 'Толстый Медведь']

Код сработал! Нам получилось перемешать список с именами игроков.

Давайте разберём ещё одну функцию модуля random, позволяющую генерировать случайное число - randint(). К примеру, мы хотим получить любое число от 1 до 100:
import random
random_number = random.randint(1, 100)

print(random_number)
Обратите внимание, что в функции randint() второе переданное число, в нашем случае 100, тоже участвует в поиске случайного числа. Выполним код:
8
Мы получили случайное число 8, отлично!
Перенесёмся в Minecraft
Давайте перенесёмся в Minecraft и используем знакомый нам код для строительства арены. Если наш игрок окажется на блоке с золотом, то пусть построится арена из случайных блоков. Ключевое слово здесь - "случайных". Именно для этого мы будем использовать модуль random. Разберём следующий код:
import random
from mcpi.minecraft import Minecraft
mc = Minecraft.create()

while True:
    player_x, player_y, player_z = mc.player.getPos()
    arena_x = player_x + 10
    arena_y = player_y - 1
    arena_z = player_z
    if mc.getBlock(player_x, player_y - 1, player_z) == 41:
        mc.setBlock(player_x, player_y - 1, player_z, 51)
        mc.postToChat("Строим арену!")
        for length_bias in range(10):
            for height_bias in range(10):
                for width_bias in range(10):
                    block_id = random.randint(220, 240)
                    if height_bias == 0 or height_bias == 9 or width_bias == 0 or width_bias == 9:
                        mc.setBlock(player_x + length_bias, player_y + height_bias, player_z + width_bias, block_id)
  • Мы создали бесконечный цикл WHILE, в котором создали переменные с координатами нашего игрока и координатами начала строительства арены;
  • Далее в теле цикла у нас срабатывает условие: если под ногами нашего игрока блок с золотом, то мы меняем его на блок с огнём, выводим сообщение в чат и возводим арену при помощи циклов FOR;
  • Обратите внимание, что для каждой итерации цикла FOR мы записываем в переменную block_id при помощи функции randint() случайный ID блока в промежутке ОТ 220 ДО 240 ВКЛЮЧИТЕЛЬНО. Теперь, возводя каждый отдельный блок арены, функция setBlock() в качестве ID будет получать случайное число из заданного промежутка.
Давайте выполним код, встанем на золотой блок в Minecraft и посмотрим, что получилось:
Отлично, мы получили арену, построенную из множества разных блоков!

Мы познакомились с двумя модулями стандартной библиотеки - random и time. Важно знать, что это самая малость того функционала, который может дать Python. На практике вам далеко не обязательно помнить работу всех функций каждого модуля до тех пор, пока в этом действительно не возникнет необходимости. Помнить всё невозможно, но желательно примерно понимать в каком модуле есть нужная функция, а детали её работы можно узнать в документации Python. В следующем блоке лекций мы разберём ещё два модуля стандартной библиотеки - os и file.