Задача про драконьи яйца. Призываем программистов
vk f t

Задача про драконьи яйца. Призываем программистов

Уровень: начинающий

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

Фермер решил завести 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:

    
language: JavaScript
// сюда мы будем складывать наш общий доход и расход

var margin, expense;

// переменная поможет нам отследить седьмой день, когда снесутся все драконы и мы продадим все яйца. Пока ничего не началось, это считается нулевым днём.

var day = 0;

// считаем количество дней всего, один год — это 365 дней

var dayZ = 0;


Скопировать код

Код скопирован

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

    
language: JavaScript
// число драконов на старте — 20

var group = 20;

// сколько яиц нам принесли все драконы за неделю, в самом начале пока — 0

var eggs = 0;

// устанавливаем стартовые цифры дохода и расхода на первый день работы

margin = 0;

expense = 500;


Скопировать код

Код скопирован

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

    
language: JavaScript
function runDragonSimulator() {

   // тут будет наш код

}


Скопировать код

Код скопирован

Шаг 3. Цикл

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

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

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

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

    
language: JavaScript
while (margin < expense) {

   // тут будет основной код программы

}


Скопировать код

Код скопирован

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

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

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

day += 1;

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

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

day += 1;

dayZ += 1;

expense += 500;

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

    
language: JavaScript
// каждый седьмой день нам приносит доход, это значит:

if (day == 7) {

   

   // все драконы несут за неделю по два яйца

   eggs += group * 2;

   

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

   margin += eggs * 50;

   

   //каждую неделю покупаем два дракона по 300 серебряных…

   expense += 2 * 300;

   

   // …это увеличивает общее количество рептилий

   group += 2;

   

   // все яйца распродали, начинаем сначала

   eggs = 0;

   

   // начинаем отсчитывать дни заново

   day = 0;

}


Скопировать код

Код скопирован

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

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

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

    
language: JavaScript
// если прошёл год, а проект так и не взлетел (хехех) — останавливаемся и считаем убытки

if ((dayZ == 356) && ((margin - expense) < 0)) {

   

   // выводим сообщение о доходах и расходах за этот год

   console.log('Драконы за год так и не принеcли вам денег, милорд. Ваши убытки за это время составили ' + (expense - margin) + ' c.');

   

   // останавливаем функцию и больше ничего не вычисляем

   return;

}


Скопировать код

Код скопирован

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

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

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

Собираем всё

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

    
language: JavaScript
// сюда мы будем складывать наш общий доход и расход

var margin, expense;

 

// переменная поможет нам отследить седьмой день, когда все драконы снесут яйца

var day = 0;

 

// считаем количество дней всего, один год — это 365 дней

var dayZ = 0;

 

// количество драконов, на старте — 20

var group = 20;

 

// сколько яиц нам принесли все рептилии за неделю, в самом начале пока — 0

var eggs = 0;

 

// устанавливаем стартовые цифры дохода и расхода на первый день работы

margin = 0;

expense = 500;

 

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

// объявляем функцию с названием runDragonSimulator

function runDragonSimulator() {

   

   // пока расходы превышают доходы — работаем

   while (margin < expense) {

       

       // наступил новый день из семи

       day += 1;

       

       // и сразу считаем общее количество дней

       dayZ += 1;

       

       // каждый день мы тратим 500 серебряных…

       expense += 500;

                      

       // каждый седьмой день нам приносит доход, это значит:

       if (day == 7) {

           

           // все драконы несут за неделю по два яйца

           eggs +=group * 2;

           

           // берём наш доход и увеличиваем его на доход от продажи всех яиц, собранных за неделю

           margin += eggs * 50;

           

           //каждую неделю покупаем два дракона по 300 серебряных…

           expense += 2 * 300;

           

           // …это увеличивает общее количество рептилий

           group += 2;

           

           // все яйца распродали, начинаем сначала

           eggs = 0;

           

           // отсчитываем дни заново

           day = 0;

       }

       

       // если прошёл год, а проект так и не взлетел — останавливаемся и считаем убытки

       if ((dayZ == 356) && ((margin - expense) < 0)) {

           

           // выводим сообщение о доходах и расходах за этот год

          console.log('Драконы за год так и не принеcли вам денег, милорд. Ваши убытки за это время составили ' + (expense - margin) + ' c.');

           

           // останавливаем функцию и больше ничего не вычисляем

           return;

       }

       

   }

   

   // если мы всё-таки вышли из цикла, значит доходы превысили расходы — выведем это на экран

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

}

 

// запускаем функцию с расчётами

runDragonSimulator();


Скопировать код

Код скопирован

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

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

Ещё по теме