Книга: Swift. Основы разработки приложений под iOS, iPadOS и macOS. 5-е изд. дополненное и переработанное
Назад: 19. Консольное приложение «Сумма двух чисел»
Дальше: Часть VI. Нетривиальные возможности Swift

20. Консольная игра «Отгадай число»

Консоль замечательно подходит для создания простых в реализации приложений с минимальным функционалом. В этой главе мы создадим игру «Отгадай число», в которой пользователь должен отгадать случайное число, загаданное компьютером.

Суть игры в следующем: компьютер загадывает целое число в диапазоне от 1 до 50, а игрок пытается угадать его за минимальное количество ходов. После каждой попытки приложение должно сообщать, как введенное число соотносится с загаданным: оно больше, меньше или равно ему. Игровой процесс продемонстрирован на рис. 20.1.

Рис. 20.1. Игра «Отгадай число»

Наша будущая игра будет функционировать по следующему алгоритму:

• Генерация случайного числа.

• Запрос числа у пользователя.

• Сопоставление сгенерированного числа с запрошенным.

• Вывод результата сопоставления.

• Если числа одинаковые, то работа программы завершается.

• Если числа разные, то происходит переход к шагу 2.

20.1. Код приложения «Угадай число»

Создайте в Xcode новый консольный проект с именем «UnknownNumber». После этого удалите весь код, находящийся в файле main.swift.

ПРИМЕЧАНИЕ Если вы забыли, как создается консольное приложение, то вернитесь к предыдущей главе.

Для начала в файле main.swift реализуем механизм генерации случайного числа (листинг 20.1).

Листинг 20.1

import Foundation

// генерация случайного числа

let randomNumber = UInt8(arc4random_uniform(50))

Теперь константа randomNumber содержит случайно сгенерированное число.

ПРИМЕЧАНИЕ Напомню, что для генерации случайного числа используется функция arc4random_uniform(_:), которая входит в состав библиотеки Foundation.

Пара слов про оптимизацию приложения. В общем случае любая оптимизация — это поиск компромисса, обычно между памятью и процессорным временем устройства. Обратите внимание, что в листинге 20.1 в качестве типа данных константы randomNumber используется Int8. Если не определить тип данных самостоятельно, то Swift автоматически определит его как Int, а это 32 (или 64) бита памяти вместо 8. Да, конечно, в данном случае экономия не имеет какой-либо практической пользы, но я настоятельно советую вам привыкать к процессу оптимизации с самого начала вашего пути разработчика. Не бойтесь ошибок, ведь автодополнение и справка в Xcode всегда подскажут вам правильный путь.

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

ПРИМЕЧАНИЕ Случайные числа, генерируемые большинством языков программирования (в том числе и Swift), в действительности являются псевдослучайными! Для их вычисления используются алгоритмы, не использующие какие-либо непредсказуемые составляющие. Да и вообще, ничего случайного внутри вашего компьютера нет, все подчиняется заранее запрограммированным алгоритмам. Для создания по-настоящему случайных чисел используются специальные аппаратные решения, обычно измеряющие значения внешних физических явлений. Но вам не стоит задумываться об этом, так как функции arc4random_uniform(_:) вполне достаточно для программы практически любого уровня сложности.

Шаги 2–6 описанного выше алгоритма — это цикл. Ваша программа не должна завершать работу до тех пор, пока число не будет отгадано. Для реализации этого условия лучше всего использовать конструкцию repeat while (листинг 20.2).

Листинг 20.2

import Foundation

// генерация случайного числа

let randomNumber = UInt8(arc4random_uniform(50))

print("Компьютер случайным образом загадал число. Вам требуется отгадать его.")

// в переменную будет записываться число с консоли

var myNumber: String?

// цикл с постпроверкой условия

repeat {

    print("Введите ваш вариант и нажмите Enter")

    // получение значения с клавиатуры пользователя

    myNumber = readLine()

    // сравнение введенного и сгенерированного чисел

    if (UInt8(myNumber!) == randomNumber){

        print("Вы угадали!")

    }else if (UInt8(myNumber!)! < randomNumber){

        print("Ваш вариант меньше загаданного числа")

    }else if (UInt8(myNumber!)! > randomNumber){

        print("Ваш вариант больше загаданного числа")

    }

} while randomNumber != UInt8(myNumber!)

Ваша программа готова. Запустите ее и попробуйте себя в борьбе с искусственным интеллектом.

20.2. Устраняем ошибки приложения

Если бы вы сдали это приложение на code rewiev (проверка кода) или отдали его тестировщикам, то, скорее всего, лишились бы премии, так как, реализуя его код, мы пошли по пути наименьшего сопротивления и реализовали программу по принципу «авось прокатит». Выделим основные проблемы вашей/нашей программы:

• Многократное использование UInt8.

• Аварийное завершение работы приложения при вводе нецифровых символов.

• Аварийное завершение работы приложения при вводе числа больше 255 (верхняя граница типа UInt8).

• При доступе к значению опционала используется принудительное извлечение значения.

Если вы чувствуете в себе силы самостоятельно исправить эти ошибки, то дерзайте. В любом случае ознакомьтесь с решением, которое предлагаю вам я (листинг 20.3).

Листинг 20.3

import Foundation

print("Компьютер случайным образом загадал число. Вам требуется отгадать его.")

// словарь сообщений

let message = [

    "start":"Введите вариант числа и нажмите Enter",

    "more":"Ваш вариант больше загаданного числа",

    "less":"Ваш вариант меньше загаданного числа",

    "win":"Вы угадали число!"]

// храним загаданное число в виде строки, чтобы избежать

// тройного преобразования:

// 1) результат функции readLine(_:) из String? в String

// 2) полученный String в UInt8? с помощью UInt8(_:)

// 3) полученный UInt8? в UInt8 для сравнения с randomNumber

let randomNumber = String(arc4random_uniform(50))

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

var userNumber: String = ""

// цикл проверки

repeat {

    print(message["start"]!)

    //получение числа

    let myNumber = readLine()

    userNumber = myNumber ?? ""

    if userNumber < randomNumber{

        print(message["less"]!)

    }else if userNumber > randomNumber{

        print(message["more"]!)

    }

} while userNumber != randomNumber

print(message["win"]!)

Вот она, оптимизация! Решение сменить тип переменной randomNumber с UInt8 на String позволило убрать все описанные проблемы, при этом доступ к опционалу, возвращаемому функцией readLine(_:), теперь происходит с помощью безопасного извлечения значения с использованием оператора ??.

Обратите внимание, что все информационные сообщения были вынесены в словарь message. Такой подход позволяет, с одной стороны, с легкостью менять эти сообщения, даже если они были использованы в нескольких местах в коде программы, а с другой — открывает простейший путь к локализации (вы можете, к примеру, определить активный язык системы и подгрузить необходимые значения в данный словарь).

Назад: 19. Консольное приложение «Сумма двух чисел»
Дальше: Часть VI. Нетривиальные возможности Swift