Игра в жизнь

DateTagsPython

Меня уже давно интересовала конвеевская игра «Жизнь», и вот наконец-то у меня дошли руки попробовать реализовать ее наиболее простым и элегантным способом. Если кто-не знает, что это за игра — на русской Википедии есть хорошее описание. Для тех, кто знает, но забыл, я постараюсь напомнить классические правила:

  • Время дискретно.
  • Игровое поле — плоскость, состоящая из клеток, у каждой клетки восемь соседей.
  • Клетки могут находится в двух состояниях: живом или мертвом. Совокупность живых клеток называется поколением.
  • В процессе игры каждая клетка может неограниченное количество раз переходить из одного состояния в другое.

Правила перехода тоже не отличаются сложностью:

  • Каждое следующее поколение рассчитывается на основе предыдущего.
  • Мертвая клетка оживает, если рядом с ней находится ровно 3 живые клетки.
  • Живая клетка продолжает жить, если рядом с ней находится 2 или 3 живые клетки.

Все идеально подходит для программного моделирования. К сожалению, код будет представлен только для последней версии — я в творческом порыве не удосужился начать сразу все складывать в git.

Наивная реализация

Начать лучше с самого прямолинейного способа. Он выглядит примерно так:

  1. Создается двумерный массив из булевых значений, где True бы означал наличие живой клетки, а False — мертвой.
  2. Генерируется новый массив.
  3. Для каждой клетки определяется число живых соседей и на основании этого определяется, будет ли она жить в новом поколении.
  4. Новый массив записывается на место старого.
  5. Происходит возврат к пункту 2.

Обычное в этом месте появляется потребность определять соседей клетки для реализации пункта 3. Поскольку сейчас мы имеем игровое поле, представленное в виде массива — есть риск выйти за его пределы. Есть несколько способов этого избежать:

  • Отбрасывать в функции все обращения к i, j < 0 и i >= n, j >= m (где m и n — размер строки и столбца соответственно).
  • Замкнуть поле на само себя — все обращения к клеткам из предыдущего пункта считать обращением к клетке с другой стороны поля. Т.е i < 0 будет обращением к m — i, i > m будет обращением к клетке m — i. Для j формулы аналогичны.
  • Оградить игровое поле со всех сторон элементами, обозначающими его границу. Такие элементы физически не являются ни мертвым, ни живыми, но при подсчете числа живых соседей считаются мертвыми.

Алгоритм хоть и прост для понимания, но имеет ряд недостатков:

  • Имеет линейное от размеров поля время работы.
  • Двойное потребление памяти (нужно хранить предыдущее поколение + новое).
  • Требует много кода для реализации.

Все это хочется взять и оптимизировать.

Наивная оптимизация

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

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

Кардинальная оптимизация с переосмыслением

Если продолжить развивать идею из предыдущего пункта (хранение списка координат живых клеток и их соседей), можно уйти и от моделирования «в лоб». В конце концов, состояние поля можно выразить просто списком живых клеток. Почему это возможно? Зная только координаты живых клеток мы можем воссоздать поле в изначальном состоянии. Хранить состояние соседних с живыми клеток нам тоже не требуется — поскольку состояний у клеток всего два (жива-мертва) — все, кто не живы, мертвы, а получить координаты соседей по координатам клетки тоже достаточно просто. Плюсы этого варианта достаточно заманчивы:

  • Экономия памяти. Нам не нужно хранить все поле целиком.
  • Время такое же, как и в предыдущем разделе.
  • Динамичность игрового поля. Размеры поля можно менять на протяжении игры в зависимости от расположения живых клеток.
  • Алгоритм очень легко параллелится. Можно создать несколько потоков, которые бы принимали список живых клеток и координаты рассматриваемой клетки. Результат работы потоков склеивается и получается новый список живых клеток.
  • Он красивый:)

Я не люблю везде засовывать ООП, поэтому мой вариант основан целиком на функциях.

Все начинается с функции, генерирующей новое поколение клеток на основе списка живых клеток предыдущего поколения:

defnew_step(alive_cons):board=itertools.chain(*map(get_neighbors,alive_cons))new_board=set([conforconinboardifis_alive_con(con,alive_cons)])returnlist(new_board)

Запутанная конструкция всего лишь сохраняет в переменной board список всех живых ячеек и их соседей. Теперь настала очередь функции is_alive_con.

defis_alive_con(con,alive_cons):alive_neighbors=calculate_alive_neighbors(con,alive_cons)if(alive_neighbors==3or(alive_neighbors==2andconinalive_cons)):returnTruereturnFalse

В следующей реализованы правила перехода клетки из одного состояния в другое.

defcalculate_alive_neighbors(con,alive_cons):returnlen(filter(lambdax:xinalive_cons,get_neighbors(con)))

Осталось рассмотреть самое важное — функцию, которая возвращает список всех соседей клетки на основе ее координат.

defget_neighbors(con):x,y=conneighbors=[(x+i,y+j)foriinxrange(-1,2)forjinxrange(-1,2)ifnoti==j==0]returnneighbors

Здесь нет необходимости проверять корректность координат клеток соседей — некорректные координаты (приводящие к обращению за пределы поля) и так не должны будут встретиться в списке живых. Корректность имеет смысл проверять уже после создания нового поколения. У меня для этого реализованы две функции — одна проверяет корректность координат клетки, другая делает нечто с «неправильными» — в этой реализации просто отбрасывает все некорректные значения:

defis_correct_con(size,con):x,y=conreturnall(0<=coord<=size-1forcoordin[x,y])defcorrect_cons(size,cons):returnfilter(lambdax:is_correct_con(size,x),cons)

Заключение

Подход, при котором сохраняются только координаты клеток с определенным состоянием может применяться не только для моделирования GoF, но и для других игр, реализуемых в терминах клеточных автоматов. Например, при реализации крестиков-ноликов можно сохранять только координаты клеток, в которых стоит крестик или нолик, а не моделировать все поле сразу. Это достаточно удобно, если нужно смоделировать игру по нестандартным правилам (доска не 3*3 и требуется поставить в ряд не 3 символа). Проверять, образовалась ли на доске выигрышная комбинация можно по аналогии с определением живости клетки GoF, но чуть сложнее:

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

Полную реализацию можно посмотреть на github

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

Похожие статьи


Все необходимые инструкции для подключения и работы вашего клуба мы отправим Вам на email.

Инструкция для самостоятельной игры в систему Чемпион.

1. Нужно зарегистрироваться в системе. 

Для этого нажимаем кнопку регистрация

  1. Выберите удобный для Вас способ регистрации — Через email или мобильный телефон.
  2. Введите Ваш действующий email
  3. Придумайте свой пароль, и никому не сообщайте его.
  4. Введите свой пароль повторно без ошибок.
  5. Выберите свою страну из списка
  6. Код партнера не меняется
  7. Обязательно соглашаемся с правилами сайта.
  8. Для окончания регистрации необходимо ввести текст с картинки и нажать кнопку ОТПРАВИТЬ

Если Вы все сделали правильно, на экране появится вот такое сообщение

2. После этого Вам нужно перейти на свой почтовый ящик (email) и открыть новое письмо от support@championcasino.net

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

*если на почте нет нового письма, проверьте папку «спам», письмо по ошибке может попасть в нее

3. Теперь Ваш аккаунт активирован, на экране появится соответствующее окно (рис 3)

4. Для входа в аккаунт вам нужно ввести логин и пароль и нажать кнопку ВХОД (рис 4)

*напоминаем, что логин — это email который Вы указали при регистрации

После входа Вы увидите свой Баланс, кнопки для пополнения и вывода средств. (Рис 5)

5. ПОПОЛНЕНИЕ

Для пополнения баланса нажмите на кнопку Пополнить

Появится окошко с выбором валют (рис 6)

Тут можно выбрать такие валюты:

-Евро

-Казахстанский тенге

-Рубль

-Гривна

Для каждой валюты есть свои способы пополнения, смотрите рис 7-10

Вы можете выбрать любой удобный для Вас способ пополнения.  Минимальная сумма для пополнения 5 евро (по курсу в любой валюте)

Рассмотрим на примере пополнения с помощью банковской карты Visa или MasterCard, этот способ пополнения есть для каждой валюты.

К примеру Мы хотим пополнить баланс на 160 грн и выбрали способ оплаты Visa и MasterCard

Появится окно (рис 11)

1)Сумма на которую Вы хотите пополнить баланс

2)Номер вашей карты

3)Дата окончания действия вашей карты (она есть прямо на карте)

4)Код карты (он есть с обратной стороны, три цифры)

5)Номер вашего телефона

6. ВЫВОД ДЕНЕГ

ВНИМАНИЕ!! Вывести деньги могут только верифицированные (проверенные) пользователи. Это делается для того чтобы удостовериться что Вы совершеннолетний пользователь.

Проверка занимает несколько минут, для этого нажимаем НАСТРОЙКИ (рис 5) и в появившемся окошке вводим все данные. (Рис 12)

В последнем пункте скан-копия паспорта — достаточно сфотографировать первую страницу своего паспорта на мобильный телефон.

После проверки красный значок NOT VERIFIED сменится на зеленый VERIFIED (рис 13)

После этого Вы можете выводить средства с баланса. Минимальная сумма для вывода 20 евро по курсу валюты на вашей карте.

*деньги вы получается в валюте вашей карты

В упомянутом выше слове ударение ставят на слог с буквой О — телепОрт.

Ближайший стационарный телепо́рт находился в трёх часах ходьбы отсюда, и путь по скалам, окружавшим замок, нас изрядно вымотал. — Антон Текшин, В игре (сборник), 2014

Быстро активировав магией телепо́рт, который располагался в стене, я шагнула в него вслед за принцем. — Наталья Косухина, О вкусах не спорят, о вкусах кричат, 2013

Я быстро шагнул в телепо́рт, надеясь отсечь все проблемы переходом. — Алекс Кош, Огненный Факультет, 2005

А кто зарядит мне телепо́рт? — Рустам Панченко, Герцог.

За что боролись…, 2013

На телепо́рт или портал в другой мир. — Ярослав Коваль, Магия госбезопасности, 2009

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

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *