Помогите пройти уровень на CodeCombat

Решил в качестве хобби поизучать синтаксис пайтон и хорошо провести время играя в CodCombat пока есть свободное время на работе... И застрял на уровне который никак не могу пройти.

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

Вот мой код:

# Собери 150 золота, избегая огров с помощью телепортов.
# С каждой победой уровень становится сложнее (но и вознаграждение больше).
# Если проиграешь, придётся ждать сутки до следующей попытки.
# Запомни, обстановка меняется каждый раз.
def escape1():
    hero.say("Ищу угол через Таргет")
    dis = [100] 
    dis.append(hero.distanceTo({"x": 5, "y": 19}))
    dis.append(hero.distanceTo({"x": 5, "y": 49}))
    dis.append(hero.distanceTo({"x": 76, "y": 19}))
    dis.append(hero.distanceTo({"x": 76, "y": 49}))
    dismin = 100
    for d in dis:
        if d < dismin:
            dismin = d
    hero.say(dismin)
    if dismin == dis[1]:
        return 1
    elif dismin == dis[2]:
        return 2
    elif dismin == dis[3]:
        return 4
    elif dismin == dis[4]:
        return 3

def escape():
    hero.say("Ищу угол")
    x = hero.pos.x
    y = hero.pos.y
    if abs(x-5) < abs (x-77):
        if abs(y-19) < abs(y-51):
            return 1
        else:
            return 2
    else:
        if abs(y-19) < abs(y-51):
            return 4
        else:
            return 3

def run():
    time = hero.time
    while hero.time < time + 4:
        res = escape1()
        if res == 1:
            hero.moveXY(6, 19)
            break
        elif res == 2:
            hero.moveXY(6, 49) 
            break
        elif res == 3:
            hero.moveXY(75, 51)
            break
        elif res == 4:
            hero.moveXY(75, 18)
            break

def collect():
    hero.say("Собираю")
    time = hero.time + 5
    while hero.time < time:
        item = hero.findNearestItem()
        if item and hero.distanceTo(item) < 15:
            hero.moveXY(item.pos.x, item.pos.y)
        else:
            break

# основной цикл
while True:  
    collect()
    run()

пояснения: Красным крестом отмечены порталы, куда можно убежать (телепортируют в любой другой рандомный портал) в основном цикле выполняется две задачи - убегать и собирать монеты, обе выполняются до прерывания (5 секунд).

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

функция escape() работает очень долго (пока происходит вычисление огры успевают догнать и убить главного героя. )

escape 1 работает быстрее но все равно недостаточно быстро.

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

Что не получается: 1. сделать алгоритм поиска ближайшей точки портала быстрее 2. сделать алгоритм поиска монет привязанным к ближайшему к герою порталу (чтобы он далеко от него не убегал) 3. собрать монеты вовремя (из за того что герой постоянно бегает в портал - он не успевает собрать монеты вовремя)

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

Но мне кажется это как то слишком сложно для простенькой игры, может я не туда копаю? Кто что думает?

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

while True:
    enemy = hero.findNearestEnemy()
    coin = hero.findNearestItem()
    x = hero.pos.x
    y = hero.pos.y
    if enemy:
        distance = hero.distanceTo(enemy)
        if distance < 10:
            if x < 40 and y > 36:
                hero.moveXY(7, 49)
            if x > 40 and y > 36:
                hero.moveXY(74, 51)
                hero.moveXY(75, 51)
            if x < 40 and y < 36:
                hero.moveXY(7, 19)
                hero.moveXY(6, 19)
            if x > 40 and y < 36:
                hero.moveXY(75, 19)
        else:
                if hero.isReady("jump") and hero.distanceTo(coin) > 20:
                    hero.say("прыгаю")
                    hero.jumpTo({"x": coin.pos.x, "y": coin.pos.y})

                hero.moveXY(coin.pos.x, coin.pos.y)

Ответы (1 шт):

Автор решения: user244413

Функция distanceTo, скорее всего вычисляет расстояние по формуле sqrt((x-x1)^2 + (y-y1)^2). Возведение в квадрат относительно медленная операция, а вычисление квадратного корня еще медленнее.
Но если порталы находятся на вершинах прямоугольника, координаты которого известны, то можно искать ближайший портал так: делим все поле заранее на 4 части, каждая часть относится к определенному порталу, и потом только определяем, на территории какого портала мы находимся. Чужой код так и делает.
Если порталы находятся на вершинах прямоугольника, но при разных запусках они могут отличаться, то стоит в начале, до основного цикла, определить, где порталы. а потом в цикле пользоваться этой информацией пользоваться.

def escape():
    hero.say("Ищу угол")
    x = hero.pos.x
    y = hero.pos.y
    if x > middle_x:
        escape_x = right_x
    else:
        escape_x = left_x
    if y > middle_y:
    ...
    hero.moveXY(escape_x, escape_y)



# подготовительный код:
# объявляем глобальные переменные, которые будут видны во всех функциях.
left_x = 5 
right_x = 79
middle_x = (right_x-left_x) // 2  # операция // дает целую часть от деления

left_y      ...

# основной цикл
while True:
 ...

Есть 2 основных варианта прерывания сбора монет:
- проверка времени, как это делаете вы,
- или проверка расстояния до огра, как в другом коде.
Вторая проверка более точная, но и менее быстрая.
Вы можете попробовать их комбинировать: первые сколько-то секунд собираем монеты, а потом начинаем оглядываться на огров.

Можно также поменять алгоритм сбора монет. Сейчас герой каждый раз собирает возле портала, но когда они там кончаются, ему приходится отходить все дальше и дальше.
Можно сделать наоборот: в начале цикла мы обнаруживаем, что находимся, например, в верхнем левом углу. Давайте сразу начнем сбор монет с точки посередине между верхними порталами, а потом мы, скорее всего, будем продвигаться к какому-нибудь порталу. Или на линии между порталами на трети расстояния от ближнего портала. И это мы делаем только один раз за всю игру на каждую удаленную точку сбора.
В подготовительном коде пишем

checked_upper_left = 0

А в цикле:

if checked_upper_left == 0:
    checked_upper_left = 1
    # go to upper left corner 
# collect coins

Еще вопросы:
Что это за функция jumpTo и почему вы ее не используете?

→ Ссылка