Voglio vederti morire di sete


12

Sei un viaggiatore che attraversa il deserto tra due città. Non puoi trasportare abbastanza acqua per attraversare senza fermarti. Questa è una variante di un puzzle classico.

Le regole

Un deserto si presenta così: una griglia WxH di spazio per lo più vuoto. Lo spazio segnato Sè dove inizi, Eè dove vuoi finire e un quadrato contrassegnato con il numero N contiene N unità di acqua. I quadrati contrassegnati con una .sospensione zero acqua.

.....................................
........S............................
.....................................
.........7...........................
.....................................
.......................3.............
.....5...............................
................................2....
.....................................
.....................................
.....................................
...............................E.....
.....................................
....................7................
.....................................
.....................................

Si inizia da S con 5 unità di acqua.

Puoi trasportare al massimo 5 unità di acqua.

Ogni turno ti

  1. sposta di un quadrato in alto, in basso, a sinistra o a destra,
  2. consuma 1 unità di acqua che stai trasportando,
  3. raccogliere o rilasciare un numero di unità di acqua.

Un turno è notata così: (direction)(+|-)(units of water), +indica che si sta raccogliendo l'acqua, -che si sta farlo cadere.

Giri di esempio:

D+0        Move Down
R+0        Move Right
D+2        Move Down, pick up two units of water.
U-1        Move Up, drop one unit of water.

Se esegui queste mosse iniziando da S nell'esempio sopra, il deserto si presenta così dopo.

.....................................
........S............................
.........1...........................
.........5...........................
.....................................
.......................3.............
.....5...............................
................................2....
.....................................
.....................................
.....................................
...............................E.....
.....................................
....................7................
.....................................
.....................................

Non puoi raccogliere più acqua di quella che è già sulla tua piazza. Quando raccogli l'acqua, deduci quel numero di unità dal conteggio delle tessere.

Puoi raccogliere solo acqua per contenere un massimo di 5 unità.

Nessuna tessera può contenere più di 9 unità, ad eccezione di S che contiene unità di infinito.

Puoi far cadere solo quanta più acqua stai trattenendo.

L'acqua sul terreno rimane invariata fino a quando non la riprendi.

Se ritorni a S, puoi raccogliere qualsiasi quantità di acqua senza esaurirla.

Se raggiungi E, allora vinci . Vinci ancora se consumi l'ultima unità di acqua su E.

Se, dopo il tuo turno, non hai acqua e non sei su E, muori .

Ingresso e uscita

Il tuo programma riceverà una mappa iniziale di dimensioni arbitrarie su STDINcome arte ASCII nel formato sopra. Puoi supporre che sia rettangolare, cioè tutte le linee della stessa lunghezza, che ci siano esattamente uno Se un Equadrato, tutte le linee sono terminate \ne l'intero STDIN sarà conforme a questa regex:/^[SE1-9\.\n]+$/

Il programma scriverà il seguente output su STDOUT:

  1. l'elenco delle mosse,
  2. lo stato finale della mappa.

È possibile visualizzare l'elenco delle mosse in qualsiasi formato conveniente.

Lo stato finale della mappa verrà stampato nello stesso formato dell'input, tranne per il fatto che mostrerà inoltre il percorso che hai intrapreso nel deserto contrassegnando tutte le tessere visitate con #, se quella tessera non contiene acqua e non è S o E (es. lo è .).

Ingresso ESEMPIO :

.....S.
.......
.......
E......
....8..

ESEMPIO output vincente:

D+0
D+0
D+0
D+0
L+5
L+0
L+0
L+0
L+0
U+0
.....S.
.....#.
.....#.
E....#.
####3#.

Nontriviality

Quando pubblichi il tuo codice, pubblica un input di mappa di esempio in cui il tuo codice trova una soluzione per la quale soddisfa le seguenti condizioni di non banalità:

  • S ed E sono distanti almeno 10 mosse.
  • Ogni quadrato che inizialmente contiene N unità di acqua deve essere circondato da un bordo di larghezza N in cui sono presenti tutti i quadrati .(niente acqua, non S o E)

ESEMPIO

........2.
..........
..........
S.1..2....
..........
..........
........1.
..3.......
.........E

Se aumenti la quantità di acqua su qualsiasi piastrella, quanto sopra diventa banale.

Requisiti

Presumibilmente il tuo programma incontrerà una serie di tentativi falliti prima di trovare una soluzione, se presente.

  1. Il tuo programma deve eventualmente risolvere qualsiasi input risolvibile.
  2. Voglio vederti morire - il tuo programma produrrà le mosse e la mappa finale del percorso verso la morte per ogni tentativo fallito di trovare una soluzione.
  3. Se trovi una soluzione vincente, stampa l'output completo per quello e termina.
  4. Esegui fino a quando non viene trovata una soluzione, ma non tentare la stessa soluzione due volte : tutti i decessi dovrebbero avvenire secondo percorsi distinti.
  5. Usa questo un input di test:

(richiede almeno una mossa per rilasciare una cache d'acqua ad un certo punto medio).

 S........
 .........
 .........
 ........E

Vince il codice più breve che viene pubblicato con un input dimostrativo non banale che risolve .


Questo deve essere chiarito per specificare se il programma deve essere in grado di risolvere qualsiasi mappa risolvibile o se deve funzionare solo per una mappa. Incoraggerei sicuramente il primo; nel caso di una mappa, sarà più semplice codificare la soluzione piuttosto che calcolarla.

A cura di chiarezza. Sì, vinci se hai più di zero acqua e sì, il tuo programma deve risolvere tutti gli input risolvibili.
spruzzo il

Cosa ti impedisce di usare un algoritmo come A * e di rilasciare un percorso di 5 unità su ciascuna tessera in cui vai successivamente e torni all'inizio se passi su una tessera senza 5 acqua?
Blu,

Niente. Vai avanti.
spruzzo il

La strategia "portare tutta l'acqua dalla S" dovrebbe funzionare, sebbene sarà terribilmente noiosa. Considera S.,.,.,.,. E .... E dove, ed e sono davvero punti. Le virgole sono dove tieni gli stash lungo la strada, e 'e' è dove devi avere 5 acqua per fare una corsa per E. 4 passi per spostare 1 acqua alla prima virgola (E + 0 E-1 W + 0 W + 4). 16 passaggi per spostare 1 acqua alla seconda virgola. 52 al terzo, 160 al quarto, 484 per far cadere 1 acqua per e e tornare ai passi di S. 1926 e tu stai trasportando 5 acqua, 5 in più per correre verso E, 1931 passi. Ogni due passaggi del percorso triplica efficacemente la lunghezza della soluzione.
Sparr,

Risposte:


12

Perl, 299 + 1 = 300 254 + 1 = 255 byte

Questo sarà quasi certamente battuto da una delle lingue del golf quando le persone vedranno il mio algoritmo :-)

Esegui con -n(penalità di 1 byte).

La versione precedente non era del tutto conforme alle specifiche perché lasciava più acqua sulla mappa e non la mostrava nella versione finale della mappa; cambiando l'algoritmo per far fronte a quella situazione, sono riuscito a golfizzare un po 'più piccolo nel processo.

push@a,[split//];($s,$t)=(length$`,$.)if/S/;($e,$f)=(length$`,$.)if/E/}{$_.=$s<$e?($s++,R):$s>$e?($s--,L):$t<$f?($t++,D):($t--,U);$\=reverse$";$".=y/LRUD/RLDU/r.Y.reverse.($"=~y/Y/X/r);$a[$t-1][$s]=~y/./#/;$\.=$s-$e||$t-$f?redo:$_;print map{join'',@$_}@a

Esempio (ho aggiunto interruzioni di linea all'output per evitare la necessità di scorrere e mostrare la struttura):

E .....
# .....
# .....
# .....
#####S
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUXDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUUXDDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUXDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUUUYDDDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUXDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUUYDDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUYDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLYRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLYRRRR
 LXR LLXRR LXR LLLYRRR LXR LLYRR LYR LLLLLUUUU

Nella notazione usata dal programma, il movimento è rappresentato tramite L, R, U, e Dper sinistra, su, destra, giù rispettivamente. Per impostazione predefinita, raccogli 1 unità di acqua dopo ogni mossa, ma questa può essere modificata aggiungendo un personaggio:

  • X: rilascia 2 unità di acqua, invece di raccoglierne 1
  • Y: rilascia 1 unità di acqua, invece di raccoglierne 1
  • (es. spazio): rabbocco completo sull'acqua (uscita solo dopo uno spostamento in S; il programma viene anche emesso con uno spazio iniziale, il che ha senso perché si inizia in pieno sull'acqua)

Come puoi vedere, è possibile attraversare questa mappa (piuttosto sterile) senza alcun pool aggiuntivo. In effetti, è possibile ottenere qualsiasi distanza in base a queste regole, su qualsiasi mappa, senza l'aiuto di acqua preimpostata. Come tale, questo algoritmo ignora semplicemente qualsiasi acqua preimpostata, il che significa che non devo sprecare byte cercando di gestirlo. Questo significa anche che non vedrai morire il robot, scusa. Non si sposta mai oltre l'intervallo in cui sa che è garantito per sopravvivere.

Il motivo per cui abbiamo bisogno di entrambi Xe Y(e un po 'di codice extra per assicurarci di avere la Xmaggior parte della strategia, ma l'occasionale Yalla fine) è che le specifiche richiedono l'output della versione finale della mappa. Il modo più semplice per implementarlo è lasciare la mappa completamente intatta (a parte il nostro percorso attraverso quadrati inizialmente vuoti), specialmente come un quadrato che inizia con l' 9acqua finirebbe con 10(rompendo l'arte ASCII) se si trovasse sul percorso e abbiamo usato soloX, e quindi dobbiamo trovare una soluzione che eviti di far cadere altra acqua sulla mappa. L'algoritmo qui avrebbe "naturalmente" lasciato cadere 1 unità aggiuntiva di acqua su ogni quadrato del percorso; come tale, la penultima volta che visitiamo ogni piazza, riduciamo la quantità di acqua caduta di 1 tramite l'uso di Y anziché X, così che durante la nostra visita finale, dreniamo la piazza alla sua quantità originale di acqua, piuttosto che lasciandolo leggermente più umido rispetto a quando abbiamo iniziato.

Consiglio di non eseguirlo su una grande mappa, perché ha prestazioni O (2 ^ n) (anche se il bot non muore mai di sete, è plausibile pensare che morirebbe di fame usando una strategia come questa.)

Versione leggibile:

# implicit with -n: read a line of input into $_
push @a, [split //]; #/ split $_ into characters, store at the end of @a
($s,$t) = (length$`,$.) if /S/; # if we see S, store its coordinates
($e,$f) = (length$`,$.) if /E/  # if we see E, store its coordinates
}{ # Due to -n, loop back to start if there are more lines.

# From here onwards, $" stores the partial solution this iteration;
#                    $\ stores the partial solution last iteration;
#                    $_ stores the path from ($s,$t) to S.
# At the start of the program, $" is a space, $\ and $_ are empty.

$_ .=  # Work out the next step on the path:
  $s < $e ? ($s++,R) # if too far left, move right, record that in $_;
: $s > $e ? ($s--,L) # if too far right, move left, record that in $_;
: $t < $f ? ($t++,D) # if too far up,    move down, record that in $_;
: ($t--,U);          # in other cases we must be too far down.
$\ = reverse $";     # Store last iteration; $" is constructed backwards.
$" .=                # Extend $" by appending
  y/LRUD/RLDU/r .    # the path from ($s, $t) back to S;
  Y .                # a literal 'Y';
  reverse .          # $/ backwards (i.e. the path from S to ($s, $t);
  ($"=~y/Y/X/r);     # a copy of $" with all 'Y' changed to 'X'.
$a[$t-1][$s] =~      # At the current path coordinate,
  y/./#/;            # replace any '.' with '#'.
$\ .=                # Start appending to $\;
  $s-$e||$t-$f?redo  # if we're not at E, abort that and jump back to {{,
: $_;                # otherwise append $_ (the path from S to E).
print map            # For each element of some array
  {join'',@$_}       # output its concatenated elements
  @a                 # specifying that array as @a.
# Implicitly: print $\ (which specifies the sort of newline print uses).

L'alluvione che riempie il mondo in una spirale sarebbe meno codice del tuo blocco di condizionali per la direzione per cercare l'uscita?
Sparr,

1
Non penso che sarebbe (anche se è possibile che ci sia un modo in cui non sto vedendo; è certamente un'idea che vale la pena considerare). Devi ancora avere a che fare con quattro direzioni e ora devi anche occuparti del bordo della mappa, cosa che non è un problema in questa versione dell'algoritmo.

Buon punto, re bordi. Penso che potrebbe essere fatto su una mappa infinita.
Sparr,
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.