Navigare con successo in un campo di asteroidi


36

introduzione

Tutti sanno che la possibilità di navigare con successo in un campo di asteroidi è di circa 3.720 a 1. Ma nonostante il tuo avvertimento, Han Solo è ancora disposto a tentare la fortuna.

Temendo per la tua vita artificiale, decidi di codificare, nel peculiare dialetto della nave ( leggi: la tua lingua preferita di Code Golf ), un programma di evitamento degli asteroidi che deciderà quale percorso prendere in un labirinto ASCII di campo di asteroidi.

Ingresso

Millenium Falcon ha un programma di mappatura dei campi di asteroidi, che fornisce dati simili a questo:

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

Le file superiori sono a sinistra del Falco, le file inferiori sono a destra del Falco e le colonne rappresentano ciò che si trova di fronte alla nave.

  • Ognuno #è un ostacolo.
  • Ogni spazio è spazio vuoto in cui la nave può volare.
  • L'input ha sempre 7 caratteri. Questo è il limite di larghezza della mappatura degli asteroidi.
  • L'input è sempre lungo 32 caratteri (30 per il campo stesso e 2 per i limiti di inizio e fine). Questo è il limite di profondità della mappatura degli asteroidi. Le barre verticali |indicano l'inizio e la fine della mappatura.
  • @è il falco. È sempre nella riga centrale (4a riga) e nella prima colonna nell'input.
  • Lo spazio lasciato nelle barre verticali sull'ultima colonna è il punto in cui la nave deve arrivare. È sempre nella riga centrale (quarta riga) e nell'ultima colonna nell'input.

L'input può essere preso come una stringa a più righe, un array di stringhe, da STDIN o parametri di una funzione, oppure letto da un file.

Possibili manovre

Sei inseguito da TIE-Fighters, quindi devi sempre andare avanti. Esistono quindi tre modi in cui la nave può volare ad ogni passo:

  • - Inoltrare

  • / Avanti e gira a sinistra

  • \ Avanti e gira a destra

Ad esempio, questi sono percorsi validi:

@---

  --
 /  \ /
@    -

   -
  / \
 /   \
@     \

Come puoi vedere, c'è sempre esattamente una mossa per colonna. Il Falcon è un pezzo di spazzatura, quindi non può fare curve violente. Ciò significa che mosse come /\o non\/ sono consentite . Deve esserci almeno un puro avanzamento -tra due turni opposti. D'altra parte, è possibile girare in un modo per più passaggi consecutivi, come visto sopra.

Il Falcon si schianta se una mossa porta la nave a trovarsi in un punto in cui si trova un ostacolo. Ad esempio, queste mosse portano a crash:

@-#

@
 \
  #

  #
 /
@

Si noti che questo non è un arresto anomalo:

@-#
  \
   -

Produzione

È necessario generare lo stesso campo di asteroidi ASCII, con un percorso valido fino alla fine. Il Falcon deve essere stampato nel punto finale anziché nel punto iniziale.

Ad esempio, un output valido per l'esempio di input fornito in precedenza sarebbe:

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

Il tuo percorso non deve solo arrestare il falco. Non deve essere il percorso più breve possibile.

Puoi presumere che ci sarà sempre almeno un possibile percorso fino alla fine.

È possibile eseguire l'output su STDOUT, in un file o in qualsiasi altro equivalente, purché il campo di asteroidi sia stampato esattamente come in questo post (ad es. L'output di un elenco di coordinate per il percorso non è valido).

Casi test

  • Un normale campo di asteroidi

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

    Uscita possibile

    |   #####           #########  |
    | ######  #--------  ###   #   |
    |   # #  #/ #  ####\  #        |
     ---------      ##  \ #### ----@
    |#   #   #       ### \ ## /    |
    |##      ##      #### \ #/ #   |
    |####           ##### #-- ##   |
    
  • Campo di asteroidi iperregolari

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

    Uscita possibile

    |# # # # # # # # # # # # # # # |
    | # # # # # # # # # # # # # # #|
    |# # # # # # # # # # # # # # # |
     -# #-# #-# #-# #-# #-# #-# #--@
    |#\#/#\#/#\#/#\#/#\#/#\#/#\#/# |
    | #-# #-# #-# #-# #-# #-# #-# #|
    |# # # # # # # # # # # # # # # |
    
  • Nucleo della Morte Nera

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

    Uscita possibile

    |    #    #    #   --    #     |
    |  ---    #    #  / #\   -     |
    | /  #\   #    # /  # \ /#\    |
     -   # \  #    #/   #  - # ----@
    |    #  \ # ----    #    #     |
    |    #   \#/   #    #    #     |
    |    #    -    #    #    #     |
    
  • Trincea della Morte Nera

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

    Produzione

    |##############################|
    |##############################|
    |##############################|
     ------------------------------@
    |##############################|
    |##############################|
    |##############################|
    
  • Grotta degli asteroidi

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

    Uscita possibile

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

punteggio

R2D2 è impegnato a nuotare nelle paludi, quindi dovrai programmare il controller del Falcon da solo, il che è noioso. Pertanto vince il codice più breve .


@DJMcMayhem: tecnicamente la prima riga è "Introduzione";)
Alex A.

2
In un certo senso capisco come dovrebbe essere basato sugli esempi, ma la codifica delle mosse mi confonde ancora un po '. Se si guarda ad esempio all'esempio "iperregolare", ad eccezione della prima / ultima mossa, si sposta sempre in diagonale. Eppure ha -il percorso ad ogni turno, che è definito come una mossa "in avanti". Ma le mosse effettive sono sempre due diagonali-sinistra seguite da due diagonali-destra.
Reto Koradi,

@RetoKoradi Riesco a capire che non è poi così ovvio, ma l'idea di base è che tutte le mosse ti fanno viaggiare la larghezza di un personaggio verso destra. Il percorso deve apparire continuo, motivo per cui la mossa precedente / successiva dopo le svolte a destra e a sinistra è una riga sopra / sotto la precedente.
Fatalizza l'

@apsillers Entrambi sono validi, se ti capisco correttamente la tua risposta dovrebbe essere buona
Fatalizza l'

Risposte:


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)

Snippet eseguibile:

<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

Questa funzione accetta una singola stringa con newline. La funzione divide la stringa in un array usando l' ...operatore e ottiene l'indice per le (x,y)coordinate (33 * y) + x.

La funzione viene eseguita in modo ricorsivo, testando diverse mosse possibili per ogni spazio. Quando incontra un ostacolo, restituisce un valore falso e quando raggiunge lo spazio dell'obiettivo finale, ritorna true. (Nel codice golf, questo trueè creato da !console.log(...).)

Si noti che questo codice non utilizza lunghe serie di mosse con svolta a destra, ma le punteggia con mosse dritte. Cioè, fa la seconda opzione di seguito, non la prima:

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

Questo sembra essere legale, dal momento che -può venire legalmente prima o dopo un turno, quindi perché non entrambi contemporaneamente? Sembra particolarmente strano alla fine, quando la mossa finale è \ma viene visualizzata come @:

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

Il mio brutto gioco di golf preferito qui è l'abuso di argomenti predefinito con c=k=" ". Gli argomenti (i,l,c=" ")direbbero "usa la stringa " "per cse fnon viene fornito un terzo argomento". Tuttavia, facendo c=k=" ", diciamo "se cnon viene fornito, memorizza " "nella variabile globale ke quindi memorizza anche quel valore c". Poiché la ricorsione inizia con un solo argomento, kviene sempre inizializzata alla prima chiamata di funzione.

Leggermente ungolfed:

// `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]=" ");

}

@ vihan1086 Giusto, mi mancavano totalmente quegli spazi durante il golf-ifying. D: Anche il passaggio da un array a una stringa divisa è un bel cambiamento. Grazie. :) Ho anche apportato alcune altre modifiche (rendendo l'attuale carattere di spostamento un terzo argomento anziché determinato all'interno della funzione e memorizzandolo " "in una variabile) che ha portato il mio punteggio ancora più in basso.
apsillers,

7

C (programma completo), 249 247 235 byte

Questo è un programma completo che legge l'input da un file e invia il risultato a stdout. Il nome del file viene passato come parametro al programma.

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

Ungolfed:

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

Produzione:

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

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

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

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

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

Sembra che tu abbia perso il punto finale nel primo test.
Reto Koradi,

@RetoKoradi È -seguito da un \, ma \è coperto da @. (Il mio programma fa la stessa cosa.)
apsillers

1
@RetoKoradi Precedenti iterazioni di questo caso hanno gestito meglio quel caso. Sono +4 byte. Ho notato che la soluzione di apsillers si è comportata in modo simile, quindi ho deciso di risparmiare spazio.
Cole Cameron,

Vedo. Non mi sembra giusto, ma spetta all'OP decidere cosa è permesso. Ho visto che hanno dato una certa libertà su come sono rappresentate le mosse. Mi sarebbe piaciuto vedere una definizione chiara e unica sin dall'inizio. Sembrava un problema divertente, ma non è altrettanto interessante con l'ambiguità.
Reto Koradi,

3

Lisp comune, 303 byte

Mi sono divertito molto con questa sfida, è il primo compito di codegolf che ho fatto. Fondamentalmente c'è una semplice funzione ricorsiva che prova ogni mossa praticabile fino al raggiungimento della posizione finale.

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

Legge l'input da un file i nella directory di lavoro. Sono abbastanza sicuro che ci sia ancora spazio per miglioramenti.

Codice semplice

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

Uscita campione

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

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

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

2

ActionScript 3, 364 byte

L'ho diviso in due funzioni; uno per cambiare l'array in un array di array e uno ricorsivo per calcolare la traiettoria di volo.

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

Versione non golfata in un programma con un campo asteroide di esempio definito:

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);
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.