Книга: Выразительный JavaScript
Оживляем мир
Оживляем мир
Следующий шаг – создание метода turn
(ход) для мирового объекта, дающего существам возможность действовать. Он будет обходить сетку методом forEach
, и искать объекты, у которых есть метод act
. Найдя объект, turn
вызывает этот метод, получая объект action
и производит это действие, если оно допустимо. Пока мы понимаем только действие “move”
.
Есть одна возможная проблема. Догадаетесь, какая? Если мы позволим существам двигаться по мере того, как мы их перебираем, они могут перейти на клетку, которую мы ещё не обработали, и тогда мы позволим им сдвинуться ещё раз, когда очередь дойдёт до этой клетки. Таким образом, нам надо хранить массив существ, которые уже сделали свой шаг, и игнорировать их при повторном проходе.
World.prototype.turn = function() {
var acted = [];
this.grid.forEach(function(critter, vector) {
if (critter.act && acted.indexOf(critter) == -1) {
acted.push(critter);
this.letAct(critter, vector);
}
}, this);
};
Второй параметр метода forEach
используется для доступа к правильной переменной this
во внутренней функции. Метод letAct
содержит логику, которая позволяет существам двигаться.
World.prototype.letAct = function(critter, vector) {
var action = critter.act(new View(this, vector));
if (action && action.type == "move") {
var dest = this.checkDestination(action, vector);
if (dest && this.grid.get(dest) == null) {
this.grid.set(vector, null);
this.grid.set(dest, critter);
}
}
};
World.prototype.checkDestination = function(action, vector) {
if (directions.hasOwnProperty(action.direction)) {
var dest = vector.plus(directions[action.direction]);
if (this.grid.isInside(dest))
return dest;
}
};
Сначала мы просто просим существо действовать, передавая ему объект view
, который знает про мир и текущее положение существа в мире (мы скоро зададим View
). Метод act
возвращает какое-либо действие.
Если тип действия не “move”
, оно игнорируется. Если “move”
, и если у него есть свойство direction
, ссылающееся на допустимое направление, и если клетка в этом направлении пустует (null
), мы назначаем клетке, где только что было существо, null
, и сохраняем существо в клетке назначения.
Заметьте, что letAct
заботится об игнорировании неправильных входных данных. Он не предполагает по умолчанию, что направление допустимо, или, что свойство типа имеет смысл. Такого рода защитное программирование в некоторых ситуациях имеет смысл. В основном это делается для проверки входных данных, приходящих от источников, которые вы не контролируете (ввод пользователя или чтение файла), но оно также полезно для изолирования подсистем друг от друга. В нашем случае его цель – учесть, что существа могут быть запрограммированы неаккуратно. Им не надо проверять, имеют ли их намерения смысл. Они просто запрашивают возможность действия, а мир сам решает, разрешать ли его.
Эти два метода не принадлежат к внешнему интерфейсу мирового объекта. Они являются деталями внутренней реализации. Некоторые языки предусматривают способы объявлять определённые методы и свойства «приватными», и выдавать ошибку при попытке их использования снаружи объекта. JavaScript не предусматривает такого, так что вам придётся полагаться на другие способы сообщить о том, что является частью интерфейса объекта. Иногда помогает использование схемы именования свойств для различения внутренних и внешних, например, с особыми приставками к именам внутренних, типа подчёркивания (_
). Это облегчит выявление случайного использования свойств, не являющихся частью интерфейса.
А пропущенная часть, тип View
, выглядит следующим образом:
function View(world, vector) {
this.world = world;
this.vector = vector;
}
View.prototype.look = function(dir) {
var target = this.vector.plus(directions[dir]);
if (this.world.grid.isInside(target))
return charFromElement(this.world.grid.get(target));
else
return "#";
};
View.prototype.findAll = function(ch) {
var found = [];
for (var dir in directions)
if (this.look(dir) == ch)
found.push(dir);
return found;
};
View.prototype.find = function(ch) {
var found = this.findAll(ch);
if (found.length == 0) return null;
return randomElement(found);
};
Метод look
вычисляет координаты, на которые мы пытаемся посмотреть. Если они находятся внутри сетки, то получает символ, соответствующий элементу, находящемуся там. Для координат снаружи сетки look
просто притворяется, что там стена – если вы зададите мир без окружающих стен, существа не смогут сойти с края.
- 8.2. Языки программирования Виды программирований
- 1.1. Введение в объектно-ориентированное программирование
- Покупатель на крючке. Руководство по созданию продуктов, формирующих привычки
- Язык программирования Python
- Формирование платежного поручения
- Программирование на языке Пролог для искусственного интеллекта
- 4.2. Формирование концепции репутационного менеджмента
- Программируя Вселенную. Квантовый компьютер и будущее науки
- Команды и формирование культуры по инициативе сверху
- 6.7. Формирование составных целевых утверждений
- Глава 1 Предел возможностей иерархии в мире перемен
- Цель текста – сформировать мнение