Eine Maus mit Dynamit

23

Du bist eine Maus. Ihre Mausefreunde wurden alle gefangen genommen und sind bewusstlos und in einem Labyrinth gefangen, das nur einen Ein- / Ausgang hat. Sie haben zufällig eine perfekte Karte des Labyrinths, sodass Sie eine Lösung finden können, um sie alle in Sicherheit zu bringen. Das Labyrinth ist jedoch mit einem Sicherheitssystem geschützt, das einen Alarm auslöst, wenn eine Schwelle von 1000erreicht wird, wodurch Sie gefangen genommen werden und Ihre Rettungsmission nicht bestehen.

Aus früheren Untersuchungen des Labyrinths geht hervor, dass jedes Feld, auf das Sie treten (dh jede horizontale oder vertikale Bewegung - Mäuse können sich nicht diagonal bewegen ) 1, dem Zähler des Sicherheitssystems hinzugefügt wird. Wenn Sie jedoch ein Gewicht tragen (entweder einen Dynamitblock oder einen bewusstlosen Mäusefreund), fügt es stattdessen hinzu, 2da es den zusätzlichen Druck erkennt. Der Eingangs- / Ausgangsplatz verfügt nicht über dieses Sicherheitssystem und wird daher nicht zur Theke hinzugefügt.

Sie haben einen unbegrenzten Vorrat an Dynamit, den Sie zum Eingang gebracht haben, sodass Sie einfach alle Wände sprengen können , um Ihre Freunde zu befreien. Aber Sie müssen vorsichtig sein, denn jede Explosion 50erhöht den Konter durch den Druck der Erschütterung. Außerdem können Sie jeweils nur eine Maus oder einen Dynamitblock tragen. Da jeder Dynamitblock nur einen Wandbereich zur Detonation bringen kann, bedeutet dies, dass Sie bei mehreren Wänden in einer Reihe mit leeren Händen zum Eingang zurückkehren müssen, um mehr zu erhalten.

Durchgearbeitetes Beispiel

Angenommen, unser Labyrinth sieht folgendermaßen aus:

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

Ich werde cfür die Theke verwenden. Wir beginnen bei der ENtrance, bewegen uns ein Feld nach links, während wir Dynamit tragen c=2. Wir zünden das Dynamit, um die Mauer zu sprengen c=52. Wir bewegen uns mit leeren Händen zwei Quadrate nach links, um zu kommen c=54, und stehen jetzt auf dem Quadrat der Maus. Wir Eheben unseren Freund auf und bewegen 3 Felder zurück zum Ausgang, aber das letzte Feld zählt nicht, da es keine Sensoren hat. Das sind also nur 2 Felder mit etwas auf dem Rücken. Das heißt, wenn wir den Ausgang mit der letzten Maus erreichen, c=58ist das weniger als 1000, und daher ist die Mission erfolgreich.

Herausforderung

Wenn Sie ein Eingabelabyrinth haben, geben Sie an, ob Sie als Mausheld alle gefangenen Mäuse innerhalb der oben genannten Einschränkungen erfolgreich retten können oder ob die Mission fehlgeschlagen ist.

Eingang

  • Ein 2D-Labyrinth in einem beliebigen zulässigen Format (mehrzeilige Zeichenfolge, Zeichenfolgenarray usw.).
  • Für diese Herausforderung werde ich #sowohl die Innen- als auch die Außenwände, Mdie Mausefreunde und Eden Eingang verwenden.
  • Der Eingang wird niemals unmittelbar an eine Innenwand angrenzen (es wird immer mindestens einen Raum geben, in dem Sie sich frei bewegen können).
  • Sie können beliebige druckbare ASCII-Zeichen ersetzen, sofern dies konsistent ist. Dies hat ermöglicht es Ihnen , gegen Außenwände zwei verschiedene Symbole für Innenwände zu verwenden, so lange , wie Sie Konsistenz zu erhalten (zB wenn Sie verwenden @für Innenwände statt, und lassen #für außen, jeder muss Innenwand sein @und jede Außenwand #).
  • Das Labyrinth wird immer vollständig von Wänden begrenzt, muss aber nicht rechteckig sein. Falls gewünscht, können Sie davon ausgehen, dass das Labyrinth mit Leerzeichen aufgefüllt ist, um rechteckige Eingaben vorzunehmen (optional).
  • Das Labyrinth kann Abschnitte enthalten, die ohne Dynamit nicht erreichbar sind.
  • Sie können die Außenwände des Labyrinths nicht dynamisieren.

Ausgabe

Ein wahrer / falscher Wert. Wahrheit für "Ja, die Maus kann jede andere Maus retten" oder Falsey für "Nein, das Alarmsystem wird ausgelöst".

Die Regeln

  • Es ist entweder ein vollständiges Programm oder eine Funktion zulässig.
  • Standardlücken sind verboten.
  • Dies ist daher gelten alle üblichen Golfregeln, und der kürzeste Code (in Byte) gewinnt.

Beispiele

Wahrheitsbeispiele, durch Leerzeilen getrennt.

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

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

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

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

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

Falsche Beispiele, durch Leerzeilen getrennt

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

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

###############
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMME#
###############
AdmBorkBork
quelle
3
Die Mäuse waren wütend (milder Spoiler)
Luis Mendo
3
@AlexA. Tut mir leid, dass du es von einem Fremden im Internet lernen musstest. ;-)
AdmBorkBork
2
Ich vermute, die meisten Leute würden es schwer haben, dies mit normalem Code zu lösen, geschweige denn, es zu spielen. Es ist eine große Herausforderung, an der ich momentan leider keine Zeit habe.
Moshe Katz
2
Ist es akzeptabel, ein anderes Zeichen für die Außenwände zu verwenden (da diese nicht zerstörbar sind)?
Tensibai
2
@ Moshe Katz , sicher hast du keine Zeit. Du willst das Mäuse einfach nicht retten.
msh210

Antworten:

19

Perl, 216 215 Bytes

Beinhaltet +2 für -0p

Geben Sie eine Eingabe für STDIN ein. Verwendung %für Außenwände, #für Innenwände, 0für Freiflächen, 8für Mäuse und rfür die Ausgangsposition. Die gesamten Boards müssen gepolstert sein, damit sie ein Rechteck bilden. Sie können die Beispiele wie folgt transformieren und ausführen:

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

Ersetzen Sie die \xhhEscapezeichen für die beanspruchte Punktzahl.

Das Programm kann komplexe Fälle nicht realistisch verarbeiten. Insbesondere kann es keinen der Fehlerfälle behandeln. Dies liegt daran, dass es einfach zu viele verschiedene Möglichkeiten gibt, die Innenwände in die Luft zu jagen oder Mäuse aufzunehmen, sodass die Suche zu umfangreich wird und zu viel Speicherplatz beansprucht, obwohl sie mindestens so intelligent ist, dass sie niemals denselben Status mehrmals verarbeitet. Das Drucklimit muss auf 100ungefähr erträgliche Laufzeiten und Speicherauslastung gesenkt werden .

Erläuterung

Ich benutze das Bitmuster eines Zeichens, um den Zustand eines Feldes darzustellen:

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)

Zum Beispiel muss sich der Held ( 01000000), der Dynamit ( 00000001) trägt, an einer Stelle befinden, die er gehen kann ( 00010000), und wir möchten, dass alle Werte in ASCII ( 00100000) gedruckt werden können . Das bitweise Nehmen oraller dieser Bitmasken ergibt, 01110001wofür der ASCII-Code ist q. Insgesamt wird dies ::

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

Das Programm berücksichtigt nur, dass sich der Held nach rechts bewegt (die später erläuterte Drehung kümmert sich um die anderen Richtungen). Die Bitmasken wurden sorgfältig ausgewählt, sodass der Held immer durch einen Buchstaben und einen Ort dargestellt wird, zu dem er sich mit einer Ziffer bewegen kann (mit Ausnahme des Helden, der zu Hause ein Opfer trägt, aber auch dies ist beabsichtigt, damit der Held nur einen Schritt fallen lässt das Opfer). Ein Held, der sich vorwärts bewegen kann, trifft also auf /\pL\d/. Die übereinstimmende Teilzeichenfolge muss so geändert werden, dass der Held und das, was er trägt, aus dem ersten Zeichen entfernt und zum zweiten hinzugefügt werden. Dies kann bitweise xormit demselben Wert für beide Zeichen erfolgen. Der xor-Wert besteht aus dem Hero-Bit ( 01000000), dem Dynamit-Bit ( 00000001) und dem Carry-Mouse-Bit ( 00000100). Zusammen sie orzu01000101Das ist ASCII E. So wird das Bewegen des Helden:

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

Der Held kann eine Mauer sprengen, wenn er direkt davor steht und Dynamit trägt ( q, soder y). Der Held verliert sein Dynamit ( xormit 00000001) und die Wand #verwandelt sich in eine Passage 0(oder mit 00010011)

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

Um die anderen Richtungen zu handhaben, wird das gesamte Board gedreht (gedrehtes Board endet in $o):

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

Neben dem Bewegen hat der Held noch eine Reihe anderer Entscheidungen, die er treffen kann:

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

Dies geschieht durch

y/xr|/ytx/

Das Brett ist fertig, wenn der Held zu Hause ist xund keine Opfer mehr zu retten sind (nein 2). Dies kann bequem mit getestet werden

/x/>/2/

Sobald das Board gelöst ist, möchte ich mich an diesen Zustand erinnern und ihn am Ende ausdrucken. Dafür trage ich das "ist gelöst" -Flag ein $\und drucke das am Ende des Programms ohne zu drucken aus $_, also

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

Die bei Druck 0 zu bearbeitenden Zustände bleiben erhalten @0, bei Druck 1 an @1usw. Der aktuelle Druck bleibt erhalten $%. Die Verwendung von $noder so ähnlich wäre kürzer, aber der Code funktioniert nicht, wenn die Variable nicht mit etwas initialisiert wird, da sich die Autovivierung ansonsten $nin eine ARRAY-Referenz ändert. Das Überblättern der Zustände bei einem bestimmten Druck erfolgt mit a forund nicht mit a mapbecause Mit a können forSie das Array erweitern, während es noch durchgeschleift ist, und die neuen Elemente aufnehmen. Dies ist erforderlich, da die Rotationen und Einzelfeldwahlen des Helden in 0-Zeit erfolgen und im selben Druckfeld enden. Die Schleife für einen gegebenen Druck erfolgt also durch

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

Dies wird wiederholt, bis der Druck 1000 erreicht oder eine Lösung gefunden wird:

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

Alles, was übrig bleibt, ist das Hinzufügen neu entdeckter Zustände zu ihren jeweiligen Druckarrays. Das wird per Subroutine erledigt f. Wir möchten nur einen Zustand hinzufügen, der noch nicht gesehen wurde. Zustände, die zuvor gesehen wurden, werden folgendermaßen beibehalten %a:

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

$nrepräsentiert den neuen Druck für einen Staat. Ich werde das aus dem Zustand ableiten, den die Regex-Variablen als Ergebnis der Aktion des Helden, die zu diesem Aufruf führt, noch haben:

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

Dies führt zu folgender Formel für $n:

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

Alle Ersetzungen erhalten einen rModifikator, sodass sie den geänderten Zustand zurückgeben und den aktuellen Zustand in $_Ruhe lassen. fwird dann bei diesem geänderten Zustand aufgerufen, so dass man Code wie folgt bekommt

f y/xr|/ytx/r;

Da bei der Berechnung der $nBedürfnisse die Regex-Variablen standardmäßig deaktiviert werden müssen, falls die Substitutionen nichts ändern (da die Bedingung zum Auslösen nicht erfüllt ist). Ich darf auch keine regulären Ausdrücke aus einer vorherigen Schleife aufnehmen. Daher werden die Ersetzungen in {}Blöcke eingeschlossen, um den regulären Ausdruckszustand zu speichern und wiederherzustellen. So bekommt man Aussagen wie

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

Insbesondere wird die Rotation so gewickelt, dass sie fohne reguläre Ausdrücke aufruft und einen Druckbeitrag von 0 erhält.

Das einzige, was Sie noch tun müssen, ist, @0am Anfang mit dem Anfangszustand zu beginnen

f$_

Dies ist in der Hauptschleife, so dass auch versucht wird, $_zu späteren Druckanordnungen hinzuzufügen , aber da der Anfangszustand bereits in ist, wird %anichts passieren.

Zusammen ergibt dies im Grunde die kürzeste Lösung unter Verwendung des Dijkstra-Algorithmus . Es gibt jedoch ein potenzielles Problem. Der aktuelle Code fügt keinen Status hinzu, wenn er mit einem niedrigeren Druck als bei der ersten Erkennung erneut erkannt wird. Ich war nicht in der Lage, ein Board zu konstruieren, das dies auslösen würde, aber ich konnte auch nicht beweisen, dass es unmöglich ist.

Tonne Hospel
quelle
3
Oh, faszinierend. Das ist deutlich kürzer als ich erwartet hatte. Können Sie ein bisschen Erklärung hinzufügen? Ich habe keine Ahnung, was Perl ist.
AdmBorkBork
3
@TimmyD Ok, die Erklärung wurde mit genügend Details hinzugefügt, damit auch ein Nicht-Perl-Programmierer zumindest einen Eindruck davon bekommt, wie es funktioniert
Ton Hospel
1
Sehr beeindruckend!
Emigna
Geniales Golfen, das ist wirklich beeindruckend ... Wenn ich denke, dass ich mit Perl nicht so schlecht Golf spielen kann, schaue ich mir Ihre Golfplätze an, und dieses Gefühl verschwindet ziemlich schnell!
Dada
13

JavaScript, 863 834 785 781 Bytes

Dank ETHproductions 29 Bytes gespart
Dank Jordan 53 Bytes gespart

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

Übernimmt die Eingabe als mehrzeilige Zeichenfolge.

Dies definiert eine anonyme Funktion, die eine rekursive Funktion verwendet f, um zu bestimmen, ob Sie den Alarm auslösen, bevor Sie alle Mäuse abrufen. fGibt zurück, 1000wenn der Druck über 1000 liegt (um endlose Rekursionen zu vermeiden), gibt den Druck zurück, wenn keine Mäuse mehr zu retten sind und die Maus im Ausgang ist, und gibt den minimalen Druck aller möglichen Bewegungen aus dem aktuellen Zustand zurück. Es verwendet ein Array L, um bereits besuchte Positionen zu verfolgen, L[pos]==0wenn es besucht wird, und undefiniert, wenn es nicht besucht wird. Dies ist möglicherweise nicht erforderlich, verhindert jedoch, dass die Maus unnötige Bewegungen ausführt und zumindest Rekursionsfehler verursacht. (Dies bedeutet, dass Sie neu definieren sollten, Lwenn Sie dies mehrmals testen.)

Hierbei wird das in der Frage verwendete Format verwendet. Es ist jedoch erforderlich, dass Sie für die Außenwände ein anderes Zeichen verwenden. (Alles andere als # MEmecd)

Mehr lesbare Version:

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
DanTheMan
quelle
Nutzlose Leerzeichen bei s in L|p > 999.
Yytsi
@TuukkaX Danke, dass du mich daran erinnert hast, das bytecount war schon für die Version ohne Leerzeichen.
DanTheMan
Sehen Sie, wenn Sie Bytes durch Umwickeln Sie den Code in sparen evalund das Ersetzen @mit $1(nicht sicher , ob dies funktionieren wird, aber Sie schreiben $1viel)
Cyoce
Ich denke, Sie könnten ein paar sparen, indem Sie feinen f=(...)=>s in L|p>999?1e3:!/M/.test(s,L[s]=0)&/E/.test(s)?p:Math.min(...
Einzeiler machen
@Cyoce Ich benutze $118 mal und .replace("@","$1")ist 18 Bytes. Ich sehe keine Möglichkeit, das durchzuziehen.
DanTheMan