Перейти к содержанию

KTB-05 Разработать меню тренажера

📝 Описание

Создание консольного меню тренажёра

В соответствии с техническим заданием, на 1 этапе разработки необходимо реализовать консольный тренажёр. Это предполагает вывод информации в консоль и считывание ввода пользователя.

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

Начать следует с разработки основного меню приложения.

Требования к меню

При запуске приложения должно отображаться меню следующего вида:

Меню:
1  Учить слова
2  Статистика
0  Выход

Реализация логики меню

Необходимо создать базовую структуру приложения с использованием бесконечного цикла, который будет ожидать ввод пользователя. В начале каждой итерации цикла необходимо будет обрабатывать ввод пользователя и в зависимости от него выводить новые сообщения в консоль или останавливать цикл (выходить из программы).

🗒 Задание

  1. Создать консольное меню тренажёра внутри бесконечного цикла с использованием while(true).
  2. На каждой итерации цикла:
    • Выводить в консоль меню с пунктами 1, 2 и 0.
    • Считывать ввод пользователя.
    • Обрабатывать ввод с помощью конструкции when, выполняя соответствующие действия:
      • При вводе "1": выводить сообщение о выборе пункта "Учить слова".
      • При вводе "2": выводить сообщение о выборе пункта "Статистика".
      • При вводе "0": прерывать цикл и останавливать программу.
      • При вводе любого другого значения: выводить предупреждение с сообщением "Введите число 1, 2 или 0".
  3. Создать метод loadDictionary() и вынести в него логику создания словаря. Метод должен возвращать список объектов класса Word.
  4. Создать переменную dictionary в начале метода main() и проинициализировать ее значением, которое будет возвращать loadDictionary().

❌ Ошибки

✏️ Комментарии

Давай вынесем код по загрузки словаря в отдельный метод `loadDictionary()`
Лучше сначала зачистить файл при помощи `writeText("")`, а потом добавлять слова по одному через `appendText()`.
Лучше не накапливать строку в памяти, как у тебя, а делать `appendText` в файл, предварительно его очистив.

extension’ы

Лучше не перебарщивать с extension-функциями. Для файла не применимо "сохранение словаря", это "засоряет" скоуп файла.
Нам не нужен extension, нам нужны два метода: загрузка словаря и сохранение словаря.

В целом проще перезаписывать файл целиком, это не сильно дороже по ресурсам. Можешь попробовать `RandomAccessFile`, если хочешь перезаписывать определенные строки.
Не нужно создавать `RandomAccessFile` каждый раз в цикле, а только при старте.
Переписывать файл — это норма. Но если формировать строку заранее, она может стать слишком большой и вызвать ошибку `OutOfMemory`.
Ввод варианта ответа можно сделать с использованием `readln().toIntOrNull()`, а затем обрабатывать через `when`.
Файл по сути последовательность 1 и 0 записанных на накопитель. При этом если ты хочешь вставить что-то посередине файла, все что идет после нужно сдвигать. То есть, ты можешь идти по файлу до переноса строки и искать нужную, но проще перезаписывать файл целиком, к тому же по ресурсам это не сильно дороже. 

Если же хочешь искать определенную строку – можешь попробовать [RandomAccessFile](https://devcolibri.com/%D1%83%D1%80%D0%BE%D0%BA-4-randomaccessfile-%D0%B8-%D0%B5%D0%B3%D0%BE-%D0%B2%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D0%B8/)

⌨️ Решение

val dictionary = loadDictionary()

while (true) {
    println(
        """
        Меню:
        1 – Учить слова
        2 – Статистика
        0 – Выход
    """.trimIndent()
    )
    val input = readln()
    when (input) {
        "1" -> {
            println("Учить слова")
        }

        "2" -> {
            println("Статистика")
        }

        "0" -> {
            return
        }
    }

}

private fun loadDictionary(): List<Word> {

        val dictionary = mutableListOf<Word>()
        val wordsFile = File("words.txt")

        for (string in wordsFile.readLines()) {
            val split = string.split("|")
            val word = Word(
                original = split[0],
                translate = split[1],
                correctAnswersCount = split[2].toIntOrNull() ?: 0
            )
            dictionary.add(word)
        }
        return dictionary
}