Wykonaj BackFlip dla ais523!

16

To wyzwanie jest nagrodą dla ais523 za zwycięstwo w kategorii „ Świeżak roku ” w „ Best of PPCG 2016 ”. Gratulacje!


BackFlip to ezoteryczny język programowania stworzony przez użytkownika ais523 , który stworzył ponad 30 innych interesujących esolangów .

BackFlip to język 2D, taki jak Befunge lub > <>, w którym wskaźnik instrukcji przemierza siatkę tekstu (program), poruszając się w górę, w dół, w lewo i w prawo, zmieniając kierunek w zależności od znaku, na którym jest ustawiony. Krytycznie siatka w programie BackFlip zmienia się w trakcie ruchu, trochę jak Ant Langtona .

W przypadku tego wyzwania można założyć, że program BackFlip jest zawsze prostokątną siatką tekstu (wszystkie linie tej samej długości) o rozmiarze co najmniej 1 × 1, zawierającym tylko znaki ./\<>^V. ( .służy do widoczności, a nie do przestrzeni.) Semantycznie BackFlip, którego tu użyjemy, jest identyczny z oryginalną specyfikacją .

Wskaźnik instrukcji (IP) w BackFlip zawsze zaczyna się na lewo od lewego górnego rogu programu, na prawo. Istnieją trzy rodzaje poleceń, które może napotkać:

  1. .jest zakazem. IP kontynuuje w kierunku, w którym zmierza. No-op pozostaje no-op.

  2. /i \są zwierciadłami. Odbijają IP w kierunku wskazanym przez ich kąt, a następnie zmieniają się w inny rodzaj lustra .

    • Na przykład, jeśli IP zmierza w lewo \, zaczyna przesuwać się w górę zamiast w lewo i \staje się /.
  3. <, >, ^, I Vsą strzałkami. Przekierowują adres IP w kierunku, w którym wskazują, a następnie zmieniają się w strzałkę wskazującą kierunek, z którego pochodzi adres IP (przeciwnie do kierunku, w którym poruszał się adres IP) .

    • Na przykład, jeśli IP zmierza w dół >, zaczyna się przesuwać w prawo zamiast w dół i >staje się, ^ponieważ jest to kierunek, z którego pochodzi IP.

Program BackFlip kończy się, gdy IP wykracza poza granice, tzn. Znika z siatki. Okazuje się, że wszystkie programy BackFlip ostatecznie się kończą, ponieważ nieskończone pętle są niemożliwe. (Możesz założyć, że to prawda).

Twoim celem w tym wyzwaniu jest napisanie programu lub funkcji, która pobierze program BackFlip i wyświetli liczbę ruchów, jakie wskaźnik instrukcji wykona przed zakończeniem programu. To znaczy, ile kroków wykonuje IP w trakcie uruchamiania programu? Obejmuje to wstępny krok na siatkę i ostatni krok z niej.

Na przykład wskaźnik instrukcji wykonuje 5 kroków w trywialnej siatce ....:

 ....  <- empty 4×1 grid
012345 <- step number of the IP

Więc wyjściem ....jest 5.

W bardziej złożonej siatce 4 × 2

\...
\.><

IP opuszcza siatkę na 9 kroku, więc wynik jest następujący 9:

step  grid  IP position (@)
0     \...  @....
      \.><   ....

1     \...   @...
      \.><   ....

2     /...   ....
      \.><   @...

3     /...   ....
      /.><   .@..

4     /...   ....
      /.><   ..@.

5     /...   ....
      /.<<   ...@

6     /...   ....
      /.<<   ..@.

7     /...   ....
      /.><   .@..

8     /...   ....
      /.><   @...

9     /...   ....
      \.><   ....
             @

Najkrótszy kod w bajtach wygrywa.

W razie potrzeby dane wejściowe można traktować jako tablicę wierszy lub macierzy znaków zamiast ciągu wielowierszowego, ale należy użyć tych znaków ./\<>^V(nie liczb całkowitych). Możesz użyć spacji zamiast, .jeśli wolisz. W porządku, jeśli takie znaki \wymagają zmiany znaczenia na wejściu. Wyjście jest zawsze liczbą całkowitą więcej niż jedną.

Przypadki testowe

....
5

\...
\.><
9

.
2

..
3

.
.
2

\
2

^
2

.^.
3

<.
2

\\
\/
7

>V
^<
6

>\
>/
6

\><
2

\><
\><
7

\><
\><
\><
12

\.V.
\.\<
5

\.V.
\./<
9

V./\
V./\
>./<
..\/
14

\V..
.^..
\/><
.V..
.^..
20

\.V.V.
\./.\<
.>\<..
..^.^.
31

\.V.V.V.
\./>/.\<
.>\>\<..
..^.^.^.
69

\.V.V.V.V.
\./>/>/.\<
.>\>\>\<..
..^.^.^.^.
145

\.V.V.V.V.V.V.V.V.V.V.
\./>/>/>/>/>/>/>/>/.\<
.>\>\>\>\>\>\>\>\>\<..
..^.^.^.^.^.^.^.^.^.^.
9721
Hobby Calvina
źródło
1
Szkoda, że ​​nie można zrobić rozwiązania BackFlip w tym ...
HyperNeutrino
Mylić o lusterkach ... robi / odwraca kierunki w lewo => w górę i w górę => w lewo ,?
officialaimm
1
@officialaimm Kierunek od lewej do /spowoduje, że IP wzrośnie, a skierowanie do góry /sprawi, że IP pójdzie w prawo, jakby to była piłka odbijająca się od ściany. (Ale pamiętaj o /zmianach w odwrotnym ukośniku po tym, jak IP ich dotknie.)
Calvin's Hobbies
dlaczego „\\” <LF> „\ /” ma wartość 7 zamiast 6?
tsh

Odpowiedzi:

3

JavaScript (ES6), 158 bajtów

f=(a,x=0,y=0,d=3)=>a[x]&&(c=a[x][y])?(a[x][y]=c=='.'?c:c=='/'?(d^=3,'\\'):c=='\\'?(d^=1,'/'):'v>^<'[d][d='^<v>'.search(c),0],f(a,d<3?x+d-1:x,d?y+d-2:y,d)+1):1

Opracowany niezależnie od odpowiedzi @ tsh, choć uderzająco podobny.

Odwzorowanie kierunków ^<v>na liczby całkowite 0-3 jest regulowane przez fakt, że .search('^')zwraca 0, ponieważ ^jest metaznakiem wyrażenia regularnego.

Neil
źródło
Czuję się tak mocno pobity. Na koniec byłem dość zdezorientowany, dopóki nie zdałem sobie sprawy, że xiy zostały odwrócone w porównaniu do tego, czego się spodziewałem.
Ørjan Johansen
@ ØrjanJohansen To dobra uwaga; może powinienem zamieniać xyy wszędzie, aby ułatwić zrozumienie.
Neil,
2

Haskell , 333 325 bajtów

EDYTOWAĆ:

  • -8 bajtów: fUsprawniono punkt i scalono b.

bpobiera listę Strings i zwraca an Integer.

data C a=C{c::a->(a,C a)}
b g=[0,0]#([0,1],map(maybe(C$m 1)C.(`lookup`zip"./^>V<"[n,m(-1),a[-1,0],a[0,1],a[1,0],a[0,-1]]))<$>g)
[y,x]#(d,g)|g&y||g!!0&x=1|n@([f,e],_)<-(($d).c)?x?y$g=1+[y+f,x+e]#n
l&i=i<0||i>=length l
(f?i)l|(p,a:r)<-splitAt i l=(p++).(:r)<$>f a
n d=(d,C n)
a v d=(v,C$a$(0-)<$>d)
m s[d,e]=([s*e,s*d],C$m(-s))

Wypróbuj online!

Jak to działa

  • C ajest wykorzystywanym typem danych, ponieważ Haskell nie zezwoli na rekurencję typu bez wyraźnego zadeklarowania go. Cjest także konstruktorem opakowującym i cjest odpowiednią funkcją rozpakowywania. Jest używany tylko z a=[Int].
    • Typ C [Int]reprezentuje polecenie komórkowe, jako funkcję, która przyjmuje [Int]argument direction ( ) i zwraca parę nowego kierunku i nowej C [Int]wartości.
  • bjest główną funkcją. Konwertuje każdy znak na Cwartość, a następnie wywołuje #.
    • g to siatka jako lista ciągów znaków.
    • Ponieważ \należy uciec i jest to najdłuższy znak do wspomnienia, jego wynik jest zamiast tego używany jako wartość domyślna dla wyszukiwania listy.
  • #uruchamia główną symulację, sprawdzając granice &i generując nowe siatki za pomocą ?. [y,x]jest bieżącą pozycją, dbieżącym kierunkiem i gbieżącą siatką. [f,e]jest kolejnym kierunkiem i nskłada się z pary i następnej siatki.
  • l&isprawdza, czy indeks ijest poza zakresem dla listy l. (Wraca Truepoza boisko, ponieważ pozwala to uniknąć manekina wartownika #.)
  • Kiedy f(l!!i)==(d,x), (f?i)l==(d,m)gdzie mjest lista lz ielementem th zamienionym na x.
    • Technicznie (?i)jest bardziej ogólnym obiektywem, skupiającym się na i-tym elemencie listy, w tym przypadku używanym z (,) [Int]instancją funktora.
  • n to funkcja reprezentująca kropkę.
  • a vto funkcja reprezentująca strzałkę w kierunku v.
  • m sjest funkcją reprezentującą lustro; s==1za \\i s==-1za /.
Ørjan Johansen
źródło
1

JavaScript, 172 bajty

f=(a,d=3,x=0,y=0,n=1)=>(p=a[y]||[],q=p[x])?(p[x]=~(t='^<V>'.indexOf(q))?'^<V>'[d^2]:q=='/'?(t=3-d,'\\'):q=='\\'?(t=d^1,'/'):(t=d,q),f(a,t,x+(t&1&&t-2),y+(~t&1&&t-1),n+1)):n

Ale nie mogę przetestować ostatniej skrzynki testowej, ponieważ mam przepełnienie stosu na moim komputerze. (powinien działać, jeśli istnieje maszyna z większym tłokiem)

Używamy numeru do kierowania:

  • 0: ^
  • 1: <
  • 2: V
  • 3:>

Niech dbędzie numerem kierunku ...

  • jeśli napotkamy „/”, potrzebujemy d = 3 - d;
  • jeśli napotkamy „\”, potrzebujemy d = d ^ 1;
  • jeśli napotkamy „^ <V>”, potrzebujemy d = „^ <V>” .indexOf (uwaga)

Pozwolić (x, y)być aktualna pozycja, następna pozycja to: x+(t&1&&t-2),y+(~t&1&&t-1)

Uwaga:

Funkcja przyjmuje jeden parametr o następującym formacie:

[ [ '\\', '.', 'V', '.', 'V', '.', 'V', '.', 'V', '.' ],
  [ '\\', '.', '/', '>', '/', '>', '/', '.', '\\', '<' ],
  [ '.', '>', '\\', '>', '\\', '>', '\\', '<', '.', '.' ],
  [ '.', '.', '^', '.', '^', '.', '^', '.', '^', '.' ] ]

Sprawdź to tutaj

f=(a,d=3,x=0,y=0,n=1)=>(p=a[y]||[],q=p[x])?(p[x]=~(t='^<V>'.indexOf(q))?'^<V>'[d^2]:q=='/'?(t=3-d,'\\'):q=='\\'?(t=d^1,'/'):(t=d,q),f(a,t,x+(t&1&&t-2),y+(~t&1&&t-1),n+1)):n

    ;k=x=>x.split('\n').map(t=>t.split(''));
<textarea id=v>\.V.V.V.V.
\./>/>/.\<
.>\>\>\<..
..^.^.^.^.</textarea><br/><button onclick="r.textContent=f(k(v.value))">Solve</button>
<p>Result: <output id=r></output></p>

tsh
źródło
Żeby udokumentować, dostaję Uncaught RangeError: Maximum call stack size exceeded16 GB pamięci RAM.
Zeb McCorkle,
1
@ZebMcCorkle aha, po prostu dowiedz się, że „użyj ścisłego” i niektóre vardeklaracje sprawiają, że przechodzi on ostatnią próbę (interpreter js optymalizuje wywołanie ogonowe w trybie ścisłym)
tsh
1

C, 232 221 bajtów

d,n,t,m[4]={1,-1};char*w="><^V/\\.",*P;main(o,v)char**v;{for(P=v[1],m[2]=-(m[3]=strchr(P,10)-P+1);P>=v[1]&&P<strchr(v[1],0)&&*P^10;++n)*P=w[((o=d,t=strchr(w,*P)-w)<4)?d=t,o^1:(t<6)?d^=t-2,9-t:6],P+=m[d];printf("%d",n+1);}

Pobiera dane wejściowe w pierwszym argumencie, wyświetla wynik. Wymaga, aby dane wejściowe zawierały co najmniej 1 nową linię (więc jeśli jest tylko 1 wiersz, musi kończyć się nową linią)

Przykładowe użycie:

./backflip '....
';

Awaria:

d,                                          // Direction
n,                                          // Step counter
t,
m[4]={1,-1};                                // Movement offsets
char*w="><^V/\\.",                          // Command lookup
*P;                                         // Cursor location
main(o,v)char**v;{
    for(P=v[1],                             // Begin at 0,0
        m[2]=-(m[3]=strchr(P,10)-P+1);      // Find width of grid
        P>=v[1]&&P<strchr(v[1],0)&&*P^10;   // While inside grid:
        ++n)                                //  Increment step
        *P=w[                               //  Update the current cell
            ((o=d,t=strchr(w,*P)-w)         //  (store current direction & command)
              <4)?d=t,o^1:                  //   If <>^V, write backflip & set dir
            (t<6)?d^=t-2,9-t:               //   If / or \, write flip & bounce
            6],                             //   If ., write unchanged & continue
        P+=m[d];                            //  Move
    printf("%d",n+1);                       // Print result
}
Dave
źródło
1

Python 3 , 286 bajtów

[f () przyjmuje dane wejściowe w postaci, {(0,0):'/',(0,1):'.'}więc napisałem również funkcję g () do konwersji tablicy linii do tej postaci]

def f(r):
 x=y=0;d='>';s=1
 while 1:
  try:c=r[y,x];m='>^<V'.find(d)
  except:break
  if(c=="\\"):d='V<^>'[m];r[y,x]="/"
  elif(c=="/"):d='^>V<'[m];r[y,x]="\\"
  elif(c!="."):r[y,x]='<V>^'[m];d=c
  x+=1if(d=='>')else-1if(d=='<')else 0;y+=1if(d=='V')else-1if(d=='^')else 0;s+=1
 return s

Wypróbuj online!

Officialaimm
źródło