Un topo con dinamite


23

Sei un topo. I tuoi amici del mouse sono stati tutti catturati, incoscienti e intrappolati in un labirinto che ha solo un'entrata / uscita. Ti capita di avere una mappa perfetta del labirinto, quindi puoi tracciare una soluzione per correre e portarli tutti in salvo. Tuttavia, il labirinto è sorvegliato da un sistema di sicurezza che attiverà un avviso se 1000viene raggiunta una soglia , causando la cattura e il fallimento della missione di salvataggio.

Dalle tue precedenti indagini sul labirinto, ogni quadrato che passi (cioè ogni movimento orizzontale o verticale - i topi non possono muoversi in diagonale ) aggiunge 1al contatore del sistema di sicurezza. Tuttavia, se stai trasportando un peso (o un blocco di dinamite o un amico del mouse incosciente), invece aggiunge 2perché rileva la pressione aggiuntiva. Il quadrato di entrata / uscita non ha questo sistema di sicurezza, quindi non si aggiunge al bancone.

Hai una scorta illimitata di dinamite che hai portato all'ingresso, quindi puoi semplicemente far saltare in aria tutte le pareti per liberare i tuoi amici. Ma devi essere cauto nel farlo, poiché ogni esplosione si aggiunge 50al contatore dalla pressione concussiva. Inoltre, puoi trasportare solo una cosa alla volta, un mouse o un blocco di dinamite. Poiché ogni blocco di dinamite può far esplodere solo uno spazio di muro, ciò significa che se ci sono più muri di fila, è necessario fare un viaggio a mani vuote all'entrata per prenderne di più.

Esempio elaborato

Supponiamo che il nostro labirinto sia simile al seguente:

######
#M# E#
######

Userò cper il contatore. Partiamo dalla EN-Trance, spostarsi di una sinistra piazza mentre trasportano la dinamite, c=2. Facciamo esplodere la dinamite per far esplodere il muro c=52. Spostiamo due quadrati a sinistra, a mani vuote, per ottenere c=54, e ora siamo in piedi sul quadrato del mouse. Raccogliamo il nostro amico e spostiamo di nuovo 3 quadrati sulla Exit, ma l'ultimo quadrato non conta poiché non ha sensori, quindi sono solo 2 quadrati con qualcosa sulla schiena. Ciò significa che quando raggiungiamo l'uscita con il mouse finale c=58, che è inferiore a 1000, e quindi la missione ha successo.

Sfida

Dato un labirinto di input, ottieni se tu, l'eroe del topo, riesci a salvare con successo tutti i topi intrappolati entro i vincoli descritti sopra o se la missione è fallita.

Ingresso

  • Un labirinto 2D in qualsiasi formato accettabile (stringa multilinea, matrice di stringhe, ecc.).
  • Per questa sfida, userò sia #per le pareti interne che esterne, Mper gli amici del topo e Eper l'ingresso.
  • L'ingresso non sarà mai immediatamente adiacente ad una parete interna (ci sarà sempre almeno uno spazio in cui muoversi liberamente).
  • Puoi sostituire qualsiasi carattere ASCII stampabile che desideri purché sia ​​coerente. Questo non consente di utilizzare due simboli differenti per le pareti interne contro le pareti esterne, fino a quando si mantiene la consistenza (ad esempio, se si sceglie di utilizzare @per le pareti interne, invece, e congedi #per esterni, ogni parete interna deve essere @e ogni parete esterna #).
  • Il labirinto sarà sempre completamente delimitato da pareti, ma non è necessariamente rettangolare. Se lo si desidera, si può presumere che il labirinto sia riempito di spazi per creare input rettangolari (opzionale).
  • Il labirinto può avere sezioni irraggiungibili senza dinamite.
  • Non puoi dinamite le pareti esterne del labirinto.

Produzione

Un valore di verità / falsità . Verità per "Sì, il mouse può salvare tutti gli altri mouse" o Falsey per "No, il sistema di allarme verrà attivato."

Le regole

  • È accettabile un programma completo o una funzione.
  • Sono vietate le scappatoie standard .
  • Si tratta di quindi si applicano tutte le normali regole del golf e vince il codice più breve (in byte).

Esempi

Esempi veritieri, separati da righe vuote.

#####
#M E#
#####

######
#M# E#
######

########
#E  # M#
#   #  #
#   #  #
#      #
########

#############################
#    ##      #       #      #
#  M ## M    #       #      #
#    ##      #   M   #    E #
#M   ##      #       #      #
#############################

###############
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMM MM#
#MMMMMMMMMMMME#
###############

Esempi di Falsey, separati da righe vuote

#############################
#M   ##      ##      ##     #
#  M ## M    ##      ##     #
#    ##      ##  M   ##   E #
#M   ##      ##      ##     #
#############################
#############################
                     ########
                     ########
                     #   #  #
                     # M # M#
                     ########

              #####
              # M #
              #####
              #####
              #####
              #####
###################
# # # ##   ## # # #
#M#M#M## E ##M#M#M#
# # # ##   ## # # #
###################
#######
######
#####
####
# M#
####

###############
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMME#
###############


3
@AlexA. Mi dispiace che tu abbia dovuto impararlo da uno sconosciuto su Internet. ;-)
AdmBorkBork,

2
Sospetto che la maggior parte delle persone avrebbe difficoltà a risolvere questo problema con un codice normale, figuriamoci a giocare a golf. È una grande sfida su cui purtroppo non ho tempo su cui lavorare.
Moshe Katz,

2
È accettabile avere un carattere diverso per i muri esterni (poiché non sono distruttibili)?
Tensibai,

2
@Moshe Katz , certo che non hai tempo. Semplicemente non vuoi salvare il Mäuse.
msh210,

Risposte:


19

Perl, 216 215 byte

Include +2 per -0p

Dare input su STDIN. Utilizzare %per pareti esterne, #per pareti interne, 0per spazi vuoti, 8per topi e rper la posizione di partenza. Tutte le schede devono essere imbottite in modo da formare un rettangolo. Puoi trasformare ed eseguire gli esempi come:

cat dynamite.txt | perl -p0e 's/.+/$a^=$&/egr;s//sprintf"%-*s",length$a,$&/eg;1while/\n/,s/^ *\K#|#(?= *$)|^ *.{@{-}}\K#|\A[^\n]*\K#|#(?=[^\n]*\n\z)|#(?=.{@{-}} *$)/%/sm;y/ EM/0x2/' | dynamite.pl

dynamite.pl:

#!/usr/bin/perl -0p
sub f{@a{@_}||=push@{$%+($&?$1?50:$&=~8?0:$&&"5"?2:1:0)},@_}f$_;for(@{$%}){f y/xr|/ytx/r;{f s/\pL\d/$&^(E&$&)x2/er}{f s/(q|s|y)#/$&^"\x01\x13"/er}my$o;{$\|=/x/>/2/;$o.="
"while s/.$/$o.=$&,""/meg}f$o}$%++>999|$\||redo}{

Sostituisci le \xhhfughe per il punteggio richiesto.

Il programma non può realisticamente gestire casi complessi. In particolare non è in grado di gestire nessuno dei casi di errore. Questo perché ci sono troppi modi diversi di far esplodere le pareti interne o raccogliere topi in modo che la ricerca diventi troppo ampia e usi troppa memoria anche se almeno è abbastanza intelligente da non elaborare mai lo stesso stato più volte. Il limite di pressione deve essere abbassato a100 circa per runtime e sopportabilità della memoria piuttosto sopportabili.

Spiegazione

Uso lo schema di bit di un personaggio per rappresentare lo stato di un campo:

contains victim: 0000 0010
has hero:        0100 0000
carry dynamite   0000 0001
carry mouse      0000 0100
home             0000 1000
walkable         0001 0000 (not really needed but results in shorter regexes)
make printable   0010 0000
wall             0010 xxxx (bit patterns not very important,
permawall        0010 xxxx  just avoid letters and digits)

Ad esempio, l'eroe ( 01000000) che trasporta dynamite ( 00000001) deve trovarsi in un punto in cui può camminare ( 00010000) e vogliamo che tutti i valori siano ASCII stampabili ( 00100000). Prendendo il bit orper bit di tutte queste maschere di bit si dà 01110001quale è il codice ASCII per q. In totale questo diventa:

p: hero                     r hero on victim
q: hero carrying dynamite   s hero carrying dynamite on victim
t: hero carrying mouse      v hero carrying mouse on victim

x : hero at home
y : hero at home carrying dynamite
| : hero at home carrying mouse

0: empty  without hero
8: home   without hero
2: victim without hero

%: permanent wall
#: normal wall

Il programma considererà solo l'eroe che si sposta verso destra (la rotazione spiegata in seguito si occuperà delle altre direzioni). Le maschere di bit sono state accuratamente scelte in modo tale che l'eroe sia sempre rappresentato da una lettera e da un punto in cui può spostarsi di una cifra (tranne l'eroe a casa che trasporta una vittima, ma di nuovo questo è intenzionale in modo che l'unica mossa dell'eroe sarà quella di far cadere la vittima). Quindi un eroe che può andare avanti è abbinato da /\pL\d/. La sottostringa abbinata deve essere modificata in modo tale che l'eroe e ciò che sta trasportando vengano rimossi dal primo personaggio e aggiunti al secondo, cosa che può essere fatta con un bit xora bit con lo stesso valore per entrambi i personaggi. Il valore xor è costituito dal bit hero ( 01000000), dal bit dinamite ( 00000001) e dal bit carry mouse ( che è ASCII00000100 ). Insieme ora01000101E . Quindi spostando l'eroe diventa:

s/\pL\d/$&^(E&$&)x2/e

L'eroe può far saltare un muro se è in piedi di fronte a essa e sta portando la dinamite ( q, so y). L'eroe perderà la sua dinamite ( xorcon 00000001) e il muro #cambierà in un passaggio 0(o con 00010011), quindi

s/(q|s|y)#/$&^"\x01\x13"/e

Per gestire le altre direzioni, l'intera scheda viene ruotata (la scheda ruotata finisce in $o):

my$o;$o.="\n"while s/.$/$o.=$&,""/meg

Oltre a spostare l'eroe ha anche una serie di altre scelte che può fare:

When at home, pick up dynamite:                   x -> y
When on victim not carrying anything pick him up: r -> t
When at home carrying a victim, drop him off:     | -> x

Questo è fatto da

y/xr|/ytx/

Il tabellone è finito se l'eroe è in casa senza nulla ( x) e non ci sono più vittime da salvare (no 2). Questo può essere comodamente testato usando

/x/>/2/

Una volta risolto il problema, voglio ricordare questo stato e alla fine stamparlo. Per questo porto il flag "è risolto" $\e lo stampa alla fine del programma senza stampare $_, quindi

$\|=/x/>/2/  ...   }{

Gli stati da elaborare alla pressione 0 vengono mantenuti @0, alla pressione 1 su @1ecc. Viene mantenuta la pressione corrente $%. L'uso $no qualcosa del genere sarebbe più breve, ma il codice non funziona se la variabile non è inizializzata su qualcosa perché l'autovivificazione cambierà altrimenti $nin un riferimento ARRAY. Il passaggio sugli stati a una certa pressione viene eseguito utilizzando un fore non un mapperché con a forpuoi estendere l'array mentre è ancora in loop e raccoglierà i nuovi elementi. Ciò è necessario perché le rotazioni e le scelte a campo singolo dell'eroe avvengono in 0 tempo e finiscono nella stessa matrice di pressione. Quindi il ciclo per una data pressione viene eseguito da

for(@{$%}){...}

Questo viene ripetuto fino a quando la pressione raggiunge 1000 o viene trovata una soluzione:

$%++>999|$\||redo

Non resta che aggiungere nuovi stati scoperti ai rispettivi array di pressione. Ciò avverrà tramite subroutine f. Vogliamo solo aggiungere uno stato se non è stato ancora visto. Gli stati che sono stati visti prima sono mantenuti in questo %amodo:

sub f{@a{@_}||=push@$n, @_}

$nrappresenta la nuova pressione per uno stato. Deriverò dallo stato che le variabili regex hanno ancora come risultato dell'azione dell'eroe che porta a questa chiamata:

if $1 is set it was from s/(q|s|y)#/$&^"\x01\x13"/e which blows a wall -> 50
else if $& is set it was from s/\pL\d/$&^(E&$&)x2/e, so the hero moves.
    if $& contains 8 the hero went home -> 0
    else if the hero has carry bits (5) -> 2
    else                                   1
else ($& was not set) it was from y/xr|/ytx/r -> 0

Questo porta alla seguente formula per $n:

$%+($&?$1?50:$&=~8?0:$&&"5"?2:1:0)

Tutte le sostituzioni ottengono un rmodificatore in modo da restituire lo stato modificato e lasciare solo lo stato corrente $_. fviene quindi chiamato su questo stato modificato, in modo da ottenere un codice simile

f y/xr|/ytx/r;

Poiché il calcolo delle $nesigenze ha bisogno delle variabili regex, esse devono essere impostate per impostazione predefinita nel caso in cui le sostituzioni non cambino nulla (perché la condizione per attivarle non è soddisfatta). Inoltre, non devo prendere alcuna variabile regex da un ciclo precedente. Pertanto le sostituzioni sono racchiuse in {}blocchi per salvare e ripristinare lo stato regex. È così che ottieni dichiarazioni come

{f s/\pL\d/$&^(E&$&)x2/er}

In particolare la rotazione è così avvolta che chiama f senza variabili regex e ottiene un contributo di pressione di 0.

L'unica cosa ancora da fare è @0innescare con lo stato iniziale all'inizio

f$_

Questo è nel circuito principale, quindi tenta anche di aggiungere $_ulteriori array di pressione, ma poiché lo stato iniziale sarà già presente%a nulla, non accadrà.

Insieme, tutto questo fondamentalmente trova la soluzione più breve usando l'algoritmo di Dijkstra . C'è un potenziale problema però. Il codice corrente non aggiungerà uno stato se viene riscoperto con una pressione inferiore rispetto al primo rilevamento. Non sono stato in grado di costruire una scheda che lo innescasse, ma non sono stato in grado di dimostrare che sia impossibile.


3
Ooo, intrigante. Questo è significativamente più breve di quanto mi aspettassi una risposta. Puoi aggiungere un po 'di spiegazione? Non sto davvero scherzando con Perl.
AdmBorkBork,

3
@TimmyD Ok, spiegazione aggiunta con abbastanza dettagli in modo che anche un programmatore non perl dovrebbe avere almeno un'impressione di come funziona
Ton Hospel,

1
Molto impressionante!
Emigna,

Giocare a golf fantastico, è davvero impressionante ... Quando penso di non essere così male a giocare a golf con Perl, guardo i tuoi giochi a golf e quella sensazione scompare abbastanza velocemente!
Dada,

13

JavaScript, 863 834 785 781 byte

Risparmiato 29 byte grazie a ETHproductions
Risparmiato 53 byte grazie alla Giordania

L=[]
f=(S,r="",R="",p=0,s=S.replace(RegExp(r),R),l=`((.|\n){${s.split`
`[0].length}})`,q=p+1,o=p+2,n=p+50)=>s in L|p>999?1e3:!/M/.test(s,L[s]=0)&/E/.test(s)?p:Math.min(...[[/ E/,"me",q],[/ E/,"de",o],[/ME/,"ce",q],[/E /,"em",q],[/E /,"ed",o],[/EM/,"ec",q],[`E${l} `,"e$1m",q],[`E${l} `,"e$1d",o],[`E${l}M`,"e$1c",q],[` ${l}E`,"m$1e",q],[` ${l}E`,"d$1e",o],[`M${l}E`,"c$1e",q],[/ m/,"m ",q],[/m /," m",q],[`m${l} `," $1m",q],[` ${l}m`,"m$1 ",q],[/ (d|c)/,"$1 ",o],[/(d|c) /," $1",o],[`(d|c)${l} `," $2$1",o],[` ${l}(d|c)`,"$3$1 ",o],[/d#/,"m ",n],[/#d/," m",n],[`#${l}d`," $1m",n],[`d${l}#`,"m$1 ",n],[/mM/," c",q],[/Mm/,"c ",q],[`M${l}m`,"c$1 ",q],[`m${l}M`," $1c",q],[/[mc]e/," E",p],[/e[mc]/,"E ",p],[`e${l}[mc]`,"E$1 ",p],[`[mc]${l}e`," $1E",p]].map(a=>f(s,...a)))
F=s=>f(s)<1e3

Accetta l'input come stringa multilinea.

Questo definisce una funzione anonima che utilizza una funzione ricorsiva fper determinare se si scatta l'allarme prima di recuperare tutti i topi. fritorna 1000se la pressione è superiore a 1000 (per evitare la ricorsione infinita), restituisce la pressione se non ci sono più topi da salvare e il mouse in uscita e restituisce la pressione minima di tutti i possibili movimenti dallo stato corrente in caso contrario. Utilizza un array Lper tenere traccia delle posizioni già visitate dove L[pos]==0se è visitato e indefinito se non lo è. Questo potrebbe non essere necessario, ma impedisce al mouse di fare mosse inutili e di lanciare almeno errori di ricorsione. (Questo significa che dovresti ridefinireL se lo stai testando più volte)

Questo utilizza il formato nella domanda diverso da quello che richiede di utilizzare un carattere diverso per i muri esterni. (Qualsiasi cosa diversa da # MEmecd)

Versione più leggibile:

stateList = []
f=(s,regex="",replacement="",pressure=0,state=s.replace(regexp(regex),replacement),line=`((.|\n){${state.split("\n")[0].length}})`)=>{
    if (state in stateList || pressure > 999) return 1e3
    if (!/M/.test(state) && /E/.test(state)) return pressure

    stateList[state] = 0

    return [
        [/ E/,"me",pressure+1],
        [/ E/,"de",pressure+2],
        [/ME/,"ce",pressure+1],
        [/E /,"em",pressure+1],
        [/E /,"ed",pressure+2],
        [/EM/,"ec",pressure+1],
        [`E${line} `,"e$1m",pressure+1],
        [`E${line} `,"e$1d",pressure+2],
        [`E${line}M`,"e$1c",pressure+1],
        [` ${line}E`,"m$1e",pressure+1],
        [` ${line}E`,"d$1e",pressure+2],
        [`M${line}E`,"c$1e",pressure+1],
        [/ m/,"m ",pressure+1],
        [/m /," m",pressure+1],
        [`m${line} `," $1m",pressure+1],
        [` ${line}m`,"m$1 ",pressure+1],
        [/ ([dc])/,"$1 ",pressure+2],
        [/([dc]) /," $1",pressure+2],
        [`([dc])${line} `," $2$1",pressure+2],
        [` ${line}([dc])`,"$3$1 ",pressure+2],
        [/d#/,"m ",pressure+50],
        [/#d/," m",pressure+50],
        [`#${line}d`," $1m",pressure+50],
        [`d${line}#`,"m$1 ",pressure+50],
        [/mM/," c",pressure+1],
        [/Mm/,"c ",pressure+1],
        [`M${line}m`,"c$1 ",pressure+1],
        [`m${line}M`," $1c",pressure+1],
        [/[mc]e/," E",pressure],
        [/e[mc]/,"E ",pressure],
        [`e${line}[mc]`,"E$1 ",pressure],
        [`[mc]${line}e`," $1E",pressure]
    ].map(a=>f(state,...a)).reduce((a,b)=>a-b<0?a:b) //reduce used for support in more browsers.
}
s=>f(s)>1e3

Spazi vuoti inutili a s in L|p > 999.
Yytsi,

@TuukkaX Grazie per avermelo ricordato, il resoconto era per la versione senza gli spazi già.
DanTheMan,

Vedi se riesci a salvare byte avvolgendo il codice evale sostituendolo @con $1(non sono sicuro che funzioni, ma scrivi $1molto)
Cyoce

Penso che potresti risparmiare un sacco facendo funa f=(...)=>s in L|p>999?1e3:!/M/.test(s,L[s]=0)&/E/.test(s)?p:Math.min(...
riga

@Cyoce lo uso $118 volte, ed .replace("@","$1")è 18 byte. Non vedo un modo per farlo.
DanTheMan,
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.