ПОЗДРАВЛЕНИЯ @kuroineko. Выигрывает награда за отличную скорость (672 хода) на дорожке Gauntlet.
ЛИДЕР: * Ними набрал легкий 2129. Другие записи больше, но демонстрируют серьезную скорость.
* Лидер может измениться из-за более поздних записей.
Ваша задача - написать небольшую программу, которая может быстро управлять гоночным автомобилем.
правила
Ваша программа будет читать изображение трека. Вы можете завести свой автомобиль на любом желтом пикселе, и вы должны закончить, пересекая любой черный пиксель. Путь вашего автомобиля должен идти только по серой ((c, c, c), где 30 <= c <= 220) трассе.
Ваш автомобиль будет двигаться по прямой линии каждый оборот со скоростью v, состоящей из целых чисел vx и vy (начиная с (0,0)). В начале каждого хода ваша программа может изменить vx и vy так, чтобы:
abs(vx2-vx1) + abs(vy2-vy1) <= 15
Обновление: увеличено максимальное ускорение до 15.
Затем ваша программа построит прямую линию от вашего текущего местоположения до (location + v) белым цветом с синей точкой в начале. Если пиксель под этой линией черный, вы закончили гонку. В противном случае, если все пиксели под этой линией серые или желтые, вы можете перейти к следующему повороту.
Ваша программа должна вывести изображение дорожки с добавленной дорожкой белого и синего цвета.
Дополнительный вывод (добавлено 2015-01-15):
Если вы хотите побороться за выигрыш или бонус , ваша программа также должна вывести ваш список очков (синие точки) для города или рукавицы соответственно. Включите список точек с вашим ответом (для проверки). Точки должны выглядеть следующим образом : (x0,y0), (x1,y1), ... (xn,yn)
. Вы можете свободно использовать пробелы, включая '\n'
символы, чтобы разместить данные на странице.
Вы можете использовать сторонние библиотеки для чтения и записи изображений, рисования линий и доступа к пикселям. Вы не можете использовать библиотеки поиска пути. Если вы хотите, вы можете конвертировать изображения PNG в другие графические форматы (такие как GIF, JPG, BMP), если вам нужно.
Несколько треков для езды
Простой трек для начала:
Гоночная трасса:
Полоса препятствий:
Город:
След Кошмара: Рукавица (если другие слишком легки)
счет
Ваш результат будет зависеть от вашего результата на трассе City. Очки равны длине вашей программы в байтах плюс 10 баллов за каждый ход, который ваш гоночный автомобиль сделал, чтобы закончить. Самый низкий балл побеждает. Пожалуйста, включите в свой ответ беговую дорожку города - мы хотели бы увидеть ваш стиль вождения.
Пожалуйста, используйте название для вашего ответа в формате:
<Racing Driver or Team Name> <Language> <Score>
Например: Slowpoke Perl 5329
Ваша программа должна иметь возможность управлять любым изображением дорожки, следуя приведенным выше правилам. Вы не должны жестко задавать оптимальный путь или какие-либо параметры тестовых дорожек. Другие стандартные лазейки применяются.
Подобные проблемы
Эта головоломка похожа на головоломку от Мартина: Вектори! - Векторный гоночный Гран При . Эта головоломка имеет ряд отличий:
- Проезд через стены не допускается.
- Вы можете использовать неограниченную память и время.
- Вам не нужно запускать чужой код на своем компьютере.
- Вам не нужно ничего скачивать, кроме одного изображения.
- Размер вашего кода учитывается при подсчете очков. Меньше, тем лучше.
- Вы наносите свое решение на изображение дорожки.
- Вы можете легко создавать свои собственные треки с пакетом краски.
- Более высокое разрешение способствует более реалистичному торможению и поворотам.
- Ускорение 15 создает около 450 возможностей за ход. Это делает BFS менее выполнимым и стимулирует новые интересные алгоритмы.
Эта головоломка должна вдохновить новый круг программистов на пробные решения и позволить программистам со старыми решениями переосмыслить их в новой среде.
источник
Ответы:
TS # 1 - Haskell - 1699 + 430 = 2129
Пачка # 1
Почти так же, как и у оригинального гонщика Туту, за исключением того, что он использует толщину 3 для раздутого пути, а 2-й A * (скорость-положительное пространство) идет с постоянной эвристикой
1
. Входное изображение больше не передается в качестве аргумента командной строки, оно должно иметь имяi
. Имя выходного изображенияo
. Программа выводит рассчитанные точки на пути в виде списка пар x, y (исходный вывод - одна строка):Я мог бы сохранить много байтов, когда начну удалять всю карту, устанавливать структуры данных и заменять их простыми связанными списками. Только два оператора импорта сохранят 60 байтов. Тем не менее, это замедлит работу программы, так что ожидание результата - это чистая боль. Эта версия занимает более 45 минут для города, по сравнению с 7-ми годами оригинала. Я остановлю здесь торговлю байтами для выполнения скорости.
Код должен компилировать флаг -XNoMonomorphismRestriction.
Город - ТС № 1 - 43 ступени
источник
FirstRacer Java (5825 + 305 * 10 = 8875)
Просто чтобы начать. Требуется 305 сегментов по городу.
Эта программа Java делает это конвейерным:
Я думаю, что Race Track дает вам лучшее впечатление о том, как работает FirstRacer.
источник
болван PHP (5950 + 470 = 6420)
Еще одна вариация BFS с непоследовательным заголовком, с некоторой предварительной обработкой для сокращения пространства поиска.
Выбор языка
PHP довольно хорошо справляется с обработкой изображений.
Он также имеет встроенную ассоциативную память, что делает программирование поиска узлов BFS быстрым.
Недостатком является то, что хеширование идентификаторов узлов не очень эффективно по времени, поэтому результат не очень быстрый.
Из моих экспериментов с предыдущим соревнованием я не сомневаюсь, что C ++ 11 и его хеш-таблицы будут работать намного лучше, но исходный код будет как минимум удвоен, плюс необходимость в любой внешней библиотеке png (даже LodePNG) сделать для грязной сборки.
Perl и его более продвинутые потомки, вероятно, позволят создать более компактный и эффективный код (из-за лучшей производительности хеширования), но я не достаточно знаком с любым из них, чтобы попробовать порт.
Ядро BFS
Поиск работает в пространстве «позиция + скорость», то есть узел представляет данное местоположение, посещенное с данной скоростью.
Это, конечно, создает довольно большое пространство для поиска, но дает оптимальные результаты при условии изучения всех возможных местоположений треков.
Очевидно, что, учитывая количество пикселей даже на небольшом изображении, об исчерпывающем поиске не может быть и речи.
Порезы
Я решил вырезать пространство позиций, выбрав только подмножество пикселей дорожки.
Решатель рассмотрит все позиции в пределах досягаемости, ограниченные только ускорением.
Дорожка выложена квадратами, максимальный размер которых рассчитывается так, чтобы два соседних квадрата могли быть достигнуты с максимально допустимым ускорением (т.е. 14x14 пикселей с текущим ограничением скорости).
После заполнения дорожки большими квадратами плитки меньшего размера используются для заполнения оставшегося пространства.
Только центр каждой плитки рассматривается как возможный пункт назначения.
Вот пример:
Этого эвристического выбора достаточно, чтобы решатель провалился на ночной кошмарной карте. Я полагаю, что можно попытаться уменьшить максимальный размер плитки до тех пор, пока не будет найдено какое-либо решение, но с текущими настройками решатель работает около часа и использует 600 Мб, поэтому для получения более точных результатов потребуется неоправданно много времени и памяти.
В качестве второго среза квадраты размером всего 1 пиксель могут быть пропущены.
Это, конечно, ухудшит решение или даже помешает решателю найти что-либо, но значительно улучшает время вычислений и обычно дает довольно близкие результаты на «простых» картах.
Например, в городе, оставляя квадраты 1x1 пикселей, уменьшает количество исследованных узлов дерева BFS с 660K до 90K для решений 47 против 53 ходов.
BFS против A *
A * требует больше кода и даже не гарантируется, что он даст более быстрые результаты в пространстве позиции / скорости, поскольку оценка лучшего следующего кандидата не так проста, как в классическом пространстве только позиции (которое можно легко победить с помощью специально созданной культуры). в любом случае).
Кроме того, хотя PHP имеет несколько приоритетных очередей, которые, кстати, поддерживают динамическое переупорядочение, контрастируют с их кузенами C ++, я сомневаюсь, что их будет достаточно для эффективной реализации A *, и перезапись двоичной кучи или любой другой выделенной структуры очереди A * будет требует слишком много строк кода.
Проверка стены
Я не использовал алгоритм Брезенхэма для проверки столкновений со стеной, поэтому траектория могла обрезать нечетный пиксель стены. Это не должно позволять пересекать стену, все же.
Выступления
Этот решатель уверен, что нет шестиногого крольчонка.
Без дополнительного среза карта может занять более 10 минут на моем ПК среднего класса.
Я предлагаю установить минимальный размер плитки на 2 или 3, если вы хотите поиграться с кодом, не тратя много времени на ожидание результата.
Потребление памяти разумно для этого вида алгоритма и языка: около 100 МБ или меньше, за исключением кошмара, который достигает максимума свыше 600 МБ.
Результаты и оценка
Результаты приведены без учета «минимального размера плитки».
Поскольку внимательный читатель может сделать вывод из моих общих замечаний, мне не очень важна игра в гольф. Я не понимаю, как выполнение моего кода через обфускатор или удаление некоторых оптимизаций и / или процедур отладки для сокращения исходного кода сделало бы это более увлекательным.
Пусть пока просто S будет исходным байтом:
дорожка S + 1020
город S + 470
препятствия S + 280
кошмар -> не удается
источник
SecondRacer Java (1788 + 72 * 10 = 2508)
(2708) (2887) (3088) (3382) (4109 + 72 * 10 = 4839) (4290 + 86 * 10 = 5150)Похож на FirstRacer. Но отличается в шагах 2.2 и 3., что приводит к дальнозоркому вождению и использованию передачи.
Спектакль
Ни один из этих треков не является проблемой для A *. Всего несколько секунд (<10), чтобы решить (даже «Nightmare Track: The Gauntlet») на моем компьютере среднего класса.
Стиль Пути
Я называю это осьминогом. Далеко не так элегантно, как решение для свиньи (от kuroi neko).
Стиль кода
Я вошел в умеренный боевой режим, сохраняя читабельность. Но добавили гольф-версию.
golfed -> ПРЕДУПРЕЖДЕНИЕ: заменяет оригинальный файл!
Все изображения показаны на градиентах A *. Начиная от светло-желтого до коричневого (= темно-желтый). В то время как - согласно A * - полученный путь отслеживается в обратном направлении (здесь от коричневого до светло-желтого).
Ипподром S + 175
Полоса препятствий S + 47
Город S + 72
Рукавица S + 1133
источник
Пачка - Хаскелл - (
3460265424762221206019921900 + 50x10 = 2400)Стратегия:
Гольф:
Я не игрок в Haskell, поэтому я не знаю, сколько еще можно сохранить (Edit: оказалось довольно много).
Спектакль:
Gauntlet работает с 9: 21мин на моем 1,7 ГГц Core i5 с 2011 года. Город занимает 7,2 с. (используется -O1 с ghc, -O2 делает программу ужасно медленной)
Варианты настройки - толщина раздутого пути. Для меньших карт расстояние 3 сохраняет один или два шага, но Gauntlet работает слишком долго, поэтому я остаюсь с расстоянием 2. Гонка всегда начинается с первого (то есть в верхнем левом углу) желтого пикселя, оптимизация вручную должна сохранить дополнительный шаг.
Правило соответствия:
«Вы не можете использовать библиотеки для поиска путей.» - Да, но источник включен. А * Функция поиска
немногосильно golfed версии библиотеки Data.Graph.AStar Кейл Гиббарды (см http://hackage.haskell.org/package/astar ).Город - 50 шагов
Рукавица - 722 шага
Ungolfed:
Golfed:
Братья и сестры пачки -TS # 1 - (1764 + 43 = 2194)Изменить: TS # 1 теперь отдельный ответ.
Редактировать II: путь для города
В The Gauntlet Tutu движется следующим образом
источник
-O2
тормозит программу ?? странно. пробовал-O3
?Maybe
много. может быть, вы можете заменитьMaybe
списками:Maybe a
есть[a]
,Nothing
есть[]
иJust x
есть[x]
.Maybe
монада становится эквивалентнойList
монадой. то вы можете использовать много список функций для них:null
,head
,(:[])
,map
и так далее.Гонщик со скрещенными звездами - PHP - 4083 + 440 = слишком тяжелый
Хорошо, через 3 недели без подключения к Интернету (вот что происходит, когда один из самых свирепых конкурентов вашего провайдера отвечает за сборку патч-залов, или, по крайней мере, так происходит в Париже, Франция). Наконец-то я могу опубликовать моя следующая попытка.
На этот раз я использовал алгоритм A * и более эффективную стратегию распределения путевых точек.
В качестве дополнительного бонуса я написал своего рода PHP cruncher для решения гольф-задачи.
И теперь, когда решатель работает на всех предложенных картах, ошибка трассировки линии была исправлена.
Больше не нужно обрезать стены (хотя по-прежнему происходит выпас стен, как и должно быть :)).
запуск кода
дайте коду любое имя, которое вам нравится (
runner.php
например), затем вызовите его так:После долгого безмолвия должно получиться
_track.png
решение, показывающее решение.Как вы можете видеть на выходных изображениях, код очень медленный. Вы были предупреждены.
Конечно, моя собственная частная версия полна следов и дает прекрасное представление различной информации (включая периодическую картину, показывающую прогресс A *, помогающий убить время), но за игру в гольф приходится платить ...
гольф-код
негольфированная версия
Полученные результаты
Картинки сделаны в более богатой версии, которая выводит точно такое же решение с некоторой статистикой внизу (и рисует путь с сглаживанием).
Карта города является довольно хорошим примером того, почему алгоритмы, основанные на позициях, во многих случаях обязаны находить результаты низкого уровня: короче не всегда означает быстрее.
(672 хода, если вы не хотите увеличивать масштаб)
A *
К моему удивлению, A * неплохо работает в пространстве позиции-скорости. Во всяком случае, лучше, чем BFS.
Однако мне пришлось немного потеть, чтобы получить рабочую эвристическую оценку расстояния.
Мне также пришлось несколько оптимизировать его, поскольку число возможных состояний огромно (несколько миллионов) по сравнению с вариантом только для позиции, который снова требует больше кода.
Нижняя граница, выбранная для количества ходов, необходимых для достижения цели из заданной позиции, представляет собой просто время, необходимое для преодоления расстояния до ближайшей цели по прямой линии с нулевой начальной скоростью .
Конечно, прямой путь обычно ведет прямо в стену, но это примерно та же проблема, что и использование евклидова прямого расстояния для поиска A * только в пространстве.
Точно так же, как евклидово расстояние для вариантов только для пространства, главное преимущество этой метрики, помимо приемлемости использования наиболее эффективного варианта A * (с закрытыми узлами), состоит в том, что требуется очень мало топологического анализа трека.
При максимальном ускорении A число n ходов, необходимое для преодоления расстояния d, является наименьшим целым числом, удовлетворяющим соотношению:
d <= A n (n + 1) / 2
Решение этого для n дает оценку оставшегося расстояния.
Чтобы рассчитать это, строится карта расстояния до ближайшей цели с использованием алгоритма заливки, засеянного позициями целей.
У него приятный побочный эффект - устранение точек трека, из которых невозможно достичь цели (как это происходит в некоторых местах трассы кошмара).
Количество ходов вычисляется как значение с плавающей запятой, так что узлы, расположенные ближе к цели, могут быть дополнительно различены.
Waypoints
Как и в моей предыдущей попытке, идея состояла в том, чтобы уменьшить количество точек трека до как можно меньшей подвыборки путевых точек. Хитрость заключается в том, чтобы попытаться выбрать наиболее «полезные» позиции на трассе.
Эвристика начинается с регулярного перераспределения по всей дорожке, но увеличивает плотность в двух типах областей:
Вот пример.
Области высокой плотности выделены красным цветом, а области низкой плотности - зеленым. Синие пиксели - это выбранные путевые точки.
Обратите внимание на группы точек на крутых поворотах (среди множества бесполезных пятен на наклонных кривых из-за недостаточной фильтрации).
Чтобы вычислить положения дорожек, весь трек сканируется на расстояние до ближайшей стены. Результатом является поле векторов, указывающих на границу ближайшего трека.
Это векторное поле затем обрабатывается для получения грубой оценки локальной кривизны.
Наконец, кривизна и расстояние до стены объединяются, чтобы получить желаемую локальную плотность, и довольно неуклюжий алгоритм пытается соответственно распределить путевые точки по треку.
Заметным улучшением по сравнению с предыдущей стратегией является то, что узкие полосы (по-видимому) всегда будут получать достаточно путевых точек для проезда, что позволяет перемещаться по кошмарной карте.
Как всегда, это вопрос выбора между временем вычислений и эффективностью.
Снижение плотности на 50% разделит время вычислений более чем на 4, но с более грубыми результатами (48 ходов вместо 44 в городе, 720 вместо 670 в кошмаре).
Игра в гольф
Я все еще думаю, что игра в гольф наносит вред творчеству в этом конкретном случае: удаление сглаживания из выходных данных достаточно для получения 30 баллов и требует гораздо меньших усилий, чем переход от 47 до 44 ходов на карте города.
Даже переход от 720 до 670 ходов в кошмаре принесет всего 500 очков, хотя я очень сомневаюсь, что A * только с позицией сможет приблизиться к этому.
Просто ради удовольствия, я все равно решил написать свой собственный PHP-компрессор.
Как оказалось, эффективное переименование идентификаторов в PHP - задача не из легких. На самом деле, я не думаю, что это вообще возможно сделать в общем случае. Даже при полном семантическом анализе возможность использовать строки или косвенные переменные для обозначения объектов потребует знания каждой семантики функции.
Однако, поскольку очень удобный встроенный синтаксический анализатор позволяет сразу перейти к семантическому анализу, мне удалось создать что-то, что, кажется, работает на подмножестве PHP, достаточном для написания «пригодного для игры в гольф» кода (держитесь подальше от $$ и не использовать косвенные вызовы функций или другие строки доступа к объектам).
Нет практического смысла говорить и не иметь ничего общего с исходной проблемой, но, тем не менее, очень интересно писать код.
Я мог бы забить код дальше, чтобы получить дополнительные 500 символов или около того, но поскольку имена графической библиотеки PHP, к сожалению, довольно длинные, это своего рода тяжелая борьба.
Дальнейшие разработки
Код выбора путевой точки - это ужасный беспорядок, настроенный методом проб и ошибок. Я подозреваю, что выполнение большего количества математики (с использованием правильных операторов градиента и скручивания) значительно улучшит процесс.
Мне любопытно посмотреть, можно ли найти лучшую эвристику на расстоянии. Я попытался учесть скорость несколькими способами, но это или сломало A *, или дало более медленные результаты.
Возможно, можно будет все это перекодировать на более быстром языке, таком как C ++, но версия PHP в значительной степени опирается на сборку мусора, чтобы сохранить разумное потребление памяти. Очистка закрытых узлов в C ++ потребует довольно много работы и большого количества дополнительного кода.
Если бы оценка была основана на выступлениях, я бы охотно пытался улучшить алгоритмы. Но так как критерий игры в гольф настолько подавляющий, то в этом нет никакого смысла или нет?
источник
ThirdRacer Java (1224 + 93 * 10 = 2154)
Похоже на SecondRacer. Но переключение фокуса с скорости на размер кода (но все же с использованием Java). Оптимизация ускорения теперь значительно упрощена, что, к сожалению, приводит к снижению скорости автомобиля.
Спектакль
Лучше, чем SecondRacer.
Стиль Пути
Нравится SecondRacer.
Стиль кода
Я вступил в тяжелый боевой режим.
golfed -> ПРЕДУПРЕЖДЕНИЕ: это делает замену оригинального файла на месте!
Город S + 93
источник
Звезда пересекла путь гонщика на карте кошмара
(согласно популярному запросу)
(код не обновляется, так как модификации тривиальны, а задача только для производительности - не игра в гольф)
Извините, что опубликовал еще одну запись, но я превышаю ограничение в 30 000 символов по сравнению с предыдущим.
Просто скажи слово, и я удалю это.
источник
Sunday Driver, Python 2, 3242
Сокращенный код = 2382 байта
Производительность: город = 86 препятствий = 46 ипподром = 188 перчатка = 1092
Вот моя программа проверки концепции, которая должна была продемонстрировать, что решение было возможным. Это требует некоторой оптимизации и лучшего игры в гольф.
операция
Создайте структуру данных о расстоянии колец от места назначения (простая производная A *, такая как заливка)
Найдите закороченную серию прямых линий по назначению, которые не пересекают пиксели без отслеживания.
Для каждой прямой линии ускоряйтесь и тормозите, чтобы минимизировать количество сделанных поворотов.
Гольф (минимизированный) код
Примеры
источник