# Финальное задание
## Консольная игра «Банкомат»
Вы прошли первый уровень курса и изучили основы Java:
переменные, условия, циклы, ввод данных, обработку ошибок.
Теперь ваша задача — **написать полноценную консольную программу**, которая имитирует работу банкомата.
Для решения этой задачи мы подготовили уже знакомый вам тренажер. Но в этот раз тренажер не будет проверять правильность работы вашей программы. Вы выступите в роли проектировщика, разработчика и тестировщика собственного приложения.
Откройте [тренажер](https://td-java.compassplus.ru/index.html?problem=88) в соседней вкладке или новом окне браузера.
---
### Общая идея программы
Для начала определим функции простейшего банкомата. К ним относятся: отображение баланса и меню операций, а также проведение операций по снятию и внесению наличных.
Из этого следует, что наша программа должна:
* хранить баланс пользователя;
* показывать меню с действиями;
* выполнять выбранные операции;
* корректно реагировать на ошибки;
* работать, пока пользователь не выйдет.
Это не просто набор `println()`, а **настоящая программа с состоянием**.
### Стратегия решения
Разработаем пошаговый план действий и разделим нашу большую задачу на мелкие::
1. Сначала сделаем **основу**: баланс + цикл.
2. Потом добавим **меню**.
3. Затем научим банкомат **что-то делать**.
4. После этого защитим программу от **ошибок пользователя**.
5. В конце всё соберём в цельную игру.
Универсальное правило разработки: любую сложную программу нужно создавать постепенно, шаг за шагом. Разработка любой программы по частям - лучшая стратегия. **Каждый шаг — уже рабочая программа.**
## Шаг 1. Баланс и приветствие
На данном этапе нужно ответить на следующие вопросы:
* Где хранить баланс?
* Какой тип данных подойдёт?
* С чего программа должна начинаться?
В реальных системах, безусловно, это могут быть доллары и центы, рубли и копейки и так далее. Но для нашей задачи нам достаточно будет целых чисел. Наша валюта не будет делиться на сотые. Следовательно, если баланс — это число. Значит, подойдёт `int`.
### Подсказка
Зададим зачальное значение - 1000. Начнём с такого кода:
```java
int balance = 1000;
Если запустить этот код: программа **уже будет работать**, она поприветствует пользователя и покажет его текущий баланс. Но она пока ничего не умеет делать. Двигаемся дальше.
## Шаг 2. Основной цикл программы
Теперь ответим на следующий важный вопрос: "Банкомат должен завершить работу сразу или он должен работать, пока пользователь сам не выйдет?"
Если мы хотим, чтобы банкомат продолжал работу после любого действия пользователя, то очевидно — нам нужен **цикл**.
### Подсказка
Нам нужен цикл, который:
* работает, пока пользователь не выбрал «Выйти»;
* управляется логической переменной.
Для решения такой задачи идеально подходит цикл `while`. Но чтобы цикл знал, когда прекратить работу, ему нужен "флаг" - логическая переменная, указывающая нужно ли продолжать работу.
```java
boolean isRunning = true;
while (isRunning) {
// здесь будет вся логика банкомата
}
```
Сейчас цикл пустой, но **каркас уже есть**. Мы построим всю программу **внутри него**.
## Шаг 3. Меню операций
На этом шаге, нужно представить работу пользователя с банкоматом, для этого ответим на следующий список вопросов:
* Как пользователь будет выбирать действие?
* Как показать ему варианты?
* Как считать его выбор?
Поскольку мы разрабатываем консольное приложение, пользователь будет взаимодействовать с программой через ввод/вывод консоли. Следовательно, меню в нашем случае — это просто ввод/вывод текста. Однако использование полных фраз (например, «Показать баланс») в качестве команд неэффективно: такой ввод занимает много времени и сопряжён с риском опечаток.
### Подсказка
Самое простое решение - ассоциировать кнопки с цифрами. Внутри цикла можно написать:
```java
System.out.println("\nВыберите операцию:");
System.out.println("1 — Показать баланс");
System.out.println("2 — Пополнить счёт");
System.out.println("3 — Снять деньги");
System.out.println("4 — Выйти");
```
А выбор сохранить в переменную:
```java
int choice;
```
Но пользователь может ввести **не число**. Это значит — **понадобится `try / catch`**.
## Шаг 4. Ввод данных и защита от ошибок
Что будет, если пользователь введёт текст, или случайно нажмёт `Enter`? Программа **не должна падать**. Нужно предусмотреть эти случаи и подготовить программу к работе с ними.
### Подсказка
Используем уже знакомый нам `Scanner` и блок `try / catch`:
```java
Scanner scanner = new Scanner(System.in);
try {
choice = scanner.nextInt(); // пользовательский ввод числа (пункта меню) с консоли
} catch (Exception e) {
System.err.println("Ошибка: введите число!");
scanner.nextLine(); // очистка ввода
continue; // возвращаемся в начало цикла
}
```
Теперь банкомат: **не сломается** от неверного ввода и сможет корректно реагировать на ошибки.
## Шаг 5. Обработка выбора пользователя
Итак, у нас уже есть переменная `choice`, в которую помещается номер пункта меню банкомата, выбранного пользователем. Далее нужно научить выполнять банкомат **разные действия** в зависимости от полученного значения.
Лучший инструмент для этого - конструкция `switch`.
Сейчас меню банкомата имеет следующий вид:
```text
Выберите операцию:
1 — Показать баланс
2 — Пополнить счёт
3 — Снять деньги
4 — Выйти
```
* В случае нажатия `1` показываем баланс
* В случае нажатия `2` увеличиваем баланс на введённое значение
* В случае нажатия `3` уменьшаем баланс на полученное значение
* В случае нажатия `4` завершаем цикл (тут нам пригодится созданный флаг `isRunning`)
### Подсказка
Каркас логики может выглядеть так:
```java
switch (choice) {
case 1:
System.out.println("Ваш баланс: " + balance);
break;
case 2:
// пополнение (допишем позже)
break;
case 3:
// снятие (допишем позже)
break;
case 4:
System.out.println("До свидания!");
isRunning = false; // завершаем цикл
break;
default:
System.err.println("Неверный пункт меню");
}
```
* меню работает;
* выход из программы реализован;
* структура понятна.
Осталось описать логику изменения баланса (увеличения и уменьшения) при нажатии пунктов меню `2` и `3`.
## Шаг 6. Пополнение счёта
После того, как пользователь вводит сумму, нужно проверить, что сумма **положительна** и увеличить баланс.
### Подсказка
У нас уже есть переменная `balance`, в которую помещается текущий баланс, сканер проверит, что введённое значение является числом, а конструкция `if-else` проверит, что это число положительное.
Внутри `case 2` опишем:
```java
System.out.print("Введите сумму пополнения: ");
int amount; // объявим переменную для хранения введёной суммы
try {
amount = scanner.nextInt(); // считываем сумму с консоли и помещаем в переменную
} catch (Exception e) {
System.err.println("Ошибка: введите число");
scanner.nextLine();
break;
}
if (amount <= 0) {
System.err.println("Сумма должна быть положительной");
} else {
balance += amount; // если сумма положительна, увеличиваем баланс
System.out.println("Баланс успешно пополнен");
}
```
Отлично! Пополнение баланса реализовано. Переходим к следующей важной функции — снятию наличных.
## Шаг 7. Снятие денег
При описании логики уменьшения баланса, нам также необходимо проверить, что введённая сумма положительна. Однако, в отличие от пополнения, еще следует убедиться, что сумма снятия не превышает доступный баланс.
### Подсказка
Внутри `case 3` опишем следующую логику:
```java
System.out.print("Введите сумму для снятия: ");
int amount; // объявим переменную для хранения введёной суммы
try {
amount = scanner.nextInt(); // считываем сумму с консоли и помещаем в переменную
} catch (Exception e) {
System.err.println("Ошибка: введите число");
scanner.nextLine();
break;
}
if (amount <= 0) {
System.err.println("Сумма должна быть положительной");
} else if (amount > balance) {
System.err.println("Недостаточно средств");
} else {
balance -= amount; // если сумма положительна, и на балансе достаточно средств - уменьшаем баланс
System.out.println("Операция выполнена");
}
```
## Финал. Вы написали полноценную программу
Если Вы дошли до этого момента, поздравляем! Вы уже умеете:
* управлять потоком выполнения;
* работать с состоянием;
* защищать ввод;
* писать осмысленный код.
Ниже мы предлагаем вам — **эталонное решение**, с которым можно свериться.
### Эталонное решение (полный код)
```java
Scanner scanner = new Scanner(System.in);
int balance = 1000;
boolean isRunning = true;
System.out.println("Добро пожаловать в банкомат!");
case 2:
System.out.print("Введите сумму пополнения: ");
try {
int amount = scanner.nextInt();
if (amount <= 0) {
System.err.println("Сумма должна быть положительной");
} else {
balance += amount;
System.out.println("Баланс пополнен");
}
} catch (Exception e) {
System.err.println("Ошибка ввода");
scanner.nextLine();
}
break;
case 3:
System.out.print("Введите сумму для снятия: ");
try {
int amount = scanner.nextInt();
if (amount <= 0) {
System.err.println("Сумма должна быть положительной");
} else if (amount > balance) {
System.err.println("Недостаточно средств");
} else {
balance -= amount;
System.out.println("Операция выполнена");
}
} catch (Exception e) {
System.err.println("Ошибка ввода");
scanner.nextLine();
}
break;
case 4:
System.out.println("Спасибо за использование банкомата!");
isRunning = false;
break;
default:
System.err.println("Неверный пункт меню");
}
}
```
## Задание со звёздочкой. Улучшаем банкомат
Если у вас есть желание пойти дальше и сделать приложение **чуть более «живым» и продуманным**, предлагаем улучшить работу банкомата. Это задание **необязательное**, но отлично помогает прокачать навыки проектирования логики и работы с пользовательским вводом.
Ниже приведены идеи, которые можно реализовать **по отдельности или комбинировать между собой**.
___
### Подтверждение операций
Сейчас операции выполняются сразу после ввода суммы. В реальных системах так почти не бывает — пользователю обычно предлагают подтвердить действие.
**Задача:**
Добавьте подтверждение перед выполнением операций пополнения и снятия средств.
Пример логики (на уровне идеи):
* пользователь вводит сумму;
* программа выводит сообщение с уточнением:
* «Вы уверены, что хотите пополнить счёт на сумму 1000?»
* «Вы уверены, что хотите снять наличные в размере 500?»;
* пользователь подтверждает или отменяет операцию.
Если пользователь **отказывается**, баланс не должен изменяться.
___
### Подтверждение продолжения работы
После выполнения любой операции (показ баланса, пополнение, снятие) банкомат сразу возвращает пользователя в меню.
Попробуйте сделать поведение более осознанным.
**Задача:**
После каждой операции задавать вопрос, хочет ли пользователь продолжить работу с банкоматом.
Например:
* «Хотите продолжить работу?»
* варианты ответа — `да / нет` или `1 / 2`.
Если пользователь отказывается — программа должна корректно завершить работу.
---
### Повторный запуск без перезапуска программы
Сейчас после выхода из банкомата программа полностью завершается. Чтобы начать заново, её нужно перезапускать.
**Задача:**
Сделайте так, чтобы после выхода пользователь мог **снова начать работу с банкоматом**, не перезапуская программу.
Идея:
* после выхода программа предлагает начать заново;
* пользователь вводит команду (слово, число или другой триггер);
* при подтверждении банкомат «запускается» снова с начального состояния.
---
### Своя идея улучшения ?
Вы можете предложить **собственное улучшение**, если оно:
* работает в рамках консольного приложения;
* использует изученные конструкции (условия, циклы, ввод, обработку ошибок);
* делает поведение программы более удобным или логичным.
Например:
* ограничение на максимальную сумму операции;
* отдельное сообщение при первой ошибке ввода;
* приветствие при повторном входе;
* дополнительный пункт меню.
---
### Важно помнить
Это задание — **про логику и мышление**, а не про «идеальный код». Даже частичная реализация одной идеи — уже отличный результат.
Если вы чувствуете, что программа стала понятнее, удобнее и «умнее» — значит, вы всё сделали правильно.