Марейн Хавербеке - Выразительный JavaScript
- Название:Выразительный JavaScript
- Автор:
- Жанр:
- Издательство:неизвестно
- Год:неизвестен
- ISBN:978-1593275846
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Марейн Хавербеке - Выразительный JavaScript краткое содержание
В процессе чтения вы познакомитесь с основами программирования и, в частности, языка JavaScript, а также выполните несколько небольших проектов. Один из самых интересных проектов — создание своего языка программирования.
Выразительный JavaScript - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Если существа только двигаются и едят друг друга, мир вскоре поддастся возрастающей энтропии, в нём закончится энергия и он превратится в пустыню. Для предотвращения этого финала (или оттягивания), мы добавляем в него растения. Они не двигаются. Они просто занимаются фотосинтезом и растут (нарабатывают энергию), и размножаются.
Чтобы это заработало, нам нужен мир с другим методом 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);
}
};
Новый метод letAct
проверяет, было ли передано хоть какое-то действие, затем – есть ли функция, обрабатывающая его, и в конце – возвращает ли эта функция true
, показывая, что действие выполнено успешно. Обратите внимание на использование call
, чтобы дать функции доступ к мировому объекту через this
.
Если действие по какой-либо причине не сработало, действием по умолчанию для существа будет ожидание. Он теряет 0,2 единицы энергии, а когда его уровень энергии падает ниже нуля, он умирает и исчезает с сетки.
Обработчики действий
Самое простое действие – рост, его используют растения. Когда возвращается объект action типа {type: "grow"}
, будет вызван следующий метод-обработчик:
actionTypes.grow = function(critter) {
critter.energy += 0.5;
return true;
};
Рост всегда успешен и добавляет половину единицы к энергетическому уровню растения.
Движение получается более сложным.
actionTypes.move = function(critter, vector, action) {
var dest = this.checkDestination(action, vector);
if (dest == null ||
critter.energy <= 1 ||
this.grid.get(dest) != null)
return false;
critter.energy -= 1;
this.grid.set(vector, null);
this.grid.set(dest, critter);
return true;
};
Это действие вначале проверяет, используя метод checkDestination
, объявленный ранее, предоставляет ли действие допустимое направление. Если нет, или же в том направлении не пустой участок, или же у существа недостаёт энергии – move
возвращает false
, показывая, что действие не состоялось. В ином случае он двигает существо и вычитает энергию.
Кроме движения, существа могут есть.
actionTypes.eat = function(critter, vector, action) {
var dest = this.checkDestination(action, vector);
var atDest = dest != null && this.grid.get(dest);
if (!atDest || atDest.energy == null)
return false;
critter.energy += atDest.energy;
this.grid.set(dest, null);
return true;
};
Поедание другого существа также требует предоставления допустимой клетки направления. В этом случае клетка должна содержать что-либо с энергией, например существо (но не стену, их есть нельзя). Если это подтверждается, энергия съеденного переходит к едоку, а жертва удаляется с сетки.
И наконец, мы позволяем существам размножаться.
actionTypes.reproduce = function(critter, vector, action) {
var baby = elementFromChar(this.legend,
critter.originChar);
var dest = this.checkDestination(action, vector);
if (dest == null ||
critter.energy <= 2 * baby.energy ||
this.grid.get(dest) != null)
return false;
critter.energy -= 2 * baby.energy;
this.grid.set(dest, baby);
return true;
};
Размножение отнимает в два раза больше энергии, чем есть у новорожденного. Поэтому мы создаём гипотетического отпрыска, используя elementFromChar
на оригинальном существе. Как только у нас есть отпрыск, мы можем выяснить его энергетический уровень и проверить, есть ли у родителя достаточно энергии, чтобы родить его. Также нам потребуется допустимая клетка направления.
Если всё в порядке, отпрыск помещается на сетку (и перестаёт быть гипотетическим), а энергия тратится.
Населяем мир
Теперь у нас есть основа для симуляции существ, больше похожих на настоящие. Мы могли бы поместить в новый мир существ из старого, но они бы просто умерли, так как у них нет свойства energy
. Давайте сделаем новых. Сначала напишем растение, которое, по сути, довольно простая форма жизни.
function Plant() {
this.energy = 3 + Math.random() * 4;
}
Plant.prototype.act = function(context) {
if (this.energy > 15) {
var space = context.find(" ");
if (space)
return {type: "reproduce", direction: space};
}
if (this.energy < 20)
return {type: "grow"};
};
Растения начинают со случайного уровня энергии от 3 до 7, чтобы они не размножались все в один ход. Когда растение достигает энергии 15, а рядом есть пустая клетка – оно размножается в неё. Если оно не может размножиться, то просто растёт, пока не достигнет энергии 20.
Теперь определим поедателя растений.
function PlantEater() {
this.energy = 20;
}
PlantEater.prototype.act = function(context) {
var space = context.find(" ");
if (this.energy > 60 && space)
return {type: "reproduce", direction: space};
var plant = context.find("*");
if (plant)
return {type: "eat", direction: plant};
if (space)
return {type: "move", direction: space};
};
Для растений будем использовать символ *
— то, что будет искать существо в поисках еды.
Вдохнём жизнь
И теперь у нас есть достаточно элементов для нового мира. Представьте следующую карту как травянистую долину, где пасётся стадо травоядных, лежат несколько валунов и цветёт буйная растительность.
var valley = new LifelikeWorld(
["############################",
"##### ######",
"## *** **##",
"# *##** ** O *##",
"# *** O ##** *#",
"# O ##*** #",
"# ##** #",
"# O #* #",
"#* #** O #",
"#*** ##** O **#",
"##**** ###*** *###",
"############################"],
{"#": Wall,
"O": PlantEater,
"*": Plant}
);
Большую часть времени растения размножаются и разрастаются, но затем изобилие еды приводит к взрывному росту популяции травоядных, которые съедают почти всю растительность, что приводит к массовому вымиранию от голода. Иногда экосистема восстанавливается и начинается новый цикл. В других случаях какой-то из видов вымирает. Если травоядные, тогда всё пространство заполняется растениями. Если растения – оставшиеся существа умирают от голода, и долина превращается в необитаемую пустошь. О, жестокость природы…
Читать дальшеИнтервал:
Закладка: