Найти тему
Журнал «Код»

Помогите Дейнерис забрать все драконьи яйца и выиграть «Игру престолов»

Призываем программистов

Обещаем, что это последний материал про «Игру престолов». Зато это задача, которая решается с помощью программирования!

Фермер решил завести 20 драконов, чтобы продавать яйца по 50 серебряных за штуку. Рептилии (драконы же рептилии?) несут по два яйца в неделю, и каждую неделю все яйца продаются. С этих денег фермер покупает ещё по два дракона в неделю по 300 серебряных, чтобы увеличить свой доход. Но расходы велики: чтобы кормить и содержать питомцев, хозяин каждый день тратит 500 серебряных.

Фермер решил: если прибыли от рептилий так и не будет, через год он продаст их всех одной любительнице драконов, а если будет — продолжит дело. Как вы думаете, продаст он их или нет?

Решение

Как будем решать

Можно было бы составить сложную формулу, которая выводит зависимость прибыли от дня года, и начертить график. Там, где график пересёк бы ноль и пошёл вверх, мы бы увидели прибыль. Но формулы — это для математиков, а мы программисты. Мы напишем программу, которая симулирует экономику нашего фермера, перебирая день за днём. Сегодня потратил столько-то, завтра продал яйца и заработал столько-то, послезавтра потратил ещё столько-то… — и будем проверять, когда появится прибыль.

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

Задачу будем решать на JavaScript, потому что он у вас уже работает в браузере и вы можете спокойно повторить решение прямо сейчас, используя консоль (в меню «Вид» → «Разработчикам» → «Консоль»).

Шаг 1. Знакомство с функцией

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

Чтобы задать функцию, которую мы назовём runDragonSimulator, используем такой код:

function runDragonSimulator() { }

Вначале идёт команда function, которая говорит компьютеру: мы хотим сделать новую функцию, которая будет называться runDragonSimulator. В круглых скобках пишутся параметры функции, оказывающие влияние на её работу. В нашем случае таких параметров нет, функция всегда будет действовать одинаково, поэтому в этих скобках сейчас ничего не указываем. А внутри фигурных скобок {} мы напишем тело функции — код, который выполнится, когда мы вызовем эту функцию.

Почему функция называется именно так — прочитайте в нашей статье о названии функций.

Когда компьютер встретит наименование функции, он поймёт, что нужно выполнить тот кусок кода, который стоит между фигурными скобками. Он его выполнит, а потом снова вернётся к программе — с того же места, где он прервался на функцию.

Например, заведём пару функций, которые будут выводить в консоль определённые слова:

function question() {
     console.log('Валар моргулис');
    // И можно выполнить какие угодно ещё команды, в любом количестве
}
function answer() {
    console.log('Валар дохаэрис');
    // Тут тоже можно выполнить любые дополнительные команды
}

Теперь если мы просто напишем названия этих функций, то компьютер выполнит те команды, которые мы описали в нашей функции:

    question();
    > 'Валар моргулис'
    answer();
    > 'Валар дохаэрис'

У функции есть свойство: её можно прервать в любой момент командой return. Когда компьютер в функции доходит до выполнения команды return, он прекращает осуществлять задачу ровно в этом месте и возвращается в главную программу.

Нам это поможет вот для чего. Так как мы не можем сказать точно, в какой момент доходы фермера от драконов превысят расходы, мы будем постоянно проверять, произошло это или нет. Если не произошло — работаем дальше, а если вышли в плюс по доходам — прерываем выполнение командой return и выводим данные о доходах.

Если схематично построить логику нашего решения, оно будет выглядеть так:

  • Считаем дни, сколько прошло всего и с начала месяца.
  • Ежедневно тратим деньги на корм и содержание драконов.
  • Каждую неделю продаём все яйца.
  • На полученные деньги каждую неделю покупаем ещё двух драконов.
  • Если после этого доходы всё ещё меньше расходов — возвращаемся в первый пункт, в противном случае прерываем подсчёты и подводим итоги: пишем, сколько мы заработали и сколько времени на это ушло.

Шаг 2. Установка переменных

Переменные — это ячейки памяти компьютера, где будут храниться нужные нам данные. Нам понадобятся переменные для дохода и расходов, для общего количества дней и для подсчёта каждого седьмого дня, который приносит нам доход.

Переменные в языке JavaScript задаются словами var или let. У них есть разница в видимости, сейчас она нас не волнует, но в будущем о них поговорим. Для этого примера используем var:

Теперь задаём количество драконов в самом начале и стартовые значения дохода и расхода:

Весь основной код мы поместим в нашу функцию:

Шаг 3. Цикл

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

while ( условие ) { команды }

Смысл такой: пока условие верное, команды выполняются раз за разом. После каждой отработки всех команд цикл возвращается в начало и снова проверяет — выполняется ли до сих пор условие. Если да, снова делаем все команды, если нет — цикл на этом прекращается.

Мы поставим такое условие: пусть всё работает, пока доходы меньше расходов, а мы посмотрим, что настанет раньше — пройдёт один год или мы выйдем в плюс:

Шаг 4. Подсчёты

Началась ежедневная рутина: подсчёт дней, понедельный и общий, и постоянные траты на корм и уход за драконами.

Чтобы увеличить количество прошедших дней на единицу, можно использовать привычную команду day = day + 1. Она берёт старое значение day, прибавляет к нему единицу и результат помещает снова в day. А можно сделать изящнее:

day += 1;

Команда += 1 увеличивает значение нашей переменной на единицу. Если бы мы написали day +=3, то это бы увеличило значение day на 3.

Что у нас происходит каждый день: меняется текущий день недели, возрастает общее количество прошедших дней, тратим 500 серебряных на содержание драконов:

day += 1;
dayZ += 1;
expense += 500;

Теперь надо проверить, вдруг сегодня уже седьмой день, — и тогда мы соберём по два яйца с каждого дракона, продадим их и купим ещё рептилий:

Мы увеличиваем размер нашего дохода на ту сумму, которую нам принесёт продажа яиц, а затем обнуляем количество прошедших дней с начала недели: седьмой день закончился, начинаем отсчёт сначала.

Шаг 5. Проверки

Теперь надо проверить, вдруг прошёл уже один год и общая сумма наших доходов до сих пор не превысила все наши расходы? Если так, то сообщаем об итогах эксперимента и останавливаем выполнение функции:

Но что будет, если наш цикл однажды закончится сам? Речь про условие while (margin < expense).

Если однажды это условие не выполнится, это значит, что доходы перестали быть меньше расходов, то есть мы вышли в плюс. Тогда нужно вывести количество дней, сколько у нас ушло на это, а также прибыль, которую мы получили:

console.log('За ' + dayZ + ' дней вы вышли в плюс. Всего вы потратили ' + expense + ' и заработали ' + margin + ' c. Первая прибыль: ' + (margin — expense));

Собираем всё

Все части программы у нас уже есть. Собираем готовый код:

Обратите внимание: сначала мы создали функцию, описали все её ветки и логику. Но когда мы её описываем, она ещё не работает. Мы как бы задали правила игры, но саму игру не запустили. И только на последней строке кода мы говорим runDragonSimulator(); — и вот тогда программа действительно активируется и выполняет всё, как мы её просили.

Такую программу можно доработать под себя и использовать уже для оценки реальных проектов — насколько они рентабельны и какие нужны условия для того, чтобы они стали прибыльными.

Подписывайтесь на наш канал, чтобы ваши драконы вас слушались!