Detectar janelas de arte ASCII feitas de caracteres M e S

28

Uma Janela é um quadrado de arte ASCII com comprimento de lado ímpar de pelo menos 3, com uma borda de caractere único ao redor da borda e traços verticais e horizontais no meio:

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

Uma janela do MS é uma janela em que a borda é feita apenas dos caracteres Me S. Sua tarefa é escrever um programa (ou função) que pega uma string e gera um valor verdadeiro se a entrada for uma janela MS válida e um valor falsey se não for.

Especificações

  • Você pode considerar a entrada como uma sequência separada por nova linha ou uma matriz de sequências representando cada linha.
  • A borda de uma janela do MS pode conter uma mistura de caracteres M e S, mas o interior sempre será composto de espaços.
  • Você pode optar por detectar apenas janelas com novas linhas à direita, ou apenas janelas sem novas linhas, mas não as duas.

Casos de teste

Verdade:

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
fonte
3
Essa é uma grande reviravolta nas artes ASCII, um problema de decisão para detectar uma certa estrutura.
Xnor
4
@ xnor Sinto que podemos querer uma tag diferente para a arte ASCII reversa como esta.
Esolanging Fruit
2
enquanto não específicas para arte ASCII, correspondência de padrão pode ser uma boa escolha para uma nova tag
Destrutível Lemon
Você pode adicionar um caso de teste ou dois em que a string não forma uma matriz retangular?
Greg Martin
1
@ Mestre, você está certo! Talvez as necessidades de desafio esclarecer
Chris M

Respostas:

1

Pyke, 34 31 bytes

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

Experimente aqui!

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
fonte
8

Retina , 68 67 bytes

A contagem de bytes assume a codificação ISO 8859-1.

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

Experimente online!

Martin Ender
fonte
7

Grime , 39 38 bytes

Agradecemos ao Zgarb por economizar 1 byte.

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

Experimente online!

Não tenho certeza se existe uma maneira mais simples de aplicar a proporção quadrada dos componentes individuais da janela do que usar um não terminal recursivo, mas isso parece estar funcionando muito bem.

Explicação

É melhor ler o programa de baixo para cima.

W=[MS

Isso simplesmente define um não-terminal (que você pode considerar uma sub-rotina que corresponde a um retângulo) Wque corresponde a um Mou a S(existe um implícito ]no final da linha).

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

Isso define um não terminal Bque corresponde a cerca de um quarto da saída, ou seja, um painel de janela com as bordas esquerda e superior. Algo assim:

MSM
S  
M  

Para garantir que esse painel da janela seja quadrado, definimos Brecursivamente. É um caractere de janela Wou B/W\ * W/\ /*adiciona uma camada à direita e na parte inferior. Para ver como isso acontece, vamos remover um pouco de açúcar sintático:

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

É o mesmo, porque a concatenação horizontal pode ser escrita um ABou outro A B, mas o último tem precedência menor do que a concatenação vertical, /enquanto o primeiro tem maior. O mesmo B/W[ ]*ocorre Bcom um caractere de janela e uma linha de espaços abaixo. E então acrescentamos horizontalmente W/[ ]/*um caractere de janela com uma coluna de espaços.

Por fim, reunimos esses não-terminais na forma final da janela:

BB/BB/W+ W/+

São quatro painéis de janela Bseguidos por uma linha de caracteres da janela e uma coluna de caracteres da janela. Observe que não fazemos nenhuma afirmação explícita de que os quatro painéis da janela são do mesmo tamanho, mas se não forem, é impossível concatená-los em retângulo.

Finalmente e`, o início é simplesmente uma configuração que diz ao Grime para verificar se toda a entrada pode ser correspondida por esse padrão (e é impressa 0ou de 1acordo).

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

Recebe a entrada como uma matriz de matrizes de caracteres (adicione 5 bytes para uma matriz de seqüências de caracteres) e retorna 1ou 0. Após verificar se a altura é ímpar, todas as linhas são verificadas para garantir que a matriz seja quadrada e cada caractere é verificado como sendo um dos caracteres que esperamos nessa posição específica. Editar: salvou 2 bytes graças a @PatrickRoberts.

Neil
fonte
Você pode alterar (...).includes(c)para ~(...).search(c)salvar 1 byte
Patrick Roberts
1
Na verdade, ainda melhor, você pode alterá-lo para (...?/ /:/[MS]/).test(c)salvar 2 bytes em vez de apenas 1.
Patrick Roberts
@PatrickRoberts Cute, thanks!
Neil
5

Perl, 124 123 119 95 93 84

O script Perl a seguir lê uma janela MS candidata a partir da entrada padrão. Em seguida, ele sai com um status de saída zero, se o candidato for uma janela do MS e com um status de saída diferente de zero, se não for.

Ele funciona gerando duas expressões regulares, uma para as linhas superior, média e inferior e uma para todas as outras linhas e verificando a entrada com elas.

Obrigado, @Dada. E de novo.

map{$s=$"x(($.-3)/2);$m="[MS]";($c++%($#a/2)?/^$m$s$m$s$m$/:/^${m}{$.}$/)||die}@a=<>
nwk
fonte
Não tenho certeza de fornecer o resultado, pois o status de saída é permitido (não tenho tempo para procurar a meta post relevante). Independentemente disso, você pode salvar alguns bytes:@a=<>;$s=$"x(($.-3)/2);$m="[MS]";map{$a[$_]!~($_%($./2)?"$m$s$m$s$m":"$m${m}{$.}")&&die}0..--$.
Dada
@ Dadá: Obrigado! Essa é uma melhoria impressionante: 24 caracteres. (Havia um "$ m" perdido no seu código, por isso é ainda mais curto do que parecia no início.) Eu não tinha certeza se o relatório do resultado com um código de saída era permitido em geral, mas usei o "escrever um programa ( ou função) ", permitindo que se seja flexível com a forma como o resultado é retornado nesse caso específico; os códigos de saída são praticamente os valores de retorno da função do ambiente * nix. :-)
nwk 8/01/19
Faça esses 26 caracteres.
Nwk 8/01
1
Na verdade, estou diminuindo $.no final para evitar o uso duas vezes $.-1(especialmente desde a primeira vez em que foi ($.-1)/2necessário um parêntese extra), para que o $min $m${m}{$.}não seja um erro. Além disso, acabei de perceber agora, mas os regexs devem estar cercados por ^...$(para que caracteres extras no final ou no início os façam falhar) ou mais curtos: use em nevez de !~.
Dada
Deixa pra lá, obviamente você não pode usar em nevez de!~ (I não deve escrever mensagens quando eu fui acordado por apenas 15 minutos!). Então, você terá que usar ^...$em ambas as expressões 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]]&

Função sem nome, tendo uma lista de listas de caracteres como entrada e retorno Trueou False. Aqui está uma versão menos golfe:

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

A primeira linha define a função t, que separa uma lista de comprimento dem duas partes, a primeira das quais são as primeiras, médias e últimas entradas da lista, e a segunda das demais é o restante. A segunda linha verifica se a entrada é uma matriz quadrada em primeiro lugar. A quarta linha usa tduas vezes, uma vez na entrada em si e uma vez em todas as seqüências de caracteres na entrada, para separar os caracteres que deveriam ser "M"ou "S"dos caracteres que deveriam ser espaços; as quinta e sétima linhas verificam se elas realmente são o que deveriam ser.

Greg Martin
fonte
2

JavaScript (ES6), 108 106 bytes

Entrada: matriz de seqüências de caracteres / Saída: 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)

Casos de teste

Arnauld
fonte
2

JavaScript (ES6), 140 138 141 140 bytes

Sei que essa não é uma contagem de bytes vencedora (embora graças a Patrick Roberts por -3 e percebi que ela gerava falsos positivos para 1 em vez de M / S: +3), mas fiz de uma maneira um pouco diferente, sou novo nisso, e foi divertido ...

Aceita uma matriz de seqüências de caracteres, uma para cada linha e retorna verdadeiro ou falso. Nova linha adicionada para maior clareza (não incluída na contagem 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])

Em vez de verificar a entrada contra um padrão generalizado, construo uma janela 'M' do mesmo tamanho, substituo S por M na entrada e comparo as duas.

Ungolfed

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 teste

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
fonte
1
Para referência futura, a menos que a função seja recursiva, f=não precise ser incluída na contagem de bytes, portanto, na verdade, é um envio de 138 bytes.
Patrick Roberts
Você pode substituir z=-1+s/2|0por z=(s-3)/2para salvar 1 byte
Patrick Roberts
Você também pode substituir e.replace(/S/g,'M')==...com e.split`S`.join`M`==...para salvar outro byte
Patrick Roberts
Obrigado! z=-1+s/2|0existe para retornar um número inteiro positivo para s == 1 e até s, ou seja, a função retorna false sem que Array () a interrompa. Caso contrário, a lógica necessária a tornaria mais longa. Grande dica sobre divisão / associar-se, graças
Chris M
Boa captura, não considerei o s=1caso, pois meu regex inválido falha silenciosamente.
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$`)

Edit : Uau, Arnauld me salvou 6 bytes mudando s.split`\n`.lengthpara s.search`\n`! Obrigado!

Isso RegExputiliza uma única cadeia de linhas múltiplas e constrói uma validação baseada em usando o comprimento da cadeia de entrada. Retorna trueou false. Assume uma janela válida tem não tem uma nova linha final.

Demo

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
fonte
Boa abordagem! Você poderia usar em r=s.search('\n')vez de split / length?
Arnauld
@Arnauld sugestão impressionante, obrigado!
Patrick Roberts
Os parênteses ativados s=>!s.split`S`.join`M`.search([...])podem ser removidos, sem causar erros de sintaxe.
Ismael Miguel
@IsmaelMiguel corrigir, mas então a string é passada como um modelo, o que invalida o implícitoRegExp
Patrick Roberts
Isso é péssimo ... Eu realmente não estava esperando isso ...
Ismael Miguel