FELICITACIONES a @kuroineko. Gana la recompensa por una velocidad excelente (672 movimientos) en la pista Gauntlet.
LÍDER: * Nimi anotando un peso ligero 2129. Otras entradas son más grandes pero muestran una velocidad seria.
* El líder puede cambiar debido a entradas posteriores.
Su tarea es escribir un pequeño programa que pueda conducir un automóvil de carreras rápido.
Reglas
Su programa leerá una imagen de la pista. Puede encender su automóvil en cualquier píxel amarillo, y debe terminar cruzando cualquier píxel negro. La ruta de su automóvil debe estar solo en la pista gris ((c, c, c) donde 30 <= c <= 220).
Su automóvil se moverá en línea recta cada vuelta con una velocidad v compuesta de enteros vx y vy (comenzando con (0,0)). Al comienzo de cada turno, su programa puede cambiar vx y vy de manera que:
abs(vx2-vx1) + abs(vy2-vy1) <= 15
Actualización: Aumento de la aceleración máxima a 15.
Su programa trazará una línea recta desde su ubicación actual hasta (ubicación + v) en blanco con un punto azul al comienzo. Si un píxel debajo de esta línea es negro, has terminado la carrera. De lo contrario, si todos los píxeles debajo de esa línea son grises o amarillos, puede continuar al siguiente turno.
Su programa debe generar la imagen de la pista con su ruta en blanco y azul agregado.
Producto adicional (agregado 2015-01-15):
Si desea competir por la victoria o el bono , su programa también debe generar su lista de puntos (los puntos azules) para la Ciudad o el Guantelete respectivamente. Incluya la lista de puntos con su respuesta (para verificación). Los puntos deben ser: (x0,y0), (x1,y1), ... (xn,yn)
. Puede utilizar el espacio en blanco libremente, incluidos los '\n'
caracteres para ajustar los datos en la página.
Puede utilizar la lectura y escritura de imágenes de terceros, dibujos lineales y bibliotecas de acceso de píxeles. No puede usar bibliotecas de búsqueda de rutas. Si lo desea, puede convertir las imágenes PNG a otros formatos gráficos (como GIF, JPG, BMP) si es necesario.
Algunas pistas para conducir
Una pista simple para comenzar:
Una pista de carreras:
Una carrera de obstáculos:
La ciudad:
Nightmare Track: The Gauntlet (si los otros son demasiado fáciles)
Tanteo
Su puntaje se basará en su resultado en la pista de la ciudad. Los puntos son iguales a la longitud de su programa en bytes más 10 puntos por cada turno que su auto de carrera tomó para terminar. La puntuación más baja gana. Incluya su imagen de recorrido de la ciudad con su respuesta; nos gustaría ver su estilo de conducción.
Utilice un título para su respuesta en el formato:
<Racing Driver or Team Name> <Language> <Score>
Por ejemplo: Slowpoke Perl 5329
Su programa debe poder conducir en cualquier imagen de pista siguiendo las reglas anteriores. No debe codificar la ruta óptima ni ningún parámetro de las pistas de prueba. Se aplican las otras lagunas estándar.
Desafíos similares
Este es un rompecabezas similar al que plantea Martin: To Vectory! - El Gran Premio de Vector Racing . Este rompecabezas tiene una serie de diferencias:
- Conducir a través de las paredes no está permitido.
- Puede usar memoria y tiempo ilimitados.
- No tiene que ejecutar el código de otra persona en su computadora.
- No tiene que descargar nada excepto una imagen.
- El tamaño de su código cuenta en la puntuación. Más pequeño es mejor.
- Traza su solución en la imagen de la pista.
- Puede crear fácilmente sus propias pistas con un paquete de pintura.
- La resolución más alta fomenta frenadas y curvas más realistas.
- La aceleración de 15 crea alrededor de 450 posibilidades por turno. Esto hace que BFS sea menos factible y alienta nuevos algoritmos interesantes.
Este rompecabezas debería inspirar a una nueva ronda de programadores a probar soluciones y permitir que los programadores con soluciones antiguas los repensen en el nuevo entorno.
fuente
Respuestas:
TS # 1 - Haskell - 1699 + 430 = 2129
Hermano Tutu # 1
Muy similar al Tutu original, excepto que usa un grosor de 3 para la ruta hinchada y el segundo A * (espacio de velocidad-posición) va con una heurística constante de
1
. La imagen de entrada ya no se pasa como un argumento de línea de comando, debe nombrarsei
. El nombre de la imagen de salida eso
. El programa imprime los puntos calculados en la ruta como una lista de pares x, y (la salida original es una sola línea):Podría ahorrar una gran cantidad de bytes cuando empiezo a eliminar todo el mapa y establecer estructuras de datos y reemplazarlos con listas enlazadas simples. Solo las dos declaraciones de importación ahorrarían 60 bytes. Sin embargo, ralentizaría el programa, por lo que esperar un resultado es puro dolor. Esta versión toma más de 45 minutos para The City, en comparación con los 7 del original. Pararé aquí intercambiando bytes por la velocidad de ejecución.
El código necesita el indicador -XNoMonomorphismRestriction para compilar.
La ciudad - TS # 1 - 43 pasos
fuente
FirstRacer Java (5825 + 305 * 10 = 8875)
Solo para empezar. Necesita 305 segmentos en la ciudad.
Este programa Java lo canaliza:
Creo que Race Track te da una mejor impresión de cómo funciona FirstRacer.
fuente
PHP cabeza de cerdo (5950 + 470 = 6420)
Otra variación BFS (terca), con algo de preprocesamiento para reducir el espacio de búsqueda.
Elección de idioma
PHP es bastante bueno en el manejo de imágenes.
También tiene memoria asociativa nativa, lo que hace que la búsqueda de nodos de programación BFS sea muy fácil.
La desventaja es que el hashing de identificadores de nodo no es terriblemente eficiente en el tiempo, por lo que el resultado es todo menos rápido.
De mis experimentos con el desafío de carreras anterior, no tengo dudas de que C ++ 11 y sus tablas hash funcionarían mucho mejor, pero el código fuente al menos se duplicaría, más la necesidad de cualquier biblioteca png externa (incluso LodePNG) hacer una construcción desordenada.
Perl y sus descendientes más avanzados probablemente permitirían un código más compacto y eficiente (debido a mejores rendimientos de hashing), pero no estoy lo suficientemente familiarizado con ninguno de estos para probar un puerto.
Núcleo BFS
La búsqueda opera en una posición + espacio de velocidad, es decir, un nodo representa una ubicación determinada visitada con una velocidad determinada.
Por supuesto, esto crea un espacio de búsqueda bastante grande, pero produce resultados óptimos siempre que se examinen todas las ubicaciones posibles de las pistas.
Claramente, dado el número de píxeles incluso en una imagen pequeña, una búsqueda exhaustiva está fuera de discusión.
Cortes
Elegí cortar el espacio de posición, seleccionando solo un subconjunto de píxeles de pista.
El solucionador considerará todas las posiciones a su alcance, limitadas solo por la aceleración.
La pista está en mosaico con cuadrados, cuyo tamaño máximo se calcula de modo que se puedan alcanzar dos cuadrados adyacentes con la aceleración máxima permitida (es decir, 14x14 píxeles con el límite de velocidad actual).
Después de llenar la pista con cuadrados grandes, se utilizan fichas cada vez más pequeñas para llenar el espacio restante.
Solo el centro de cada mosaico se considera un posible destino.
Aquí hay un ejemplo:
Esta elección heurística es suficiente para hacer que el solucionador falle en el mapa de pesadilla. Supongo que se podría tratar de reducir el tamaño máximo de mosaico hasta que se encuentre alguna solución, pero con la configuración actual, el solucionador funciona durante una hora y usa 600 Mb, por lo que los resultados más precisos requerirían una cantidad de tiempo y memoria irrazonables.
Como segundo corte, los cuadrados de solo 1 píxel pueden quedar fuera.
Por supuesto, esto degradará la solución o incluso evitará que el solucionador encuentre alguna, pero mejora mucho el tiempo de cálculo y generalmente produce resultados bastante cercanos en mapas "simples".
Por ejemplo, en la ciudad, dejar de lado los cuadrados de 1x1 píxeles reduce los nodos de árbol BFS explorados de 660K a aproximadamente 90K para soluciones de 47 contra 53 movimientos.
BFS vs A *
A * necesita más código y ni siquiera se garantiza que produzca resultados más rápidos en el espacio de posición / velocidad, ya que evaluar al mejor próximo candidato no es nada tan simple como en el espacio de posición clásica solamente (que puede ser fácilmente derrotado con un culto hecho a propósito) de-sacs de todos modos).
Además, aunque PHP tiene algunas colas de prioridad, que por cierto admiten el reordenamiento dinámico en contra de sus primos C ++, dudo que sean suficientes para una implementación eficiente de A * y reescribir un montón binario o cualquier estructura de cola A * dedicada. requieren demasiadas líneas de código.
Control de la pared
No utilicé un algoritmo de Bresenham para verificar las colisiones de la pared, por lo que la trayectoria podría recortar el píxel de la pared impar. Sin embargo, no debe permitir cruzar una pared.
Actuaciones
Este solucionador está seguro de que no tiene liebre de seis patas.
Sin el corte adicional, un mapa puede tardar más de 10 minutos en resolverse en mi PC de rango medio.
Sugiero configurar el tamaño mínimo de mosaico en 2 o 3 si desea jugar con el código sin pasar años esperando un resultado.
El consumo de memoria es razonable para este tipo de algoritmo y lenguaje: alrededor de 100 Mb o menos, excepto para la pesadilla que alcanza un máximo de 600 Mb.
Resultados y puntuación
Las puntuaciones se dan sin el corte de "tamaño mínimo de mosaico".
Como un lector cuidadoso puede deducir de mis comentarios generales, no me importa mucho la parte de golf de este desafío. No veo cómo ejecutar mi código a través de un ofuscador o eliminar algunas optimizaciones y / o rutinas de depuración para reducir la fuente haría que esto fuera más divertido.
Deje que S sea el tamaño del byte de origen por ahora:
pista S + 1020
ciudad S + 470
obstáculos S + 280
pesadilla -> falla
fuente
SecondRacer Java (1788 + 72 * 10 = 2508)
(2708) (2887) (3088) (3382) (4109 + 72 * 10 = 4839) (4290 + 86 * 10 = 5150)Similar a FirstRacer. Pero es diferente en los pasos 2.2 y 3. lo que resulta en conducir con visión de futuro y usar el equipo.
Actuación
Ninguna de estas pistas es un problema para A *. Solo unos pocos segundos (<10) para resolver (incluso el "Nightmare Track: The Gauntlet") en mi PC de rango medio.
Estilo de ruta
Yo lo llamo octopussy. No es tan elegante como la solución de cabeza de cerdo (de kuroi neko).
Estilo de código
Entré en un modo de lucha moderado manteniendo la legibilidad. Pero agregó una versión de golf.
golfed -> ADVERTENCIA: ¡reemplaza en su lugar el archivo original!
Todas las imágenes se muestran en su paisaje degradado A *. A partir de amarillo claro a marrón (= amarillo oscuro). Mientras que, según A *, la ruta resultante se sigue hacia atrás (aquí, de marrón a amarillo claro).
Pista de carreras S + 175
Carrera de obstáculos S + 47
Ciudad S + 72
Guantelete S + 1133
fuente
Tutú - Haskell - (
3460265424762221206019921900 + 50x10 = 2400)Estrategia:
Golf:
No soy un golfista de Haskell, así que no sé cuánto se puede guardar más (Editar: resultó ser un montón).
Actuación:
El Gauntlet se ejecuta en 9: 21min en mi Core i5 de 1,7 GHz a partir de 2011. La ciudad tarda 7,2 segundos. (usado -O1 con ghc, -O2 hace que el programa sea extremadamente lento)
Las opciones de ajuste son el grosor de la ruta hinchada. Para los mapas más pequeños, la distancia 3 guarda uno o dos pasos, pero The Gauntlet corre demasiado tiempo, así que me quedo con la distancia 2. La carrera comienza siempre en el primer píxel amarillo (es decir, arriba a la izquierda), la optimización a mano debería ahorrar un paso adicional.
Conformidad de la regla:
"No puede utilizar bibliotecas de búsqueda de rutas". Sí, pero la fuente está incluida. Las funciones de búsqueda A * son versiones
ligeramentedesarrolladas de la biblioteca Data.Graph.AStar de Cale Gibbard (consulte http://hackage.haskell.org/package/astar ).La ciudad - 50 pasos
The Gauntlet - 722 pasos
Sin golf:
Golfizado:
Hermanos tutú -TS # 1 - (1764 + 43 = 2194)Editar: TS # 1 ahora respuesta por separado.
Edición II: el camino para la ciudad es
En The Gauntlet Tutu se mueve de la siguiente manera
fuente
-O2
ralentiza el programa? extraño. intentado-O3
?Maybe
mucho. tal vez pueda reemplazarMaybe
con listas:Maybe a
es[a]
,Nothing
es[]
yJust x
es[x]
. laMaybe
mónada se vuelve equivalente a laList
mónada. a continuación, puede utilizar una gran cantidad de funciones de lista para ellos:null
,head
,(:[])
,map
y así sucesivamente.Corredor cruzado estrella - PHP - 4083 + 440 = demasiado pesado
Ok, después de 3 semanas sin una conexión a Internet (eso es lo que sucede cuando uno de los competidores más feroces de su proveedor está a cargo del parche de construcción, o al menos lo hace en París, Francia). Finalmente puedo publicar Mi próximo intento.
Esta vez utilicé el algoritmo A * y una estrategia de distribución de waypoints más eficiente.
Como un bono adicional, escribí algún tipo de cruncher PHP para abordar la parte de golf del desafío.
Y ahora que el solucionador funciona en todos los mapas propuestos, se ha solucionado el error de trazado de línea.
No más recorte de la pared (aunque todavía se produce el pastoreo de la pared, como debería :)).
ejecutando el código
asigne al código el nombre que desee (
runner.php
por ejemplo), luego invoque de esta manera:Después de permanecer en silencio durante bastante tiempo, debería producir un
_track.png
salida que muestre la solución.Como puede ver en las imágenes de salida, el código es realmente lento. Has sido advertido.
Por supuesto, mi propia versión privada está llena de rastros y produce buenas representaciones de diversas informaciones (incluida una imagen periódica que muestra el progreso A * para ayudar a matar el tiempo), pero hay un precio que pagar por jugar al golf ...
código de golf
versión sin golf
Resultados
Las fotos son producidas por una versión más rica que genera exactamente la misma solución con algunas estadísticas en la parte inferior (y dibuja el camino con antialiasing).
El mapa de la ciudad es un buen ejemplo de por qué los algoritmos basados en la posición están obligados a encontrar resultados inferiores en muchos casos: más corto no siempre significa más rápido.
(672 movimientos si no quieres hacer zoom)
UNA*
Para mi sorpresa, A * funciona bastante bien en el espacio de posición-velocidad. Mejor que BFS, en cualquier caso.
Sin embargo, tuve que sudar un poco para producir una estimación heurística de la distancia de trabajo.
También tuve que optimizarlo un poco, ya que la cantidad de estados posibles es enorme (unos pocos millones) en comparación con una variante de solo posición, que nuevamente requiere más código.
El límite inferior elegido para la cantidad de movimientos necesarios para alcanzar un objetivo desde una posición determinada es simplemente el tiempo necesario para cubrir la distancia al objetivo más cercano en línea recta con velocidad inicial cero .
Por supuesto, la ruta en línea recta generalmente conducirá directamente a una pared, pero este es aproximadamente el mismo problema que usar la distancia recta euclidiana para búsquedas A * de solo espacio.
Al igual que la distancia euclidiana para variantes de solo espacio, la principal ventaja de esta métrica, además de ser aceptable para usar la variante A * más eficiente (con nodos cerrados), es requerir muy poco análisis topológico de la pista.
Dada una aceleración máxima A , el número n de movimientos necesarios para cubrir una distancia d es el número entero más pequeño que satisface la relación:
d <= A n (n + 1) / 2
Resolviendo esto para n da una estimación de la distancia restante.
Para calcular esto, se construye un mapa de distancia al objetivo más cercano, utilizando un algoritmo de relleno de inundación sembrado con posiciones de objetivo.
Tiene el agradable efecto secundario de eliminar los puntos de seguimiento desde donde no se puede alcanzar ningún objetivo (como sucede en algunas áreas de la pista de pesadilla).
El número de movimientos se calcula como un valor de coma flotante, por lo que los nodos más cercanos a la meta pueden ser discriminados aún más.
Waypoints
Como en mi intento anterior, la idea es reducir el número de puntos de seguimiento a una submuestra de puntos de referencia lo más pequeña posible. El truco es tratar de elegir las posiciones más "útiles" en la pista.
La heurística comienza con una distribución regular en toda la pista, pero aumenta la densidad en dos tipos de áreas:
Aquí hay un ejemplo.
Las áreas de alta densidad están en rojo, de baja densidad en verde. Los píxeles azules son los puntos de referencia seleccionados.
Observe los grupos de puntos de referencia en curvas cerradas (entre muchas manchas inútiles en curvas inclinadas, debido a un filtrado insuficiente).
Para calcular las posiciones de los carriles, se escanea la pista completa en busca de distancia a la pared más cercana. El resultado es un campo de vectores que apuntan hacia el borde de la pista más cercana.
Este campo vectorial se procesa para producir una estimación aproximada de la curvatura local.
Finalmente, la curvatura y la distancia a la pared se combinan para producir una densidad local deseada, y un algoritmo bastante torpe intenta rociar puntos de ruta sobre la pista en consecuencia.
Una mejora notable con respecto a la estrategia anterior es que los carriles estrechos (aparentemente) siempre obtendrán suficientes puntos de referencia para conducir, lo que permite navegar por el mapa de pesadilla.
Como siempre, se trata de encontrar un punto óptimo entre el tiempo de cálculo y la eficiencia.
Una disminución del 50% en la densidad dividirá el tiempo de cálculo entre más de 4, pero con resultados más gruesos (48 movimientos en lugar de 44 en la ciudad, 720 en lugar de 670 en la pesadilla).
Golf
Todavía creo que el golf daña la creatividad en este caso específico: eliminar el antialiasing de la salida es suficiente para ganar 30 puntos, y requiere mucho menos esfuerzo que pasar de 47 a 44 movimientos en el mapa de la ciudad.
Incluso pasar de 720 a 670 movimientos en pesadilla ganaría solo 500 puntos, aunque dudo mucho que un A * solo de posición pueda llegar a cualquier lugar cerca de eso.
Solo por el gusto de hacerlo, decidí escribir mi propio compresor PHP de todos modos.
Como parece, renombrar identificadores de manera eficiente en PHP no es una tarea fácil. De hecho, no creo que sea posible hacerlo en el caso general. Incluso con un análisis semántico completo, la posibilidad de utilizar cadenas o variables indirectas para designar objetos requeriría el conocimiento de la semántica de cada función.
Sin embargo, dado que el analizador incorporado muy conveniente permite saltar al análisis semántico de inmediato, logré producir algo que parece funcionar en un subconjunto de PHP suficiente para escribir código "golfable" (manténgase alejado de $$ y no usar llamadas indirectas a funciones u otro acceso de cadena a objetos).
No hay uso práctico para hablar y nada que ver con el problema original, pero es muy divertido codificarlo.
Podría haber descifrado el código aún más para obtener unos 500 caracteres más o menos, pero como los nombres de la biblioteca gráfica de PHP son desafortunadamente bastante largos, es una especie de lucha cuesta arriba.
Nuevos desarrollos
El código de selección de waypoint es un desastre horrible, ajustado por prueba y error. Sospecho que hacer más matemáticas (usando operadores de gradiente y curvatura adecuados) mejoraría en gran medida el proceso.
Tengo curiosidad por ver si se puede encontrar una mejor distancia heurística. Intenté tener en cuenta la velocidad de varias maneras, pero rompió el A * o produjo resultados más lentos.
Es posible recodificar todo esto en un lenguaje más rápido como C ++, pero la versión de PHP depende en gran medida de la recolección de basura para mantener el consumo de memoria razonable. La limpieza de nodos cerrados en C ++ requeriría bastante trabajo y una cantidad considerable de código adicional.
Would the scoring have been based on performances, I would have eagerly tried to improve the algorithms. But since the golfing criterion is so overwhelming, there is no real point, or is it?
fuente
ThirdRacer Java (1224 + 93*10 = 2154)
Similar to SecondRacer. But switching focus from speed to code size (but still using Java). Optimizing the acceleration is much simplified now, sadly resulting in a slower car.
Performance
Better than SecondRacer.
Path Style
Like SecondRacer.
Code Style
I entered into a heavy fighting mode.
golfed -> WARNING: it does in-place replacement of the original file!
City S+93
fuente
Star crossed racer path on the nightmare map
(as per popular request)
(code not updated since the modifications are trivial and the performance-only challenge is not golfed)
Sorry to post yet another entry, but I'm hitting the 30.000 characters limit on the previous one.
Just say the word and I'll delete this one.
fuente
Sunday Driver, Python 2, 3242
Minified code = 2382 bytes
Performance: city=86 obstacles=46 racetrack=188 gauntlet=1092
Here is my proof-of-concept program that was to demonstrate that a solution was possible. It needs some optimisation and better golfing.
Operation
Create a data structure of distance rings out from the destination (simple A* derivative, like flood fill)
Find the shorted series of straight lines to destination that do not cross non-track pixels.
For each straight line, accelerate and brake to minimise the turns taken.
Golfed (minified) Code
Examples
fuente