Code Golf: Lasery


152

Wyzwanie

Najkrótszy kod pod względem liczby znaków, aby wprowadzić reprezentację 2D płytki i wyprowadzić „prawda” lub „fałsz” zgodnie z danymi wejściowymi .

Plansza składa się z 4 rodzajów płytek:

 # - 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)

Jest tylko jeden laser i tylko jeden cel . Ściany muszą tworzyć jednolity prostokąt o dowolnej wielkości, w którym umieszczony jest laser i cel. Możliwe są ściany wewnątrz „pokoju”.

Promień lasera wystrzeliwuje i przemieszcza się od początku do kierunku, w którym wskazuje. Jeśli promień lasera uderzy w ścianę, zatrzymuje się. Jeśli promień lasera uderza w lustro, odbija się o 90 stopni w kierunku, na który wskazuje lustro. Lustra są dwustronne, co oznacza, że ​​obie strony są „odblaskowe” i mogą odbijać promień na dwa sposoby. Jeśli promień lasera trafi w sam laser ( ^v><), jest traktowany jak ściana (promień lasera niszczy promień, więc nigdy nie trafi w cel).

Przypadki testowe

Wejście:
    ##########
    # / \ #
    # #
    # \ x #
    #> / #
    ########## 
Wynik:
    prawdziwe

Wejście:
    ##########
    # vx #
    # / #
    # / #
    # \ #
    ##########
Wynik:    
    fałszywe

Wejście:
    #############
    # # #
    #> # #
    # # #
    # # x #
    # # #
    #############
Wynik:
    fałszywe

Wejście:
    ##########
    # / \ / \ / \ #
    # \\ // \\\ #
    # // \ / \ / \\ #
    # \ / \ / \ / x ^ #
    ##########
Wynik:
    prawdziwe

Liczba kodów obejmuje wejście / wyjście (tj. Pełny program).


84
IMMA CHARGIN 'MAH LAZER!
Ólafur Waage

37
To jest niesamowite .
GManNickG

33

49
@GameFreak: Robi się naprawdę stary.
Artelius

24
Czy to właściwie jest rekin z cholernym lazerem na głowie?
Nathan Feger

Odpowiedzi:


78

Perl, 166160 znaków

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

Rozwiązanie miało 166 uderzeń po zakończeniu tego konkursu, ale A.Rex znalazł kilka sposobów, aby zgolić 6 dodatkowych znaków:

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,$/

Pierwsza linia ładuje dane wejściowe do %ttabeli tablicy, w której $t{99*i+j}znajduje się znak w wierszu i , kolumnie j . Następnie,

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

wyszukuje elementy lub pod %tkątem znaku, który pasuje > ^ <lub v, i jednocześnie ustawia $dwartość z zakresu od 0 do 3, która wskazuje początkowy kierunek wiązki laserowej.

Na początku każdej iteracji w głównej pętli aktualizujemy, $dczy wiązka jest aktualnie na lustrze. XOR o 3 daje prawidłowe zachowanie \lustra, a XOR o 1 daje prawidłowe zachowanie /lustra.

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

Następnie aktualna pozycja $rjest aktualizowana zgodnie z aktualnym kierunkiem.

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

Znak na bieżącej pozycji przypisujemy $_do, aby wygodnie używać operatorów dopasowania.

/[\/\\ ]/ && redo

Kontynuuj, jeśli jesteśmy na pustej przestrzeni lub postaci lustrzanej. W przeciwnym razie zakończymy, truejeśli jesteśmy w miejscu docelowym ( $_ =~ /x/) lub w falseprzeciwnym razie.

Ograniczenie: może nie działać w przypadku problemów z więcej niż 99 kolumnami. To ograniczenie mogłoby zostać usunięte kosztem 3 dodatkowych znaków,


OK, mam do 323 znaków. = D
strager

5
Mogę zmienić 99 na 1E5, aby uczynić go bardzo solidnym kosztem 3 znaków.
mob

2
Twoje najlepsze rozwiązanie byłoby bardziej widoczne na górze wpisu.
strager

13
Ale używanie wyrażeń regularnych do obracania tablicy? To było chore. To jak automatyczna premia za 20 uderzeń.
mob

1
@mobrule: Zapisz sześć kresek: s!.!$t{$s++}=$&!ge,$s=$r+=99for<>;zmień kolejność pierwszej linii jako , zmień %d=split//,.." to % d = .. = ~ /./ g , and change grep {..}% t` nagrep..,%t
A. Rex

75

Perl, 177 znaków

Pierwszy podział linii można usunąć; pozostałe dwa są obowiązkowe.

$/=%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}

Wyjaśnienie:

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

Jeśli belka poruszająca się w prawo wpada na {pustą przestrzeń, lustro skierowane w górę, lustro odchylone w dół}, staje się {wiązką poruszającą się w prawo, wiązką poruszającą się w górę, wiązką poruszającą się w dół}. Inicjalizuj $/po drodze - na szczęście „6” nie jest prawidłowym znakiem wejściowym.

$_ = <>;

Przeczytaj tablicę do $_.

$s="#";

$sjest symbolem tego, na czym obecnie znajduje się belka. Ponieważ emiter laserowy ma być traktowany jak ściana, ustaw ją jako ścianę.

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

Jeśli promień lasera wskazuje w jakikolwiek sposób poza prawą, obróć jego symbol, a następnie obróć całą tablicę w miejscu (również obracając symbole luster). Jest to obrót w lewo o 90 stopni, osiągnięty efektywnie poprzez odwrócenie rzędów podczas transpozycji wierszy i kolumn, z nieco diabelskim s///eefektem ubocznym. W kodzie golfowym tr jest zapisany w formie, y'''która pozwala mi pominąć jeden lewy ukośnik.

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

Zakończ odpowiednią wiadomością, jeśli trafimy w cel lub ścianę.

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

Jeśli przed laserem jest wolne miejsce, przejdź do przodu. Jeśli przed laserem znajduje się lustro, przejdź do przodu i obróć wiązkę. W obu przypadkach umieść „zapisany symbol” z powrotem w lokalizacji starej belki i umieść rzecz, którą właśnie nadpisaliśmy, w zapisanym symbolu.

redo;

Powtarzaj do zakończenia. {...;redo}to o dwa znaki mniej niż for(;;){...}i trzy mniej niż while(1){...}.


4
Obróć planszę ... Szalone. Regexp ... Bardziej szalony. O_o
strager

39
Ty ... Ty potworze!
LiraNuna

4
LiraNuna: Postanawiam potraktować to jako komplement.
hobbs

21
Golf się skończył. Jak możesz pokonać obracanie tablicy 2D za pomocą wyrażeń regularnych ?!
Konrad Rudolph

13
wtf? programiści perla są czarodziejami.
Johannes Schaub - litb

39

C89 (209 znaków)

#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;);}

Wyjaśnienie

Prawdopodobnie trudno będzie śledzić tę potworność, jeśli nie zrozumiesz C. To tylko ostrzeżenie.

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

To małe makro sprawdza, czy bieżący znak ( *p) jest równy temu, co aznajduje się w postaci znakowej ( *#a). Jeśli są równe, ustaw wektor ruchu na b( m=b), zaznacz ten znak jako ścianę ( *p=1) i ustaw punkt początkowy na bieżące położenie ( q=p). To makro zawiera część „else”.

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

Zadeklaruj niektóre zmienne. * qto aktualna lokalizacja światła. * Gto plansza jako tablica 1D. * pto bieżąca lokalizacja odczytu podczas wypełniania G. * wto szerokość deski.

main(m){

To oczywiste main. mjest zmienną przechowującą wektor ruchu. (Jest to parametr służący do mainoptymalizacji).

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

Zapętlaj przez wszystkie znaki, wypełniając Gza pomocą p. Pomiń G[0]jako optymalizację (nie musisz ponownie marnować pisania znaków pw trzeciej części for).

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

Jeśli to możliwe, użyj wspomnianego wyżej makra, aby zdefiniować lazer. -1i 1odpowiadają odpowiednio lewej i prawej stronie oraz -woraz w wgórę i w dół.

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

Jeśli bieżący znak jest znacznikiem końca linii (ASCII 10), ustaw szerokość, jeśli nie została jeszcze ustawiona. Pominięte G[0]pozwala nam pisać w=p-Gzamiast w=p-G+1. To również kończy ?:łańcuch od M.

    for(;
        q+=m,

Przesuń światło za pomocą wektora ruchu.

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

Odzwierciedlaj wektor ruchu.

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

Jeśli jest to ściana lub x, zakończ z odpowiednim komunikatem ( m=0przerywa pętlę). W przeciwnym razie nic nie rób (noop; m=m)

    );
}

8
Fuj! Pracowałem nad rozwiązaniem typu C, kiedy w moim kompleksie mieszkalnym włączył się alarm pożarowy. Teraz zostałem pokonany. Dobre rozwiązanie.
rlbond

Wydaje mi się, że użycie zmiennej tymczasowej do kroków zamiany i zamiany / negacji pozwoliłoby zaoszczędzić kilka znaków.
Artelius

@ Artelius, tak, zdałem sobie sprawę z tego i kilku innych rzeczy. Dzięki.
strager

1
TCC właściwie nie lubi nietypowych deklaracji i błędów z g.c:3: declaration expected:(
Mark Rushakoff

2
Usunięcie putsdeklaracji pomogło, ale nie wystarczyło, aby zmniejszyć ją poniżej 170. 209 jest jednak całkiem niezłe, więc myślę, że zostawię to na tym. Dzięki za pomoc, chłopaki. Bardzo to doceniam. =] (Cokolwiek, aby zdetronizować te wiedźmy Perla!)
strager

36

Założę się, że ludzie czekali na to DUŻO DŁUGO. (Co masz na myśli mówiąc, że wyzwanie się skończyło i nikogo to już nie obchodzi?)

Spójrz ... Przedstawiam tutaj rozwiązanie w

Befunge-93!

Waży aż 973 znaków (lub 688, jeśli jesteś na tyle charytatywny, aby ignorować białe znaki, które są używane tylko do formatowania i nie robią nic w rzeczywistym kodzie).

Zastrzeżenie : Napisałem własną befunge-93 tłumacza w Perlu krótką chwilę temu i niestety to wszystko ja naprawdę miałem czasu, z którym go przetestować. Jestem dość pewny, że ogólnie jest poprawny, ale może mieć dziwne ograniczenie w odniesieniu do EOF: Ponieważ <>operator Perla zwraca wartość undef na końcu pliku, jest to przetwarzane jako 0 w kontekście liczbowym. W przypadku implementacji opartych na języku C, w których EOF ma inną wartość (powiedzmy -1), ten kod może nie działać.

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"\:"<"\: "^"   -!#^_-!#^_-!      ^
               >                       ^

Wyjaśnienie

Jeśli nie znasz składni i działania Befunge, zajrzyj tutaj .

Befunge to język oparty na stosie, ale istnieją polecenia, które pozwalają na pisanie znaków w kodzie Befunge. Korzystam z tego w dwóch miejscach. Najpierw skopiowałem całe dane wejściowe na płytę Befunge, ale umieściłem kilka wierszy poniżej rzeczywistego napisanego kodu. (Oczywiście nigdy nie jest to widoczne po uruchomieniu kodu).

Drugie miejsce znajduje się w lewym górnym rogu:

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

W tym przypadku obszar, który zaznaczyłem powyżej, to miejsce, w którym przechowuję kilka współrzędnych. Pierwsza kolumna w środkowym rzędzie to miejsce, w którym zapisuję współrzędną x dla aktualnej "pozycji kursora"; druga kolumna to miejsce, w którym przechowuję współrzędną y; następne dwie kolumny służą do przechowywania współrzędnych x i y źródła wiązki laserowej, gdy zostanie znalezione; a ostatnia kolumna (ze znakiem „a”) jest ostatecznie nadpisywana, aby zawierała aktualny kierunek wiązki, który oczywiście zmienia się w miarę śledzenia ścieżki wiązki.

Program rozpoczyna się od umieszczenia (0,27) jako początkowej pozycji kursora. Następnie wejście jest odczytywane po jednym znaku na raz i umieszczane w pozycji kursora; znaki nowej linii powodują jedynie zwiększenie współrzędnej y i powrót współrzędnej x do 0, tak jak prawdziwy powrót karetki. Ostatecznie interpreter odczytuje wartość undef i ta wartość znaku 0 jest używana do zasygnalizowania końca wprowadzania i przejścia do kroków iteracji lasera. Kiedy odczytywany jest znak lasera [<> ^ v], jest on również kopiowany do repozytorium pamięci (nad znakiem „a”), a jego współrzędne są kopiowane do kolumn po lewej stronie.

Końcowym rezultatem tego wszystkiego jest to, że cały plik jest w zasadzie kopiowany do kodu Befunge, trochę poniżej rzeczywistego kodu, przez który przeszedł.

Następnie położenie belki jest kopiowane z powrotem do lokalizacji kursora i wykonywana jest następująca iteracja:

  • Sprawdź aktualny kierunek wiązki i odpowiednio zwiększ lub zmniejsz współrzędne kursora. (Robię to najpierw, aby uniknąć konieczności zajmowania się narożną obudową wiązki lasera już przy pierwszym ruchu).
  • Przeczytaj postać w tym miejscu.
  • Jeśli znakiem jest „#”, umieść na stosie nową linię i „fałsz”, wypisz i zakończ.
  • Porównaj to ze wszystkimi znakami belki [<> ^ v]; jeśli jest dopasowanie, wypisz również "false \ n" i zakończ.
  • Jeśli znak jest spacją, opróżnij stos i kontynuuj.
  • Jeśli znak jest ukośnikiem do przodu, przenieś wiązkę na stos i porównaj ją z każdym ze znaków kierunku po kolei. Kiedy zostanie znaleziony, nowy kierunek jest zapisywany w tym samym miejscu w kodzie i pętla się powtarza.
  • Jeśli znak jest ukośnikiem odwrotnym, zrób w zasadzie to samo, co powyżej (z wyjątkiem odpowiedniego odwzorowania ukośnika odwrotnego).
  • Jeśli znakiem jest „x”, trafiliśmy w cel. Wydrukuj „true \ n” i zakończ.
  • Jeśli znak nie jest żadnym z tych znaków, wypisz "błąd \ n" i zakończ.

Jeśli jest na to wystarczające zapotrzebowanie, spróbuję wskazać dokładnie, gdzie w kodzie to wszystko jest osiągane.


14
+1 - tylko dlatego, że można go błędnie zinterpretować jako plik EXE otwarty w notatniku.
Kyle Rosendo

1
Um ... święty ****. Bawiłem się z Befunge i to jest naprawdę imponujące.
Almo

Koduj golfa w zaciemnionych językach ... jak masło orzechowe i cayenne!
wberry

29

F #, 36 linii, bardzo czytelne

Ok, żeby uzyskać odpowiedź:

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    

Próbki:

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

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

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

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

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

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

54
PRAWDZIWIE MOGĘ CZYTAĆ TEN! CUDOWNY!
Jeff Atwood

17
Golf kodu Java / C # jest liczony według wierszy, a nie znaków. To jest handicap.
Nathan Feger

3
@strager nie jest przygnębiające od 3 lat, kiedy jesteś zatrudniony do obsługi kodu, a oryginalny programista już dawno odszedł.
Nathan Feger

To kończy się niepowodzeniem przy użyciu F # w programie Visual Studio 2010. Seq.to_list nie istnieje (ok, zmieniono go na toList), a następnie wiersz 25, niekompletne dopasowanie do wzorca.
Russell

2
Tak, zmień teraz to_list na toList. Ostrzeżenie o niepełnym dopasowaniu jest w porządku; to jest kod golfowy, więc nie robiłem kodu takiego jak: | _ -> porażka z „niemożliwym”
Brian

29

Golfscript - 83 znaki (połączenie mojego i stragera)

Nowa linia jest tutaj tylko do zawijania

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

Golfscript - 107 znaków

Nowa linia jest tam tylko dla przejrzystości

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

Jak to działa.

Pierwsza linia określa początkową lokalizację i kierunek.
Druga linia przechodzi przez obracanie się, gdy laser uderza w lustro.


18

353 znaki w Rubim:

314277 znaków teraz!

OK, 256 znaków w Rubim i gotowe. Ładny okrągły numer, na którym można się zatrzymać. :)

247 znaków. Nie mogę przestać.

223 203 201 znaków w języku 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)}

Z białymi znakami:

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)
}

Lekko refaktoryzowany:

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

Ale ... możesz zmienić nazwę chna Club dowolną inną 1-znakową literę, aby zapisać 2 znaki!
LiraNuna

Ok ok, dobrze ... Właściwie zdałem sobie sprawę, że cała ta zmienna jest niepotrzebna, ponieważ używam jej tylko raz. To i kilka innych ulepszeń zmniejszyło liczbę znaków do 247 znaków.
Jeremy Ruten

1
Nie i++(zamiast i+=1)?
LiraNuna

6
Nie. Możesz zrobić ++ i, ale to sprawia, że ​​jest naprawdę tak pozytywny, jak kiedyś.
DigitalRoss

Podoba mi się wersja skompresowana: #; p
SeanJA

17

Pyton

294 277 253 240 232 znaków ze znakami nowej linii:

(pierwszy znak w liniach 4 i 5 to tabulator, a nie spacje)

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

Zapomniałem, że Python ma nawet opcjonalne średniki.

Jak to działa

Kluczową ideą tego kodu jest użycie liczb zespolonych do reprezentowania pozycji i kierunków. Rzędy są wyimaginowaną osią, rosnącą w dół. Kolumny są rzeczywistą osią, rosnącą w prawo.

l='>v<^';lista symboli lasera. Kolejność jest tak dobrana, aby indeks znaku kierunku lasera odpowiadał potędze sqrt (-1)

x={'/':'^<v>','\\':'v>^<',' ':l};tabela transformacji określająca, jak zmienia się kierunek, gdy belka opuszcza różne płytki. Kafelek jest kluczem, a nowe kierunki to wartości.

b=[1];trzyma planszę. Pierwszym elementem jest 1 (jest oceniany jako prawda), więc pętla while zostanie uruchomiona co najmniej raz.

r=p=0 rbieżący numer wiersza wejścia, pto bieżąca pozycja wiązki lasera.

while b[-1]: przestań ładować dane tablicy, gdy raw_input zwróci pusty ciąg

b+=[raw_input()];r+=1 dołącz następny wiersz wejścia do tablicy i zwiększ licznik wierszy

for g in l: odgadnij po kolei każdy kierunek lasera

c=b[r].find(g) ustaw kolumnę na lokalizację lasera lub -1 jeśli nie znajduje się w linii (lub wskazuje w innym kierunku)

if-1<c:p=c+1j*r;d=gjeśli znaleźliśmy laser, ustaw aktualną pozycję pi kierunek d. djest jednym ze znaków wl

Po załadowaniu płytki b, aktualna pozycja pi kierunek dzostały ustawione zgodnie ze źródłem lasera.

while' '<d: spacja ma niższą wartość ASCII niż którykolwiek z symboli kierunku, więc używamy go jako flagi zatrzymania.

z=l.find(d);indeks bieżącego kierunku znaku w lciągu. zjest później używany zarówno do określenia nowego kierunku belki za pomocą xtabeli, jak i do zwiększania pozycji.

p+=1j**z;zwiększyć pozycję za pomocą potęgi i. Na przykład l.find('<')==2-> i ^ 2 = -1, co spowoduje przeniesienie do lewej kolumny.

c=b[int(p.imag)][int(p.real)]; przeczytaj znak w bieżącej pozycji

d=x.get(c,' '*4)[z]poszukaj nowego kierunku belki w tabeli transformacji. Jeśli bieżący znak nie istnieje w tabeli, ustaw dna spację.

print'#'<c print false, jeśli zatrzymaliśmy się na czymkolwiek innym niż cel.


9
p+=1j**z: Jakie to słodkie.
dmckee --- ex-moderator kitten

16

To jest był bezpośredni port rozwiązanie Briana do C # 3, minus interakcje konsoli. To nie jest wpis w wyzwaniu, ponieważ nie jest to kompletny program, po prostu zastanawiałem się, jak niektóre z konstrukcji F #, których użył, mogą być reprezentowane w 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};
    }
}

Edytować: po pewnym eksperymentowaniu następujący raczej szczegółowy kod wyszukiwania:

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;
        }
    }
}

został zastąpiony bardziej kompaktowym kodem LINQ to Objects:

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

8
o mój Boże. Cóż za fajny przykład pokazujący, jak potężne stały się linq i c #. 1+ bo jestem wielkim fanem C #. x)
Emiswelt

16

F #, 255 znaków (i nadal raczej czytelne!):

Ok, po nocnym odpoczynku bardzo to poprawiłem:

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])

Porozmawiajmy o tym linijka po linijce.

Najpierw zrzuć wszystkie dane wejściowe do dużej jednowymiarowej tablicy (tablice 2D mogą być złe dla golfa kodu; po prostu użyj tablicy 1D i dodaj / odejmij szerokość jednej linii do indeksu, aby przesunąć w górę / w dół o linię).

Następnie obliczamy „w”, szerokość wiersza wejściowego i „c”, pozycję początkową, poprzez indeksowanie w naszej tablicy.

Teraz zdefiniujmy „następną” funkcję „n”, która przyjmuje bieżącą pozycję „c” i kierunek „d”, który wynosi 0, 1, 2, 3 dla góra, lewo, prawo, dół.

Indeks-epsilon „e” i „co-nowy-kierunek-jeśli-uderzymy-ukośnik” są obliczane przez tabelę. Na przykład, jeśli bieżący kierunek „d” wynosi 0 (w górę), to pierwszy element tabeli mówi „-w, 2”, co oznacza, że ​​zmniejszamy indeks o w, a jeśli trafimy ukośnik, nowy kierunek wynosi 2 (dobrze).

Teraz przechodzimy do następnej funkcji 'n' z (1) następnym indeksem ("c + e" - bieżący plus epsilon) i (2) nowym kierunkiem, który obliczamy patrząc w przyszłość, aby zobaczyć, co jest w tablicy ta następna komórka. Jeśli znak wyprzedzenia jest ukośnikiem, nowy kierunek to „s”. Jeśli jest to odwrotny ukośnik, nowy kierunek to 3-s (nasz wybór kodowania 0123 sprawia, że ​​to działa). Jeśli jest to przestrzeń, po prostu idziemy w tym samym kierunku „d”. A jeśli jest to jakikolwiek inny znak „c”, gra się kończy, wypisując „true”, jeśli znak był „x”, a w przeciwnym razie - fałsz.

Na początek nazywamy funkcję rekurencyjną „n” z początkową pozycją „c” i kierunkiem początkowym (który wykonuje początkowe kodowanie kierunku na 0123).

Myślę, że prawdopodobnie mogę jeszcze zgolić kilka znaków więcej, ale jestem z tego całkiem zadowolony (a 255 to niezła liczba).


11

Ważenie 18203 znaków to rozwiązanie w Pythonie, które może:

  • radzą sobie z lustrami na zewnątrz `` pokoju ''
  • obliczyć trajektorię, gdy nie ma `` pokoju '' na podstawie ograniczeń 2D (specyfikacja mówi wiele o tym, co musi być w `` pokoju '', ale nie, jeśli pomieszczenie musi istnieć)
  • zgłaszać błędy

Nadal wymaga trochę uporządkowania i nie wiem, czy fizyka 2D narzuca, że ​​wiązka nie może się przeciąć ...

#!/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()

Skrypt bash pokazujący raportowanie błędów kolorów:

#!/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#
#\\^/\\ /  #
##########"

Unittesty użyte w rozwoju:

#!/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
Fizyka lasera podpowiada, że ​​wiązka może się przeciąć. Powyższy komentarz jest ważnym odniesieniem kulturowym.
dmckee --- ex-moderator kitten

5
Tortoise and the Hare podejście do kodowania golfa. Dostarcz coś ze zbyt dużą liczbą znaków (91 razy więcej niż obecny zwycięzca), ale zwróć uwagę na każdą literę specyfikacji. Powolny i stały generalnie sprawia, że ​​pracuję mniej na zlecenie.
Metalshark

Wydaje się, że Twojemu niezadowolonemu brakuje jakiejś części. Jest odcięty na „self.NOT_DIRECTIO”
BioGeek

@BioGeek - osiągnij limit długości posta;). Oprócz testów stylu BASH wykazano podświetlanie kolorów.
Metalshark

11

Ruby, 176 znaków

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}

Użyłem prostej maszyny stanowej (jak większość plakatów), nic nadzwyczajnego. Po prostu starałem się to łagodzić, używając każdej sztuczki, o której mogłem pomyśleć. Bitowy XOR używany do zmiany kierunku (przechowywany jako liczba całkowita w zmiennejc ) był dużym ulepszeniem w stosunku do warunków warunkowych, które miałem we wcześniejszych wersjach.

Podejrzewam, że kod, który się powiększa xi ymógłby zostać skrócony. Oto sekcja kodu, która wykonuje inkrementację:

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

Edycja : udało mi się nieco skrócić powyższe:

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

Aktualny kierunek lasera c jest zapisywany w następujący sposób:

0 => w górę
1 => w dół
2 => w lewo
3 => w prawo

Kod opiera się na tym fakcie w celu zwiększenia x i yo odpowiednią kwotę (0, 1 lub -1). Próbowałem przestawić, które liczby odwzorowują się w każdym kierunku, szukając układu, który pozwoliłby mi wykonać jakąś bitową manipulację w celu zwiększenia wartości, ponieważ mam dokuczliwe wrażenie, że byłby krótszy niż wersja arytmetyczna.


9

C # 3.0

259 znaków

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;}}

Nieco bardziej czytelne:

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;
    }
}

Wydaje się, że głównym marnotrawstwem znaków jest znalezienie szerokości mapy i położenia źródła lasera. Jakieś pomysły, jak to skrócić?


Nie jestem pewien, czy to krótsze, ale to moja szansa na znalezienie lasera i znalezienie szerokości: używając L = List <string>; używając P = System.Drawing.Point; używając L = List <string>; L r = new L () {"v", "<", ">", "^"}; P p = new 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] .Length; v jest listą <string> zawierającą tabelę i wyprowadza punkt reprezentujący położenie lasera + int reprezentujący szerokość
RCIX

lepiej: używając L = List <string>; L l = 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
Problemy wymagają pełnego programu, a nie funkcji.
strager

co while(1)
powiesz na

9

C + ASCII, 197 znaków:

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");}

To rozwiązanie C zakłada zestaw znaków ASCII, co pozwala nam użyć sztuczki lustrzanej XOR. Jest również niesamowicie delikatny - na przykład wszystkie linie wejściowe muszą mieć tę samą długość.

Łamie się poniżej znaku 200 znaków - ale do diabła, nadal nie pokonałem tych rozwiązań Perla!


= O! +1! Gratulacje za pokonanie mnie. =]
strager

2
Większość dobrych rozwiązań przyjmuje tutaj założenie „wszystkie linie mają taką samą długość”. W golfie i na wojnie wszystko jest sprawiedliwe.
hobbs

Gdyby było to wymagane, linie nie były tej samej długości, dodałbym dla niego przypadek testowy. ale jasno powiedziałem, że to było zamierzone :)
LiraNuna

9

Golfscript (83 znaki)

Cześć, 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

Odczytuje dane wejściowe z pliku o nazwie „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

Aby odczytać ze stdin, zamień pierwszą linię na this

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

Jeśli potrzebujesz małych liter prawda / fałsz, zmień ostatnią linię na

print`D<5`.lower()

Ile znaków potrzeba, aby zmienić Truena truei Falsena false? ;-)
mob

Czy nie mógłbyś usunąć 1 znaku, zmieniając „print D<5” na „print D <5”? Czy jest coś, czego mi brakuje?
Ponkadoodle

@wallacoloo, oczywiście, że tak. Jest potrzebny tylko w przypadku małych liter prawda / fałsz
John La Rooy,

7

JavaScript - 265 znaków

Aktualizacja IV - Szanse są to ostatnia runda aktualizacji, udało się uratować jeszcze kilka postaci, przełączając się na pętlę do-while i przepisując równanie ruchu.

Aktualizacja III - Dzięki sugestii stragera dotyczącej usunięcia Math.abs () i umieszczenia zmiennych w globalnej przestrzeni nazw, co w połączeniu z pewnym przestawieniem przypisań zmiennych spowodowało, że kod został zmniejszony do 282 znaków.

Aktualizacja II - Trochę więcej aktualizacji kodu, aby usunąć użycie! = -1, a także lepsze wykorzystanie zmiennych dla dłuższych operacji.

Aktualizacja - Po przejściu i dokonaniu pewnych zmian, tworząc odniesienie do funkcji indexOf (dzięki LiraNuna!) I usuwając nawiasy, które nie były potrzebne.

To mój pierwszy raz, kiedy gram w code-golfa, więc nie jestem pewien, o ile mogłoby to być lepsze, każda informacja zwrotna jest mile widziana.

W pełni zminimalizowana wersja:

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}

Wersja oryginalna z komentarzami:

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;
    }

Strona internetowa do przetestowania z:

<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>

jak to wymaga wkładu? Chcę to przetestować i zweryfikować. Możesz także uratować wiele postaci, jeśli zapiszesz odniesienie do a.indexOf
LiraNuna

Wymień index != -1się index > 0proszę! (Miejmy nadzieję, że nikt nie umieści lazera w lewym górnym rogu, aby 0nie został zwrócony. =]) Możesz varpołączyć instrukcje w łańcuch lub całkowicie się ich pozbyć (umieszczając zmienne w globalnej przestrzeni nazw). Myślę, że Math.abs(m)==1można go zastąpić m==-1|m==1. Czy movement = ...; location += movementmożna zoptymalizować location += movement =?
strager

@ strager- Właśnie zobaczyłem Twój komentarz, wygląda na to, że opublikowałeś go, gdy aktualizowałem kod, do 300 znaków. Zobaczę, co mogę zrobić z eliminacją Math.abs ().
rjzii

function(a){return g.indexOf(a)}można zastąpić function(a)g.indexOf(a)w najnowszych wersjach JavaScript.
user1686

6

Dom Luster

Nie jest to faktyczne wejście do wyzwania, ale napisałem grę opartą na tej koncepcji (nie tak dawno temu).

Jest napisany w Scali, open-source i dostępny tutaj :

Robi trochę więcej; zajmuje się kolorami i różnymi typami luster i urządzeń, ale wersja 0.00001 zrobiła dokładnie to, czego wymaga to wyzwanie. Straciłem jednak tę wersję i i tak nigdy nie została zoptymalizowana pod kątem liczby znaków.


Czy byłoby możliwe przesłanie skompilowanej wersji działającej pod Windows bez konieczności instalowania scali?
Mediolan,

Dostępna jest wersja z dołączonymi bibliotekami Scala. Spójrz na listę pobranych plików. Ale w każdym razie, jeśli już zainstalowałeś Scala, cieszę się, że to zrobiłem :)
HRJ

6

c (K&R) 339 niezbędnych znaków po większej liczbie sugestii od stragera.

Fizyk we mnie zauważył, że operacje propagacji i odbicia są niezmienne w czasie, więc ta wersja wyrzuca promienie z celu i sprawdza, czy dotarły do ​​emitera laserowego.

Reszta implementacji jest bardzo prosta i jest mniej więcej dokładnie zaczerpnięta z moich wcześniejszych działań.

Sprężony:

#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");}

Nieskompresowany (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");
}

Nie ma sprawdzania poprawności danych wejściowych, a złe dane wejściowe mogą wysłać je do nieskończonej pętli. Działa poprawnie z wejściem nie większym niż 99 na 99. Wymaga kompilatora, który połączy standardową bibliotekę bez dołączania żadnego z nagłówków. I myślę, że skończyłem, strager mnie pokonał znacznie, nawet z jego pomocą.

Raczej mam nadzieję, że ktoś zademonstruje subtelniejszy sposób wykonania zadania. Nie ma w tym nic złego, ale nie jest to głęboka magia.


Nie ma potrzeby =0na globale, ponieważ są one domyślnie inicjowane na 0. Zastąp stałe znakowe ich odpowiednikami w postaci dziesiętnej. Użyj >0zamiast, !=EOFaby sprawdzić pod kątem EOF (i \0). Prawdopodobnie możesz usunąć #defineczęść kodu w casetaki sam sposób, jak ja zrobiłem z if. Nie ma potrzeby umieszczania dodatkowego \nw putsas i tak putsmusi drukować nowy wiersz. for(;;)jest krótszy niż while(1). Mam nadzieję że to pomoże. =]
strager

@strager: Dzięki. Zawsze podchodzę do nich iteracyjnie, ponieważ nie myślę w ten sposób ...
dmckee --- ex-moderator kitten

2
"There is no input validation"- Nie powinno być żadnych. Aby ułatwić grę golfistom, zakłada się, że dane wejściowe są zawsze „czyste”, chyba że określono inaczej.
LiraNuna

@dmckee, nie martw się, nasi profesjonaliści w Code Golf również pracują iteracyjnie. Jednak generalnie używamy kilku sztuczek od samego początku (jak połowa tych, o których wspomniałem), ale wiąże się to z doświadczeniem. =]
strager

O ile źle nie liczę, program ma 390 znaków, a nie 380.
strager

6

Ruby - 146 znaków

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

Postscriptum , 359 bajtów

Pierwsza próba, dużo miejsca na ulepszenia ...

/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 znaków (zoptymalizowanej)

Nadal używa generycznej maszyny stanów, zamiast czegokolwiek sprytnego:

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}

Czytelna wersja:

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

Wierzę w ponowne wykorzystanie kodu, użyłbym jednego z twoich kodów jako API :).

  puts Board.new.validate (wejście)

32 znaki \ o / ... wohoooo


6
to jest podwójny straszak!
Jeff Atwood

3
Beat you to it: p Board.new.validate input 26 characters \ o /
Alessandra Pereyra

3

C ++: 388 znaków

#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 bez nagłówków)


Jak to działa:

Najpierw wczytywane są wszystkie linie, a następnie znajduje się laser. Poniższe zostaną ocenione 0tak długo, jak długo nie znaleziono strzały laserowej, a jednocześnie zostaną przypisane do xpozycji poziomej.

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

Następnie sprawdzamy, w jakim kierunku znaleźliśmy i zapisujemy i. Parzyste wartości ito góra / lewo („malejące”), a nieparzyste to dół / prawo („rosnące”). Zgodnie z tym pojęciem ustalane są d(„kierunek”) i r(„orientacja”). Indeksujemy tablicę wskaźników zz orientacją i dodajemy kierunek do otrzymanej liczby całkowitej. Kierunek zmienia się tylko wtedy, gdy uderzymy cięciem, podczas gdy pozostaje taki sam, gdy uderzymy w back-slash. Oczywiście, gdy trafimy w lustro, zawsze zmieniamy orientację ( r = !r).


Zmuszasz mnie do zrobienia własnego rozwiązania w C ++. =]
strager

2
@strager, to jednak robi się nudne. Zróbmy rozwiązanie, które wyświetla „prawda” lub „fałsz” w czasie kompilacji xD
Johannes Schaub - litb

dodane wyjaśnienie, ponieważ myślę, że zostanę na tym :)
Johannes Schaub - litb

2

Groovy @ 279 znaków

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

DO#

1020 znaków.
1088 znaków (dodane wejście z konsoli).
925 znaków (zmienne refaktoryzowane).
875 znaków (usunięto nadmiarowy inicjator słownika; zmieniono na Binary i operatory)

Postanowiłem nie patrzeć na niczyjego przed wysłaniem. Jestem pewien, że może być trochę LINQ. A cała metoda FindLaser w wersji do odczytu wydaje mi się strasznie podejrzana. Ale działa i jest późno :)

Zauważ, że czytelna klasa zawiera dodatkową metodę, która drukuje bieżącą arenę, gdy laser się porusza.

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);}}

Wersja do odczytu (nie do końca ostateczna wersja golfowa, ale to samo założenie):

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
Program powinien brać udział. Najczęściej ze stdin.
LiraNuna

0

Perl 219
Moja wersja Perl jest 392 342 znaków (miałem obsłużyć przypadek wiązki uderzanie laser):
Aktualizacja , dzięki Hobbs za przypomnienie mi tr//, to teraz 250 znaków:
Aktualizacja , usuwając mw m//zmieniając dwie whilepętle wniesionych kilka oszczędności; teraz wymagane jest tylko jedno miejsce.
( L:it;goto Lma taką samą długość jak 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

Ogoliłem trochę, ale z niektórymi z nich ledwo konkuruje, choć późno.
Wygląda trochę lepiej jako:

#!/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

Cóż ... Szczerze mówiąc, powinno to być oczywiste, jeśli zrozumiesz, że @bjest to tablica tablic znaków w każdym wierszu i możesz odczytać proste trwyrażenie regularne i instrukcje.


Wskazówka: możesz skrócić kod lustrzany w górę. $_=$s;tr/^v<>/<>^v/i $_=$s;tr/v^<>/<>^v/odpowiednio. Nie potrzebujesz też pliku min m//.
hobbs

Przepraszam, zrób to drugie$_=$s;tr/v^></<>^v/;
hobbs

Nadal masz kilka, if m/.../które mogą if/.../zaoszczędzić dwie postacie na pop.
hobbs

Możesz użyć y///zamiast, tr///aby zapisać dwie postacie.
Platinum Azure

0

F # - 454 (lub mniej więcej)

Trochę za późno do gry, ale nie mogę się powstrzymać przed wysłaniem mojej próby 2d.

Aktualizacja nieznacznie zmodyfikowana. Teraz zatrzymuje się poprawnie, jeśli nadajnik zostanie trafiony. Uchwyciłem pomysł Briana na IndexOfAny (szkoda, że ​​wiersz jest tak rozwlekły). Właściwie nie udało mi się wymyślić, jak sprawić, aby ReadToEnd powrócił z konsoli, więc biorę to trochę na zaufanie ...

Jestem zadowolony z tej odpowiedzi, jakby była dość krótka, ale nadal dość czytelna.

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))

Są ozdobą. Sprawdź moje inne wyzwania, to tylko kwestia formatowania.
LiraNuna

@LiraNuna, ok jak się okazuje ta iteracja i tak je zjada :)
Benjol

Byłoby miło porównać z implementacją 1-d. Po prostu dodaj / odejmij 1 dla lewej i prawej i dodaj / odejmij w w górę iw dół. Spodziewałbym się, że zaoszczędzisz sporo znaków
John La Rooy,

@gnibbler, Brian już to zrobił, nie jestem pewien, czy mógłbym go pokonać, ale może spróbuję.
Benjol,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.