Уважаемые пользователи Голос!
Сайт доступен в режиме «чтение» до сентября 2020 года. Операции с токенами Golos, Cyber можно проводить, используя альтернативные клиенты или через эксплорер Cyberway. Подробности здесь: https://golos.io/@goloscore/operacii-s-tokenami-golos-cyber-1594822432061
С уважением, команда “Голос”
GOLOS
RU
EN
UA
rassen
7 лет назад

Стать программистом. Практика JavaScript для новичков. Занятие 15.

Доброго времени суток, друзья!

В нашем прошлом занятии мы с Вами научили нашего Муравья не только двигаться куда ему заблагорассудится, но и играть. Собственно, помимо тих двух действий нам осталось реализовать еще одно – мы должны научить нашего Питомца спать. Давайте начнем наше сегодняшнее занятие с этого. Чтоб уже закрыть вопрос с умениями нашего Муравья.

Стать программистом. Практика JavaScript для новичков. Занятие 15.

Научить нашего Питомца спать на самом деле так же просто, как и играть. За визуальное представление нашего Муравья в спящем состоянии так же как в случае с игрой отвечает специальный класс sleep. Давайте посмотрим в браузере как это выглядит:

Соответственно реализация методов для этого состояния будет абсолютна идентична с методами для Игры. Давайте так же создадим два метода startSleep() и stopSleep().

Вот наши методы. Они идентичны тем, которые мы создавали в прошлом занятии, поэтому разъяснять их я думаю не требуется. Всё их различие сводится к названию класса и тексту для информационного блока. Давайте удостоверимся что наши новые методы работают.

Ну что ж отлично. Теперь наш Питомец умеет всё то же самое что и питомец в прототипе. Собственно здесь можно заняться оптимизацией, так как у нас идет явное дублирование кода в метода startPlay() и startSleep(), а так же в методах stopPlay() и stopSleep(). И я уже было хотел, но подумал, что Вы уже должны быть в состоянии сами сделать оптимизацию такого рода. Поэтому давайте в нашем текущем состоянии опустим этот момент. Вы попробуете его реализовать самостоятельно. А если возникнут вопросы, то напишите о них в комментариях, и я постараюсь помочь.
Ну а мы вспомним наш список по Питомцу из занятия 11 и посмотрим, что у нас еще осталось:

  1. Питомец должен уметь появляться в разных участках своего домика.
  2. Питомец, должен уметь двигаться внутри своего домика. Он должен уметь это делать во всех направлениях (вниз, вверх, влево, вправо) и при этом не должен выходить за пределы.
  3. Питомец должен уметь играть.
  4. Питомец должен уметь спать.
  5. Питомец должен уметь выполнять все доступные ему действия в произвольном порядке, для эмулирования интеллекта.
  6. Питомец должен уметь прекращать выполнение текущего действия и прекращать выполнения всех действий одновременно.

Как видим мы достигли почти всех поставленных целей. Шестой пункт у нас реализован частично (ведь в отдельности мы можем останавливать текущее действие). То есть мы переходим к реализации пятого пункта. Мы должны собрать все доступные нашему питомцу действия и сделать так, чтобы наш Муравей случайным образом выполнял их на протяжении всей игры. Это и будет эмуляцией нашего искусственно интеллекта, о которой я писал ранее.
Итак, задача поставлена, давайте попробуем ее реализовать. Как наш муравей должен понимать, что ему делать в данный промежуток времени? Снова-таки, давайте сначала определимся что он может делать. Он у нас может гулять, играть и спать, то есть совершать три действия. Значит мы можем создать массив из трех действий, и случайно выбирать действие из него. Понимаете, куда я клоню? Да, мы уже реализовывали нечто подобно с направлениями для движения. То есть как минимум, у нас уже предвидится две функциональности, которые будут использовать случайный выбор элемента массива. Сейчас реализация такого выбора находится в методе getDirection():

Ну а раз она у нас будет более общей, я предлагаю переименовать этот метод в более обобщенный вариант, например makeChoice() и расположить его в объекте, который мы пока еще не успели затронуть - {Brain}. Располагается он в файле ant_brain.js. А сам метод getDirection() преобразовать в соответствие с этими изменениями. Давайте сделаем это:

var Brain = {
    /* Случайный выбор из массива */
    makeChoice: function(array) {
        var index = Math.ceil(Math.random()*array.length) - 1;

        return array[index];
    }
}

Обратите внимание как мы преобразовали нашу случайную выборку из метода getDirection() в методе makeChoice(). Теперь массив с доступными вариантами из которого надо выбрать случайный мы передаем как параметр. И так как массивы у нас могут быть разной длины (то есть раньше мы брали индекс в промежутке от 0 до 4, где 4 - это были четыре варианта для движения) то в такой организации у нас могут быть массивы с разным количеством доступных вариантов. Например, в наших вариантах действий (Играть, Гулять, Спать) их будет только три. То нам пришлось заменить жестко заданную четверку на длину массива (первая зеленая стрелочка) и после этого мы возвращаем выбранный вариант с помощью оператора return (вторая зеленая стрелочка). Ну а метод getDirection() теперь преобразовался в следящий вид:

То есть массив с направлениями остался без изменений, но работу по выбору случайно элемента из этого массива мы уже передали методу makeChoice() объекта {Brain}. Проверим будет ли наш Муравей менять направления своего движения после наших изменений.


Как видим всё в порядке, и мы ничего не сломали. Давайте теперь проверим, будет ли без ошибок выбираться нужное значение из массива другой длины. Для этого давайте создадим метод makeAction() (делать действие) для нашего Питомца, где и создадим массив с возможными действиями.

Теперь у нас есть массив с тремя доступными действиями и снова с помощью нашего метода makeChoice() мы уже будем делать случайный выбор из него. Давайте посмотрим на результат работы:

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

И вот, что у нас получилось. Сначала в наш метод stopAllActions() мы добавили методы для остановки действия спать и играть. После этого в нашем методе makeAction() мы создали интервал, который отрабатывает каждые 5 секунд, выбирает действия из массива actions (оранжевая стрелка), останавливает все текущие действия (зеленая стрелка) и в зависимости от выбранного действия, запускает нужный метод для его начала (синие стрелки). И как следствие наш муравей каждые пять секунд начинает делать то что ему заблагорассудится:

Актуальный код:

var Pet = function(x, y) {
    this.selector = document.getElementById('pet');
    this.x = x || 0;
    this.y = y || 0;
    this.walkInterval = null;
    this.movementInterval = null;
}

Pet.prototype =  {
    /* Задаем начальную позицию питомца в домике */
    setPosition: function() {
        var x = this.x;
        var y = this.y;
        var petWidth = this.selector.offsetWidth;
        var petHeight = this.selector.offsetHeight;
        var bottomEnd = Booth.getHeight() - petHeight;
        var rightEnd = Booth.getWidth() - petWidth;
        /* проверрим на верхнюю и левую границы домика */
        if( x < 0 ) {
            this.x = x = 0;
        }
        if( y < 0 ) {
            this.y = y = 0;
        }
        /* проверрим на правую и нижнюю границы домика */
        if( x > rightEnd) {
            this.x = x = rightEnd;
        }
        if( y > bottomEnd) {
            this.y = y = bottomEnd;
        }

        this.selector.style.top = y + 'px';
        this.selector.style.left = x + 'px';
    },
    /* передвижение питомца */
    startWalk: function(direction) {
        var self = this;
        var timeForWalk = 50;
        var petWidth = this.selector.offsetWidth;
        var petHeight = this.selector.offsetHeight;
        var leftOrUp = function(styleName) {
            var current = parseInt(self.selector.style[styleName]) || 0;
                if(current != 0) {
                    self.selector.style[styleName] = (current - 1) + 'px';
                }
        };
        var rightOrBottom = function(styleName, border) {
                var current = parseInt(self.selector.style[styleName]) || 0;
                if(current < border) {
                    self.selector.style[styleName] = (current + 1) + 'px';
                }
        };
        var currentIntervalFunction = null;

        if(direction == 'left') {
            currentIntervalFunction = function() {
                leftOrUp('left');
            }
        }

        if(direction == 'right') {
            currentIntervalFunction = function() {
                var border = Booth.getWidth() - petWidth;
                rightOrBottom('left', border);
            }
        }

        if(direction == 'up') {
            currentIntervalFunction = function() {
                leftOrUp('top');
            }
        }

        if(direction == 'down') {
            currentIntervalFunction = function() {
                var border = Booth.getHeight() - petHeight;
                rightOrBottom('top', border);
            }
        }

        this.selector.setAttribute('class', direction);

        this.walkInterval = setInterval(currentIntervalFunction, timeForWalk);

    },
    /* остановка интервала питомца */
    stopInterval: function(intervalLink) {
        clearInterval(intervalLink);
    },
    /* начинает движение */
    startMovement: function() {
        var self = this;
        var timForMovementInOneDirection = 2000;

        this.movementInterval = setInterval(function() {
            var newDirection = self.getDirection()

            self.stopInterval(self.walkInterval);

            self.startWalk(newDirection);

        }, timForMovementInOneDirection);


        self.startWalk(this.getDirection());

        Booth.informer.setText('Гуляет...');

    },
    /* Выбор направления движения */
    getDirection: function() {
        var directionArray = ['left', 'right', 'up', 'down'];
        var currentDirection = Brain.makeChoice(directionArray);

        return currentDirection;
    },
    /*Остановить все дейсвтия питомца */
    stopAllActions: function() {
        this.stopInterval(this.movementInterval);
        this.stopInterval(this.walkInterval);
        this.stopPlay();
        this.stopSleep();
    },
    /* Начать играть с мячом */
    startPlay: function() {
        this.selector.setAttribute('class', 'playing');
        Booth.informer.setText('Играет...');
    },
    /* Перестать играть с мячом*/
    stopPlay: function() {
        this.selector.removeAttribute('class');
    },
    /* Начать играть с мячом */
    startSleep: function() {
        this.selector.setAttribute('class', 'sleep');
        Booth.informer.setText('Спит...');
    },
    /* Перестать играть с мячом*/
    stopSleep: function() {
        this.selector.removeAttribute('class');
    },
    /* Выбор действия */
    makeAction: function() {
        var self = this;
        var actions = ['sleep', 'walk', 'play'];
        var timeForChangeAction = 5000;

        this.makeActionInterval = setInterval(function() {
            var intervalAction = Brain.makeChoice(actions);

            /* Останавливаем все действия */
            self.stopAllActions();

            /* Начинаем случайно выбранное действие */
            if(intervalAction == 'walk') {
                self.startMovement();
            }

            if(intervalAction == 'sleep') {
                self.startSleep();
            }

            if(intervalAction == 'play') {
                self.startPlay();
            }

        }, timeForChangeAction);

        /* Для того чтобы не ждать начала интервала */
        this.startMovement();
        
    }
}

/* Создаем муравья, используя функцию-родитель */

var ant = new Pet(0, 0);

А на сегодня всё. Продолжение следует…

Ссылки на предыдущие занятия:

Практика JavaScript для новичков. Занятие 1,
Практика JavaScript для новичков. Занятие 2,
Практика JavaScript для новичков. Занятие 3,
Практика JavaScript для новичков. Занятие 4,
Практика JavaScript для новичков. Занятие 5,
Практика JavaScript для новичков. Занятие 6,
Практика JavaScript для новичков. Занятие 7,
Практика JavaScript для новичков. Занятие 8,
Практика JavaScript для новичков. Занятие 9,
Практика JavaScript для новичков. Занятие 10,
Практика JavaScript для новичков. Занятие 11,
Практика JavaScript для новичков. Занятие 12,
Практика JavaScript для новичков. Занятие 13,
Практика JavaScript для новичков. Занятие 14

Ссылки на предыдущий курс:

Урок 1 - Окружение.,
Урок 2 - Некоторые особенности синтаксиса.,
Урок 3 - Переменные.,
Урок 4 - Типы переменных, как основа для их взаимодействия.,
Урок 5 - Операции с переменными одного типа.,
Урок 6 - Операции с переменными одного типа. Часть II.,
Урок 7 - Взаимодействие переменных с разными типами.,
Урок 8 - Взаимодействие переменных разного типа. часть II.,
Урок 9 - Взаимодействие переменных разного типа. Часть III.,
Урок 10 - Другие возможные взаимодействия между переменными.,
Урок 11 - Другие возможные взаимодействия между переменными. Часть II.,
Урок 12 - Другие возможные взаимодействия между переменными. Операторы присваивания.,
Урок 13 - Другие возможные взаимодействия между переменными. Операторы сравнения.,
Урок 14 - Сложные переменные. Array и Object.,
Урок 15 - Условные операторы.,
Урок 16 - Циклы.,
Урок 17 - Циклы. Часть II.,
Урок 18 - Функции.,
Урок 19 - Функции. Часть II.,
Урок 20 - Профилирование. Функции, часть III.,
Урок 21 - Функции, Часть IV. Аргументы.,
Урок 22 - Objects (Объекты).,
Урок 23 - Встроенные функции и объекты.,
Урок 24 - Встроенные функции и Объекты, Часть II. Глобальные функции и переменные.,
Урок 25 - Встроенные функции и Объекты, Часть III. Document Object Model.,
Урок 26 - Встроенные функции и Объекты, Часть III. Document Object Model.
Урок 27 - Встроенные объекты. Объект Style, Events, Часть II.
Урок 28 - Встроенная переменная this. Глобальная и локальная области видимости.
Урок 29 - Объектно-ориентированное Программирование. Введение.
Урок 30 - Объектно-ориентированное Программирование. Часть II. Полиморфизм.
Урок 31 - OОП. Наследование, Часть I. Оператор new.
Урок 32 - ООП. Наследование, Часть II. PROTOTYPE.
Урок 33 - ООП. Часть II. Полиморфизм.
Урок 34 - ООП. Часть III. Инкапсуляция.

5
112.624 GOLOS
На Golos с December 2016
Комментарии (5)
Сортировать по:
Сначала старые