Une souris avec de la dynamite

23

Tu es une souris. Vos amis souris ont tous été capturés et sont inconscients et piégés dans un labyrinthe qui n'a qu'une seule entrée / sortie. Vous avez une carte parfaite du labyrinthe, vous pouvez donc trouver une solution pour vous précipiter et les transporter tous en sécurité. Cependant, le labyrinthe est gardé avec un système de sécurité qui déclenchera une alerte si un seuil de 1000est atteint, vous faisant être capturé et échouer votre mission de sauvetage.

D'après vos enquêtes précédentes sur le labyrinthe, chaque carré que vous faites (c'est-à-dire chaque mouvement horizontal ou vertical - les souris ne peuvent pas se déplacer en diagonale ) s'ajoute 1au compteur du système de sécurité. Cependant, si vous portez un poids (soit un bloc de dynamite ou un ami souris inconscient), il ajoute à la place 2car il détecte la pression supplémentaire. La place d'entrée / sortie n'a pas ce système de sécurité, et ne s'ajoute donc pas au comptoir.

Vous avez un approvisionnement illimité de dynamite que vous avez apporté à l'entrée, vous pouvez donc simplement faire sauter tous les murs pour libérer vos amis. Mais vous devez être prudent avec cela, car chaque explosion ajoute 50au compteur de la pression concussive. De plus, vous ne pouvez transporter qu'une seule chose à la fois, soit une souris ou un bloc de dynamite. Étant donné que chaque bloc de dynamite ne peut faire exploser qu'un espace de mur, cela signifie que s'il y a plusieurs murs d'affilée, vous devez faire un voyage les mains vides à l'entrée pour en saisir plus.

Exemple élaboré

Supposons que notre labyrinthe ressemble à ceci:

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

Je vais utiliser cpour le comptoir. Nous commençons à l' Eentrée, nous nous déplaçons d'un carré à gauche tout en portant de la dynamite,c=2 . Nous faire exploser la dynamite pour faire exploser le mur, c=52. Nous déplaçons deux carrés vers la gauche, les mains vides, pour obtenir c=54, et nous sommes maintenant sur le carré de la souris. Nous ramassons notre ami et replaçons 3 carrés dans le Exit, mais le dernier carré ne compte pas car il n'a pas de capteurs, il n'y a donc que 2 carrés avec quelque chose sur le dos. Cela signifie que lorsque nous atteignons la sortie avec la souris finale c=58, ce qui est inférieur à 1000, et donc la mission réussit.

Défi

Étant donné un labyrinthe d'entrée, affichez si vous, le héros de la souris, pouvez sauver avec succès toutes les souris piégées dans les limites décrites ci-dessus, ou si la mission est un échec.

Contribution

  • Un labyrinthe 2D dans n'importe quel format acceptable (chaîne multiligne, tableau de chaînes, etc.).
  • Pour ce défi, je vais utiliser #à la fois pour les murs intérieurs et extérieurs, Mpour les amis souris, etE pour l'entrée.
  • L'entrée ne sera jamais immédiatement adjacente à un mur intérieur (il y aura toujours au moins un espace pour se déplacer librement).
  • Vous pouvez remplacer tous les caractères ASCII imprimables que vous souhaitez tant qu'il est cohérent. Cela ne vous permet d'utiliser deux symboles différents pour murs intérieurs contre les murs extérieurs, tant que vous maintenez la cohérence (par exemple, si vous choisissez d'utiliser @pour les murs intérieurs à la place, et le congé #pour l' extérieur, chaque mur intérieur doit être @et chaque mur extérieur #).
  • Le labyrinthe sera toujours entièrement délimité par des murs, mais n'est pas nécessairement rectangulaire. Si vous le souhaitez, vous pouvez supposer que le labyrinthe est rempli d'espaces pour effectuer une entrée rectangulaire (facultatif).
  • Le labyrinthe peut avoir des sections inaccessibles sans dynamite.
  • Vous ne pouvez pas dynamiter les murs extérieurs du labyrinthe.

Sortie

Un vrai / falsey valeur . Truthy pour "Oui, la souris peut sauver toutes les autres souris" ou Falsey pour "Non, le système d'alarme sera déclenché."

Les règles

  • Un programme complet ou une fonction sont acceptables.
  • Failles standard sont interdites.
  • Il s'agit de donc toutes les règles de golf habituelles s'appliquent et le code le plus court (en octets) l'emporte.

Exemples

Exemples véridiques, séparés par des lignes blanches.

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

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

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

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

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

Exemples de Falsey, séparés par des lignes vides

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

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

###############
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMME#
###############
AdmBorkBork
la source
3
Les souris étaient furieuses (spoiler léger)
Luis Mendo
3
@AlexA. Désolé d'avoir dû l'apprendre d'un inconnu sur Internet. ;-)
AdmBorkBork
2
Je soupçonne que la plupart des gens auraient du mal à résoudre ce problème avec un code normal, sans parler de jouer au golf. C'est un grand défi sur lequel je n'ai malheureusement pas le temps de travailler actuellement.
Moshe Katz
2
Est-il acceptable d'avoir un caractère différent pour les murs extérieurs (car ils ne sont pas destructibles)?
Tensibai
2
@Moshe Katz , bien sûr, vous n'avez pas le temps. Vous ne voulez tout simplement pas sauver le Mäuse.
msh210

Réponses:

19

Perl, 216 215 octets

Comprend +2 pour -0p

Donnez votre avis sur STDIN. Utiliser %pour les murs extérieurs, #pour les murs intérieurs, 0pour les espaces vides, 8pour les souris et rpour la position de départ. Toutes les planches doivent être rembourrées pour former un rectangle. Vous pouvez transformer et exécuter les exemples comme:

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

Remplace le \xhh évasions pour le score réclamé.

Le programme ne peut pas gérer de manière réaliste les cas complexes. En particulier, il ne peut gérer aucun des cas de défaillance. En effet, il existe simplement trop de façons différentes de faire sauter les murs internes ou de prendre des souris, de sorte que la recherche devient trop large et utilise trop de mémoire, même si elle est au moins suffisamment intelligente pour ne jamais traiter le même état plusieurs fois. La limite de pression doit être abaissée à100 environ pour des durées d'exécution et une utilisation de la mémoire quelque peu supportables.

Explication

J'utilise le motif binaire d'un caractère pour représenter l'état d'un champ:

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)

Par exemple, le héros ( 01000000) porteur de dynamite ( 00000001) doit être sur un endroit où il peut marcher ( 00010000) et nous voulons que toutes les valeurs soient imprimables en ASCII ( 00100000). Prendre le bit à bit orde tous ces masques de bits donne 01110001quel est le code ASCII pour q. Au total, cela devient ::

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

Le programme ne considérera que le héros se déplaçant vers la droite (la rotation expliquée plus tard s'occupera des autres directions). Les bitmasks ont été soigneusement choisis de telle sorte que le héros soit toujours représenté par une lettre et un endroit où il peut se déplacer par un chiffre (sauf le héros à la maison portant une victime, mais là encore, c'est intentionnel pour que le seul mouvement du héros soit de tomber) la victime). Donc, un héros qui peut avancer est égalé par /\pL\d/. La sous-chaîne correspondante doit être modifiée afin que le héros et ce qu'il porte soient supprimés du premier personnage et ajoutés au second, ce qui peut être fait avec un bit à bitxor avec la même valeur pour les deux caractères. La valeur xor se compose du bit de héros ( 01000000), du bit de dynamite ( 00000001) et du bit de souris de transport ( 00000100). Ensemble , ils orà01000101qui est ASCIIE. Déplacer le héros devient donc:

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

Le héros peut faire exploser un mur s'il se tient juste en face et porte de la dynamite ( q, sou y). Le héros perdra sa dynamite ( xoravec 00000001) et le mur #se transformera en un passage 0(xor avec 00010011), donc

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

Pour gérer les autres directions, la planche entière est tournée (la planche tournée se termine $o):

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

Outre le déplacement, le héros a également un certain nombre d'autres choix qu'il peut faire:

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

Cela se fait par

y/xr|/ytx/

Le plateau est terminé si le héros est à la maison sans rien porter ( x) et qu'il n'y a plus de victime à sauver (non 2). Cela peut être facilement testé en utilisant

/x/>/2/

Une fois le tableau résolu, je veux me souvenir de cet état et à la fin l'imprimer. Pour cela je porte le drapeau "est résolu" $\et je l' imprime à la fin du programme sans imprimer$_ , donc

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

Les états à traiter à la pression 0 sont maintenus @0, à la pression 1 activée, @1etc. La pression actuelle est maintenue $%. L' aide $nou quelque chose comme ça serait plus court mais le code ne fonctionne pas si la variable est pas initialisé à quelque chose parce que autovivification va autrement changer $nà un reference.Looping ARRAY sur les états à une certaine pression est effectuée à l' aide d' un foret non pas mapparce que avec un, forvous pouvez étendre le tableau pendant qu'il est encore en boucle et récupérer les nouveaux éléments. Cela est nécessaire car les rotations et les choix de champ unique du héros se produisent en 0 temps et se retrouvent dans le même tableau de pression. Donc, la boucle pour une pression donnée se fait par

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

Ceci est répété jusqu'à ce que la pression atteigne 1000 ou qu'une solution soit trouvée:

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

Il ne reste plus qu'à ajouter des états récemment découverts à leurs réseaux de pression respectifs. Cela se fera par sous-programme f. Nous voulons seulement ajouter un état s'il n'a pas encore été vu. Les États qui ont été vus auparavant sont conservés %a:

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

$nreprésente la nouvelle pression pour un état. Je tirerai cela de l'état que les variables d'expression régulière ont encore à la suite de l'action du héros menant à cet appel:

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

Cela conduit à la formule suivante pour $n:

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

Toutes les substitutions obtiennent un rmodificateur afin qu'elles retournent l'état modifié et laissent l'état actuel $_seul. fest ensuite appelé sur cet état modifié, vous obtenez donc du code comme

f y/xr|/ytx/r;

Parce que le calcul des $nbesoins des variables regex, ils doivent par défaut être désactivés au cas où les substitutions ne changeraient rien (car la condition pour les déclencher n'est pas remplie). Je ne dois pas non plus récupérer de variables regex dans une boucle précédente. Par conséquent, les substitutions sont enveloppées dans des {}blocs pour enregistrer et restaurer l'état regex. Voilà comment vous obtenez des déclarations comme

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

En particulier, la rotation est tellement enveloppée qu'elle appelle fsans variables d'expression régulière et obtient une contribution de pression de 0.

Il ne reste plus qu'à amorcer @0avec l'état initial au départ

f$_

C'est dans la boucle principale, donc il essaie également d'ajouter $_aux tableaux de pression ultérieurs, mais puisque l'état initial sera déjà dans %arien, il ne se passera rien.

Ensemble, tout cela trouve fondamentalement la solution la plus courte en utilisant l'algorithme de Dijkstra . Il y a cependant un problème potentiel. Le code actuel n'ajoutera pas d'état s'il est redécouvert avec une pression inférieure à la première découverte. Je n'ai pas été en mesure de construire un tableau qui déclencherait cela, mais je n'ai pas non plus été en mesure de prouver que c'est impossible.

Ton Hospel
la source
3
Ooo, intrigant. C'est beaucoup plus court que ce à quoi je m'attendais. Pouvez-vous ajouter quelques explications? Je n'aime pas vraiment Perl.
AdmBorkBork
3
@TimmyD Ok, explication ajoutée avec suffisamment de détails pour que même un programmeur non perl puisse avoir au moins une
idée de
1
Très impressionnant!
Emigna
Golf génial, c'est vraiment impressionnant ... Quand je pense que je ne suis pas si mauvais au golf avec Perl, je jette un œil à vos golfs, et ce sentiment disparaît assez vite!
Dada
13

JavaScript, 863 834 785 781 octets

29 octets enregistrés grâce à ETHproductions
53 octets enregistrés grâce à Jordan

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

Prend l'entrée comme une chaîne multiligne.

Cela définit une fonction anonyme qui utilise une fonction récursive fpour déterminer si vous déclenchez l'alarme avant de récupérer toutes les souris. frenvoie 1000si la pression est supérieure à 1000 (pour éviter une récursion sans fin), renvoie la pression s'il n'y a plus de souris à sauver et la souris à la sortie, et renvoie la pression minimale de tous les mouvements possibles depuis l'état actuel sinon. Il utilise un tableau Lpour garder une trace des positions déjà visitées où L[pos]==0s'il est visité et indéfini s'il ne l'est pas. Cela peut ne pas être nécessaire, mais cela empêche la souris de faire des mouvements inutiles et de lancer des erreurs de récursion à tout le moins. (Cela signifie que vous devez redéfinir Lsi vous testez cela plusieurs fois)

Cela utilise le format de la question autre que celui qui vous oblige à utiliser un caractère différent pour les murs extérieurs. (Tout autre chose que# MEmecd )

Version plus lisible:

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
la source
Espaces blancs inutiles à s in L|p > 999.
Yytsi
@TuukkaX Merci de me l'avoir rappelé, le bytecount était pour la version sans les espaces déjà.
DanTheMan
Voyez si vous pouvez économiser des octets en encapsulant le code evalet en le remplaçant @par $1(vous ne savez pas si cela fonctionnera, mais vous écrivez $1beaucoup)
Cyoce
Je pense que vous pourriez économiser un tas en faisant fun one-liner:f=(...)=>s in L|p>999?1e3:!/M/.test(s,L[s]=0)&/E/.test(s)?p:Math.min(...
ETHproductions
@Cyoce J'utilise $118 fois et .replace("@","$1")fait 18 octets. Je ne vois pas de moyen de le retirer.
DanTheMan