PARABÉNS a @kuroineko. Ganha a recompensa por uma velocidade excelente (672 movimentos) na pista de Gauntlet.
LÍDER: * Nimi com 2129 leve. Outras entradas são maiores, mas mostram uma certa velocidade.
* O líder pode mudar devido a entradas posteriores.
Sua tarefa é escrever um pequeno programa que possa dirigir um carro de corrida rapidamente.
Regras
Seu programa lerá uma imagem da faixa. Você pode iniciar seu carro com qualquer pixel amarelo e terminar cruzando qualquer pixel preto. O caminho do seu carro deve estar apenas na pista cinza ((c, c, c), onde 30 <= c <= 220).
Seu carro se moverá em uma linha reta a cada turno com a velocidade v composta pelos números inteiros vx e vy (começando com (0,0)). No início de cada turno, seu programa pode alterar vx e vy de modo que:
abs(vx2-vx1) + abs(vy2-vy1) <= 15
Atualização: aumento da aceleração máxima para 15.
Seu programa traçará uma linha reta da sua localização atual para (localização + v) em branco com um ponto azul no início. Se um pixel abaixo desta linha for preto, você terminou a corrida. Caso contrário, se todos os pixels abaixo dessa linha forem cinza ou amarelo, você poderá continuar na próxima curva.
Seu programa deve gerar a imagem da faixa com o seu caminho em branco e azul adicionado.
Saída adicional (adicionado 2015-01-15):
Se você deseja competir pela vitória ou pelo bônus , seu programa também deve exibir sua lista de pontos (os pontos azuis) para a Cidade ou Manopla, respectivamente. Inclua a lista de pontos com sua resposta (para verificação). Os pontos deve ser parecido: (x0,y0), (x1,y1), ... (xn,yn)
. Você pode usar o espaço em branco livremente, incluindo '\n'
caracteres para ajustar os dados na página.
Você pode usar leitura e gravação de imagens de terceiros, desenho de linhas e bibliotecas de acesso a pixels. Você não pode usar bibliotecas de localização de caminhos. Se desejar, você pode converter as imagens PNG em outros formatos gráficos (como GIF, JPG, BMP), se necessário.
Algumas faixas para dirigir
Uma trilha simples para começar:
Uma pista de corrida:
Um curso de obstáculo:
A cidade:
Nightmare Track: The Gauntlet (se os outros são fáceis demais)
Pontuação
Sua pontuação será baseada no seu resultado na pista da cidade. Os pontos são iguais ao tamanho do seu programa em bytes, mais 10 pontos por cada turno que seu carro de corrida levou para terminar. Menor pontuação ganha. Inclua sua imagem da pista da cidade com sua resposta - gostaríamos de ver seu estilo de direção.
Por favor, use um título para sua resposta no formato:
<Racing Driver or Team Name> <Language> <Score>
por exemplo: Slowpoke Perl 5329
Seu programa deve ser capaz de conduzir em qualquer imagem de faixa, seguindo as regras acima. Você não deve codificar o caminho ideal ou quaisquer parâmetros das pistas de teste. As outras brechas padrão se aplicam.
Desafios semelhantes
Este é um quebra-cabeça semelhante ao de Martin: To Vectory! - O Grand Racing de vetor . Este quebra-cabeça tem várias diferenças:
- Dirigir através das paredes não é permitido.
- Você pode usar memória e tempo ilimitados.
- Você não precisa executar o código de outra pessoa no seu computador.
- Você não precisa baixar nada, exceto uma imagem.
- O tamanho do seu código conta na pontuação. Menor é melhor.
- Você plota sua solução na imagem da faixa.
- Você pode criar facilmente suas próprias faixas com um pacote de tinta.
- A resolução mais alta incentiva travagens e curvas mais realistas.
- A aceleração de 15 cria cerca de 450 possibilidades por turno. Isso torna o BFS menos viável e incentiva novos algoritmos interessantes.
Esse quebra-cabeça deve inspirar uma nova rodada de programadores a tentar soluções e permitir que programadores com soluções antigas repensem-nos no novo ambiente.
fonte
Respostas:
TS # 1 - Haskell - 1699 + 430 = 2129
Irmão Tutu # 1
Muito parecido com o piloto Tutu original, exceto que ele usa uma espessura de 3 para o caminho inchado e o 2º A * (espaço de posição de velocidade) segue uma heurística constante de
1
. A imagem de entrada não é mais transmitida como argumento de linha de comando, deve ser nomeadai
. O nome da imagem de saída éo
. O programa imprime os pontos calculados no caminho como uma lista de pares x, y (a saída original é uma única linha):Eu pude salvar muitos bytes quando começo a remover todo o mapa e definir estruturas de dados e substituí-los por simples listas vinculadas. Apenas as duas instruções de importação economizariam 60 bytes. No entanto, isso atrasaria o programa para que esperar por um resultado fosse pura dor. Esta versão demora mais de 45 minutos para The City, em comparação com os 7s do original. Vou parar por aqui trocando bytes pela velocidade de execução.
O código precisa do sinalizador -XNoMonomorphismRestriction para compilar.
A cidade - TS # 1-43 passos
fonte
FirstRacer Java (5825 + 305 * 10 = 8875)
Só para começar. Precisa de 305 segmentos na cidade.
Este programa Java faz pipeline:
Acho que o Race Track dá uma melhor impressão de como o FirstRacer funciona.
fonte
cabeçalho PHP (5950 + 470 = 6420)
Outra variação do BFS (simplificada), com algum pré-processamento para reduzir o espaço de pesquisa.
Escolha de idioma
PHP é muito bom em lidar com imagens.
Ele também possui memória associativa nativa, o que facilita a programação da pesquisa de nós BFS.
A desvantagem é que o hash dos identificadores de nó não é muito eficiente em termos de tempo, portanto o resultado é tudo menos rápido.
Dos meus experimentos com o desafio de corrida anterior, não tenho dúvidas de que o C ++ 11 e suas tabelas de hash teriam um desempenho muito melhor, mas o código-fonte teria pelo menos o dobro, mais a necessidade de qualquer biblioteca png externa (mesmo o LodePNG) faça uma construção bagunçada.
O Perl e seus descendentes mais avançados provavelmente permitiriam um código mais compacto e eficiente (devido a melhores desempenhos de hash), mas não estou familiarizado o suficiente com nenhum deles para tentar uma porta.
Núcleo BFS
A pesquisa opera em uma posição + espaço de velocidade, ou seja, um nó representa um determinado local visitado com uma determinada velocidade.
É claro que isso cria um espaço de pesquisa bastante grande, mas gera ótimos resultados, desde que todos os locais de trilhas possíveis sejam examinados.
Claramente, dado o número de pixels em uma imagem pequena, uma pesquisa exaustiva está fora de questão.
Cortes
Eu escolhi cortar o espaço da posição, selecionando apenas um subconjunto de pixels da trilha.
O solucionador considerará todas as posições ao seu alcance, limitadas apenas pela aceleração.
A pista é lado a lado com quadrados, cujo tamanho máximo é calculado para que dois quadrados adjacentes possam ser alcançados com a aceleração máxima permitida (ou seja, 14x14 pixels com o limite de velocidade atual).
Depois de embalar a pista com grandes quadrados, ladrilhos cada vez menores são usados para preencher o espaço restante.
Somente o centro de cada bloco é considerado como um destino possível.
Aqui está um exemplo:
Essa escolha heurística é suficiente para fazer com que o solucionador falhe no mapa do pesadelo. Suponho que alguém possa tentar reduzir o tamanho máximo do bloco até encontrar alguma solução, mas, com as configurações atuais, o solucionador dura cerca de uma hora e usa 600 Mb, portanto, resultados mais precisos exigiriam uma quantidade razoável de tempo e memória.
Como segundo corte, quadrados de apenas 1 pixel podem ser deixados de fora.
Obviamente, isso degradará a solução ou até impedirá que o solucionador encontre alguma, mas melhora muito o tempo de computação e geralmente produz resultados bastante próximos em mapas "simples".
Por exemplo, na cidade, deixar de fora os quadrados de 1 x 1 pixel reduz os nós da árvore BFS explorados de 660K para cerca de 90K para soluções de 47 x 53 movimentos.
BFS vs A *
A * precisa de mais código e não garante nem mesmo a obtenção de resultados mais rápidos no espaço de posição / velocidade, pois avaliar o melhor candidato seguinte não é nada tão simples quanto no espaço de posição clássica (que pode ser facilmente derrotado com a cultura de propósito específico). de qualquer maneira).
Além disso, embora o PHP tenha algumas filas prioritárias de tipo, que, a propósito, suporte a reordenação dinâmica contrasta com seus primos C ++, duvido que sejam suficientes para uma implementação A * eficiente e reescrevendo um heap binário ou qualquer estrutura de fila A * dedicada. exigem muitas linhas de código.
Verificação de parede
Eu não usei o algoritmo de Bresenham para verificar colisões na parede, portanto a trajetória pode cortar o pixel da parede ímpar. Não deve permitir atravessar uma parede, no entanto.
Performances
Este solucionador certamente não tem um coelho de seis patas.
Sem o corte extra, um mapa pode levar mais de 10 minutos para ser resolvido no meu PC de gama média.
Sugiro definir o tamanho mínimo do bloco como 2 ou 3, se você quiser mexer no código sem gastar muito tempo esperando pelo resultado.
O consumo de memória é razoável para esse tipo de algoritmo e linguagem: cerca de 100 Mb ou menos, exceto para pesadelos que atingem picos acima de 600 Mb.
Resultados e pontuação
As pontuações são dadas sem o corte "tamanho mínimo do ladrilho".
Como um leitor cuidadoso pode deduzir dos meus comentários gerais, não me importo muito com a parte do golfe desse desafio. Não vejo como executar meu código através de um ofuscador ou excluir algumas otimizações e / ou rotinas de depuração para reduzir a fonte tornaria isso mais divertido.
Por enquanto, apenas S seja o tamanho do byte de origem:
faixa S + 1020
cidade S + 470
obstáculos S + 280
pesadelo -> falha
fonte
SecondRacer Java (1788 + 72 * 10 = 2508)
(2708) (2887) (3088) (3382) (4109 + 72 * 10 = 4839) (4290 + 86 * 10 = 5150)Semelhante ao FirstRacer. Mas diferente nas etapas 2.2 e 3., o que resulta em dirigir com visão de futuro e usar a marcha.
atuação
Nenhuma dessas faixas é um problema para o A *. Apenas alguns segundos (<10) para resolver (até a "Nightmare Track: The Gauntlet") no meu PC de nível médio.
Estilo do caminho
Eu chamo de polvo. De longe, não é tão elegante quanto a solução do cabeçote (de kuroi neko).
Estilo do código
Entrei em um modo de luta moderado, mantendo a legibilidade. Mas adicionou uma versão para golfe.
golfed -> AVISO: substitui o arquivo original!
Todas as imagens são mostradas na paisagem gradiente A *. Começando de amarelo claro a marrom (= amarelo escuro). Enquanto - de acordo com A * - o caminho resultante é rastreado para trás (aqui do marrom ao amarelo claro).
Pista de Corrida S + 175
Curso de Obstáculo S + 47
Cidade S + 72
Manoplas S + 1133
fonte
Tutu - Haskell - (
3460265424762221206019921900 + 50x10 = 2400)Estratégia:
Golfe:
Eu não sou um jogador de golfe Haskell, então não sei quanto pode ser economizado (Edit: acabou por ser um bando).
Atuação:
O Gauntlet funciona em 9: 21min no meu Core i5 a 1,7 GHz de 2011. A cidade leva 7,2 segundos. (usado -O1 com ghc, -O2 torna o programa muito lento)
As opções de ajuste são a espessura do caminho inchado. Para os mapas menores, a distância 3 salva um ou dois passos, mas o Gauntlet corre muito tempo, então eu permaneço com a distância 2. A corrida começa sempre no primeiro pixel amarelo (ou seja, no canto superior esquerdo); a otimização manual deve salvar uma etapa adicional.
Conformidade da regra:
“Você não pode usar bibliotecas de localização de caminhos.” - Sim, mas a fonte está incluída. As funções de pesquisa A * são versões
levementepesadas da biblioteca Data.Graph.AStar de Cale Gibbard (consulte http://hackage.haskell.org/package/astar ).A cidade - 50 degraus
A Manopla - 722 passos
Ungolfed:
Golfe:
Irmãos tutu -TS # 1 - (1764 + 43 = 2194)Edit: TS # 1 agora resposta separada.
Edit II: O caminho para a cidade é
Em The Gauntlet, Tutu se move da seguinte maneira
fonte
-O2
retarda o programa ?? esquisito. tentou-O3
?Maybe
muito. talvez você possa substituirMaybe
por listas:Maybe a
é[a]
,Nothing
é[]
eJust x
é[x]
. aMaybe
mônada se torna equivalente àList
mônada. então você pode usar um monte de funções de lista para eles:null
,head
,(:[])
,map
e assim por diante.Piloto cruzado em estrela - PHP - 4083 + 440 = muito pesado
Ok, depois de três semanas sem conexão com a Internet (é o que acontece quando um dos concorrentes mais ferozes do seu provedor está no comando do patch bay do edifício, ou pelo menos em Paris, na França), posso finalmente publicar minha próxima tentativa.
Dessa vez, usei o algoritmo A * e uma estratégia de distribuição de waypoints mais eficiente.
Como um bônus adicional, escrevi algum tipo de triturador de PHP para abordar a parte do desafio do golfe.
E agora que o solucionador funciona em todos os mapas propostos, o bug de rastreamento de linha foi corrigido.
Não é mais necessário recorte de parede (embora o pastejo na parede ainda ocorra, como deveria :)).
executando o código
forneça ao código o nome que você quiser (
runner.php
por exemplo) e, em seguida, chame-o da seguinte maneira:Depois de ficar em silêncio por um bom tempo, ele deve produzir uma
_track.png
saída mostrando a solução.Como você pode ver nas imagens de saída, o código é realmente lento. Voce foi avisado.
É claro que minha versão privada é cheia de traços e produz representações agradáveis de várias informações (incluindo uma imagem periódica mostrando o progresso A * para ajudar a matar o tempo), mas há um preço a pagar pelo golfe ...
código de golfe
versão ungolfed
Resultados
As fotos são produzidas por uma versão mais rica que gera exatamente a mesma solução com algumas estatísticas na parte inferior (e desenha o caminho com antialiasing).
O mapa da cidade é um bom exemplo de por que algoritmos baseados em posição tendem a encontrar resultados abaixo da média em muitos casos: mais curto nem sempre significa mais rápido.
(672 se move, se você não deseja ampliar)
UMA*
Para minha surpresa, A * tem um bom desempenho no espaço de velocidade da posição. Melhor que o BFS, de qualquer forma.
Eu tive que suar um pouco para produzir uma estimativa de distância heurística de trabalho.
Também tive que otimizá-lo um pouco, já que o número de estados possíveis é enorme (alguns milhões) em comparação com uma variante apenas de posição, que novamente requer mais código.
O limite inferior escolhido para o número de movimentos necessários para alcançar um objetivo a partir de uma determinada posição é simplesmente o tempo necessário para percorrer a distância até o objetivo mais próximo em uma linha reta com velocidade inicial zero .
Obviamente, o caminho da linha reta geralmente leva diretamente a uma parede, mas esse é o mesmo problema que usar a distância reta euclidiana para pesquisas A * com apenas espaço.
Assim como a distância euclidiana para variantes com apenas espaço, a principal vantagem dessa métrica, além de ser aceitável usar a variante A * mais eficiente (com nós fechados), é exigir muito pouca análise topológica da pista.
Dada uma aceleração máxima A , o número n de movimentos necessários para percorrer uma distância d é o menor número inteiro que satisfaz a relação:
d <= A n (n + 1) / 2
Resolver isso para n fornece uma estimativa da distância restante.
Para calcular isso, é construído um mapa de distância até a meta mais próxima, usando um algoritmo de preenchimento de inundação semeado com as posições da meta.
Ele tem o agradável efeito colateral de eliminar pontos de trilha de onde nenhum objetivo pode ser alcançado (como acontece em algumas áreas da trilha de pesadelo).
O número de movimentos é calculado como um valor de ponto flutuante, para que os nós mais próximos da meta possam ser discriminados ainda mais.
Waypoints
Como na minha tentativa anterior, a idéia é reduzir o número de pontos de trilha para uma subamostra o menor possível de pontos de referência. O truque é tentar escolher as posições mais "úteis" na pista.
A heurística começa com uma repartição regular em toda a faixa, mas aumenta a densidade em dois tipos de áreas:
Aqui está um exemplo.
Áreas de alta densidade estão em vermelho, baixa densidade em verde. Pixels azuis são os waypoints selecionados.
Observe os aglomerados de pontos de referência em curvas acentuadas (entre muitas manchas inúteis em curvas inclinadas, devido à filtragem insuficiente).
Para calcular as posições das faixas, a faixa inteira é digitalizada quanto à distância da parede mais próxima. O resultado é um campo de vetores apontando para a borda da pista mais próxima.
Esse campo vetorial é então processado para produzir uma estimativa aproximada da curvatura local.
Finalmente, a curvatura e a distância à parede são combinadas para produzir a densidade local desejada, e um algoritmo bastante desajeitado tenta borrifar os waypoints sobre a pista de acordo.
Uma melhoria notável em relação à estratégia anterior é que as faixas estreitas (aparentemente) sempre terão pontos de passagem suficientes para percorrer, o que permite navegar no mapa do pesadelo.
Como sempre, é uma questão de encontrar um ponto ideal entre tempo de computação e eficiência.
Uma redução de 50% na densidade dividirá o tempo de computação em mais de 4, mas com resultados mais grossos (48 movimentos em vez de 44 na cidade, 720 em vez de 670 no pesadelo).
Golfe
Eu ainda acho que o golfe prejudica a criatividade neste caso específico: remover o antialiasing da saída é suficiente para ganhar 30 pontos e requer muito menos esforço do que ir de 47 para 44 movimentos no mapa da cidade.
Mesmo passando de 720 para 670 jogadas no pesadelo ganharia meros 500 pontos, embora eu duvide que um A * somente na posição possa chegar perto disso.
Apenas por diversão, decidi escrever meu próprio compressor PHP de qualquer maneira.
Como parece, renomear identificadores com eficiência no PHP não é uma tarefa fácil. De fato, não acho que seja possível fazê-lo no caso geral. Mesmo com uma análise semântica completa, a possibilidade de usar cadeias ou variáveis indiretas para designar objetos exigiria o conhecimento de toda semântica de funções.
No entanto, como o analisador embutido muito conveniente permite pular para a análise semântica imediatamente, consegui produzir algo que parece funcionar em um subconjunto de PHP suficiente para escrever código "golfable" (fique longe de $$ e não use chamadas de função indiretas ou outro acesso de seqüência a objetos).
Não há uso prático para falar e nada a ver com o problema original, mas é muito divertido codificar.
Eu poderia ter massacrado o código ainda mais para ganhar 500 caracteres extras, mais ou menos, mas como os nomes da biblioteca gráfica PHP são infelizmente muito longos, é uma espécie de luta árdua.
Desenvolvimentos futuros
O código de seleção do waypoint é uma bagunça horrenda, sintonizada por tentativa e erro. Eu suspeito que fazer mais matemática (usando operadores adequados de gradiente e ondulação) melhoraria bastante o processo.
Estou curioso para ver se uma heurística a distância melhor pode ser encontrada. Tentei levar em consideração a velocidade de várias maneiras, mas isso quebrou o A * ou produziu resultados mais lentos.
Pode ser possível recodificar tudo isso em uma linguagem mais rápida como C ++, mas a versão PHP depende muito da coleta de lixo para manter o consumo de memória razoável. Limpar nós fechados no C ++ exigiria bastante trabalho e uma quantidade considerável de código extra.
Se a pontuação tivesse sido baseada em performances, eu tentaria ansiosamente melhorar os algoritmos. Mas como o critério de golfe é tão avassalador, não há nenhum ponto real, ou é?
fonte
ThirdRacer Java (1224 + 93 * 10 = 2154)
Semelhante ao SecondRacer. Mas mudar o foco da velocidade para o tamanho do código (mas ainda usando Java). A otimização da aceleração é muito simplificada agora, resultando tristemente em um carro mais lento.
atuação
Melhor que o SecondRacer.
Estilo do caminho
Como o SecondRacer.
Estilo do código
Entrei em um modo de luta pesado.
golfed -> AVISO: substitui no local o arquivo original!
Cidade S + 93
fonte
Estrela cruzou o caminho do piloto no mapa do pesadelo
(conforme solicitação popular)
(código não atualizado, pois as modificações são triviais e o desafio somente de desempenho não é jogado)
Desculpe postar mais uma entrada, mas estou atingindo o limite de 30.000 caracteres na anterior.
Apenas diga a palavra e eu excluirei esta.
fonte
Driver de domingo, Python 2, 3242
Código reduzido = 2382 bytes
Desempenho: cidade = 86 obstáculos = 46 pista = 188 manopla = 1092
Aqui está o meu programa de prova de conceito que demonstrava que uma solução era possível. Precisa de alguma otimização e melhor golfe.
Operação
Crie uma estrutura de dados de anéis de distância fora do destino (derivada A * simples, como preenchimento)
Encontre as séries retas em curto para o destino que não cruzam os pixels que não são da faixa.
Para cada linha reta, acelere e freie para minimizar as curvas.
Código de golfe (minificado)
Exemplos
fonte