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

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


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


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.


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=<>

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.


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


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')))
})


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));});


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.