Détectez les fenêtres ASCII en caractères M et S

28

Une fenêtre est un carré de style ASCII avec une longueur de côté impair d'au moins 3, avec une bordure de caractère unique autour du bord ainsi que des traits verticaux et horizontaux au milieu:

#######
#  #  #
#  #  #
#######
#  #  #
#  #  #
#######

Une fenêtre MS est une fenêtre où la bordure est constituée uniquement des caractères Met S. Votre tâche consiste à écrire un programme (ou une fonction) qui prend une chaîne et génère une valeur véridique si l'entrée est une fenêtre MS valide et une valeur Falsey si ce n'est pas le cas.

Caractéristiques

  • Vous pouvez prendre l'entrée comme une chaîne séparée par des sauts de ligne ou un tableau de chaînes représentant chaque ligne.
  • La bordure d'une fenêtre MS peut contenir un mélange de caractères M et S, mais l'intérieur sera toujours composé d'espaces.
  • Vous pouvez choisir de détecter uniquement les fenêtres avec des retours à la ligne de fin, ou uniquement les fenêtres sans retours à la ligne de fin, mais pas les deux.

Cas de test

Vérité:

MMM
MMM
MMM

SMSMS
M M S
SMSMM
S S M
SMSMS

MMMMMMM
M  S  M
M  S  M
MSSSSSM
M  S  M
M  S  M
MMMMMMM

Falsey:

Hello, World!

MMMM
MSSM
MS M
MMMM

MMSMM
M S.M
sSSSS
M S M
MMSMM

MMMMMMM
M  M  M
MMMMMMM
M  M  M
MMMMMMM

MMMMMMM
M M M M
MMMMMMM
M M M M
MMMMMMM
M M M M
MMMMMMM

MMSSMSSMM
M   M   M
S   S   S
S   S  S
MMSSMSSMM
S   S   S
S   S   S
M   M   M
MMSSMSSMM
Esolanging Fruit
la source
3
C'est une grande torsion sur les arts ASCII, un problème de décision pour détecter une certaine structure.
xnor
4
@xnor J'ai l'impression que nous pourrions vouloir une balise différente pour l'art ASCII inversé comme celui-ci.
Esolanging Fruit
2
bien que n'étant pas spécifique à l'art ascii, la correspondance de motifs pourrait être un bon choix pour une nouvelle étiquette
Destructible Lemon
Pouvez-vous ajouter un ou deux cas de test où la chaîne ne forme pas un tableau rectangulaire?
Greg Martin
1
@Mast, vous avez tout à fait raison! Peut-être que le défi doit être clarifié
Chris M

Réponses:

1

Pyke, 34 31 octets

lei}t\Mcn+it*i\M*+s.XM"QJ\S\M:q

Essayez-le ici!

lei                              -         i = len(input)//2
   }t                            -        (^ * 2) - 1
     \Mc                         -       "M".center(^)
        n+                       -      ^ + "\n"
          it*                    -     ^ * (i-1)
                 +               -    ^ + V
             i\M*                -     "M"*i
                  s              -   palindromise(^)
                   .XM"          -  surround(^, "M")
                               q - ^ == V
                       QJ        -   "\n".join(input)
                         \S\M:   -  ^.replace("S", "M")
Bleu
la source
8

Rétine , 68 67 octets

Le nombre d'octets suppose un codage ISO 8859-1.

S
M
^(M((M)*M)\2)((?<-9>¶M((?<9-3> )*(?(3)!)M|\5)\5)*(?(9)!)¶\1)\4$

Essayez-le en ligne!

Martin Ender
la source
7

Grime , 39 38 octets

Merci à Zgarb d'avoir économisé 1 octet.

e`BB/BB/W+ W/+
B=W|B/W\ * W/\ /*
W=[MS

Essayez-le en ligne!

Je ne sais pas s'il existe un moyen plus simple d'appliquer le rapport d'aspect carré des composants de fenêtre individuels que d'utiliser un non-terminal récursif, mais cela semble fonctionner assez bien.

Explication

Il est préférable de lire le programme de bas en haut.

W=[MS

Cela définit simplement un non-terminal (que vous pouvez considérer comme un sous-programme qui correspond à un rectangle) Wqui correspond à un Mou à un S(il y a un implicite ]à la fin de la ligne).

B=W|B/W\ * W/\ /*

Cela définit un non-terminal Bqui correspond à environ un quart de la sortie, c'est-à-dire un panneau de fenêtre avec la bordure gauche et supérieure. Quelque chose comme ça:

MSM
S  
M  

Pour garantir que ce panneau de fenêtre est carré, nous le définissons Brécursivement. C'est soit un caractère de fenêtre W, soit c'est celui B/W\ * W/\ /*qui ajoute un calque à droite et en bas. Pour voir comment cela fonctionne, supprimons du sucre syntaxique:

(B/W[ ]*)(W/[ ]/*)

C'est la même chose, car la concaténation horizontale peut être écrite soit ABou A B, mais celle-ci a une priorité plus faible que la concaténation verticale /tandis que pour la première, elle est plus élevée. Il en B/W[ ]*va de même Bavec un caractère de fenêtre et une rangée d'espaces ci-dessous. Et puis nous ajoutons horizontalement W/[ ]/*ce qui est un caractère de fenêtre avec une colonne d'espaces.

Enfin, nous assemblons ces non terminaux dans la forme finale de la fenêtre:

BB/BB/W+ W/+

C'est quatre panneaux de fenêtre Bsuivis d'une ligne de caractères de fenêtre et d'une colonne de caractères de fenêtre. Notez que nous n'affirmons pas explicitement que les quatre panneaux de fenêtre sont de la même taille, mais s'ils ne le sont pas, il est impossible de les concaténer en rectangle.

Enfin, e`au début est simplement une configuration qui dit à Grime de vérifier que l'entrée entière peut être mise en correspondance par ce modèle (et elle imprime 0ou en 1conséquence).

Martin Ender
la source
5

JavaScript (ES6), 115 113 bytes

a=>(l=a.length)&a.every((b,i)=>b.length==l&b.every((c,j)=>(i&&l+~i-i&&l+~i&&j&&l+~j-j&&l+~j?/ /:/[MS]/).test(c)))

Prend l'entrée comme un tableau de tableaux de caractères (ajoutez 5 octets pour un tableau de chaînes) et retourne 1ou 0. Après avoir vérifié que la hauteur est impaire, chaque ligne est vérifiée pour s'assurer que le tableau est carré, et chaque caractère est vérifié pour être l'un des caractères que nous attendons à cette position particulière. Edit: sauvé 2 octets grâce à @PatrickRoberts.

Neil
la source
Vous pouvez changer (...).includes(c)pour ~(...).search(c)enregistrer 1 octet
Patrick Roberts
1
En fait, encore mieux, vous pouvez le changer (...?/ /:/[MS]/).test(c)pour enregistrer 2 octets au lieu de seulement 1.
Patrick Roberts
@PatrickRoberts Mignon, merci!
Neil
5

Perl, 124 123 119 95 93 84

Le script Perl suivant lit une fenêtre MS candidate à partir de l'entrée standard. Il sort ensuite avec un état de sortie nul si le candidat est une MS Window et avec un état de sortie non nul s'il ne l'est pas.

Il fonctionne en générant deux expressions régulières, une pour la ligne du haut, du milieu et du bas et une pour chaque autre ligne, et en vérifiant l'entrée par rapport à elles.

Merci, @Dada. Et encore.

map{$s=$"x(($.-3)/2);$m="[MS]";($c++%($#a/2)?/^$m$s$m$s$m$/:/^${m}{$.}$/)||die}@a=<>
nwk
la source
Je ne suis pas sûr de donner le résultat car le statut de sortie est autorisé (je n'ai pas le temps de rechercher le méta-post pertinent cependant). Quoi qu'il en soit, vous pouvez enregistrer quelques octets:@a=<>;$s=$"x(($.-3)/2);$m="[MS]";map{$a[$_]!~($_%($./2)?"$m$s$m$s$m":"$m${m}{$.}")&&die}0..--$.
Dada
@Dada: Merci! C'est une amélioration impressionnante: 24 caractères. (Il y avait un "$ m" erroné dans votre code, il est donc encore plus court qu'il ne le paraissait au début.) Je ne savais pas si le rapport du résultat avec un code de sortie était autorisé en général mais j'ai pris le "écrire un programme ( ou fonction) "comme permettant d’être flexible avec la façon dont le résultat est renvoyé dans ce cas particulier; les codes de sortie sont pratiquement les valeurs de retour de fonction de l'environnement * nix. :-)
nwk
Faites 26 caractères.
nwk
1
En fait, je décrémente $.à la fin pour éviter d'utiliser deux fois $.-1(surtout depuis la première fois ($.-1)/2, il fallait donc des parenthèses supplémentaires), donc l' $min $m${m}{$.}n'est pas une erreur. De plus, je viens de me rendre compte maintenant, mais les expressions régulières doivent être entourées de ^...$(donc un caractère supplémentaire à la fin ou au début les fait échouer), ou plus court: utilisez neplutôt !~.
Dada
Peu importe, vous ne pouvez évidemment pas utiliser à la neplace de !~(je ne devrais pas écrire de messages quand je suis réveillé depuis seulement 15 minutes!). Vous devrez donc utiliser les ^...$deux expressions rationnelles, je le crains.
Dada
2

Mathematica, 166 octets

Union[(l=Length)/@data]=={d=l@#}&&{"M","S"}~(s=SubsetQ)~(u=Union@*Flatten)@{#&@@(p={#,(t=#~TakeDrop~{1,-1,d/2-.5}&)/@#2}&@@t@#),p[[2,All,1]]}&&{" "}~s~u@p[[2,All,2]]&

Fonction sans nom prenant une liste de listes de caractères en entrée et retournant Trueou False. Voici une version moins golfique:

(t = TakeDrop[#1, {1, -1, d/2 - 0.5}] &; 
Union[Length /@ data] == {d = Length[#1]}
  &&
(p = ({#1, t /@ #2} &) @@ t[#1];
SubsetQ[{"M", "S"}, Union[Flatten[{p[[1]], p[[2, All, 1]]}]]]
  && 
SubsetQ[{" "}, Union[Flatten[p[[2, All, 2]]]]])) &

La première ligne définit la fonction t, qui sépare une liste de longueur den deux parties, dont la première est la première, la moyenne et la dernière entrée de la liste, et la seconde est tout le reste. La deuxième ligne vérifie si l'entrée est un tableau carré en premier lieu. La quatrième ligne utilise tdeux fois, une fois sur l'entrée elle-même et une fois sur toutes * les chaînes de l'entrée, pour séparer les caractères qui sont censés être "M"ou "S"des caractères qui sont censés être des espaces; puis les cinquième et septième lignes vérifient si elles sont vraiment ce qu'elles sont censées être.

Greg Martin
la source
2

JavaScript (ES6), 108 106 octets

Entrée: tableau de chaînes / Sortie: 0ou1

s=>s.reduce((p,r,y)=>p&&r.length==w&(y==w>>1|++y%w<2?/^[MS]+$/:/^[MS]( *)[MS]\1[MS]$/).test(r),w=s.length)

Cas de test

Arnauld
la source
2

JavaScript (ES6), 140 138 141 141 140 octets

Je sais que ce n'est pas un nombre d'octets gagnant (bien que merci à Patrick Roberts pour -3, et je me suis rendu compte que cela donnait des faux positifs pour 1 au lieu de M / S: +3), mais je l'ai fait d'une manière légèrement différente, je ' m nouveau à ce sujet, et c'était amusant ...

Accepte un tableau de chaînes, une pour chaque ligne et renvoie vrai ou faux. Ajout d'une nouvelle ligne pour plus de clarté (non inclus dans le nombre d'octets).

f=t=>t.every((e,i)=>e.split`S`.join`M`==[...p=[b='M'.repeat(s=t.length),
...Array(z=-1+s/2|0).fill([...'MMM'].join(' '.repeat(z)))],...p,b][i])

Au lieu de vérifier l'entrée par rapport à un modèle généralisé, je construis une fenêtre «M» de la même taille, remplace S par M en entrée et compare les deux.

Non golfé

f = t => t.every( // function returns true iff every entry in t
                  // returns true below
  (e, i) => e.split`S`.join`M` // replace all S with M
                                 // to compare to mask
  == [ // construct a window of the same size made of Ms and
       // spaces, compare each row 
      ...p = [ // p = repeated vertical panel (bar above pane)
               // which will be repeated
              b = 'M'.repeat(s = t.length),
                  // b = bar of Ms as long as the input array
              ...Array(z = -1 + s/2|0).fill([...'MMM'].join(' '.repeat(z)))],
              // z = pane size; create enough pane rows with
              // Ms and enough spaces
      ...p, // repeat the panel once more
      b][i] // finish with a bar
)

console.log(f(["111","111","111"]))

console.log(f(["MMMMM","M S M","MSSSM","M S M","MSSSM"]))

Cas de test

f=t=>t.every((e,i)=>e.split`S`.join`M`==[...p=[b='M'.repeat(s=t.length),
...Array(z=-1+s/2|0).fill([...'MMM'].join(' '.repeat(z)))],...p,b][i])


truthy=`MMM
MMM
MMM

SMSMS
M M M
SMSMS
M M M
SMSMS

MMMMMMM
M  S  M
M  S  M
MSSSSSM
M  S  M
M  S  M
MMMMMMM`.split('\n\n')

falsey=`Hello, World!

MMMM
MSSM
MS M
MMMM

MMSMM
M S.M
sSSSS
M S M
MMSMM

MMMMMMM
M  M  M
MMMMMMM
M  M  M
MMMMMMM

MMMMMMM
M M M M
MMMMMMM
M M M M
MMMMMMM
M M M M
MMMMMMM`.split('\n\n')

truthy.forEach(test=>{
  console.log(test,f(test.split('\n')))
})

falsey.forEach(test=>{
  console.log(test,f(test.split('\n')))
})

Chris M
la source
1
Pour référence future, à moins que la fonction ne soit récursive, f=n'a pas besoin d'être incluse dans le nombre d'octets, il s'agit donc en fait d'une soumission de 138 octets.
Patrick Roberts
Vous pouvez remplacer z=-1+s/2|0par z=(s-3)/2pour économiser 1 octet
Patrick Roberts
Vous pouvez également remplacer e.replace(/S/g,'M')==...par e.split`S`.join`M`==...pour enregistrer un autre octet
Patrick Roberts
Merci! z=-1+s/2|0est là pour retourner un entier positif pour s == 1 et même s, c'est-à-dire que la fonction retourne false sans que Array () le plante. Sinon, la logique nécessaire l'a allongé. Excellent conseil sur le split / join, merci
Chris M
Bonne prise, je n'ai pas considéré le s=1cas, car mon expression rationnelle invalide échoue silencieusement.
Patrick Roberts
1

JavaScript (ES6), 109 107 106 105 99 octets

s=>!s.split`S`.join`M`.search(`^((M{${r=s.search`
`}})(
(M( {${w=(r-3)/2}})M\\5M
){${w}}))\\1\\2$`)

Edit : Whoa, Arnauld m'a sauvé 6 octets en passant s.split`\n`.lengthàs.search`\n` ! Merci!

Cela prend une seule chaîne multiligne et construit une RegExpvalidation basée sur la longueur de la chaîne d'entrée. Renvoie trueou false. Suppose une fenêtre valide a ne pas une nouvelle ligne de fuite.

Démo

f=s=>!s.split`S`.join`M`.search(`^((M{${r=s.search`
`}})(
(M( {${w=(r-3)/2}})M\\5M
){${w}}))\\1\\2$`);
`MMM
MMM
MMM

SMSMS
M M M
SMSMS
M M M
SMSMS

MMMMMMM
M  S  M
M  S  M
MSSSSSM
M  S  M
M  S  M
MMMMMMM

Hello, World!

MMMM
MSSM
MS M
MMMM

MMSMM
M S.M
sSSSS
M S M
MMSMM

MMMMMMM
M  M  M
MMMMMMM
M  M  M
MMMMMMM

MMMMMMM
M M M M
MMMMMMM
M M M M
MMMMMMM
M M M M
MMMMMMM`.split`

`.forEach(test=>{console.log(test,f(test));});

Patrick Roberts
la source
Belle approche! Pourriez-vous utiliser à la r=s.search('\n')place de split / length?
Arnauld
@Arnauld suggestion géniale, merci!
Patrick Roberts
Les parenthèses s=>!s.split`S`.join`M`.search([...])peuvent être supprimées sans provoquer d'erreurs de syntaxe.
Ismael Miguel
@IsmaelMiguel correct, mais ensuite la chaîne est passée en tant que modèle, ce qui invalide l'impliciteRegExp
Patrick Roberts
Ça craint ... Je ne m'attendais vraiment pas à ça ...
Ismael Miguel