Задача про драконьи яйца. Призываем программистов
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(); — и вот тогда про­грам­ма дей­стви­тель­но акти­ви­ру­ет­ся и выпол­ня­ет всё, как мы её про­си­ли.

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

Ещё по теме