Так что есть ещё одна проверка через >if
, что сканирование нужно начинать, если существо только что прошло мимо какого-либо препятствия. То есть, если пространство сзади и слева не пустое. В противном случае сканировать начинаем впереди, поэтому в пустом пространстве он будет идти прямо.
И наконец, есть проверка на совпадение >this.dir
и >start
на каждом проходе цикла, чтобы он не зациклился, когда существу некуда идти из-за стен или других существ, и оно не может найти пустую клетку.
Этот небольшой мир показывает существ, двигающихся по стенам:
>animateWorld(new World(
> ["############",
> "# # #",
> "# ~ ~ #",
> "# ## #",
> "# ## o####",
> "# #",
> "############"],
> {"#": Wall,
> "~": WallFollower,
> "o": BouncingCritter}
>));
Чтобы сделать жизнь в нашем мирке более интересной, добавим понятия еды и размножения. У каждого живого существа появляется новое свойство, >energy
(энергия), которая уменьшается при совершении действий, и увеличивается при поедании еды. Когда у существа достаточно энергии, он может размножаться, создавая новое существо того же типа. Для упрощения расчётов наши существа размножаются сами по себе.
Если существа только двигаются и едят друг друга, мир вскоре поддастся возрастающей энтропии, в нём закончится энергия и он превратится в пустыню. Для предотвращения этого финала (или оттягивания), мы добавляем в него растения. Они не двигаются. Они просто занимаются фотосинтезом и растут (нарабатывают энергию), и размножаются.
Чтобы это заработало, нам нужен мир с другим методом >letAct
. Мы могли бы просто заменить метод прототипа >World
, но я привык к нашей симуляции ходящих по стенам существ и не хотел бы её разрушать.
Одно из решений – использовать наследование. Мы создаём новый конструктор, >LifelikeWorld
, чей прототип основан на прототипе >World
, но переопределяет метод >letAct
. Новый >letAct
передаёт работу по совершению действий в разные функции, хранящиеся в объекте >actionTypes
.
>function LifelikeWorld(map, legend) {
> World.call(this, map, legend);
>}
>LifelikeWorld.prototype = Object.create(World.prototype);
>var actionTypes = Object.create(null);
>LifelikeWorld.prototype.letAct = function(critter, vector) {
> var action = critter.act(new View(this, vector));
> var handled = action &&
> action.type in actionTypes &&
> actionTypes[action.type].call(this, critter,
> vector, action);
> if (!handled) {
> critter.energy -= 0.2;
> if (critter.energy <= 0)
> this.grid.set(vector, null);