Несколько лет назад я начал преподавать свой любимый язык python школьникам. И возникла такая задача: рассказать про объектную модель, но что бы это было не скучно и как можно нагляднее. И тут меня не сразу, но осенило — пчёлы!
Идея простая: все на свете суть объекты (и субъекты, но сейчас не об этом). Программисты народ ленивый, не стал выдумывать что-то особенное и выдумал себе аналоги реальных объектов. Все выдуманные (виртуальные, другими словами) объекты программисты объединили в классы и в классе описывают общее для однотипных объектов. Потом программисты создают экземпляры виртуальных объектов и запускают их работать.То есть в программах у программистов работают объекты, а не сами программисты. Объекты живут своей виртуальной жизнью, толкают и дергают друг друга, отдают и принимают что-нибудь (данные) друг у друга. Задача программиста сделать так, что бы объекты не просто толкались и дергались, а выполнили нужную программисту задачу.
Мы тоже сделаем модель пасеки и поупражняемся в управлении объектами-пчёлами. К моменту начала пчеловодства обучаемые должны знать основы языка, его основные конструкции и операторы, иметь базовые представления о классах.
Итак — пчела:
class MyBee(Bee):
pass
Она не просто так сама по себе, она принадлежит к общему классу «Пчёлы». В python это реализуется наследованием: в скобочках указан родительский класс. В классе Bee есть все что нужно для жизни виртуальной пчелы — она рождается, может двигаться и нести мёд.
class MyBee(Bee):
def __init__(self):
Bee.__init__(self)
self.move_at(Point(200,300))
def on_stop_at_flower(self, flower):
self.load_honey_from(flower)
def on_honey_loaded(self):
if self.honey <= 100:
self.move_at(Point(300,400))
else:
self.move_at(self.my_beehive)
Помимо пчелы в нашем виртуальном мире есть цветы и улей. В цветах есть мёд, его надо собрать и отнести в улей. Пчела может нести до 100 единиц мёда, цветок же содержит от 100 до 200 единиц. Улей хранит весь принесённый в него мёд, но не больше некоего предела (зависит от количества цветов на пасеке). Количество мёда, которое содержит в себе объект, отображается через свойство объекта и называется honey. Что бы посмотреть количество меда у пчелы надо написать так — bee.honey (у цветка — flower.honey, у улья — beehive.honey). Свойства объекта часто называются атрибутами. У нашей пчелы есть еще атрибуты: flowers — список всех цветов на пасеке и my_beehive — родной улей.
С помощью атрибутов мы можем посмотреть состояние объекта. А вот изменить состояние объекта можно с помощью методов. Методы — это такие рычажки, дернул за один — объект полетел, дернул за другой — остановился. Как можно изменить состояние нашей пчелы?
Принимая и загружая мёд, к примеру:
self.load_honey_from(source) # получить мёд от другого объекта
self.unload_honey_to(target) # отдать мёд объету
Принимать мёд можно от любого объекта и загружать тоже в любой объект: цветок, улей, и даже пчелу. Мы пишем self впереди каждого метода, потому что как бы даем команду «Грузи мёд!» себе (self — англ. «я сам») изнутри кода класса. Но можно отдать команду и другому объекту:
other_bee.load_honey_from(self)
если конечно он нас послушается (мы авторизованы давать ему команды). Аналогично реализуется команда движения:
self.move_at(target) # двигаться к указанной цели
в качестве цели можно указать точку на экране или другой объект (берутся координаты объекта на момент отдачи команды)
А как окружающий мир взаимодействует с нашей пчелой? Он посылает ей сигналы, толкает в бок: «Смотри, что случилось!» Сигналы — это методы, которые вызывает окружающий мир для объекта-пчелы. Сигналы на нашей пасеке такие:
def on_stop_at_flower(self, flower):
""" пчела прилетела к цветку """
pass
def on_stop_at_beehive(self, beehive):
""" пчела прилетела к улью """
pass
def on_honey_loaded(self):
""" мёд в пчелу загружен """
pass
def on_honey_unloaded(self):
""" мёд из пчелы разгружен """
pass
И как раз в эти узловые для жизни пчелы моменты она может принять решение, что же ей делать дальше. В эти момент ход игры останавливается, как в матрице, и можно спокойно все обдумать, просмотреть все доступные объекты со всех сторон и найти женщину в красном нужный цветок. Обратите внимание: отдача команды self.move_at(finded_flower) не отправит пчелу немедленно к цветку, это просто пометка «лететь туда-то после выхода из события». Последующий вызов self.move_at(other_flower) изменит пометку и пчела полетит к последнему указанному объекту.
Именно в теле методов-сигналов реализуется искусственный интеллект пчелы. Вот, например, код поведения пчелы из мультика (файл my_bee.py)
class MyBee(Bee):
from beegarden import Bee # импортируем нужное из библиотек
def __init__(self):
""" рождение пчелы """
Bee.__init__(self)
self.flower = self.flowers.pop() # выбираем нашим первый попавшийся цветок
self.move_at(self.flower) # летим к нему
def on_stop_at_flower(self, flower):
""" пчела прилетела к цветку """
if flower.honey > 0: # если в цветке еще есть мёд
self.load_honey_from(flower) # забираем его
else: # цветок пуст
self.go_next_flower() # летим к другому цветку, если он есть
def on_honey_loaded(self):
""" мёд в пчелу загружен """
if self.honey == 100: # полностью ли я заполнен?
self.move_at(self.my_beehive) # да, летим к улью
else: # еще осталось место
self.go_next_flower() # летим к другому цветку, если он есть
def on_stop_at_beehive(self, beehive):
""" пчела прилетела к улью """
self.unload_honey_to(beehive) # просто разгружаем мёд в улей
def on_honey_unloaded(self):
""" мёд из пчелы разгружен """
self.go_next_flower() # летим к другому цветку, если он есть
def go_next_flower(self):
""" поиск следующего цветка """
if not self.flower.honey: # в моём цветке больше нет мёда
if not self.flowers: # и цветов не осталось
if self.honey: # а во мне есть мёд
self.move_at(self.my_beehive) # летим к улью
return # цветов с мёдом больше нет, стоп
else: # остались цветы с мёдом
self.flower = self.flowers.pop() # берем следующий из списка
self.move_at(self.flower) # и летим к своему цветку
Пчелы должны жить в игровом мире, давайте его создадим
from beegarden import GameEngine, Scene # импортируем нужное из библиотек
from my_bee import MyBee # импортируем код класса пчелы
game = GameEngine("My little garden") # создаем движок нашей игры
scene = Scene(flowers_count=3) # создаем нужную сцену
bee = MyBee() # вот оно - рождение пчелы!
game.go() # и запускаем наш виртуальный мир крутиться...
Но просто собирать мёд в одиночку не интересно, давайте вместе с друзьями
from beegarden import GameEngine, Scene
from my_bee import MyBee
game = GameEngine("My little garden")
scene = Scene(flowers_count=20) # цветов побольше
bees = [] # список наших пчёл
for i in range(5): # цикл создания 5 пчёл
bee = MyBee() # создаем новую
bees.append(bee) # и добавляем её к списку
game.go() # теперь мы - банда
А потом посоревнуемся с другим ульем
Слева — рабочие пчелы, каждая обрабатывает свой цветок. Справа — жадные, летят к самому тучному. Жадность проигрывает, отчего, интересно?…
Я предлагаю сообществу взять шашки в руки и сделать разминку перед новым годом — кто быстрее и больше всех соберет мёда? Тем более что есть примета: перед новым годом релизы ставить — начальство злить, новое начинать — себя не любить, старое править — со скуки помереть.
Код проекта можно скачать с гитхаба beegarden Для запуска нужен python старше 2.6 и Pygame старше 1.9, тестировалась под Ubuntu и Windows 7/Vista. Для Ubuntu установить Pygame можно так
sudo apt-get install python-pygame
Для Windows 7/Vista: нужно поставить сам пайтон python-2.7.3.msi и потом пайгейм pygame-1.9.1.win32-py2.7.msi
Шашки в руки брать так: клонировать проект в отдельную папку (или просто скачать и раззиповать архив) там смотреть/редактировать файлы my_bee.py и run.py. Соревноваться можно друг с другом, а так же с написанными мной алгоритмами РабочейПчелы и ЖаднойПчелы.
Удачи в наступающем!
Автор: suguby