Вивчай
Домашнє завдання #9 · Умовні конструкції, функції та валідація даних
100 балів+15 бонусbeginner

Домашнє завдання #9: Вечір у кафе — рахунок, чайові та паролі

П'ятниця, ти з друзями в кафе. Офіціант приносить рахунок — і починається класика: «скинемось порівну?», «а чайові рахував хтось?», «у мене тільки готівка, без копійок». Ти дістаєш ноутбук: «Спокійно. Я програміст». Сьогодні ти напишеш інструменти, якими реально можна користуватись — і які працюють на умовних конструкціях та функціях.


Частина 1: Ділимо рахунок — splitBill(total, people, tipPercent)

Функція приймає суму рахунку (грн), кількість людей і відсоток чайових. Повертає об'єкт із трьома полями: tip (чайові), grandTotal (рахунок + чайові), perPerson (скільки платить кожен).

Правила:

  • Спочатку валідація: якщо total не більший за 0 — поверни рядок "Помилка: сума рахунку має бути більшою за 0"; якщо people менший за 1 — поверни "Помилка: людей має бути щонайменше 1"
  • tip = відсоток від total
  • perPerson округлюється до цілої гривні вгору (Math.ceil) — щоб ніхто не рахував копійки, а офіціант не залишився в мінусі

Приклади (це тест-кейси — твоя функція має повертати саме це):

ВикликРезультат
splitBill(1000, 4, 10){ perPerson: 275, tip: 100, grandTotal: 1100 }
splitBill(847, 3, 15){ perPerson: 325, tip: 127.05, grandTotal: 974.05 }
splitBill(500, 1, 0){ perPerson: 500, tip: 0, grandTotal: 500 }
splitBill(1000, 0, 10)"Помилка: людей має бути щонайменше 1"
splitBill(-200, 3, 10)"Помилка: сума рахунку має бути більшою за 0"

Частина 2: Оцінюємо пароль — passwordStrength(password)

Після вечері компанія вирішує завести спільний акаунт для бронювання столиків. Хтось пропонує пароль qwerty. Твоя функція має чесно сказати, наскільки це погана ідея.

Перевір 4 критерії:

  1. Довжина щонайменше 8 символів
  2. Є хоча б одна цифра
  3. Є і великі, і малі літери
  4. Є хоча б один спецсимвол із набору !@#$%^&*

Поверни об'єкт { level, tips }, де level залежить від кількості виконаних критеріїв: 0–2 → "слабкий", 3 → "середній", 4 → "надійний". А tips — масив порад: по одній для кожного невиконаного критерію (формулювання — на твій смак, головне щоб порада відповідала критерію).

ВикликРезультат
passwordStrength("qwerty")level: "слабкий", у tips — 4 поради
passwordStrength("1234")level: "слабкий", у tips — 3 поради (цифри вже є)
passwordStrength("Korovai42")level: "середній", у tips — 1 порада (про спецсимвол)
passwordStrength("Vechir#Kavi2026")level: "надійний", tips: []

Частина 3: Генеруємо пароль — generatePassword(length, options)

Оцінювати чужі паролі легко — тепер згенеруй надійний сам. options — об'єкт із трьома полями-прапорцями: useUppercase, useDigits, useSymbols. Малі латинські літери використовуються завжди.

Правила:

  • Якщо length менший за 4 — поверни "Помилка: мінімальна довжина пароля — 4"
  • Пароль складається лише з дозволених опціями символів (спецсимволи — з набору !@#$%^&*)
  • Якщо опція ввімкнена — у паролі має бути хоча б один символ цього типу (генератор, який за useDigits: true видає пароль без жодної цифри — зламаний)

Приклади (пароль випадковий — у тебе будуть інші символи, але властивості мають збігатися):

ВикликПриклад результатуЩо перевіряємо
generatePassword(8, { useUppercase: false, useDigits: true, useSymbols: false })"k2p9mx4a"8 символів, лише малі літери й цифри, є хоча б одна цифра
generatePassword(12, { useUppercase: true, useDigits: true, useSymbols: true })"aK4#mZ8!pQ2r"12 символів, є всі чотири типи
generatePassword(3, { useUppercase: true, useDigits: true, useSymbols: true })"Помилка: мінімальна довжина пароля — 4"валідація

Перевір себе: згенерований пароль, прогнаний через passwordStrength, з усіма опціями має давати "надійний".


Бонус (+15): Нерівний поділ — splitBillUneven(foodTotal, drinksTotal, people, drinkers, tipPercent) 🍷

Найгарячіша тема вечора: двоє з чотирьох не пили вино — чому вони мають платити за нього порівну? Їжа ділиться на всіх, напої — лише між тими, хто пив, чайові рахуються від усього рахунку і діляться на всіх. Обидві суми округлюються до гривні вгору.

ВикликРезультат
splitBillUneven(800, 400, 4, 2, 10){ drinkerPays: 430, nonDrinkerPays: 230 }

Перевірка: 2 * 430 + 2 * 230 = 1320 — рівно їжа + напої + чайові (800 + 400 + 120). Ніхто не переплатив, офіціант задоволений.


Підказки

Рівень 1: напрямок думки
  • Валідацію роби першою: якщо дані погані — одразу return з рядком помилки, решта коду навіть не виконається
  • Для passwordStrength заведи лічильник виконаних критеріїв і масив порад — кожна перевірка або збільшує лічильник, або додає пораду
  • Для генератора: спочатку збери рядок з усіх дозволених символів, потім бери з нього випадкові
Рівень 2: які інструменти використати
  • Округлення вгору: Math.ceil(x)
  • Пошук цифри в рядку: пройдись по символах або використай password.includes(...) для кожної цифри; ще зручніше — порівняння password !== password.toLowerCase() каже, що є великі літери
  • Випадковий індекс: Math.floor(Math.random() * chars.length), символ за індексом — chars[index]
  • Побудувати рядок потрібної довжини — цикл for (він у наступному уроці, але для генератора знадобиться — вважай це спойлером)
Рівень 3: псевдокод генератора
функція generatePassword(length, options):
  якщо length < 4 → повернути помилку
  chars = "abc...xyz"
  якщо options.useUppercase → chars += "ABC...XYZ"
  якщо options.useDigits → chars += "0123456789"
  якщо options.useSymbols → chars += "!@#$%^&*"

  повторювати:
    password = порожній рядок
    length разів: додати випадковий символ із chars
  поки password не містить по одному символу кожного ввімкненого типу

  повернути password

Як здати

  1. Робота — в публічному GitHub-репозиторії курсу my-web-course, у папці hw-09/
  2. У папці: script.js з трьома функціями і демо-викликами (console.log для кожного прикладу з таблиць) та index.html-обгортка, щоб запустити скрипт у браузері:
<!DOCTYPE html>
<html lang="uk">
<head><meta charset="UTF-8"><title>HW-09</title></head>
<body>
  <script src="script.js"></script>
</body>
</html>
  1. Відкрий index.html у браузері → консоль (F12) — там мають бути результати всіх демо-викликів
  2. У папці — README.md: 2-3 речення що зроблено + як запустити
  3. Мінімум 3 коміти з осмисленими повідомленнями (не «fix» та «update»)
  4. Надішли посилання на репозиторій у форму на цій сторінці

Критерії оцінки

КритерійТипБали
splitBill: чайові, округлення вгору, об'єкт з трьома полями — збігається з таблицею прикладів[код]25
splitBill: обидві валідації повертають точні рядки помилок[код]10
passwordStrength: правильний level для всіх 4 прикладів[код]15
passwordStrength: tips містить пораду для кожного невиконаного критерію[код]10
generatePassword: довжина, лише дозволені символи, валідація довжини[код]15
generatePassword: кожен увімкнений тип символів гарантовано присутній[код]10
Демо-виклики всіх функцій у script.js — консоль показує результати[код]10
Контракт здачі: 3+ осмислені коміти, README[код]5
Бонус: splitBillUneven рахує чесно (перевірка сум сходиться)[код]+15

Що далі

Не видаляй цей код: у Block 5 ти навчишся підключати функції до кнопок та полів вводу на сторінці — і генератор паролів перетвориться на справжній міні-інструмент з інтерфейсом.