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

Еще раз я рассмотрю подсказку и ограничения задачи, а затем проработаю ее в режиме реального времени, когда я буду писать вместе со своими победами и неудачами. Затем я вернусь и посмотрю, где я мог бы быть более кратким или эффективным с моим кодом.

Быстрый

У этой задачи есть немного более надуманное описание, но это забавная история, которую можно создать, чтобы бросить вызов! Если вам нужно полное описание или вы хотите попробовать сами, загляните на страницу ката, в противном случае вот фон:

В другом измерении существуют два бессмертных брата: Соломон и Горомон. Как заклятые верные подданные элементаля времени, Хроникса, и Соломон, и Горомон получили право создавать временные складки. Проскользнув через эти временные складки, можно попасть в параллельные измерения, где время движется относительно быстрее или медленнее.

Горомон был недоволен и однажды предал Хроникса, украв Темпоральный кристалл, артефакт, используемый для поддержания временного континуума. Хроникс вызвал Соломона и поручил ему выследить Горомона и получить Темпоральный кристалл.

Используя карту, данную Соломону Хрониксом, вы должны найти точное местонахождение Горомона.

Представленная нам карта представляет собой список списков, примерно так:

map_example = [[1,3,5],[2,0,10],[-3,1,4],[4,2,4],[1,1,5],[-3,0,12],[2,1,12],[-2,2,6]]

Итак, у нас есть целый ряд цифр. Но что именно все они означают? Есть и более подробное описание карты:

Вот что представляют значения каждого подмассива:

Замедление времени: при вводе каждого дополнительного слоя замедления времени время замедляется в 2 раз. На слое 0 время проходит нормально. На уровне 1 время идет вдвое медленнее, чем на уровне 0. На уровне 2 время идет вдвое быстрее, чем в слое 1, и, следовательно, на четверть меньше, чем в слое 0.

Как добраться: 0 = North, 1 = East, 2 = South, 3 = West

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

The following are equivalent distances (all equal a Standard Distance of 100):
Layer: 0, Distance: 100
Layer: 1, Distance: 50
Layer: 2, Distance: 25

Итак, для каждого массива первое число говорит нам, на сколько сместить нашу дилатацию (что меняет пройденное расстояние), второе число говорит нам, в каком направлении двигаться (это похоже на простые декартовы направления), а последнее число сообщает сколько мы путешествуем на нашем текущем уровне расширения (который нужно перевести обратно в стандартное расстояние). Здесь много чего происходит!

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

Входные данные всегда действительны.

Соломон начинает свои поиски на уровне замедления времени 0, в [x,y] координатах [0,0].

Уровень замедления времени в любой момент всегда будет 0 или больше.

Стандартное расстояние - это расстояние на уровне замедления времени 0.

Для заданного расстояния d для каждого значения в массиве: d >= 0.

Не изменяйте ввод

Хорошо, что у нас есть указанная отправная точка, и нам не нужно беспокоиться об отрицательных расстояниях (поскольку наши направления уже говорят нам, в какую сторону идти). Думаю, пора заняться программированием!

Подход

Я знаю, что мне придется повторять каждую из инструкций карты, поэтому я думаю, что цикл for должен быть в порядке. Но прежде чем я перейду к этому, я должен установить некоторые переменные. Поскольку мы точно знаем, с чего начнем, и нам нужно будет отслеживать, где мы находимся, я сделаю две переменные, представляющие эту информацию:

loc = [0, 0]
level = 0

Мы также должны помнить о нашем уровне «времени», поэтому я сделаю еще одну переменную для него. Затем, когда мы проходим каждый шаг на карте, замедление времени должно измениться с помощью первой инструкции. Поскольку каждый уровень представляет собой уменьшение относительного времени вдвое, я могу просто выполнить это изменение для того, на сколько уровней ниже мы сейчас находимся. Я настроил свой цикл следующим образом:

time_dilation = 1
for step in arr:
        level += step[0]
        for i in range(level):
            time_dilation /= 2

Поначалу меня устраивает то, что я написал, но у меня появляется мучительное подозрение, что я не все настроил должным образом. Я хочу, чтобы цикл распечатал замедление времени и посмотрел, что он показывает.

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

Теперь, когда у меня есть фактор движения, я действительно должен делать правильные движения! Есть четыре направления движения, и, поскольку они являются всего лишь стандартными сторонами света, я начинаю думать о создании словаря, чтобы согласовать правильное направление с числом. Однако перевод этого на изменение конкретных loc координат, кажется, ускользает от меня. Я решаю использовать простой набор if/elif операторов, чтобы делать правильные движения.

Я начинаю с использования time_dilation, чтобы вычислить, сколько мы собираемся двигаться. Это простой расчет, мне просто нужно взять третью запись в шаге и разделить ее на коэффициент расширения. Затем я установил свои if/elif операторы, чтобы проверить, в каком направлении мы движемся, и соответствующим образом изменил loc координату.

move = step[2] / time_dilation
if step[1] == 0:
    loc[1] += move
elif step[1] == 1:
    loc[0] += move
elif step[1] == 2:
    loc[1] -= move
elif step[1] == 3:
    loc[0] -= move

Я почти готов собрать все это воедино и запустить, но я оглядываюсь на ограничения, чтобы увидеть, есть ли что-то, что я не рассматриваю. Я заметил, что на самом деле здесь не говорится, что запись «направления» всегда будет одной из четырех упомянутых, поэтому я добавляю else, который вызовет ошибку, если окажется, что направление неизвестно.

Оттуда мне просто нужно запустить цикл и внести все различные изменения в местоположение, а затем вернуть окончательный результат. Мой последний код для отправки выглядит так:

def solomons_quest(arr):
    loc = [0, 0]
    level = 0
    
    for step in arr:
        time_dilation = 1
        level += step[0]
        for i in range(level):
            time_dilation /= 2
        move = step[2] / time_dilation
        if step[1] == 0:
            loc[1] += move
        elif step[1] == 1:
            loc[0] += move
        elif step[1] == 2:
            loc[1] -= move
        elif step[1] == 3:
            loc[0] -= move
        else:
            raise "This direction not recognized"
    return loc

Успех! Мой код прошел все тесты, и Горомон сохранен! У меня было несколько тестовых прогонов с некоторыми частями кода, которые я написал вначале, поэтому я не буду считать это завершением с первой попытки.

Заключение

Несмотря на то, что я прошел испытание, моя работа еще не окончена. Я хочу вернуться и посмотреть, где я мог бы улучшить этот код. Я думаю, что больше всего прилипает то, как я рассчитал time_dilation, так как это кажется немного сложным. Я думаю, что лучшим способом сделать это было бы просто использовать текущий уровень как степень двойки, а затем умножить это на количество движения. Что-то типа:

move = (2 ** level) * step[2]

Я также все еще думаю, что есть способ обойти инструкции if/elif, но в то же время это решение может оказаться не таким явным и легким для чтения, как то, что я сделал выше. В конце концов, это непрерывная часть программирования на Python. Речь идет не только о том, чтобы выполнить вашу задачу, используя как можно меньшее количество строк или символов, но и о том, чтобы быть ясным и понятным в том, что вы делаете. Вам нужно найти лучший баланс!

Это было еще одно интересное испытание, и я благодарю вас за то, что вы отправились со мной в это путешествие в странные временные измерения! Проверьте еще раз, чтобы узнать о других платежах!