Когда я решил реализовать алгоритм, который Соруш Ханлоу так блестяще описал в своём докладе Elevated Swift, я не ожидал, что самой сложной частью окажется не алгоритм для лифта, а алгоритм для генерации трафика.
Давайте сделаем шаг назад и опишем, чего мы пытаемся добиться. У нас есть офисное здание с несколькими лифтами, поднимающимися и спускающимися по этажам. Как и в типичном офисном здании, мы хотим смоделировать реалистичный поток людей — больше в будни, меньше в выходные; больше прибывающих утром, меньше вечером. Мы также хотим сохранить элемент случайности. И давайте не забывать про обеденный перерыв, когда множество людей выходят и возвращаются, бормоча об очереди у лифта.
Учитывая всё это, следующим шагом будет решение о частоте запуска алгоритмов — как для лифтов, так и для потока людей. Поскольку мы хотим в идеале анимировать лифты в определённый момент, нам нужен цикл, работающий с заданным интервалом, в идеале коротким. Нам также нужно фиктивное игровое время, иначе наша игра будет работать только в реальном времени, и всё будет длиться вечно. Это также поможет нам реализовать элементы управления для замедления и ускорения игрового времени. После некоторых экспериментов я пришёл к следующему:
- У нас есть таймер, который «тикает» с заданным интервалом
- Каждый «тик» равен 10 секундам игрового времени
Первоначальный интервал «тика» составляет 0.5 секунды, с возможностью изменения до 0.01 секунды (меньше — быстрее). Методом проб и ошибок я достиг этих значений, которые показались мне подходящими — не слишком медленными, но и не слишком быстрыми.
После небольшого небрежного проектирования SwiftUI у нас есть первая работающая версия. Мы видим три лифта, поднимающихся и опускающихся. Люди прибывают на первый этаж. Лифты, похоже, работают хорошо.
Я показал его другу в офисе, и мы решили ускорить процесс и посмотреть, что из этого получится. Это также позволило бы мне показать ему функцию, которую я разрабатываю — экран с графиками для предпринимателя, который будет управлять компанией, обслуживающей лифты. Чем больше данных у нас будет, тем интереснее и полезнее будут графики.
Мы оставили алгоритм работать на пару часов, а затем открыли экран с графиками.
Несмотря на то, что лифты, казалось, работали исправно, мы легко заметили проблему, взглянув на диаграммы. Форма диаграммы была одинаковой независимо от дня недели. Алгоритм генерации людей должен был предотвратить это.
Хорошо, что я решил добавить диаграммы, иначе бы я это пропустил. И это не заняло много времени. Мне нужно было всего лишь сохранить данные о поездках и затем отобразить их. Более того, Swift Charts позволяет отображать диаграммы в режиме реального времени, что добавляет динамики в игру.
Каждый раз, когда мы обновляем данные о занятости здания, мы сохраняем запись в логе.
let newPeople = peopleArriving(hour: hour, day: day)
let peopleGoingOut = goingOut(hour: hour, day: day)
currentOccupancy += newPeople - peopleGoingOut
generateCalls(newPeople: newPeople, peopleGoingOut: peopleGoingOut)
log.append(
LogEntry(
date: date,
occupancy: currentOccupancy,
groundFloorQueue: hallCalls.filter { $0.from == 0 }.count
)
)
Код для отображения диаграмм довольно прост.
Button("Stats") {
showingStats.toggle()
}
.sheet(isPresented: $showingStats) {
GroupBox("Building Occupancy") {
Chart {
ForEach(dispatcher.building.log) { item in
BarMark(x: .value("Minute", item.date, unit: .minute),
y: .value("People", item.occupancy))
}
}
}
.padding([.horizontal, .top])
GroupBox("Ground Floor Queue") {
Chart {
ForEach(dispatcher.building.log) { item in
BarMark(x: .value("Minute", item.date, unit: .minute),
y: .value("Ground Floor Q", item.groundFloorQueue))
}
}
}
.padding()
.presentationDetents([.medium, .large])
}
Устранив проблему с ежедневной генерацией людей, я понял, что нужно дождаться, пока игра сгенерирует достаточно данных, чтобы увидеть результат. Я решил добавить кнопку, которая будет генерировать данные за день за секунду. Таким образом, я мог сразу увидеть результаты.
Разница стала сразу заметна. В будние дни теперь отображались ожидаемые утренний пик, обеденный спад и вечерний уход, в то время как выходные оставались, как и следовало ожидать, спокойными. Без визуального представления я мог бы потратить часы на пошаговое прохождение кода или, что ещё хуже, выпустить симуляцию, которая внешне выглядела бы хорошо, но в глубине вела себя некорректно.
Этот опыт закрепил важный урок: при создании сложных систем с временным поведением визуальная обратная связь не просто приятна, она крайне важна. Swift Charts позволили легко добавить эту возможность отладки, а вложение нескольких десятков строк кода сэкономило часы слепой отладки. Диаграммы стали не просто фичей, но и инструментом разработки, который продолжает помогать мне проверять новые функции и выявлять регрессии.
Если вы работаете с какими-либо временными рядами данных или алгоритмическим поведением, подумайте о добавлении диаграмм на ранних этапах процесса разработки. В будущем вы будете благодарны.
