Detecta ventanas de arte ASCII hechas de caracteres M y S

28

Una ventana es un cuadrado de arte ASCII con una longitud lateral impar de al menos 3, con un borde de un solo carácter alrededor del borde, así como trazos verticales y horizontales en el medio:

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

Una ventana de MS es una ventana donde el borde está hecho solo de los caracteres My S. Su tarea es escribir un programa (o función) que tome una cadena y genere un valor verdadero si la entrada es una ventana de MS válida, y un valor falso si no lo es.

Presupuesto

  • Puede tomar la entrada como una cadena separada por una nueva línea o una matriz de cadenas que representan cada línea.
  • El borde de una ventana de MS puede contener una mezcla de caracteres M y S, pero el interior siempre estará compuesto de espacios.
  • Puede elegir detectar solo ventanas con nuevas líneas finales, o solo ventanas sin nuevas líneas finales, pero no ambas.

Casos de prueba

Verdad:

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
Fruta Esolanging
fuente
3
Este es un gran giro en las artes ASCII, un problema de decisión para detectar una determinada estructura.
xnor
44
@xnor Siento que podríamos querer una etiqueta diferente para el arte ASCII inverso como este.
Esolanging Fruit
2
Si bien no es específico del arte ascii, la coincidencia de patrones podría ser una buena opción para una nueva etiqueta
Destructible Lemon
¿Puedes agregar un caso de prueba o dos donde la cadena no forma una matriz rectangular?
Greg Martin
1
@Mast, tienes toda la razón! Tal vez el desafío necesita aclaración
Chris M

Respuestas:

1

Pyke, 34 31 bytes

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

Pruébalo aquí!

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")
Azul
fuente
8

Retina , 68 67 bytes

El recuento de bytes asume la codificación ISO 8859-1.

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

Pruébalo en línea!

Martin Ender
fuente
7

Grime , 39 38 bytes

Gracias a Zgarb por guardar 1 byte.

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

Pruébalo en línea!

No estoy seguro de si hay una forma más simple de aplicar la relación de aspecto cuadrado de los componentes de la ventana individual que usar un terminal no recursivo, pero parece estar funcionando bastante bien.

Explicación

Es mejor leer el programa de abajo hacia arriba.

W=[MS

Esto simplemente define un no terminal (que se puede considerar como una subrutina que coincide con un rectángulo) Wque coincide con an Mo an S(hay un implícito ]al final de la línea).

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

Esto define un no terminal Bque coincide con aproximadamente una cuarta parte de la salida, es decir, un panel de ventana con el borde izquierdo y superior. Algo como esto:

MSM
S  
M  

Para garantizar que este panel de ventana sea cuadrado, definimos Brecursivamente. Es un personaje de ventana Wo es el B/W\ * W/\ /*que agrega una capa a la derecha y al fondo. Para ver cómo hace esto, eliminemos un poco de azúcar sintáctica:

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

Esto es lo mismo, porque la concatenación horizontal puede escribirse ABo bien A B, pero la última tiene una precedencia menor que la concatenación vertical, /mientras que la primera tiene mayor. Entonces B/W[ ]*es un Bcon un carácter de ventana y una fila de espacios a continuación. Y luego agregamos horizontalmente W/[ ]/*que es un carácter de ventana con una columna de espacios.

Finalmente, ensamblamos estos no terminales en la forma final de la ventana:

BB/BB/W+ W/+

Son cuatro paneles de ventana Bseguidos de una fila de caracteres de ventana y una columna de caracteres de ventana. Tenga en cuenta que no hacemos ninguna afirmación explícita de que los cuatro paneles de ventana son del mismo tamaño, pero si no lo son, es imposible concatenarlos en un rectángulo.

Finalmente, e`al principio es simplemente una configuración que le dice a Grime que verifique que toda la entrada pueda coincidir con este patrón (e imprime 0o en 1consecuencia).

Martin Ender
fuente
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)))

Toma la entrada como una matriz de matrices de caracteres (agrega 5 bytes para una matriz de cadenas) y devuelve 1o 0. Después de verificar que la altura es impar, se verifica cada fila para garantizar que la matriz sea cuadrada, y se verifica que cada carácter sea uno de los caracteres que esperamos en esa posición en particular. Editar: Guardado 2 bytes gracias a @PatrickRoberts.

Neil
fuente
Puede cambiar (...).includes(c)para ~(...).search(c)guardar 1 byte
Patrick Roberts
1
En realidad, aún mejor puede cambiarlo (...?/ /:/[MS]/).test(c)para ahorrar 2 bytes en lugar de solo 1.
Patrick Roberts
@PatrickRoberts Lindo, gracias!
Neil
5

Perl 124 123 119 95 93 84

El siguiente script de Perl lee una ventana MS candidata desde la entrada estándar. Luego sale con un estado de salida cero si el candidato es una ventana de MS y con un estado de salida distinto de cero si no lo es.

Funciona generando dos expresiones regulares, una para la línea superior, media e inferior y otra para cada línea, y verificando la entrada contra ellas.

Gracias, @Dada. Y otra vez.

map{$s=$"x(($.-3)/2);$m="[MS]";($c++%($#a/2)?/^$m$s$m$s$m$/:/^${m}{$.}$/)||die}@a=<>
nwk
fuente
No estoy seguro de dar el resultado ya que el estado de salida está permitido (aunque no tengo tiempo para buscar la meta publicación relevante). En cualquier caso, puede guardar algunos bytes:@a=<>;$s=$"x(($.-3)/2);$m="[MS]";map{$a[$_]!~($_%($./2)?"$m$s$m$s$m":"$m${m}{$.}")&&die}0..--$.
Dada
@Dada: ¡Gracias! Esa es una mejora impresionante: 24 personajes. (Había un "$ m" perdido en su código, por lo que es aún más corto de lo que parecía al principio). No estaba seguro de si en general se permitía informar el resultado con un código de salida, pero tomé el "escriba un programa ( o función) "como permitir que uno sea flexible con la forma en que se devuelve el resultado en este caso particular; Los códigos de salida son prácticamente los valores de retorno de la función del entorno * nix. :-)
nwk
Haz que 26 personajes.
nwk
1
En realidad, estoy disminuyendo $.al final para evitar el uso de dos veces $.-1(sobre todo desde la primera vez que era ($.-1)/2lo que necesitaba un poco de paréntesis extra), por lo que el $men $m${m}{$.}que no es un error. Además, me acabo de dar cuenta ahora, pero las expresiones regulares deben estar rodeadas de ^...$(por lo que el carácter adicional al final o al principio los hace fallar), o más cortos: usar en nelugar de !~.
Dada
No importa, obviamente no puedes usarlo en nelugar de !~(¡no debería escribir mensajes cuando he estado despierto por solo 15 minutos!). Así que ^...$me temo que tendrás que usar en ambas expresiones regulares.
Dada
2

Mathematica, 166 bytes

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]]&

Función sin nombre que toma una lista de listas de caracteres como entrada y regresa Trueo False. Aquí hay una versión menos golfista:

(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 primera línea define la función t, que separa una lista de longitud den dos partes, la primera de las cuales es la primera, intermedia y última entrada de la lista, y la segunda es el resto. La segunda línea verifica si la entrada es una matriz cuadrada en primer lugar. La cuarta línea usa tdos veces, una en la entrada en sí y otra en todas * las cadenas en la entrada, para separar los caracteres que se supone que son "M"o "S"de los caracteres que se supone que son espacios; luego las líneas quinta y séptima verifican si realmente son lo que se supone que deben ser.

Greg Martin
fuente
2

Javascript (ES6), 108 106 bytes

Entrada: conjunto de cadenas / Salida: 0o1

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)

Casos de prueba

Arnauld
fuente
2

JavaScript (ES6), 140 138 141 140 bytes

Sé que este no es un conteo de bytes ganador (aunque gracias a Patrick Roberts por -3, y me di cuenta de que arrojó falsos positivos para 1 en lugar de M / S: +3), pero lo hice de una manera ligeramente diferente, yo ' Soy nuevo en esto, y fue divertido ...

Acepta una serie de cadenas, una para cada línea y devuelve verdadero o falso. Nueva línea agregada para mayor claridad (no incluida en el recuento de bytes).

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])

En lugar de verificar la entrada contra un patrón generalizado, construyo una ventana 'M' del mismo tamaño, reemplazo S con M en la entrada y comparo las dos.

Sin 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"]))

Casos de prueba

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
fuente
1
Para referencia futura, a menos que la función sea recursiva, f=no es necesario incluirla en el recuento de bytes, por lo que en realidad se trata de un envío de 138 bytes.
Patrick Roberts el
Puede reemplazar z=-1+s/2|0con z=(s-3)/2para guardar 1 byte
Patrick Roberts
También puede reemplazar e.replace(/S/g,'M')==...con e.split`S`.join`M`==...para guardar otro byte
Patrick Roberts el
¡Gracias! z=-1+s/2|0está ahí para devolver un entero positivo para s == 1 e incluso s, es decir, la función devuelve falso sin que Array () lo bloquee. De lo contrario, la lógica necesaria lo hizo más largo. Gran consejo sobre split / join, gracias
Chris M
Buena captura, no consideré el s=1caso, ya que mi expresión regular no válida simplemente falla en silencio.
Patrick Roberts
1

JavaScript (ES6), 109 107 106 105 99 bytes

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

Editar : Whoa, Arnauld me ahorró 6 bytes al cambiar s.split`\n`.lengtha s.search`\n`! ¡Gracias!

Esto toma una sola cadena multilínea y construye una RegExpvalidación basada en la longitud de la cadena de entrada. Devolucionestrue o false. Asume una ventana válida tiene no tiene un salto de línea final.

Manifestación

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
fuente
Buen enfoque! ¿Podrías usar en r=s.search('\n')lugar de split / length?
Arnauld
@Arnauld sugerencia impresionante, gracias!
Patrick Roberts el
Los paréntesis s=>!s.split`S`.join`M`.search([...])se pueden eliminar sin causar errores de sintaxis.
Ismael Miguel
@IsmaelMiguel correcto, pero luego la cadena se pasa como una plantilla, que invalida lo implícitoRegExp
Patrick Roberts
Eso apesta ... Realmente no esperaba eso ...
Ismael Miguel