Выразительный JavaScript (Хавербеке) - страница 77

Так что есть ещё одна проверка через >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);