Pomyślnie poruszaj się po polu asteroid

36

Wprowadzenie

Wszyscy wiedzą, że możliwość udanej nawigacji po polu asteroid wynosi około 3720 do 1. Ale pomimo twojego ostrzeżenia Han Solo wciąż chce spróbować szczęścia.

Bojąc się o swoje sztuczne życie, decydujesz się na kodowanie, w specyficznym dialekcie statku ( czytaj: preferowany język Code Golf ), program unikania planetoid, który zadecyduje, którą ścieżką wybrać się w labirynt ASCII pola asteroid.

Wkład

Millenium Falcon ma program do mapowania pól asteroid, który daje dane podobne do tego:

|   #####           #########  |
| ######  #          ###   #   |
|   # #  #  #  ####   #        |
@              ##    ####       
|#   #   #       ###   ##      |
|##      ##      ####   #  #   |
|####           ##### #   ##   |

Górne rzędy są po lewej stronie Sokoła, dolne rzędy po prawej stronie Sokoła, a kolumny reprezentują to, co znajduje się przed statkiem.

  • Każdy #jest przeszkodą.
  • Każde pole to puste miejsce, w które może latać statek.
  • Dane wejściowe mają zawsze wysokość 7 znaków. Jest to limit szerokości mapowania asteroid.
  • Dane wejściowe mają zawsze 32 znaki (30 dla samego pola i 2 dla początkowego i końcowego limitu). Jest to limit głębokości mapowania asteroid. Pionowe paski |oznaczają początek i koniec mapowania.
  • @jest Sokół. Zawsze znajduje się w środkowym rzędzie (czwarty rząd) i pierwszej kolumnie na wejściu.
  • Przestrzeń pozostawiona w pionowych słupkach w ostatniej kolumnie to miejsce, do którego statek musi dotrzeć. Zawsze znajduje się w środkowym rzędzie (czwarty rząd) i ostatniej kolumnie na wejściu.

Dane wejściowe można traktować jako ciąg wieloliniowy, tablicę ciągów, ze STDIN lub parametrów funkcji, lub odczytać z pliku.

Możliwe manewry

Ścigają cię TIE-Fighters, dlatego zawsze musisz iść do przodu. Istnieją zatem trzy sposoby, w jakie statek może latać na każdym kroku:

  • - Naprzód

  • / Naprzód i skręć w lewo

  • \ Naprzód i skręć w prawo

Na przykład są to prawidłowe ścieżki:

@---

  --
 /  \ /
@    -

   -
  / \
 /   \
@     \

Jak widać, zawsze jest dokładnie jeden ruch na kolumnę. Sokół jest śmieciem, dlatego nie może wykonywać gwałtownych zwrotów. Co oznacza, że ​​ruchy takie jak /\lub \/niedozwolone . Musi być co najmniej jeden czysty ruch do przodu -między dwoma przeciwnymi zwrotami. Z drugiej strony, jak pokazano powyżej, możliwe jest obrócenie w jedną stronę wielu kroków z rzędu.

Falcon ulega awarii, jeśli jeden ruch prowadzi statek do miejsca, w którym znajduje się przeszkoda. Na przykład te ruchy prowadzą do awarii:

@-#

@
 \
  #

  #
 /
@

Pamiętaj, że to nie jest awaria:

@-#
  \
   -

Wydajność

Musisz wypisać to samo pole asteroid ASCII, z prawidłową ścieżką do końca. Sokół musi być wydrukowany w miejscu końcowym zamiast w miejscu początkowym.

Na przykład poprawne dane wyjściowe dla podanego wcześniej przykładu wejściowego to:

|   #####           #########  |
| ######  #--------  ###   #   |
|   # #  #/ #  ####\  #        |
 ---------      ##  \ #### ----@
|#   #   #       ### \ ## /    |
|##      ##      #### \ #/ #   |
|####           ##### #-- ##   |

Twoja ścieżka musi tylko nie rozbić sokoła. Nie musi to być najkrótsza możliwa ścieżka.

Możesz założyć, że zawsze będzie co najmniej jedna możliwa ścieżka do końca.

Możesz wyprowadzać dane do STDOUT, w pliku lub dowolnym równoważniku, o ile pole asteroid jest drukowane dokładnie tak, jak w tym poście (np. Podanie listy współrzędnych dla ścieżki jest nieprawidłowe).

Przypadki testowe

  • Normalne pole asteroid

    |   #####           #########  |
    | ######  #          ###   #   |
    |   # #  #  #  ####   #        |
    @              ##    ####       
    |#   #   #       ###   ##      |
    |##      ##      ####   #  #   |
    |####           ##### #   ##   |
    

    Możliwe wyjście

    |   #####           #########  |
    | ######  #--------  ###   #   |
    |   # #  #/ #  ####\  #        |
     ---------      ##  \ #### ----@
    |#   #   #       ### \ ## /    |
    |##      ##      #### \ #/ #   |
    |####           ##### #-- ##   |
    
  • Hiperregularne pole asteroid

    |# # # # # # # # # # # # # # # |
    | # # # # # # # # # # # # # # #|
    |# # # # # # # # # # # # # # # |
    @ # # # # # # # # # # # # # #   
    |# # # # # # # # # # # # # # # |
    | # # # # # # # # # # # # # # #|
    |# # # # # # # # # # # # # # # |
    

    Możliwe wyjście

    |# # # # # # # # # # # # # # # |
    | # # # # # # # # # # # # # # #|
    |# # # # # # # # # # # # # # # |
     -# #-# #-# #-# #-# #-# #-# #--@
    |#\#/#\#/#\#/#\#/#\#/#\#/#\#/# |
    | #-# #-# #-# #-# #-# #-# #-# #|
    |# # # # # # # # # # # # # # # |
    
  • Rdzeń gwiazdy śmierci

    |    #    #    #         #     |
    |         #    #    #          |
    |    #    #    #    #    #     |
    @    #    #    #    #    #      
    |    #    #         #    #     |
    |    #    #    #    #    #     |
    |    #         #    #    #     |
    

    Możliwe wyjście

    |    #    #    #   --    #     |
    |  ---    #    #  / #\   -     |
    | /  #\   #    # /  # \ /#\    |
     -   # \  #    #/   #  - # ----@
    |    #  \ # ----    #    #     |
    |    #   \#/   #    #    #     |
    |    #    -    #    #    #     |
    
  • Rów gwiazdy śmierci

    |##############################|
    |##############################|
    |##############################|
    @                               
    |##############################|
    |##############################|
    |##############################|
    

    Wydajność

    |##############################|
    |##############################|
    |##############################|
     ------------------------------@
    |##############################|
    |##############################|
    |##############################|
    
  • Jaskinia asteroid

    |### ##########################|
    |## # ############### ## ######|
    |# ###  ######## ### ## # #####|
    @ ###### ###### ### ## ###      
    |########  ### ### ## #########|
    |########## # ### ## ##########|
    |###########              #####|
    

    Możliwe wyjście

    |###-##########################|
    |##/#\############### ##-######|
    |#/###--######## ### ##/#\#####|
     -######\###### ### ##/###-----@
    |########--### ### ##/#########|
    |##########\# ### ##/##########|
    |###########--------      #####|
    

Punktacja

R2D2 jest zajęty pływaniem na bagnach, więc będziesz musiał sam zaprogramować kontroler Sokoła, co jest uciążliwe. Dlatego wygrywa najkrótszy kod .

Fatalizować
źródło
@DJMcMayhem: Technicznie pierwsza linia to „Wprowadzenie”;)
Alex A.,
2
W pewnym sensie rozumiem, jak to powinno wyglądać na podstawie przykładów, ale kodowanie ruchów jest dla mnie nieco mylące. Jeśli szukasz na przykład „hiperregularnego”, z wyjątkiem pierwszego / ostatniego ruchu, zawsze poruszasz się po przekątnej. Jednak -na każdym zakręcie ma ścieżkę, która jest definiowana jako ruch „do przodu”. Ale rzeczywiste ruchy są zawsze dwie ukośne w lewo, a następnie dwie ukośne w prawo.
Reto Koradi,
@RetoKoradi Rozumiem, że nie jest to wcale takie oczywiste, ale podstawową ideą jest to, że wszystkie ruchy powodują, że podróżujesz po szerokości znaku w prawo. Ścieżka musi wyglądać na ciągłą, dlatego poprzedni / następny ruch po skręcie w prawo i w lewo znajdują się o jedną linię powyżej / poniżej poprzedniej.
Fatalize
@apsillers Oba są ważne, jeśli dobrze cię rozumiem, twoja odpowiedź powinna być dobra
Fatalize

Odpowiedzi:

11

JavaScript (ES6), 186 201

f=([...s])=>(g=(i,l,c=k=" ")=>s[i]!=k&&s[i]!="@"?0:(i-130)?(s[i]=c,([..."/-\\"].some((c,j)=>!((--j&l&&j!=l)||!g(i+33*(l||j)+1,j,c)))||!(s[i]=k))):(s[i]="@",!console.log(s.join(""))))(99)

Urywalny fragment kodu:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><textarea cols="33" rows="7" id="t"></textarea><br><button><b>Solve &gt;&gt;&gt;</b></button><hr><button id="1">Normal</button> <button id="2">Hyperregular</button> <button id="3">Death Star core</button> <button id="4">Death Star trench</button> <button id="5">Asteroid cave</button><script>f=(function($__0){var $__2,$__3,$__4,$__5;s = $__4 = $__0.split("");return (g = (function(i, l) {var c = arguments[2] !== (void 0) ? arguments[2] : k = " ";return s[i] != k && s[i] != "@" ? 0 : (i - 130) ? (s[i] = c, ("/-\\".split("").some((function(c, j) {return !((--j & l && j != l) || !g(i + 33 * (l || j) + 1, j, c));})) || !(s[i] = k))) : (s[i] = "@",$("#t").val(s.join("")));}))(99);});$("button").click(function() {this.id?$("#t").val(inputs[this.id]):f($("#t").val());});inputs = [,`|   #####           #########  |\n| ######  #          ###   #   |\n|   # #  #  #  ####   #        |\n@              ##    ####       \n|#   #   #       ###   ##      |\n|##      ##      ####   #  #   |\n|####           ##### #   ##   |`,`|# # # # # # # # # # # # # # # |\n| # # # # # # # # # # # # # # #|\n|# # # # # # # # # # # # # # # |\n@ # # # # # # # # # # # # # #   \n|# # # # # # # # # # # # # # # |\n| # # # # # # # # # # # # # # #|\n|# # # # # # # # # # # # # # # |`,`|    #    #    #         #     |\n|         #    #    #          |\n|    #    #    #    #    #     |\n@    #    #    #    #    #      \n|    #    #         #    #     |\n|    #    #    #    #    #     |\n|    #         #    #    #     |`,`|##############################|\n|##############################|\n|##############################|\n@                               \n|##############################|\n|##############################|\n|##############################|`,`|### ##########################|\n|## # ############### ## ######|\n|# ###  ######## ### ## # #####|\n@ ###### ###### ### ## ###      \n|########  ### ### ## #########|\n|########## # ### ## ##########|\n|###########              #####|`];$("#t").val(inputs[1]);</script

Ta funkcja przyjmuje pojedynczy ciąg znaków z nowymi liniami. Funkcja dzieli ciąg na tablicę za pomocą ...operatora i pobiera indeks (x,y)współrzędnych przez (33 * y) + x.

Funkcja działa rekurencyjnie, testując różne możliwe ruchy dla każdej przestrzeni. Kiedy napotyka przeszkodę, zwraca wartość fałszu, a kiedy osiąga pole celu końcowego, wraca true. (W kodzie golfowym truejest to utworzone przez !console.log(...).)

Zauważ, że ten kod nie używa długich serii ruchów w prawo, ale interpunkuje je prostymi ruchami. Oznacza to, że robi drugą opcję poniżej, a nie pierwszą:

\                       \
 \   (<= not this!)      -   (<= yes this!)
  \                       \

Wydaje się to legalne, ponieważ -może legalnie przyjść przed lub po zakręcie, więc dlaczego nie jedno i drugie jednocześnie? To wygląda szczególnie dziwnie na końcu, gdy ostatni ruch \jest wyświetlany, ale @:

|  --#    #    #   ------#  -  |
| /  \    #    #  / #    \ / \ |
|/   #-   #    # /  #    #-   -|
     # \  #    #/   #    #     @
|    #  - # ----    #    #     |
|    #   \#/   #    #    #     |
|    #    -    #    #    #     |

Mój ulubiony paskudny hack golfowy to domyślne nadużycie podczas kłótni c=k=" ". Argumenty (i,l,c=" ")powiedziałbym „użyj ciąg " "na crazie fnie jest dostarczany trzeci argument”. W ten c=k=" "sposób mówimy „jeśli cnie jest podany, zapisz " "w zmiennej globalnej, ka następnie zapisz również tę wartość c”. Ponieważ rekurencja rozpoczyna się tylko jednym argumentem, kjest zawsze inicjowana przy pierwszym wywołaniu funkcji.

Lekko niestrzeżony:

// `i` - index in the string we're working on
// `l` - move character for this space (`/`, `\`, or `-`)
search = (i,l,c)=>{

  // not an open space; nip this recursive branch
  if(s[i]!=" "&&s[i]!="@") { return 0; }

  // we made it! the 130th space is (31,3)
  if(i==130) {
      s[i]="@";
      console.log(s.join(""));
      return true;
  }

  // fill in space with move character or a blank
  // (the space is only to blank out the initial `@`)
  s[i] = c || " ";

  // iterate through the 3 options and recursively explore the map
  return ['/','-','\\'].some((c,j)=>{
    --j;
    // if last move was sideways, and this is the opposite move, skip it
    if(l && j && j!=l) { return 0; }

    // recursively call search function on space pointed to by this move or the last move
    return search(i+33*(l||j)+1, j, c);
  })

  // if the `some` call is false (i.e. all options fail for this space)
  // then blank out this space and return false
  || !(s[i]=" ");

}
apsillery
źródło
@ vihan1086 Racja, całkowicie tęskniłem za tymi przestrzeniami podczas gry w golfa. D: A przełączanie się z tablicy na podzielony ciąg znaków jest również przyjemną zmianą. Dzięki. :) Wprowadziłem również kilka innych zmian (czyniąc bieżący znak ruchu trzecim argumentem zamiast określanym w funkcji i przechowując " "w zmiennej), które obniżyły mój wynik jeszcze niżej.
apsillers,
7

C (pełny program), 249 247 235 bajtów

Jest to kompletny program odczytujący dane wejściowe z pliku i wysyłający wynik na standardowe wyjście. Nazwa pliku jest przekazywana jako parametr do programu.

char f[7][33];g(i,j,c){return(i<0|i>6|f[i][j]%32?0:j<31?c%45-2?g(i,j+1,c)||g(i+1,j+1,92)||g(i-1,j+1,47):g(i+c/30-2,j+1,c)||g(i+c/30-2,j+1,45):1)?f[i][j]=j?j-31?c:64:32:0;}main(int p,char**v){read(open(v[1],0),f,231);g(3,0,45);puts(f);}

Nie golfowany:

/* the field */
char f[7][33];

/* i - row
 * j - col
 * c - movement
 */
g(i,j,c)
{
    return
            /* if we're in bounds and not on an obstacle */
            (i >= 0 & i<7 & f[i][j] % 32 == 0 ?
                    /* if we haven't reached the end */
                    j < 31 ?
                            /* are we going straight ahead? */
                            c%45-2 ?
                                    /* try to go straight */
                                    g(i,j+1,c)
                                    /* try to turn right */
                                    || g(i+1,j+1,92)
                                    /* try to turn left */
                                    || g(i-1,j+1,47)
                            /* turning */
                            :
                                    /* try to keep turning */
                                    g(i+c/30-2,j+1,c)
                                    /* try to go straight */
                                    || g(i+c/30-2,j+1,45)
                    /* done */
                    :1 /* replace this with c==45 to better represent the last move being a turn */
            /* invalid move, fail */
            :0)
            /* add the correct movement to the field */
            ? f[i][j] = j ? j - 31 ? c : 64 : 32
            /* no path, much sads :( */
            :0;
}

main(int p,char*v[])
{
    /* read input file */
    read(open(v[1],0),f,231);

    /* calculate the path */
    g(3,0,45);

    /* print it out */
    puts(f);
}

Wydajność:

$ ./a.out test.inp
|   #####           #########  |
| ######  #          ###   #   |
|   # #  #  #  ####   #      --|
 ------------- ##----####   /  @
|#   #   #    \ /### \ ##  /   |
|##      ##    - #### \ # /#   |
|####           ##### #---##   |

$ ./a.out test2.inp
|# # # # #-# # # # # #-# # # # |
| # # # #/#\# # # # #/#\# # # #|
|# # # #/# #\# # # #/# #\# # # |
 -# # #/# # #\# # #/# # #\# #  @
|#\# #/# # # #\# #/# # # #\# #/|
| #\#/# # # # #\#/# # # # #\#/#|
|# #-# # # # # #-# # # # # #-# |

$ ./a.out test3.inp
|    #    #    #   ------#     |
|    -    #    #  / #    \     |
|   /#\   #    # /  #    #\    |
 --- # \  #    #/   #    # \   @
|    #  \ #    /    #    #  \ /|
|    #   \#   /#    #    #   - |
|    #    ---- #    #    #     |

$ ./a.out test4.inp
|##############################|
|##############################|
|##############################|
 ------------------------------@
|##############################|
|##############################|
|##############################|

$ ./a.out test5.inp
|###-##########################|
|##/#\############### ##-######|
|#/###--######## ### ##/#\#####|
 -######\###### ### ##/###-----@
|########--### ### ##/#########|
|##########\# ### ##/##########|
|###########--------      #####|
Cole Cameron
źródło
Wygląda na to, że przegapiłeś punkt końcowy w pierwszym teście.
Reto Koradi,
@RetoKoradi To jest -po nim \, ale to \jest objęte przez @. (Mój program robi to samo.)
apsillers,
1
@RetoKoradi Wcześniejsze iteracje tego problemu lepiej obsługiwały tę sprawę. To +4 bajty. Zauważyłem, że rozwiązanie apsillerów zachowuje się podobnie, więc zdecydowałem się zaoszczędzić miejsce.
Cole Cameron,
Widzę. Nie wygląda mi to dobrze, ale to OP musi zdecydować, co jest dozwolone. Widziałem, że dali swobodę w sposobie przedstawiania ruchów. Od samego początku chciałbym zobaczyć jasną i unikalną definicję. Wyglądało to na zabawny problem, ale nie jest tak interesujący z dwuznacznością.
Reto Koradi,
3

Common Lisp, 303 bajty

Świetnie się bawiłem z tym wyzwaniem, jest to pierwsze zadanie, jakie wykonałem jako codegolf. Zasadniczo istnieje prosta funkcja rekurencyjna, która próbuje wykonać każdy wykonalny ruch, aż do osiągnięcia pozycji końcowej.

Golf / Minified

(let((s(open "i"))(n nil)(f(make-string 231)))(read-sequence f s)(labels((r(p s u d)(and(< 0 p 224)(find(aref f p)" @")(setf(aref f p)(cond((= 130 p)#\@)((or(unless d(r(- p 32)#\/ t n))(unless u(r(+ p 34)#\\ n t))(r(+ p(cond(u -32)(d 34)(t 1)))#\- n n))s)((return-from r)))))))(r 99 #\- n n)(princ f)))

Odczytuje dane wejściowe z pliku i w katalogu roboczym. Jestem pewien, że jest jeszcze miejsce na ulepszenia.

Zwykły kod

(defun run-test (file)
  (let ((stream (open file)) ;;should use with-open-file for autoclose..
        (no nil) ;; alias for brevity
        (field (make-string 231)))
    (read-sequence field stream)
    (labels ((doit (pos sym going-up going-down)
               (and
                 (< 0 pos 224)
                 (find (aref field pos) " @")
                 (setf (aref field pos)
                       (cond
                         ((= 130 pos) #\@)
                         ((or
                            (unless going-down (doit (- pos 32) #\/ t no))
                            (unless going-up (doit (+ pos 34) #\\ no t))
                            (doit (+ pos (cond (going-up -32)
                                               (going-down 34)
                                               (t 1)))
                                  #\- no no))
                          sym)
                         ((return-from doit)))))))
      (doit 99 #\- no no)
      (princ field)
      nil)))

Próbka wyjściowa

|   #####       --  #########  |
| ######  #    /  \  ###   # - |
|   # #  #  # /####\  #     / \|
--   -       / ##   \####  /   @
|#\ /#\  #  /    ### \ ## /    |
|##-   \ ##/     #### \ #/ #   |
|####   ---     ##### #-- ##   |

|  --#    #    #   --    #-    |
| /  \    #    #  / #\   / \   |
|/   #\   #    # /  # \ /#  \  |
-    # \  #    #/   #  - #   \ @
|    #  \ # ----    #    #    -|
|    #   \#/   #    #    #     |
|    #    -    #    #    #     |

|# #-# # # # # #-# # # # # #-# |
| #/#\# # # # #/#\# # # # #/#\#|
|#/# #\# # # #/# #\# # # #/# #\|
--# # #\# # #/# # #\# # #/# #  @
|# # # #\# #/# # # #\# #/# # # |
| # # # #\#/# # # # #\#/# # # #|
|# # # # #-# # # # # #-# # # # |
Florian Patzl
źródło
2

ActionScript 3, 364 bajty

Podzieliłem to na dwie funkcje; jeden, aby zmienić tablicę na tablicę, a drugi rekurencyjny, aby obliczyć ścieżkę lotu.

function m(f){for(var i=0;i<f.length;i++){f[i]=f[i].split("");}n(f,0,3,0);return f;}function n(f,x,y,m){var P=f[y][x],X=x+1,A=y-1,B=y,C=y+1,T=true,F=false,E='-';if (y<0||y>6||P=='#'||P=='|')return F;if (x==31){f[y][x]='@';return T;}if(m<0&&y>0){B=A;C=9;E='/';}else if(m>0&&y<6){A=9;B=C;E='\\';}if (n(f,X,B,0)||n(f,X,A,-1)||n(f,X,C,1)){f[y][x]=E;return T;return F;}

Wersja bez golfa w programie ze zdefiniowanym jednym przykładowym polem asteroidy:

package
{
    import flash.display.Sprite;

    public class AsteroidNavigator extends Sprite
    {
        var field:Array;
        public function AsteroidNavigator()
        {
            field = [
"|   #####           #########  |",
"| ######  #          ###   #   |",
"|   # #  #  #  ####   #        |",
"@              ##    ####       ",
"|#   #   #       ###   ##      |",
"|##      ##      ####   #  #   |",
"|####           ##### #   ##   |"];
            m(field);
            printField();
        }

        function m(f){
            for(var i=0;i<f.length;i++){
                f[i]=f[i].split("");\
            }
            n(f,0,3,0);
            return f;
        }

        private function n(field,x,y,m) {
            var C = field[y][x];
            if (x > 31 || C == '#' || C == '|') {
                return false;
            }
            if (x == 31 && y == 3) {
                field[y][x] = '@';
                return true;
            }
            if (m == 0) {
                if (n(x+1, y, 0) || ((y>0) && n(x+1, y-1, -1)) || ((y<6) && n(x+1, y+1, 1))) {
                field[y][x] = '-';
                return true;
                }
            } else if ((m<0) && (y>0)) {
                if ((n(x+1, y-1, -1) || n(x+1, y-1, 0))) {
                    field[y][x] = '/';
                    return true;
                }
            } else if ((m>0) && (y<6)) {
                if ((n(x+1, y+1, 1) || n(x+1, y+1, 0))) {
                    field[y][x] = '\\';
                    return true;
                }
            }
            return false;
        }

        private function printField() {
            var sb = "";
            for each (var row:Array in field) {
                sb += row.join("") + "\n";
            }
            trace(sb);
        }
    }
}
Brian
źródło