Code Golf: Lasers


152

O desafio

O código mais curto por contagem de caracteres para inserir uma representação 2D de uma placa e gerar 'true' ou 'false' de acordo com a entrada .

A placa é composta por 4 tipos de peças:

 # - A solid wall
 x - The target the laser has to hit
 / or \ - Mirrors pointing to a direction (depends on laser direction)
 v, ^, > or < - The laser pointing to a direction (down, up, right and left respectively)

Existe apenas um laser e apenas um alvo . As paredes devem formar um retângulo sólido de qualquer tamanho, onde o laser e o alvo são colocados no interior. Paredes dentro da 'sala' são possíveis.

O raio laser dispara e viaja desde a origem até a direção em que está apontando. Se um raio laser atinge a parede, ele para. Se um raio laser atinge um espelho, ele oscila 90 graus na direção em que o espelho aponta. Os espelhos são de duas faces, o que significa que ambos os lados são 'refletivos' e podem refletir um raio de duas maneiras. Se um raio laser atinge o próprio laser ( ^v><), ele é tratado como uma parede (o raio laser destrói o projetor e nunca atinge o alvo).

Casos de teste

Entrada:
    ##########
    # / \ #
    # #
    # \ x #
    #> / #
    ########## 
Resultado:
    verdade

Entrada:
    ##########
    # vx #
    # / #
    # / #
    # \ #
    ##########
Resultado:    
    falso

Entrada:
    #############
    # # #
    #> # #
    # # #
    # # x #
    # # #
    #############
Resultado:
    falso

Entrada:
    ##########
    # / \ / \ / \ #
    # \\ // \\\ #
    # // \ / \ / \\ #
    # \ / \ / \ / x ^ #
    ##########
Resultado:
    verdade

A contagem de códigos inclui entrada / saída (ou seja, programa completo).


84
IMMA CHARGIN 'MAH LAZER!
Ólafur Waage

37
Isso é demais .
GManNickG 26/09/09

33
NÃO CRUZE OS
FEIXES

49
@ GameFreak: Isso está ficando muito velho.
Artelius

24
Aquele '^' é na verdade um tubarão com um lazer na cabeça?
Nathan Feger

Respostas:


78

Perl, 166 160 caracteres

Perl, 251 248 246 222 214 208 203 201 193 190 180 176 173 170 166 -> 160 caracteres.

A solução teve 166 tacadas quando o concurso terminou, mas A. Rex encontrou algumas maneiras de cortar mais 6 caracteres:

s!.!$t{$s++}=$&!ge,$s=$r+=99for<>;%d='>.^1<2v3'=~/./g;($r)=grep$d|=$d{$t{$_}},%t;
{$_=$t{$r+=(1,-99,-1,99)[$d^=3*/\\/+m</>]};/[\/\\ ]/&&redo}die/x/?true:false,$/

A primeira linha carrega a entrada em %tuma tabela do quadro onde $t{99*i+j}mantém o caractere na linha i , coluna j . Então,

%d=split//,'>.^1<2v3' ; ($r)=grep{$d|=$d{$t{$_}}}%t

ele procura nos elementos %tum caractere que corresponda a > ^ <ou ve define simultaneamente $dum valor entre 0 e 3 que indica a direção inicial do feixe de laser.

No início de cada iteração no loop principal, atualizamos $dse o feixe estiver atualmente em um espelho. XOR'ing by 3 fornece o comportamento correto para um \espelho e XOR'ing by 1 fornece o comportamento correto para um /espelho.

$d^=3*/\\/+m</>

Em seguida, a posição atual $ré atualizada de acordo com a direção atual.

$r+=(1,-99,-1,99)[$d] ; $_ = $t{$r}

Atribuímos o personagem na posição atual $_para fazer uso conveniente dos operadores da partida.

/[\/\\ ]/ && redo

Continue se estiver em um espaço em branco ou em um caractere de espelho. Caso contrário, terminaremos truese estivermos no alvo ( $_ =~ /x/) ou falsenão.

Limitação: pode não funcionar em problemas com mais de 99 colunas. Essa limitação pode ser removida à custa de mais 3 caracteres,


Ok, tem 323 caracteres. = D
strager

5
Posso alterar 99 para 1E5 para torná-lo muito robusto às custas de 3 caracteres.
turba

2
Sua melhor solução seria mais visível na parte superior da postagem.
Strager 26/09/09

13
Mas usando expressões regulares para girar o quadro? Que estava doente. É como um bônus automático de 20 tempos.
turba

1
@mobrule: salve seis toques: reordene a primeira linha como s!.!$t{$s++}=$&!ge,$s=$r+=99for<>;, altere %d=split//,.." to % d = .. = ~ /./ g , and change grep {..}% t` paragrep..,%t
A. Rex

75

Perl, 177 caracteres

A primeira quebra de linha pode ser removida; os outros dois são obrigatórios.

$/=%d=split//,' >/^\v';$_=<>;$s='#';{
y/v<^/>v</?do{my$o;$o.=" 
"while s/.$/$o.=$&,""/meg;y'/\\'\/'for$o,$s;$_=$o}:/>x/?die"true
":/>#/?die"false
":s/>(.)/$s$d{$1}/?$s=$1:1;redo}

Explicação:

$/ = %d = (' ' => '>', '/' => '^', '\\' => 'v');

Se um feixe que se move para a direita esbarra em um {espaço vazio, espelho em ângulo para cima, espelho em ângulo para baixo}, ele se torna um {feixe de movimento direito, feixe de movimento para cima, feixe de movimento para baixo}. Inicialize $/ao longo do caminho - felizmente "6" não é um caractere de entrada válido.

$_ = <>;

Leia o quadro em $_.

$s="#";

$sé o símbolo do que quer que o feixe esteja no topo agora. Como o emissor a laser deve ser tratado como uma parede, defina-o como uma parede para começar.

if (tr/v<^/>v</) {
  my $o;
  $o .= "\n" while s/.$/$o .= $&, ""/meg;
  tr,/\\,\\/, for $o, $s;
  $_ = $o;
}

Se o raio laser estiver apontando de qualquer maneira, exceto para a direita, gire seu símbolo e gire toda a placa no lugar (também gire os símbolos dos espelhos). É uma rotação à esquerda de 90 graus, realizada efetivamente revertendo as linhas enquanto transpõe linhas e colunas, de maneira um pouco diabólica s///ecom efeitos colaterais. No código golfed, o tr é escrito no formato y'''que permite pular uma barra invertida.

die "true\n" if />x/; die "false\n" if />#/;

Termine com a mensagem certa se atingirmos o alvo ou uma parede.

$s = $1 if s/>(.)/$s$d{$1}/;

Se houver um espaço vazio na frente do laser, siga em frente. Se houver um espelho na frente do laser, avance e gire o feixe. Em qualquer um dos casos, coloque o "símbolo salvo" de volta no local antigo do feixe e coloque o que acabamos de sobrescrever no símbolo salvo.

redo;

Repita até o término. {...;redo}é dois caracteres menor que for(;;){...}e três menor que while(1){...}.


4
Gire o tabuleiro ... Louco. Regexp ... Mais louco. O_o
strager

39
Você ... Seu monstro!
LiraNuna 26/09/09

4
LiraNuna: Eu escolho tomar isso como um elogio.
24909 hobbs #

21
O golfe acabou. Como você pode vencer a rotação de uma placa 2D com expressões regulares ?!
26909 Konrad Rudolph

13
wtf? programadores perl são assistentes.
Johannes Schaub - litb 27/09/09

39

C89 (209 caracteres)

#define M(a,b)*p==*#a?m=b,*p=1,q=p:
*q,G[999],*p=G;w;main(m){for(;(*++p=getchar())>0;)M(<,-1)M
(>,1)M(^,-w)M(v,w)!w&*p<11?w=p-G:0;for(;q+=m,m=*q&4?(*q&1?
-1:1)*(m/w?m/w:m*w):*q&9?!puts(*q&1?"false":"true"):m;);}

Explicação

Essa monstruosidade provavelmente será difícil de seguir se você não entender C. Apenas um aviso.

#define M(a,b)*p==*#a?m=b,*p=1,q=p:

Essa pequena macro verifica se o caractere atual ( *p) é igual ao que aestá no formato de caractere ( *#a). Se forem iguais, defina o vetor de movimento como b( m=b), marque esse caractere como uma parede ( *p=1) e defina o ponto inicial para o local atual ( q=p). Essa macro inclui a parte "else".

*q,G[999],*p=G;
w;

Declare algumas variáveis. * qé a localização atual da luz. * Gé o tabuleiro do jogo como uma matriz 1D. * pé o local de leitura atual ao preencher G. * wé a largura do quadro.

main(m){

Óbvio main. mé uma variável que armazena o vetor de movimento. (É um parâmetro para mainotimização.)

    for(;(*++p=getchar())>0;)

Repete todos os caracteres, preenchendo Gusando p. Pule G[0]como uma otimização (não é necessário desperdiçar um personagem escrevendo pnovamente na terceira parte do for).

        M(<,-1)
        M(>,1)
        M(^,-w)
        M(v,w)

Use a macro mencionada acima para definir o lazer, se possível. -1e 1correspondem à esquerda e à direita, respectivamente, -we para wcima e para baixo.

        !w&*p<11
            ?w=p-G
            :0;

Se o caractere atual for um marcador de fim de linha (ASCII 10), defina a largura se ainda não tiver sido definido. O ignorado G[0]nos permite escrever em w=p-Gvez de w=p-G+1. Além disso, isso termina a ?:cadeia a partir do M.

    for(;
        q+=m,

Mova a luz pelo vetor de movimento.

        m=
        *q&4
            ?(*q&1?-1:1)*(
                m/w?m/w:m*w
            )

Reflita o vetor de movimento.

            :*q&9
                ?!puts(*q&1?"false":"true")
                :m
        ;

Se for uma parede ou x, saia com a mensagem apropriada ( m=0finaliza o loop). Caso contrário, não faça nada (noop; m=m)

    );
}

8
Ugh! Eu estava trabalhando em uma solução C quando o alarme de incêndio soou no meu complexo de apartamentos. Agora eu fui derrotado. Solução agradável embora.
rlbond 26/09/09

Pensar em usar uma variável temp para suas etapas de troca e troca / negação economizaria alguns caracteres.
Artelius

@ Artelius, sim, eu percebi isso, e algumas outras coisas. Obrigado.
Strager 26/09/09

1
TCC realmente não gosta das declarações sem tipo e erros com g.c:3: declaration expected:(
Mark Rushakoff

2
A remoção putsda declaração ajudou, mas não o suficiente para colocá-la abaixo de 170. 209 é muito boa, então acho que vou deixar por isso mesmo. Obrigado pela ajuda, pessoal. Eu realmente gostei disso. =] (Qualquer coisa para destronar aquelas bruxas Perl!) #
283

36

Aposto que as pessoas estão esperando por este há um tempo LOOOOONG. (Como assim, o desafio acabou e ninguém se importa mais?)

Eis que aqui apresento uma solução em

Befunge-93!

Ele pesa 973 caracteres (ou 688 se você for caridoso o suficiente para ignorar espaço em branco, que é usado apenas para formatação e não faz nada no código real).

Advertência : Eu escrevi meu próprio intérprete Befunge-93 em Perl há pouco tempo e, infelizmente, é tudo o que realmente tenho tempo para testá-lo. Estou razoavelmente confiante em sua correção em geral, mas pode ter uma limitação estranha em relação ao EOF: Como o <>operador do Perl retorna undef no final do arquivo, isso é processado como um 0 no contexto numérico. Para implementações baseadas em C em que o EOF tem um valor diferente (-1 digamos), esse código pode não funcionar.

003pv   >~v>  #v_"a"43g-!#v_23g03p33v>v
>39#<*v   ::   >:52*-!v   >"rorrE",vg2*
######1   >^vp31+1g31$_03g13gp vv,,<15,
    a#3     >0v       vp30+1g30<>,,#3^@
######p $     0vg34"a"<   >       >vp
^<v>  > ^   p3<>-#v_:05g-!|>:15g-!| $
 >     v^     <   <   <   >^v-g52:< $ 
  v _  >52*"eslaf",,vv|-g53:_      v   
  : ^-"#">#:< #@,,,,<<>:43p0 v0 p34< 
  >">"-!vgv<  ^0p33g31p32-1g3<       
 ^     <#g1|-g34_v#-g34_v#-g34"><v^"<<<<
    v!<^<33>13g1v>03g1-v>03g1+03p$v  $$
>^  _#-v 1>g1-1v>+13pv >03p       v  pp
^_:"^"^#|^g30 <3#   $<           $<>^33
 ^!-"<":<>"v"v^># p#$<>            $^44
^      >#^#_ :" "-#v_ ^   >         ^gg
v  g34$<   ^!<v"/":< >$3p$^>05g43p$ ^55
 >,@   |!-"\"  :_$43g:">"-!|>      ^$32
 *v"x":<      >-^    ^4g52<>:"^" -#v_^
 5>-!#v_"ror"vv$p34g51:<>#|  !-"<":<#|
 ^2,,, ,,"er"<>v      #^^#<>05g43p$$^>^
      >52*"eurt",,,,,@>15g4 3p$$$$  ^#
>:"v"\:"<"\: "^"   -!#^_-!#^_-!      ^
               >                       ^

Explicação

Se você não estiver familiarizado com a sintaxe e a operação do Befunge, verifique aqui .

Befunge é uma linguagem baseada em pilha, mas existem comandos que permitem escrever caracteres no código Befunge. Aproveito isso em dois lugares. Primeiro, copio toda a entrada no quadro Befunge, mas localizei algumas linhas abaixo do código escrito real. (Obviamente, isso nunca é realmente visível quando o código é executado.)

O outro local fica perto do canto superior esquerdo:

######
    a#
######

Nesse caso, a área que destaquei acima é onde guardo algumas coordenadas. A primeira coluna na linha do meio é onde eu armazeno a coordenada x para a atual "posição do cursor"; a segunda coluna é onde eu armazeno a coordenada y; as próximas duas colunas são para armazenar as coordenadas x e y da fonte do feixe de laser quando isso é encontrado; e a coluna final (com o caractere 'a') é substituída para conter a direção atual do feixe, que obviamente muda conforme o caminho do feixe é traçado.

O programa começa colocando (0,27) como a posição inicial do cursor. Então a entrada é lida um caractere de cada vez e colocada na posição do cursor; as novas linhas apenas fazem com que a coordenada y aumente e a coordenada x retorne a 0, exatamente como um retorno de carro real. Eventualmente, undef é lido pelo intérprete e esse valor de 0 caractere é usado para sinalizar o final da entrada e seguir para as etapas de iteração do laser. Quando o caractere a laser [<> ^ v] é lido, ele também é copiado para o repositório de memória (sobre o caractere 'a') e suas coordenadas são copiadas para as colunas à esquerda.

O resultado final de tudo isso é que o arquivo inteiro é basicamente copiado no código Befunge, um pouco abaixo do código real percorrido.

Posteriormente, o local do feixe é copiado de volta para os locais do cursor e a seguinte iteração é executada:

  • Verifique a direção atual do feixe e aumente ou diminua as coordenadas do cursor adequadamente. (Faço isso primeiro para evitar ter que lidar com a caixa de canto do feixe de laser logo no primeiro movimento.)
  • Leia o personagem nesse local.
  • Se o caractere for "#", coloque nova linha e "falso" na pilha, imprima e termine.
  • Compare com todos os caracteres do feixe [<> ^ v]; se houver uma correspondência, imprima "false \ n" e termine.
  • Se o personagem for um espaço, esvazie a pilha e continue.
  • Se o caractere for uma barra, coloque a direção do feixe na pilha e compare-a com cada um dos caracteres de direção. Quando um é encontrado, a nova direção é armazenada no mesmo local no código e o loop se repete.
  • Se o caractere for uma barra invertida, faça basicamente o mesmo que o descrito acima (exceto com o mapeamento adequado para a barra invertida).
  • Se o personagem for 'x', atingimos o alvo. Imprima "true \ n" e saia.
  • Se o caractere não for um desses, imprima "erro \ n" e saia.

Se houver demanda suficiente, tentarei apontar exatamente onde no código tudo isso é realizado.


14
+1 - Somente porque pode ser mal interpretado como um EXE aberto no bloco de notas.
Kyle Rosendo

1
Hum ... Puta merda. Eu brinquei com o Befunge, e isso é realmente impressionante.
Almo

Código de golfe em idiomas ofuscados ... como manteiga de amendoim e pimenta de Caiena!
Wberry 19/11

29

F #, 36 linhas, muito legível

Ok, apenas para obter uma resposta por aí:

let ReadInput() =
    let mutable line = System.Console.ReadLine()
    let X = line.Length 
    let mutable lines = []
    while line <> null do
        lines <- Seq.to_list line :: lines
        line <- System.Console.ReadLine()
    lines <- List.rev lines
    X, lines.Length, lines

let X,Y,a = ReadInput()
let mutable p = 0,0,'v'
for y in 0..Y-1 do
    for x in 0..X-1 do 
        printf "%c" a.[y].[x]
        match a.[y].[x] with 
        |'v'|'^'|'<'|'>' -> p <- x,y,a.[y].[x]
        |_ -> ()
    printfn ""

let NEXT = dict [ '>', (1,0,'^','v')
                  'v', (0,1,'<','>')
                  '<', (-1,0,'v','^')
                  '^', (0,-1,'>','<') ]
let next(x,y,d) =
    let dx, dy, s, b = NEXT.[d]
    x+dx,y+dy,(match a.[y+dy].[x+dx] with
               | '/' -> s
               | '\\'-> b
               | '#'|'v'|'^'|'>'|'<' -> printfn "false"; exit 0
               | 'x' -> printfn "true"; exit 0
               | ' ' -> d)

while true do
    p <- next p    

Amostras:

##########
#   / \  #
#        #
#   \   x#
# >   /  #
##########
true

##########
#   v x  #
# /      #
#       /#
#   \    #
##########
false

#############
#     #     #
# >   #     #
#     #     #
#     #   x #
#     #     #
#############
false

##########
#/\/\/\  #
#\\//\\\ #
#//\/\/\\#
#\/\/\/x^#
##########
true

##########
#   / \  #
#        #
#/    \ x#
#\>   /  #
##########
false

##########
#  /    \#
# / \    #
#/    \ x#
#\^/\ /  #
##########
false

54
EU POSSO LER REALMENTE ESTE! MILAGRE!
9139 Jeff Atwood

17
O código Java / C # golf é contado por linhas, não por caracteres. Essa é a desvantagem.
Nathan Feger

3
O @strager não é deprimente em três anos quando você é contratado para manter o código e o desenvolvedor original já se foi há muito tempo.
Nathan Feger

Isso falha ao usar o F # no Visual Studio 2010. Seq.to_list não existe (ok, alterou para Lista) e, em seguida, Linha 25, correspondência incompleta de padrões.
Russell

2
Sim, mude to_list para toList agora. O aviso de correspondência incompleta está bom; é código golf, então eu não fiz código como: | _ -> failwith "impossível"
Brian

29

Golfscript - 83 caracteres (mashup do meu e do strager)

A nova linha está aqui apenas para embalar

:|'v^><'.{|?}%{)}?:$@=?{.[10|?).~)1-1]=$+
:$|=' \/x'?\[.\2^.1^'true''false']=.4/!}do

Golfscript - 107 caracteres

A nova linha existe apenas para maior clareza

10\:@?):&4:$;{0'>^<v'$(:$=@?:*>}do;
{[1 0&--1&]$=*+:*;[{$}{3$^}{1$^}{"true "}{"false"}]@*=' \/x'?=~5\:$>}do$

Como funciona.

A primeira linha calcula a localização e a direção iniciais.
A segunda linha passa pelo giro sempre que o laser atinge um espelho.


18

353 caracteres em Ruby:

314 277 caracteres agora!

OK, 256 caracteres em Ruby e agora estou pronto. Número redondo agradável para parar. :)

247 caracteres. Eu não consigo parar

223 203 201 caracteres em Ruby

d=x=y=-1;b=readlines.each{|l|d<0&&(d="^>v<".index l[x]if x=l.index(/[>^v<]/)
y+=1)};loop{c=b[y+=[-1,0,1,0][d]][x+=[0,1,0,-1][d]]
c==47?d=[1,0,3,2][d]:c==92?d=3-d:c==35?(p !1;exit):c<?x?0:(p !!1;exit)}

Com espaço em branco:

d = x = y = -1
b = readlines.each { |l|
  d < 0 && (d = "^>v<".index l[x] if x = l.index(/[>^v<]/); y += 1)
}

loop {
  c = b[y += [-1, 0, 1, 0][d]][x += [0, 1, 0, -1][d]]

  c == 47 ? d = [1, 0, 3, 2][d] :
  c == 92 ? d = 3 - d :
  c == 35 ? (p !1; exit) :
  c < ?x ? 0 : (p !!1; exit)
}

Um pouco refatorado:

board = readlines

direction = x = y = -1
board.each do |line|
  if direction < 0
    x = line.index(/[>^v<]/)
    if x
      direction = "^>v<".index line[x]
    end
    y += 1
  end
end

loop do
  x += [0, 1, 0, -1][direction]
  y += [-1, 0, 1, 0][direction]

  ch = board[y][x].chr
  case ch
  when "/"
    direction = [1, 0, 3, 2][direction]
  when "\\"
    direction = 3 - direction
  when "x"
    puts "true"
    exit
  when "#"
    puts "false"
    exit
  end
end

Mas ... você pode renomear chpara Cou qualquer outra letra de 1 caractere para salvar 2 caracteres!
LiraNuna 26/09/09

Ok ok, tudo bem ... Eu realmente percebi que toda essa variável é desnecessária, pois eu a uso apenas uma vez. Esta e algumas outras melhorias reduziram para 247 caracteres.
Jeremy Ruten 26/09/09

1
Não i++(em vez de i+=1)?
LiraNuna 26/09/09

6
Não. Você pode fazer o ++ i, mas apenas o torna realmente tão positivo quanto costumava ser.
26410 DigitalRoss

Eu gosto da versão comprimida: #; p
SeanJA

17

Pitão

294 277 253 240 232 caracteres, incluindo novas linhas:

(o primeiro caractere nas linhas 4 e 5 é uma guia, não espaços)

l='>v<^';x={'/':'^<v>','\\':'v>^<',' ':l};b=[1];r=p=0
while b[-1]:
 b+=[raw_input()];r+=1
 for g in l:
    c=b[r].find(g)
    if-1<c:p=c+1j*r;d=g
while' '<d:z=l.find(d);p+=1j**z;c=b[int(p.imag)][int(p.real)];d=x.get(c,' '*4)[z]
print'#'<c

Eu tinha esquecido que o Python tinha até ponto e vírgula opcional.

Como funciona

A idéia principal por trás desse código é usar números complexos para representar posições e direções. As linhas são o eixo imaginário, aumentando para baixo. As colunas são o eixo real, aumentando para a direita.

l='>v<^';uma lista dos símbolos do laser. A ordem é escolhida para que o índice de um caractere de direção do laser corresponda a uma potência de sqrt (-1)

x={'/':'^<v>','\\':'v>^<',' ':l};uma tabela de transformação que determina como a direção muda quando a viga sai de blocos diferentes. O bloco é a chave e as novas direções são os valores.

b=[1];mantém o conselho. O primeiro elemento é 1 (avaliado como verdadeiro) para que o loop while seja executado pelo menos uma vez.

r=p=0 ré o número da linha atual da entrada, pé a posição atual do feixe de laser.

while b[-1]: parar de carregar os dados da placa quando raw_input retornar uma sequência vazia

b+=[raw_input()];r+=1 acrescente a próxima linha de entrada ao quadro e aumente o contador de linhas

for g in l: adivinhar cada direção do laser, por sua vez

c=b[r].find(g) defina a coluna para a localização do laser ou -1 se não estiver na linha (ou estiver apontando em uma direção diferente)

if-1<c:p=c+1j*r;d=gse encontrarmos um laser, defina a posição pe a direção atuais d. dé um dos caracteres eml

Após carregar a placa b, a posição pe a direção atuais dforam definidas para as da fonte de laser.

while' '<d: O espaço tem um valor ASCII mais baixo do que qualquer um dos símbolos de direção, portanto, o usamos como um sinalizador de parada.

z=l.find(d);índice da direção atual char na lstring. zé usado posteriormente para determinar a nova direção do feixe usando a xtabela e para incrementar a posição.

p+=1j**z;aumente a posição usando uma potência de i. Por exemplo, l.find('<')==2-> i ^ 2 = -1, que seria movido para a coluna da esquerda.

c=b[int(p.imag)][int(p.real)]; leia o caractere na posição atual

d=x.get(c,' '*4)[z]procure a nova direção da viga na tabela de transformação. Se o caractere atual não existir na tabela, defina dcomo espaço.

print'#'<c print false se pararmos em algo que não seja o alvo.


9
p+=1j**z: Isso é doce.
dmckee --- gatinho ex-moderador

16

Este é era uma porta directa de solução de Brian para C # 3, menos as interacções da consola. Esta não é uma entrada no desafio, uma vez que não é um programa completo, eu estava apenas imaginando como algumas das construções de F # que ele usou poderiam ser representadas em C #.

bool Run(string input) {
    var a = input.Split(new[] {Environment.NewLine}, StringSplitOptions.None);
    var p = a.SelectMany((line, y) => line.Select((d, x) => new {x, y, d}))
             .First(x => new[] {'v', '^', '<', '>'}.Contains(x.d));
    var NEXT = new[] {
            new {d = '>', dx = 1, dy = 0, s = '^', b = 'v'},
            new {d = 'v', dx = 0, dy = 1, s = '<', b = '>'},
            new {d = '<', dx = -1, dy = 0, s = 'v', b = '^'},
            new {d = '^', dx = 0, dy = -1, s = '>', b = '<'}
        }.ToDictionary(x => x.d);
    while (true) {
        var n = NEXT[p.d];
        int x = p.x + n.dx,
            y = p.y + n.dy;
        var d = a[y][x];
        switch (d) {
            case '/':  d = n.s; break;
            case '\\': d = n.b; break;
            case ' ':  d = p.d; break;
            default: return d == 'x';
        }
        p = new {x, y, d};
    }
}

Edit: Após algumas experiências, o seguinte código de pesquisa bastante detalhado:

int X = a[0].Length, Y = a.Length;
var p = new {x = 0, y = 0, d = 'v'};
for (var y = 0; y < Y; y++) {
    for (var x = 0; x < X; x++) {
        var d = a[y][x];
        switch (d) {
            case 'v': case '^': case '<': case '>':
                p = new {x, y, d}; break;
        }
    }
}

foi substituído por um código LINQ to Objects muito mais compacto:

var p = a.SelectMany((line, y) => line.Select((d, x) => new {x, y, d}))
         .First(x => new[] {'v', '^', '<', '>'}.Contains(x.d));

8
AMD. Que bom exemplo para demonstrar como o poderoso linq e c # se tornaram. 1+ para Eu sou um grande fã de c #. x)
Emiswelt 26/09/09

16

F #, 255 caracteres (e ainda bastante legível!):

Ok, depois de uma noite de descanso, eu melhorei bastante isso:

let a=System.Console.In.ReadToEnd()
let w,c=a.IndexOf"\n"+1,a.IndexOfAny[|'^';'<';'>';'v'|]
let rec n(c,d)=
 let e,s=[|-w,2;-1,3;1,0;w,1|].[d]
 n(c+e,match a.[c+e]with|'/'->s|'\\'->3-s|' '->d|c->printfn"%A"(c='x');exit 0)
n(c,"^<>v".IndexOf a.[c])

Vamos falar disso linha por linha.

Primeiro, agrupe toda a entrada em uma grande matriz unidimensional (as matrizes 2D podem ser ruins para o código de golfe; basta usar uma matriz 1D e adicionar / subtrair a largura de uma linha ao índice para subir / descer uma linha).

Em seguida, calculamos 'w', a largura de uma linha de entrada e 'c', a posição inicial, indexando em nossa matriz.

Agora vamos definir a 'próxima' função 'n', que assume uma posição atual 'c' e uma direção 'd' que é 0,1,2,3 para cima, esquerda, direita, baixo.

O índice-epsilon 'e' e o que-nova-direção-se-batermos-uma-barra são calculados por uma tabela. Por exemplo, se a direção atual 'd' é 0 (para cima), o primeiro elemento da tabela diz "-w, 2", o que significa que diminuímos o índice por w e, se pressionarmos uma barra, a nova direção será 2 (certo).

Agora recuamos para a próxima função 'n' com (1) o próximo índice ("c + e" - corrente mais epsilon) e (2) a nova direção, que calculamos olhando para a frente para ver o que está na matriz em aquela próxima célula. Se o char lookahead é uma barra, a nova direção é 's'. Se for uma barra invertida, a nova direção é de 3 s (nossa escolha de codificar 0123 faz esse trabalho). Se for um espaço, continuamos na mesma direção 'd'. E se houver outro caractere 'c', o jogo termina, imprimindo 'true' se o caractere for 'x' e false caso contrário.

Para começar, chamamos a função recursiva 'n' com a posição inicial 'c' e a direção inicial (que faz a codificação inicial da direção em 0123).

Acho que ainda posso raspar mais alguns personagens, mas estou muito satisfeito com isso (e 255 é um bom número).


11

A pesagem de 18203 caracteres é uma solução Python que pode:

  • lidar com espelhos fora da 'sala'
  • calcule a trajetória quando não houver uma 'sala' com base nas limitações 2D (a especificação diz muito sobre o que deve estar na 'sala', mas não se a sala precisa existir)
  • relatar erros

Ele ainda precisa ser arrumado um pouco e eu não sei se a física 2D determina que o feixe não pode se cruzar ...

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
The shortest code by character count to input a 2D representation of a board, 
and output 'true' or 'false' according to the input.

The board is made out of 4 types of tiles:

# - A solid wall
x - The target the laser has to hit
/ or \ - Mirrors pointing to a direction (depends on laser direction)
v, ^, > or < - The laser pointing to a direction (down, up, right and left
respectively)

There is only one laser and only one target. Walls must form a solid rectangle 
of any size, where the laser and target are placed inside. Walls inside the
'room' are possible.

Laser ray shots and travels from it's origin to the direction it's pointing. If
a laser ray hits the wall, it stops. If a laser ray hits a mirror, it is bounces
90 degrees to the direction the mirror points to. Mirrors are two sided, meaning
both sides are 'reflective' and may bounce a ray in two ways. If a laser ray
hits the laser (^v><) itself, it is treated as a wall (laser beam destroys the
beamer and so it'll never hit the target).
"""



SOLID_WALL, TARGET, MIRROR_NE_SW, MIRROR_NW_SE, LASER_DOWN, LASER_UP, \
LASER_RIGHT, LASER_LEFT = range(8)

MIRRORS = (MIRROR_NE_SW, MIRROR_NW_SE)

LASERS = (LASER_DOWN, LASER_UP, LASER_RIGHT, LASER_LEFT)

DOWN, UP, RIGHT, LEFT = range(4)

LASER_DIRECTIONS = {
    LASER_DOWN : DOWN,
    LASER_UP   : UP,
    LASER_RIGHT: RIGHT,
    LASER_LEFT : LEFT
}

ROW, COLUMN = range(2)

RELATIVE_POSITIONS = {
    DOWN : (ROW,     1),
    UP   : (ROW,    -1),
    RIGHT: (COLUMN,  1),
    LEFT : (COLUMN, -1)
}

TILES = {"#" : SOLID_WALL,
         "x" : TARGET,
         "/" : MIRROR_NE_SW,
         "\\": MIRROR_NW_SE,
         "v" : LASER_DOWN,
         "^" : LASER_UP,
         ">" : LASER_RIGHT,
         "<" : LASER_LEFT}

REFLECTIONS = {MIRROR_NE_SW: {DOWN : LEFT,
                              UP   : RIGHT,
                              RIGHT: UP,
                              LEFT : DOWN},
               MIRROR_NW_SE: {DOWN : RIGHT,
                              UP   : LEFT,
                              RIGHT: DOWN,
                              LEFT : UP}}



def does_laser_hit_target(tiles):
    """
        Follows a lasers trajectory around a grid of tiles determining if it
        will reach the target.

        Keyword arguments:
        tiles --- row/column based version of a board containing symbolic
                  versions of the tiles (walls, laser, target, etc)
    """

    #Obtain the position of the laser
    laser_pos = get_laser_pos(tiles)

    #Retrieve the laser's tile
    laser = get_tile(tiles, laser_pos)

    #Create an editable starting point for the beam
    beam_pos = list(laser_pos)

    #Create an editable direction for the beam
    beam_dir = LASER_DIRECTIONS[laser]

    #Cache the number of rows
    number_of_rows = len(tiles)

    #Keep on looping until an ultimate conclusion
    while True:

        #Discover the axis and offset the beam is travelling to
        axis, offset = RELATIVE_POSITIONS[beam_dir]

        #Modify the beam's position
        beam_pos[axis] += offset

        #Allow for a wrap around in this 2D scenario
        try:

            #Get the beam's new tile
            tile = get_tile(tiles, beam_pos)

        #Perform wrapping
        except IndexError:

            #Obtain the row position
            row_pos = beam_pos[ROW]

            #Handle vertical wrapping
            if axis == ROW:

                #Handle going off the top
                if row_pos == -1:

                    #Move beam to the bottom
                    beam_pos[ROW] = number_of_rows - 1

                #Handle going off the bottom
                elif row_pos == number_of_rows:

                    #Move beam to the top
                    beam_pos[ROW] = 0

            #Handle horizontal wrapping
            elif axis == COLUMN:

                #Obtain the row
                row = tiles[row_pos]

                #Calculate the number of columns
                number_of_cols = len(row)

                #Obtain the column position
                col_pos = beam_pos[COLUMN]

                #Handle going off the left hand side
                if col_pos == -1:

                    #Move beam to the right hand side
                    beam_pos[COLUMN] = number_of_cols - 1

                #Handle going off the right hand side
                elif col_pos == number_of_cols:

                    #Move beam to the left hand side
                    beam_pos[COLUMN] = 0

            #Get the beam's new tile
            tile = get_tile(tiles, beam_pos)

        #Handle hitting a wall or the laser
        if tile in LASERS \
        or tile == SOLID_WALL:
            return False

        #Handle hitting the target
        if tile == TARGET:
            return True

        #Handle hitting a mirror
        if tile in MIRRORS:
            beam_dir = reflect(tile, beam_dir)

def get_laser_pos(tiles):
    """
        Returns the current laser position or an exception.

        Keyword arguments:
        tiles --- row/column based version of a board containing symbolic
                  versions of the tiles (walls, laser, target, etc)
    """

    #Calculate the number of rows
    number_of_rows = len(tiles)

    #Loop through each row by index
    for row_pos in range(number_of_rows):

        #Obtain the current row
        row = tiles[row_pos]

        #Calculate the number of columns
        number_of_cols = len(row)

        #Loop through each column by index
        for col_pos in range(number_of_cols):

            #Obtain the current column
            tile = row[col_pos]

            #Handle finding a laser
            if tile in LASERS:

                #Return the laser's position
                return row_pos, col_pos

def get_tile(tiles, pos):
    """
        Retrieves a tile at the position specified.

        Keyword arguments:
        pos --- a row/column position of the tile
        tiles --- row/column based version of a board containing symbolic
                  versions of the tiles (walls, laser, target, etc)
    """

    #Obtain the row position
    row_pos = pos[ROW]

    #Obtain the column position
    col_pos = pos[COLUMN]

    #Obtain the row
    row = tiles[row_pos]

    #Obtain the tile
    tile = row[col_pos]

    #Return the tile
    return tile

def get_wall_pos(tiles, reverse=False):
    """
        Keyword arguments:
        tiles --- row/column based version of a board containing symbolic
                  versions of the tiles (walls, laser, target, etc)
        reverse --- whether to search in reverse order or not (defaults to no)
    """

    number_of_rows = len(tiles)

    row_iter = range(number_of_rows)

    if reverse:
        row_iter = reversed(row_iter)

    for row_pos in row_iter:
        row = tiles[row_pos]

        number_of_cols = len(row)

        col_iter = range(number_of_cols)

        if reverse:
            col_iter = reversed(col_iter)

        for col_pos in col_iter:
            tile = row[col_pos]

            if tile == SOLID_WALL:
                pos = row_pos, col_pos

                if reverse:
                    offset = -1
                else:
                    offset = 1

                for axis in ROW, COLUMN:
                    next_pos = list(pos)

                    next_pos[axis] += offset

                    try:
                        next_tile = get_tile(tiles, next_pos)
                    except IndexError:
                        next_tile = None

                    if next_tile != SOLID_WALL:
                        raise WallOutsideRoomError(row_pos, col_pos)

                return pos

def identify_tile(tile):
    """
        Returns a symbolic value for every identified tile or None.

        Keyword arguments:
        tile --- the tile to identify
    """

    #Safely lookup the tile
    try:

        #Return known tiles
        return TILES[tile]

    #Handle unknown tiles
    except KeyError:

        #Return a default value
        return

def main():
    """
        Takes a board from STDIN and either returns a result to STDOUT or an
        error to STDERR.

        Called when this file is run on the command line.
    """

    #As this function is the only one to use this module, and it can only be
    #called once in this configuration, it makes sense to only import it here.
    import sys

    #Reads the board from standard input.
    board = sys.stdin.read()

    #Safely handles outside input
    try:

        #Calculates the result of shooting the laser
        result = shoot_laser(board)

    #Handles multiple item errors
    except (MultipleLaserError, MultipleTargetError) as error:

        #Display the error
        sys.stderr.write("%s\n" % str(error))

        #Loop through all the duplicated item symbols
        for symbol in error.symbols:

            #Highlight each symbol in green
            board = board.replace(symbol, "\033[01;31m%s\033[m" % symbol)

        #Display the board
        sys.stderr.write("%s\n" % board)

        #Exit with an error signal
        sys.exit(1)

    #Handles item missing errors
    except (NoLaserError, NoTargetError) as error:

        #Display the error
        sys.stderr.write("%s\n" % str(error))

        #Display the board
        sys.stderr.write("%s\n" % board)

        #Exit with an error signal
        sys.exit(1)

    #Handles errors caused by symbols
    except (OutsideRoomError, WallNotRectangleError) as error:

        #Displays the error
        sys.stderr.write("%s\n" % str(error))

        lines = board.split("\n")

        line = lines[error.row_pos]

        before = line[:error.col_pos]

        after = line[error.col_pos + 1:]

        symbol = line[error.col_pos]

        line = "%s\033[01;31m%s\033[m%s" % (before, symbol, after)

        lines[error.row_pos] = line

        board = "\n".join(lines)

        #Display the board
        sys.stderr.write("%s\n" % board)

        #Exit with an error signal
        sys.exit(1)

    #Handles errors caused by non-solid walls
    except WallNotSolidError as error:

        #Displays the error
        sys.stderr.write("%s\n" % str(error))

        lines = board.split("\n")

        line = lines[error.row_pos]

        before = line[:error.col_pos]

        after = line[error.col_pos + 1:]

        symbol = line[error.col_pos]

        line = "%s\033[01;5;31m#\033[m%s" % (before, after)

        lines[error.row_pos] = line

        board = "\n".join(lines)

        #Display the board
        sys.stderr.write("%s\n" % board)

        #Exit with an error signal
        sys.exit(1)

    #If a result was returned
    else:

        #Converts the result into a string
        result_str = str(result)

        #Makes the string lowercase
        lower_result = result_str.lower()

        #Returns the result
        sys.stdout.write("%s\n" % lower_result)

def parse_board(board):
    """
        Interprets the raw board syntax and returns a grid of tiles.

        Keyword arguments:
        board --- the board containing the tiles (walls, laser, target, etc)
    """

    #Create a container for all the lines
    tiles = list()

    #Loop through all the lines of the board
    for line in board.split("\n"):

        #Identify all the tiles on the line 
        row = [identify_tile(tile) for tile in line]

        #Add the row to the container
        tiles.append(row)

    #Return the container
    return tiles

def reflect(mirror, direction):
    """
        Returns an updated laser direction after it has been reflected on a
        mirror.

        Keyword arguments:
        mirror --- the mirror to reflect the laser from
        direction --- the direction the laser is travelling in
    """

    try:
        direction_lookup = REFLECTIONS[mirror]
    except KeyError:
        raise TypeError("%s is not a mirror.", mirror)

    try:
        return direction_lookup[direction]
    except KeyError:
        raise TypeError("%s is not a direction.", direction)

def shoot_laser(board):
    """
        Shoots the boards laser and returns whether it will hit the target.

        Keyword arguments:
        board --- the board containing the tiles (walls, laser, target, etc)
    """

    tiles = parse_board(board)

    validate_board(tiles)

    return does_laser_hit_target(tiles)

def validate_board(tiles):
    """
        Checks an board to see if it is valid and raises an exception if not.

        Keyword arguments:
        tiles --- row/column based version of a board containing symbolic
                  versions of the tiles (walls, laser, target, etc)
    """

    found_laser = False
    found_target = False

    try:
        n_wall, w_wall = get_wall_pos(tiles)
        s_wall, e_wall = get_wall_pos(tiles, reverse=True)
    except TypeError:
        n_wall = e_wall = s_wall = w_wall = None

    number_of_rows = len(tiles)

    for row_pos in range(number_of_rows):
        row = tiles[row_pos]

        number_of_cols = len(row)

        for col_pos in range(number_of_cols):

            tile = row[col_pos]

            if ((row_pos in (n_wall, s_wall) and
                 col_pos in range(w_wall, e_wall))
                or
                (col_pos in (e_wall, w_wall) and
                 row_pos in range(n_wall, s_wall))):
                if tile != SOLID_WALL:
                    raise WallNotSolidError(row_pos, col_pos)
            elif (n_wall != None and
                  (row_pos < n_wall or
                   col_pos > e_wall or
                   row_pos > s_wall or
                   col_pos < w_wall)):

                if tile in LASERS:
                    raise LaserOutsideRoomError(row_pos, col_pos)
                elif tile == TARGET:
                    raise TargetOutsideRoomError(row_pos, col_pos)
                elif tile == SOLID_WALL:
                    if not (row_pos >= n_wall and
                            col_pos <= e_wall and
                            row_pos <= s_wall and
                            col_pos >= w_wall):
                        raise WallOutsideRoomError(row_pos, col_pos)
            else:
                if tile in LASERS:
                    if not found_laser:
                        found_laser = True
                    else:
                        raise MultipleLaserError(row_pos, col_pos)
                elif tile == TARGET:
                    if not found_target:
                        found_target = True
                    else:
                        raise MultipleTargetError(row_pos, col_pos)

    if not found_laser:
        raise NoLaserError(tiles)

    if not found_target:
        raise NoTargetError(tiles)



class LasersError(Exception):
    """Parent Error Class for all errors raised."""

    pass

class NoLaserError(LasersError):
    """Indicates that there are no lasers on the board."""

    symbols = "^v><"

    def __str__ (self):
        return "No laser (%s) to fire." % ", ".join(self.symbols)

class NoTargetError(LasersError):
    """Indicates that there are no targets on the board."""

    symbols = "x"

    def __str__ (self):
        return "No target (%s) to hit." % ", ".join(self.symbols)

class MultipleLaserError(LasersError):
    """Indicates that there is more than one laser on the board."""

    symbols = "^v><"

    def __str__ (self):
        return "Too many lasers (%s) to fire, only one is allowed." % \
               ", ".join(self.symbols)

class MultipleTargetError(LasersError):
    """Indicates that there is more than one target on the board."""

    symbols = "x"

    def __str__ (self):
        return "Too many targets (%s) to hit, only one is allowed." % \
               ", ".join(self.symbols)

class WallNotSolidError(LasersError):
    """Indicates that the perimeter wall is not solid."""

    __slots__ = ("__row_pos", "__col_pos", "n_wall", "s_wall", "e_wall",
                 "w_wall")

    def __init__(self, row_pos, col_pos):
        self.__row_pos = row_pos
        self.__col_pos = col_pos

    def __str__ (self):
        return "Walls must form a solid rectangle."

    def __get_row_pos(self):
        return self.__row_pos

    def __get_col_pos(self):
        return self.__col_pos

    row_pos = property(__get_row_pos)
    col_pos = property(__get_col_pos)

class WallNotRectangleError(LasersError):
    """Indicates that the perimeter wall is not a rectangle."""

    __slots__ = ("__row_pos", "__col_pos")

    def __init__(self, row_pos, col_pos):
        self.__row_pos = row_pos
        self.__col_pos = col_pos

    def __str__ (self):
        return "Walls must form a rectangle."

    def __get_row_pos(self):
        return self.__row_pos

    def __get_col_pos(self):
        return self.__col_pos

    row_pos = property(__get_row_pos)
    col_pos = property(__get_col_pos)

class OutsideRoomError(LasersError):
    """Indicates an item is outside of the perimeter wall."""

    __slots__ = ("__row_pos", "__col_pos", "__name")

    def __init__(self, row_pos, col_pos, name):
        self.__row_pos = row_pos
        self.__col_pos = col_pos
        self.__name = name

    def __str__ (self):
        return "A %s was found outside of a 'room'." % self.__name

    def __get_row_pos(self):
        return self.__row_pos

    def __get_col_pos(self):
        return self.__col_pos

    row_pos = property(__get_row_pos)
    col_pos = property(__get_col_pos)

class LaserOutsideRoomError(OutsideRoomError):
    """Indicates the laser is outside of the perimeter wall."""

    def __init__ (self, row_pos, col_pos):
        OutsideRoomError.__init__(self, row_pos, col_pos, "laser")

class TargetOutsideRoomError(OutsideRoomError):
    """Indicates the target is outside of the perimeter wall."""

    def __init__ (self, row_pos, col_pos):
        OutsideRoomError.__init__(self, row_pos, col_pos, "target")

class WallOutsideRoomError(OutsideRoomError):
    """Indicates that there is a wall outside of the perimeter wall."""

    def __init__ (self, row_pos, col_pos):
        OutsideRoomError.__init__(self, row_pos, col_pos, "wall")



if __name__ == "__main__":
    main()

Um script do bash para exibir o relatório de erros de cores:

#!/bin/bash

declare -a TESTS

test() {
    echo -e "\033[1m$1\033[0m"
    tput sgr0
    echo "$2" | ./lasers.py
    echo
}

test \
"no laser" \
"    ##########
    #     x  #
    # /      #
    #       /#
    #   \\    #
    ##########"

test \
"multiple lasers" \
"    ##########
    #   v x  #
    # /      #
    #       /#
    #   \\  ^ #
    ##########"

test \
"no target" \
"    ##########
    #   v    #
    # /      #
    #       /#
    #   \\    #
    ##########"

test \
"multiple targets" \
"    ##########
    #   v x  #
    # /      #
    #       /#
    #   \\  x #
    ##########"

test \
"wall not solid" \
"    ##### ####
    #   v x  #
    # /      #
    #       /#
    #   \\    #
    ##########"

test \
"laser_outside_room" \
"    ##########
 >  #     x  #
    # /      #
    #       /#
    #   \\    #
    ##########"

test \
"laser before room" \
" >  ##########
    #     x  #
    # /      #
    #       /#
    #   \\    #
    ##########"

test \
"laser row before room" \
"   >
    ##########
    #     x  #
    # /      #
    #       /#
    #   \\    #
    ##########"

test \
"laser after room" \
"    ##########
    #     x  #
    # /      #
    #       /#
    #   \\    #
    ##########  >"

test \
"laser row after room" \
"    ##########
    #     x  #
    # /      #
    #       /#
    #   \\    #
    ##########
  > "

test \
"target outside room" \
"    ##########
 x  #   v    #
    # /      #
    #       /#
    #   \\    #
    ##########"

test \
"target before room" \
" x  ##########
    #   v    #
    # /      #
    #       /#
    #   \\    #
    ##########"

test \
"target row before room" \
"   x
    ##########
    #   v    #
    # /      #
    #       /#
    #   \\    #
    ##########"

test \
"target after room" \
"    ##########
    #   v    #
    # /      #
    #       /#
    #   \\    #
    ##########   x"

test \
"target row after room" \
"    ##########
    #   v    #
    # /      #
    #       /#
    #   \\    #
    ##########
  x "

test \
"wall outside room" \
"    ##########
 #  #   v    #
    # /      #
    #       /#
    #   \\  x #
    ##########"

test \
"wall before room" \
" #  ##########
    #   v    #
    # /      #
    #       /#
    #   \\  x #
    ##########"

test \
"wall row before room" \
"    #
    ##########
    #   v    #
    # /      #
    #       /#
    #   \\  x #
    ##########"

test \
"wall after room" \
"    ##########
    #   v    #
    # /      #
    #       /#
    #   \\  x #
    ########## #"

test \
"wall row after room" \
"    ##########
    #   v    #
    # /      #
    #       /#
    #   \\  x #
    ##########
  #"

test \
"mirror outside room positive" \
"    ##########
 /  #   / \\  #
    #        #
    #   \\   x#
    # >   /  #
    ########## "

test \
"mirrors outside room negative" \
"    ##########
 \\  #   v x  #
    # /      #
    #       /#
    #   \\    #
    ##########"

test \
"mirror before room positive" \
" \\  ##########
    #   / \\  #
    #        #
    #   \\   x#
    # >   /  #
    ########## "

test \
"mirrors before room negative" \
" /  ##########
    #   v x  #
    # /      #
    #       /#
    #   \\    #
    ##########"

test \
"mirror row before room positive" \
"     \\
    ##########
    #   / \\  #
    #        #
    #   \\   x#
    # >   /  #
    ########## "

test \
"mirrors row before room negative" \
"     \\
    ##########
    #   v x  #
    # /      #
    #       /#
    #   \\    #
    ##########"

test \
"mirror after row positive" \
"    ##########
    #   / \\  #
    #        #
    #   \\   x#
    # >   /  #
    ########## /  "

test \
"mirrors after row negative" \
"    ##########
    #   v x  #
    # /      #
    #       /#
    #   \\    #
    ##########   /  "

test \
"mirror row after row positive" \
"    ##########
    #   / \\  #
    #        #
    #   \\   x#
    # >   /  #
    ########## 
 /  "

test \
"mirrors row after row negative" \
"    ##########
    #   v x  #
    # /      #
    #       /#
    #   \\    #
    ########## 
 /  "

test \
"laser hitting laser" \
"    ##########
    #   v   \\#
    #        #
    #        #
    #x  \\   /#
    ##########"

test \
"mirrors positive" \
"    ##########
    #   / \\  #
    #        #
    #   \\   x#
    # >   /  #
    ########## "

test \
"mirrors negative" \
"    ##########
    #   v x  #
    # /      #
    #       /#
    #   \\    #
    ##########"

test \
"wall collision" \
"    #############
    #     #     #
    # >   #     #
    #     #     #
    #     #   x #
    #     #     #
    #############"

test \
"extreme example" \
"    ##########
    #/\\/\\/\\  #
    #\\\\//\\\\\\ #
    #//\\/\\/\\\\#
    #\\/\\/\\/x^#
    ##########"

test \
"brian example 1" \
"##########
#   / \\  #
#        #
#/    \\ x#
#\\>   /  #
##########"

test \
"brian example 2" \
"##########
#  /    \\#
# / \\    #
#/    \\ x#
#\\^/\\ /  #
##########"

Os unittests usados ​​no desenvolvimento:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import unittest

from lasers import *

class TestTileRecognition(unittest.TestCase):
    def test_solid_wall(self):
        self.assertEqual(SOLID_WALL, identify_tile("#"))

    def test_target(self):
        self.assertEqual(TARGET, identify_tile("x"))

    def test_mirror_ne_sw(self):
        self.assertEqual(MIRROR_NE_SW, identify_tile("/"))

    def test_mirror_nw_se(self):
        self.assertEqual(MIRROR_NW_SE, identify_tile("\\"))

    def test_laser_down(self):
        self.assertEqual(LASER_DOWN, identify_tile("v"))

    def test_laser_up(self):
        self.assertEqual(LASER_UP, identify_tile("^"))

    def test_laser_right(self):
        self.assertEqual(LASER_RIGHT, identify_tile(">"))

    def test_laser_left(self):
        self.assertEqual(LASER_LEFT, identify_tile("<"))

    def test_other(self):
        self.assertEqual(None, identify_tile(" "))

class TestReflection(unittest.TestCase):
    def setUp(self):
        self.DIRECTION = LEFT
        self.NOT_DIRECTIO

6
A física do laser determina que o feixe possa se cruzar. O comentário acima é uma referência cultural importante.
dmckee --- ex-moderador gatinho

5
Tartaruga e a abordagem Hare para codificar golfe. Entregue algo com obviamente muitos caracteres (91x mais que o atual vencedor), mas preste atenção em todas as letras da especificação. Lento e constante, geralmente me dá menos contrato de trabalho.
Metalshark 28/09/09

Seu mais unido parece estar faltando alguma parte. É cortado em "self.NOT_DIRECTIO"
BioGeek

@BioGeek - atinja o limite de como o tamanho da postagem;). Além dos testes no estilo BASH, o realce das cores é exibido.
Metalshark 3/08

11

Ruby, 176 caracteres

x=!0;y=0;e="^v<>#x";b=readlines;b.map{|l|(x||=l=~/[v^<>]/)||y+=1};c=e.index(b[y][x])
loop{c<2&&y+=c*2-1;c>1&&x+=2*c-5;e.index(n=b[y][x])&&(p n==?x;exit);c^='  \/'.index(n)||0}

Eu usei uma máquina de estado simples (como a maioria dos pôsteres), nada extravagante. Eu apenas continuei usando todos os truques que consegui pensar. O XOR bit a bit usado para mudar de direção (armazenado como um número inteiro na variável c) foi uma grande melhoria em relação aos condicionais que eu tinha nas versões anteriores.

Suspeito que o código que aumente xe ypossa ser reduzido. Aqui está a seção do código que faz o incremento:

c<2&&y+=c*2-1;c>1&&x+=(c-2)*2-1

Edit : Consegui reduzir um pouco o acima:

c<2&&y+=c*2-1;c>1&&x+=2*c-5

A direção atual do laser cé armazenada da seguinte forma:

0 => para cima
1 => baixo
2 => esquerda
3 => direita

O código depende desse fato para incrementar xe ypela quantidade correta (0, 1 ou -1). Tentei reorganizar quais números são mapeados para cada direção, procurando um arranjo que me permitisse fazer alguma manipulação bit a bit para aumentar os valores, porque tenho uma sensação incômoda de que seria mais curto que a versão aritmética.


9

C # 3.0

259 caracteres

bool S(char[]m){var w=Array.FindIndex(m,x=>x<11)+1;var s=Array.FindIndex(m,x=>x>50&x!=92&x<119);var t=m[s];var d=t<61?-1:t<63?1:t<95?-w:w;var u=0;while(0<1){s+=d;u=m[s];if(u>119)return 0<1;if(u==47|u==92)d+=d>0?-w-1:w+1;else if(u!=32)return 0>1;d=u>47?-d:d;}}

Um pouco mais legível:

bool Simulate(char[] m)
{
    var w = Array.FindIndex(m, x => x < 11) + 1;
    var s = Array.FindIndex(m, x => x > 50 & x != 92 & x < 119);
    var t = m[s];
    var d = t < 61 ? -1 : t < 63 ? 1 : t < 95 ? -w : w;
    var u = 0;
    while (0 < 1)
    {
        s += d;
        u = m[s];
        if (u > 119)
            return 0 < 1;
        if (u == 47 | u == 92)
            d += d > 0 ? -w - 1 : w + 1;
        else if (u != 32)
            return 0 > 1;
        d = u > 47 ? -d : d;
    }
}

O principal desperdício de caracteres parece estar em encontrar a largura do mapa e a posição da fonte do laser. Alguma idéia de como encurtar isso?


Não tenho certeza se isso é mais curto, mas é minha chance de encontrar o laser e a largura: usando L = List <string>; usando P = System.Drawing.Point; usando L = List <string>; L r = new L () {"v", "<", ">", "^"}; P p = novo P (); r.ForEach (a => {int c = 0; v.ForEach (s => {c ++ ; if (s.IndexOf (a)! = - 1) {pX = s.IndexOf (a); pY = c;}});}); int l = v [0]. v é um List <string> que contém a tabela, e gera um ponto que representa posição do laser + um int representando largura
RCIX

melhor: usando L = List <string>; Ll = new L (4) {"v", "<", ">", "^"}; var point = new {x = 0, y = 0}; int c = 0; l.ForEach (a => {m.ForEach (s => {if (s.IndexOf (a)! = - 1) {point = new {x = s.IndexOf (a), y = c};}}); c ++;}); int w = m [0] .Length;
RCIX

4
Problemas pede um programa completo, não uma função.
Strager 27/09/09

que tal # while(1)
SSpoke

9

C + ASCII, 197 caracteres:

G[999],*p=G,w,z,t,*b;main(){for(;(*p++=t=getchar()^32)>=0;w=w|t-42?w:p-G)z=t^86?t^126?t^28?t^30?z:55:68:56:75,b=z?b:p;for(;t=z^55?z^68?z^56?z^75?0:w:-w:-1:1;z^=*b)b+=t;puts(*b^88?"false":"true");}

Esta solução C assume um conjunto de caracteres ASCII, permitindo o uso do truque do espelho XOR. Também é incrivelmente frágil - todas as linhas de entrada devem ter o mesmo comprimento, por exemplo.

Ele quebra abaixo da marca de 200 caracteres - mas, porra, ainda não derrotou as soluções Perl!


= O! +1! Grats em me bater. =]
strager

2
A maioria das boas soluções aqui faz a suposição "todas as linhas têm o mesmo comprimento". Tudo é justo no golfe e na guerra.
hobbs

Se fosse necessário que as linhas não tivessem o mesmo comprimento, adicionaria um caso de teste. mas eu disse claramente que foi intencional :)
LiraNuna

9

Golfscript (83 caracteres)

Olá, gnibbler!

:\'><v^'.{\?}%{)}?:P@=?{:O[1-1\10?).~)]=P+
:P\=' \/x'?[O.2^.1^'true''false']=.4/!}do

3
golfscript: perl ~ = 1: 1,7
John La Rooy

9

Python - 152

Lê a entrada de um arquivo chamado "L"

A=open("L").read()
W=A.find('\n')+1
D=P=-1
while P<0:D+=1;P=A.find(">^<v"[D])
while D<4:P+=[1,-W,-1,W][D];D=[D,D^3,D^1,4,5][' \/x'.find(A[P])]
print D<5

Para ler a partir de stdin, substitua a primeira linha por esta

import os;A=os.read(0,1e9)

Se você precisar de minúsculas verdadeiro / falso, altere a última linha para

print`D<5`.lower()

Quantos caracteres são necessários para mudar Truepara truee Falsepara false? ;-)
mob

Não foi possível remover 1 caractere alterando "print D<5" para "print D <5"? Ou há algo que estou perdendo?
Ponkadoodle

@wallacoloo, com certeza pode. É apenas necessário para o minúsculas verdadeiro / falso
John La Rooy

7

JavaScript - 265 caracteres

Atualização IV - As chances são de que esta será a última rodada de atualizações, conseguiu salvar mais alguns caracteres alternando para um loop do-while e reescrevendo a equação do movimento.

Atualização III - Graças à sugestão do strager em remover o Math.abs () e colocar as variáveis ​​no espaço de nomes global, que, juntamente com alguma reorganização das atribuições de variáveis, reduziu o código para 282 caracteres.

Atualização II - Mais algumas atualizações no código para remover o uso de! = -1, bem como um melhor uso de variáveis ​​para operações mais longas.

Atualização - Ao concluir e fazer algumas alterações, crie uma referência à função indexOf (obrigado LiraNuna!) E remova os parênteses que não eram necessários.

Esta é minha primeira vez em um código de golfe, então não tenho certeza de quanto isso poderia ser melhor, qualquer feedback é apreciado.

Versão totalmente minimizada:

a;b;c;d;e;function f(g){a=function(a){return g.indexOf(a)};b=a("\n")+1;a=g[c=e=a("v")>0?e:e=a("^")>0?e:e=a("<")>0?e:a(">")];d=a=="<"?-1:a==">"?1:a=="^"?-b:b;do{e=d==-1|d==1;a=g[c+=d=a=="\\"?e?b*d:d>0?1:-1:a=="/"?e?-b*d:d>0?1:-1:d];e=a=="x"}while(a!="#"^e);return e}

Versão original com comentários:

character; length; loc; movement; temp;
function checkMaze(maze) {
        // Use a shorter indexOf function
        character = function(string) { return maze.indexOf(string); }
        // Get the length of the maze
        length = character("\n") + 1;
        // Get the location of the laser in the string
        character = maze[loc = temp = character("v") > 0 ? temp :
                               temp = character("^") > 0 ? temp :
                               temp = character("<") > 0 ? temp : character(">")];
        // Get the intial direction that we should travel
        movement = character == "<" ? -1 :
                   character == ">" ? 1 :
                   character == "^" ? -length : length;
        // Move along until we reach the end
        do {
            // Get the current character
            temp = movement == -1 | movement == 1;
            character = maze[loc += movement = character == "\\" ? temp ? length * movement : movement > 0 ? 1 : -1 :
                                               character == "/" ? temp ? -length * movement : movement > 0 ? 1 : -1 : movement];                                   
            // Have we hit a target?
            temp = character == "x";
            // Have we hit a wall?
        } while (character != "#" ^ temp);
        // temp will be false if we hit the target
        return temp;
    }

Página da Web para testar:

<html>
  <head>
    <title>Code Golf - Lasers</title>
    <script type="text/javascript">
    a;b;c;d;e;function f(g){a=function(a){return g.indexOf(a)};b=a("\n")+1;a=g[c=e=a("v")>0?e:e=a("^")>0?e:e=a("<")>0?e:a(">")];d=a=="<"?-1:a==">"?1:a=="^"?-b:b;do{e=d==-1|d==1;a=g[c+=d=a=="\\"?e?b*d:d>0?1:-1:a=="/"?e?-b*d:d>0?1:-1:d];e=a=="x"}while(a!="#"^e);return e}
    </script>
  </head>
  <body>
    <textarea id="maze" rows="10" cols="10"></textarea>
    <button id="checkMaze" onclick="alert(f(document.getElementById('maze').value))">Maze</button>
  </body>
</html>

como é preciso receber informações? Quero testar e verificar isso. Além disso, você pode salvar um monte de personagens se você salvar um ref para a.indexOf
LiraNuna

Substitua index != -1por index > 0favor! (Espero que ninguém coloque o lazer no canto superior esquerdo para 0que não seja retornado. =]) Você pode encadear as varinstruções ou se livrar delas completamente (colocando as variáveis ​​no espaço de nomes global). Eu acho que Math.abs(m)==1pode ser substituído por m==-1|m==1. Pode movement = ...; location += movementser otimizado para location += movement =?
Strager 28/09/09

@ strager- Acabei de ver seu comentário, parece que você o postou enquanto eu atualizava o código, com até 300 caracteres. Vou ver o que posso fazer com a eliminação do Math.abs ().
rjzii 28/09/09

function(a){return g.indexOf(a)}pode ser substituído por function(a)g.indexOf(a)nas versões recentes do JavaScript.
user1686

6

Casa dos Espelhos

Não é uma entrada real para o desafio, mas escrevi um jogo baseado nesse conceito (não muito tempo atrás).

Está escrito em Scala, de código aberto e disponível aqui :

Faz um pouco mais; lida com cores e vários tipos de espelhos e dispositivos, mas a versão 0.00001 fez exatamente o que esse desafio pede. Porém, perdi essa versão e ela nunca foi otimizada para a contagem de caracteres.


Seria possível fazer o upload de uma versão compilada que funcione no Windows sem precisar instalar o scala?
Milão

Existe uma versão com as bibliotecas Scala incluídas. Veja a lista de downloads. Mas de qualquer maneira, se você tiver instalado Scala por agora, estou feliz que te tenho de fazer isso :)
HRJ

6

c (K&R) 339 caracteres necessários após mais sugestões do strager.

O físico em mim observou que as operações de propagação e reflexão são invariantes com inversão de tempo; portanto, esta versão lança raios do alvo e verifica se eles chegam ao emissor do laser.

O restante da implementação é muito direto e é retirado mais ou menos exatamente do meu esforço anterior.

Comprimido:

#define R return
#define C case
#define Z x,y
int c,i,j,m[99][99],Z;s(d,e,Z){for(;;)switch(m[x+=d][y+=e]){C'^':R 1==e;
C'>':R-1==d;C'v':R-1==e;C'<':R 1==d;C'#':C'x':R 0;C 92:e=-e;d=-d;C'/':c=d;
d=-e;e=-c;}}main(){while((c=getchar())>0)c==10?i=0,j++:(c==120?x=i,y=j:
i,m[i++][j]=c);puts(s(1,0,Z)|s(0,1,Z)|s(-1,0,Z)|s(0,-1,Z)?"true":"false");}

Não compactado (ish):

#define R return
#define C case
#define Z x,y
int c,i,j,m[99][99],Z;
s(d,e,Z)
{
  for(;;)
    switch(m[x+=d][y+=e]){
    C'^': 
      R 1==e;
    C'>': 
      R-1==d;
    C'v': 
      R-1==e;
    C'<': 
      R 1==d;
    C'#':
    C'x':
      R 0;
    C 92:
      e=-e;
      d=-d;
    C'/':
      c=d;
      d=-e;
      e=-c;
    }
}
main(){
  while((c=getchar())>0)
    c==10?i=0,j++:
      (c==120?x=i,y=j:i,m[i++][j]=c);
  puts(s(1,0,Z)|s(0,1,Z)|s(-1,0,Z)|s(0,-1,Z)?"true":"false");
}

Não há validação de entrada e entradas ruins podem enviá-las para um loop infinito. Funciona corretamente com entradas não maiores que 99 por 99. Requer um compilador que vinculará a biblioteca padrão sem incluir nenhum dos cabeçalhos. E eu acho que terminei, o strager me fez bater bastante, mesmo com a ajuda dele.

Espero que alguém demonstre uma maneira mais sutil de realizar a tarefa. Não há nada de errado nisso, mas não é uma mágica profunda.


Não há necessidade de =0nos globais, pois eles são inicializados em 0 por padrão. Substitua constantes de caracteres pelo seu equivalente em decimal. Use em >0vez de !=EOFpara verificar o EOF (e \0). Provavelmente você pode #defineafastar parte do código casecomo eu fiz com ifo. Não há necessidade de extra \nno putscomo putsdeve imprimir uma nova linha de qualquer maneira. for(;;)é mais curto que while(1). Espero que isto ajude. =]
strager

@ strager: Obrigado. Eu sempre vir a estes de forma iterativa, porque eu não acho que dessa forma ...
dmckee --- ex-moderador gatinho

2
"There is no input validation"- Não deveria haver nenhum. Para facilitar as coisas para os golfistas, presume-se que a entrada seja sempre 'limpa', a menos que especificado de outra forma.
LiraNuna

@dmckee, não se preocupe, os profissionais do Code Golf também trabalham iterativamente. No entanto, geralmente usamos alguns truques desde o início (como metade dos que eu mencionei), mas isso vem com a experiência. =]
strager

A menos que eu estou contando errado, o programa é de 390 caracteres, não 380.
strager

6

Ruby - 146 Chars

A=$<.read
W=A.index('
')+1
until
q=A.index(">^<v"[d=d ?d+1:0])
end
while d<4
d=[d,d^3,d^1,4,5][(' \/x'.index(A[q+=[1,-W,-1,W][d]])or 4)]
end
p 5>d

5

PostScript , 359 bytes

Primeira tentativa, muito espaço para melhorias ...

/a[{(%stdin)(r)file 99 string readline not{exit}if}loop]def a{{[(^)(>)(<)(v)]{2
copy search{stop}if pop pop}forall}forall}stopped/r count 7 sub def pop
length/c exch def[(>)0(^)1(<)2(v)3>>exch get/d exch def{/r r[0 -1 0 1]d get
add def/c c[1 0 -1 0]d get add def[32 0 47 1 92 3>>a r get c get .knownget
not{exit}if/d exch d xor def}loop a r get c get 120 eq =

4

Haskell, 395 391 383 361 339 caracteres (optimizado)

Ainda usa uma máquina de estado genérica, em vez de algo inteligente:

k="<>^v"
o(Just x)=x
s y(h:t)=case b of{[]->s(y+1)t;(c:_)->(c,length a,y)}where(a,b)=break(flip elem k)h
r a = f$s 0 a where f(c,x,y)=case i(a!!v!!u)"x /\\"["true",g k,g"v^><",g"^v<>"]of{Just r->r;_->"false"}where{i x y=lookup x.zip y;j=o.i c k;u=j[x-1,x+1,x,x];v=j[y,y,y-1,y+1];g t=f(j t,u,v)}
main=do{z<-getContents;putStrLn$r$lines z}

Uma versão legível:

k="<>^v"    -- "key" for direction
o(Just x)=x -- "only" handle successful search
s y(h:t)=case b of  -- find "start" state
  []->s(y+1)t
  (c:_)->(c,length a,y)
 where (a,b)=break(flip elem k)h
r a = f$s 0 a where -- "run" the state machine (iterate with f)
 f(c,x,y)=case i(a!!v!!u)"x /\\"["true",g k,g"v^><",g"^v<>"] of
   Just r->r
   _->"false"
  where
   i x y=lookup x.zip y -- "index" with x using y as key
   j=o.i c k -- use c as index k as key; assume success
   u=j[x-1,x+1,x,x] -- new x coord
   v=j[y,y,y-1,y+1] -- new y coord
   g t=f(j t,u,v) -- recurse; use t for new direction
main=do
 z<-getContents
 putStrLn$r$lines z

3

Acredito na reutilização de código, eu usaria um de seu código como uma API :).

  coloca Board.new.validate (entrada)

32 caracteres \ o / ... wohoooo


6
isso é um truque duplo!
Jeff Atwood

3
Vencê-lo: p Board.new.validate insere 26 caracteres \ o / #
Alessandra Pereyra

3

C ++: 388 caracteres

#include<iostream>
#include<string>
#include<deque>
#include<cstring>
#define w v[y][x]
using namespace std;size_t y,x,*z[]={&y,&x};int main(){string p="^v<>",s;deque<string>v;
while(getline(cin,s))v.push_back(s);while(x=v[++y].find_first_of(p),!(x+1));int 
i=p.find(w),d=i%2*2-1,r=i/2;do while(*z[r]+=d,w=='/'?d=-d,0:w==' ');while(r=!r,
!strchr("#x<^v>",w));cout<<(w=='x'?"true":"false");}

( 318 sem cabeçalhos)


Como funciona:

Primeiro, todas as linhas são lidas e, em seguida, o laser é encontrado. O seguinte será avaliado 0enquanto nenhuma seta do laser for encontrada ainda e, ao mesmo tempo, será atribuído à xposição horizontal.

x=v[++y].find_first_of(p),!(x+1)

Então, olhamos em que direção encontramos e a armazenamos i. Os valores ipares de são superior / esquerdo ("decrescente") e os valores ímpares são inferior / direito ("crescente"). De acordo com essa noção, d("direção") e r("orientação") são definidos. Indexamos a matriz de ponteiros zcom orientação e adicionamos a direção ao número inteiro que obtemos. A direção muda apenas se atingirmos uma barra, enquanto permanece a mesma quando atingimos uma barra invertida. Obviamente, quando atingimos um espelho, sempre mudamos a orientação ( r = !r).


Você está me fazendo fazer minha própria solução C ++. =]
strager

2
@strager, isso está ficando chato. Vamos fazer uma solução que exibe "verdadeiro" ou "falso" em tempo de compilação xD
Johannes Schaub - litb

explicação adicional desde que eu acho que vai mantê-lo neste :)
Johannes Schaub - litb

2

Caracteres Groovy @ 279

m=/[<>^v]/
i={'><v^'.indexOf(it)}
n=['<':{y--},'>':{y++},'^':{x--},'v':{x++}]
a=['x':{1},'\\':{'v^><'[i(d)]},'/':{'^v<>'[i(d)]},'#':{},' ':{d}]
b=[]
System.in.eachLine {b<<it.inject([]) {r,c->if(c==~m){x=b.size;y=r.size;d=c};r<<c}}
while(d==~m){n[d]();d=a[b[x][y]]()}
println !!d

2

C #

1020 caracteres.
1088 caracteres (entrada adicionada do console).
925 caracteres (variáveis ​​refatoradas).
875 caracteres (inicializador de dicionário redundante removido; alterado para Binário e operadores)

Fez questão de não olhar para as outras pessoas antes de postar. Tenho certeza de que poderia ser um pouco LINQ. E todo o método FindLaser na versão legível parece terrivelmente suspeito para mim. Mas, funciona e é tarde :)

Observe que a classe legível inclui um método adicional que imprime a Arena atual à medida que o laser se move.

class L{static void Main(){
A=new Dictionary<Point,string>();
var l=Console.ReadLine();int y=0;
while(l!=""){var a=l.ToCharArray();
for(int x=0;x<a.Count();x++)
A.Add(new Point(x,y),l[x].ToString());
y++;l=Console.ReadLine();}new L();}
static Dictionary<Point,string>A;Point P,O,N,S,W,E;
public L(){N=S=W=E=new Point(0,-1);S.Offset(0,2);
W.Offset(-1,1);E.Offset(1,1);D();
Console.WriteLine(F());}bool F(){
var l=A[P];int m=O.X,n=O.Y,o=P.X,p=P.Y;
bool x=o==m,y=p==n,a=x&p<n,b=x&p>n,c=y&o>m,d=y&o<m;
if(l=="\\"){if(a)T(W);if(b)T(E);if(c)T(S);
if(d)T(N);if(F())return true;}
if(l=="/"){if(a)T(E);if(b)T(W);if(c)T(N);
if(d)T(S);if(F())return true;}return l=="x";}
void T(Point p){O=P;do P.Offset(p);
while(!("\\,/,#,x".Split(',')).Contains(A[P]));}
void D(){P=A.Where(x=>("^,v,>,<".Split(',')).
Contains(x.Value)).First().Key;var c=A[P];
if(c=="^")T(N);if(c=="v")T(S);if(c=="<")T(W);
if(c==">")T(E);}}

Versão legível (não exatamente a versão final do golfe, mas a mesma premissa):

class Laser
{
    private Dictionary<Point, string> Arena;
    private readonly List<string> LaserChars;
    private readonly List<string> OtherChars;

    private Point Position;
    private Point OldPosition;
    private readonly Point North;
    private readonly Point South;
    private readonly Point West;
    private readonly Point East;

    public Laser( List<string> arena )
    {
        SplitArena( arena );
        LaserChars = new List<string> { "^", "v", ">", "<" };
        OtherChars = new List<string> { "\\", "/", "#", "x" };
        North = new Point( 0, -1 );
        South = new Point( 0, 1 );
        West = new Point( -1, 0 );
        East = new Point( 1, 0 );
        FindLaser();
        Console.WriteLine( FindTarget() );
    }

    private void SplitArena( List<string> arena )
    {
        Arena = new Dictionary<Point, string>();
        int y = 0;
        foreach( string str in arena )
        {
            var line = str.ToCharArray();
            for( int x = 0; x < line.Count(); x++ )
            {
                Arena.Add( new Point( x, y ), line[x].ToString() );
            }
            y++;
        }
    }

    private void DrawArena()
    {
        Console.Clear();
        var d = new Dictionary<Point, string>( Arena );

        d[Position] = "*";
        foreach( KeyValuePair<Point, string> p in d )
        {
            if( p.Key.X == 0 )
                Console.WriteLine();

            Console.Write( p.Value );
        }
        System.Threading.Thread.Sleep( 400 );
    }

    private bool FindTarget()
    {
        DrawArena();

        string chr = Arena[Position];

        switch( chr )
        {
            case "\\":
                if( ( Position.X == Position.X ) && ( Position.Y < OldPosition.Y ) )
                {
                    OffSet( West );
                }
                else if( ( Position.X == Position.X ) && ( Position.Y > OldPosition.Y ) )
                {
                    OffSet( East );
                }
                else if( ( Position.Y == Position.Y ) && ( Position.X > OldPosition.X ) )
                {
                    OffSet( South );
                }
                else
                {
                    OffSet( North );
                }
                if( FindTarget() )
                {
                    return true;
                }
                break;
            case "/":
                if( ( Position.X == Position.X ) && ( Position.Y < OldPosition.Y ) )
                {
                    OffSet( East );
                }
                else if( ( Position.X == Position.X ) && ( Position.Y > OldPosition.Y ) )
                {
                    OffSet( West );
                }
                else if( ( Position.Y == Position.Y ) && ( Position.X > OldPosition.X ) )
                {
                    OffSet( North );
                }
                else
                {
                    OffSet( South );
                }
                if( FindTarget() )
                {
                    return true;
                }
                break;
            case "x":
                return true;
            case "#":
                return false;
        }
        return false;
    }

    private void OffSet( Point p )
    {
        OldPosition = Position;
        do
        {
            Position.Offset( p );
        } while( !OtherChars.Contains( Arena[Position] ) );
    }

    private void FindLaser()
    {
        Position = Arena.Where( x => LaserChars.Contains( x.Value ) ).First().Key;

        switch( Arena[Position] )
        {
            case "^":
                OffSet( North );
                break;
            case "v":
                OffSet( South );
                break;
            case "<":
                OffSet( West );
                break;
            case ">":
                OffSet( East );
                break;
        }
    }
}

2
O programa deve receber informações. Mais comumente de stdin.
LiraNuna 26/09/09

0

Perl 219
Minha versão perl tem 392 342 caracteres (eu tive que lidar com o caso do raio atingindo o laser):
Atualize , obrigado Hobbs por me lembrar tr//, agora são 250 caracteres:
Atualize , remova o min m//, altere os dois whileloops trazidos algumas economias; agora há apenas um espaço necessário.
( L:it;goto Ltem o mesmo comprimento que do{it;redo}):

@b=map{($y,$x,$s)=($a,$-[0],$&)if/[<>^v]/;$a++;[split//]}<>;L:$_=$s;$x++if/>/;
$x--if/</;$y++if/v/;$y--if/\^/;$_=$b[$y][$x];die"true\n"if/x/;die"false\n"if
/[<>^v#]/;$s=~tr/<>^v/^v<>/if/\\/;$s=~tr/<>^v/v^></if/\//;goto L

Eu raspei alguns, mas mal apenas compete com algumas delas, embora tarde.
Parece um pouco melhor como:

#!/usr/bin/perl
@b = map {
    ($y, $x, $s) = ($a, $-[0], $&) if /[<>^v]/;
    $a++;
    [split//]
} <>;
L:
    $_ = $s;
    $x++ if />/;
    $x-- if /</;
    $y++ if /v/;
    $y-- if /\^/;
    $_ = $b[$y][$x];
    die "true\n"  if /x/;
    die "false\n" if /[<>^v#]/;
    $s =~ tr/<>^v/^v<>/ if /\\/;
    $s =~ tr/<>^v/v^></ if /\//;
goto L

Bem ... Honestamente, isso deve ser auto-explicativo se você entender que @bé uma matriz de caracteres em cada linha e pode ler a simples expressão regular e trdeclarações.


Dica: você pode reduzir o código do espelho. $_=$s;tr/^v<>/<>^v/e $_=$s;tr/v^<>/<>^v/respectivamente. Além disso, você não precisa do mno m//.
hobbs

Desculpe, faça o segundo$_=$s;tr/v^></<>^v/;
hobbs

Você ainda tem vários if m/.../que podem estar if/.../salvando dois caracteres por pop.
hobbs

Você pode usar em y///vez de tr///salvar dois caracteres.
Platinum Azure

0

F # - 454 (ou próximo)

Um pouco atrasado para o jogo, mas não resisto a postar minha tentativa 2D.

Atualização modificada ligeiramente. Agora pára corretamente se o transmissor for atingido. Beliscou a ideia de Brian para IndexOfAny (pena que essa linha seja tão detalhada). Na verdade, não consegui descobrir como fazer com que o ReadToEnd retorne do console, por isso estou confiando um pouco ...

Estou satisfeito com esta resposta, como se ela fosse muito curta, ainda é bastante legível.

let s=System.Console.In.ReadToEnd()       //(Not sure how to get this to work!)
let w=s.IndexOf('\n')+1                   //width
let h=(s.Length+1)/w                      //height
//wodge into a 2d array
let a=Microsoft.FSharp.Collections.Array2D.init h (w-1)(fun y x -> s.[y*w+x])
let p=s.IndexOfAny[|'^';'<';'>';'v'|]     //get start pos
let (dx,dy)=                              //get initial direction
 match "^<>v".IndexOf(s.[p]) with
 |0->(0,-1)
 |1->(-1,0)
 |2->(1,0)
 |_->(0,1)
let mutable(x,y)=(p%w,p/w)                //translate into x,y coords
let rec f(dx,dy)=
 x<-x+dx;y<-y+dy                          //mutate coords on each call
 match a.[y,x] with
 |' '->f(dx,dy)                           //keep going same direction
 |'/'->f(-dy,-dx)                         //switch dx/dy and change sign
 |'\\'->f(dy,dx)                          //switch dx/dy and keep sign
 |'x'->"true"
 |_->"false"
System.Console.Write(f(dx,dy))

Eles são decoração. Verifique meus outros desafios, é apenas uma coisa de formatação.
LiraNuna

@LiraNuna, ok, como se vê, esta iteração só come-los de qualquer maneira :)
Benjol

Seria bom comparar com uma implementação 1-d. Basta adicionar / subtrair 1 para esquerda e direita e adicionar / subtrair w para cima e para baixo. Eu esperaria que você economizasse alguns caracteres
John La Rooy

@ ignibbler, Brian já fez isso, não tenho certeza se poderia vencê-lo, mas posso tentar.
Benjol 10/02/10
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.