Обнаружение ASCII-художественных окон, сделанных из символов M и S


28

Окно - это квадрат ASCII-искусства с нечетной длиной стороны не менее 3, с границей из одного символа вокруг края, а также вертикальными и горизонтальными штрихами в середине:

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

Окно MS - это окно, в котором граница состоит только из символов Mи S. Ваша задача - написать программу (или функцию), которая принимает строку и выводит истинное значение, если вход является допустимым окном MS, и ложное значение, если это не так.

Характеристики

  • Вы можете принять входные данные как строку, разделенную новой строкой, или массив строк, представляющих каждую строку.
  • Граница окна MS может содержать смесь символов M и S, но внутренняя часть всегда будет состоять из пробелов.
  • Вы можете выбрать обнаружение только окон с завершающими символами новой строки или только окон без завершающих строк, но не обоих.

Тестовые случаи

Truthy:

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
Это большой поворот в искусстве ASCII, решение проблемы обнаружения определенной структуры.
xnor

4
@xnor Я чувствую, что мы могли бы хотеть другой тег для обратного ASCII-искусства, как это.
Esolanging Fruit

2
Хотя это не характерно для ascii art, сопоставление с образцом может быть хорошим выбором для нового тега
Destructible Lemon

Можете ли вы добавить тестовый пример или два, где строка не образует прямоугольный массив?
Грег Мартин

1
@ Маст, ты совершенно прав! Возможно, проблема нуждается в разъяснении
Крис М

Ответы:


1

Пайк, 34 31 байт

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

Попробуй это здесь!

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

Грязь , 39 38 байт

Спасибо Zgarb за сохранение 1 байта.

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

Попробуйте онлайн!

Я не уверен, существует ли более простой способ обеспечить квадратное соотношение сторон отдельных компонентов окна, чем использование рекурсивного нетерминала, но это, кажется, работает довольно хорошо.

объяснение

Лучше всего читать программу снизу вверх.

W=[MS

Это просто определяет нетерминал (о котором вы можете думать как о подпрограмме, которая соответствует прямоугольнику), Wкоторый соответствует или Mили S(есть неявный ]в конце строки).

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

Это определяет нетерминал, Bкоторый соответствует примерно четверти выходных данных, то есть одна панель окна с левой и верхней границей. Что-то вроде этого:

MSM
S  
M  

Чтобы гарантировать, что эта панель окна является квадратной, мы определяем Bрекурсивно. Это либо символ окна W, либо он B/W\ * W/\ /*добавляет один слой справа и снизу. Чтобы увидеть, как это происходит, давайте удалим синтаксический сахар:

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

Это то же самое, потому что горизонтальная конкатенация может быть записана либо, ABлибо A B, но последняя имеет более низкий приоритет, чем вертикальная конкатенация, в /то время как для первой - более высокая. Так B/W[ ]*же и Bс символом окна и рядом пробелов ниже. И затем мы добавляем по горизонтали, W/[ ]/*который является символом окна с колонкой пробелов.

Наконец, мы собираем эти нетерминалы в окончательную форму окна:

BB/BB/W+ W/+

Это четыре панели окна, Bза которыми следуют строка символов окна и столбец символов окна. Обратите внимание, что мы не делаем явного утверждения о том, что четыре оконные панели имеют одинаковый размер, но если это не так, невозможно объединить их в прямоугольник.

Наконец, e`в начале это просто конфигурация, которая говорит Грайм, чтобы проверить, что этот шаблон может быть сопоставлен со всем вводом (и он печатает 0или 1соответственно).


5

JavaScript (ES6), 115 113 байт

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

Принимает ввод как массив массивов символов (добавьте 5 байтов для массива строк) и возвращает 1или 0. После проверки того, что высота нечетная, проверяется каждая строка, чтобы убедиться, что массив является квадратным, и каждый символ проверяется как один из символов, которые мы ожидаем в этой конкретной позиции. Изменить: Сохранено 2 байта благодаря @PatrickRoberts.


Вы можете изменить, (...).includes(c)чтобы ~(...).search(c)сохранить 1 байт
Патрик Робертс

1
На самом деле, даже лучше, вы можете изменить его, чтобы (...?/ /:/[MS]/).test(c)сэкономить 2 байта вместо одного.
Патрик Робертс

@PatrickRoberts Милый, спасибо!
Нил

5

Perl, 124 123 119 95 93 84

Следующий скрипт Perl читает одно окно-кандидат MS из стандартного ввода. Затем он выходит с нулевым статусом выхода, если кандидат является окном MS, и с ненулевым статусом выхода, если это не так.

Он работает, генерируя два регулярных выражения, одно для верхней, средней и нижней строки и одно для каждой другой строки, и проверяя входные данные по ним.

Спасибо, @ Дада. И снова.

map{$s=$"x(($.-3)/2);$m="[MS]";($c++%($#a/2)?/^$m$s$m$s$m$/:/^${m}{$.}$/)||die}@a=<>

Я не уверен, что даю результат, так как статус выхода разрешен (хотя у меня нет времени искать соответствующую мета-публикацию). Несмотря на это, вы можете сохранить несколько байтов:@a=<>;$s=$"x(($.-3)/2);$m="[MS]";map{$a[$_]!~($_%($./2)?"$m$s$m$s$m":"$m${m}{$.}")&&die}0..--$.
Дада

@ Дада: Спасибо! Это впечатляющее улучшение: 24 персонажа. (В вашем коде была случайная "$ m", поэтому она даже короче, чем выглядела вначале.) Я не был уверен, разрешено ли вообще сообщать о результате с помощью кода выхода, но я взял "написать программу ( или функция) "как позволяющая быть гибким с тем, как результат возвращается в данном конкретном случае; Коды выхода являются практически значениями возврата функции среды * nix. :-)
NWK

Сделать это 26 символов.
NWK

1
На самом деле, я декремент $.в конце , чтобы избежать использования дважды $.-1(особенно , так как первый раз это было ($.-1)/2так нужна некоторые дополнительные круглые скобки), так что $mв $m${m}{$.}это не ошибка. Кроме того, я только сейчас понял, но регулярные выражения должны быть окружены ^...$(так что лишний символ в конце или начале заставляет их терпеть неудачу), или короче: используйте neвместо !~.
Дада

Неважно, очевидно, вы не можете использовать neвместо !~(я не должен писать сообщения, когда я не спал всего 15 минут!). ^...$Боюсь, вам придется использовать оба выражения.
Дада

2

Mathematica, 166 байт

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

Безымянная функция, принимающая список символов в качестве ввода и возвращающая Trueили False. Вот менее гольфовая версия:

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

Первая строка определяет функцию t, которая разделяет список длины dна две части, первая из которых - первая, средняя и последняя записи списка, а вторая - все остальные. Вторая строка проверяет, является ли ввод квадратным массивом в первую очередь. Четвертая строка использует tдважды, один раз на самом входе и один раз на всех * строках ввода, чтобы отделить символы, которые должны быть "M"или "S"от символов, которые должны быть пробелами; затем пятая и седьмая строки проверяют, действительно ли они такие, какими они должны быть.


2

JavaScript (ES6), 108 106 байт

Ввод: массив строк / Ввод: 0или1

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)

Контрольные примеры


2

JavaScript (ES6), 140 138 141 140 байт

Я знаю, что это не число выигрышных байтов (хотя благодаря Патрику Робертсу за -3 и я понял, что оно дает ложные срабатывания для 1 вместо M / S: +3), но я сделал это немного по-другому, я ' Я новичок в этом, и это было весело ...

Принимает массив строк, по одной для каждой строки, и возвращает true или false. Новая строка добавлена ​​для ясности (не включена в число байтов).

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

Вместо проверки ввода по обобщенному шаблону я создаю окно 'M' того же размера, заменяю S на M на входе и сравниваю их.

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

Контрольные примеры

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
Для дальнейшего использования, если функция не является рекурсивной, f=ее не нужно включать в число байтов, так что это фактически 138-байтовая отправка.
Патрик Робертс

Вы можете заменить z=-1+s/2|0с , z=(s-3)/2чтобы сохранить 1 байт
Патрик Робертс

Вы также можете заменить e.replace(/S/g,'M')==...на, e.split`S`.join`M`==...чтобы сохранить еще один байт
Патрик Робертс

Благодарность! z=-1+s/2|0есть, чтобы вернуть положительное целое число для s == 1 и даже s, то есть функция возвращает false без Array (), разбив его. В противном случае необходимая логика сделала это дольше. Отличный совет по разделению / присоединению, спасибо
Крис М

Хороший улов, я не рассматривал s=1случай, так как мое неверное регулярное выражение просто молча терпит неудачу.
Патрик Робертс

1

JavaScript (ES6), 109 107 106 105 99 байт

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

Редактировать : Вау, Арно спас мне 6 байтов, изменив s.split`\n`.lengthна s.search`\n`! Благодарность!

Это берет единственную многострочную строку и создает RegExpвалидацию на основе длины входной строки. Возвращает trueили false. Предполагает действительное окно имеет не символ конца строки.

демонстрация

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


Хороший подход! Не могли бы вы использовать r=s.search('\n')вместо split / length?
Арно

@ Арналд классное предложение, спасибо!
Патрик Робертс

Скобки s=>!s.split`S`.join`M`.search([...])могут быть удалены, не вызывая синтаксических ошибок.
Исмаэль Мигель

@IsmaelMiguel правильно, но затем строка передается как шаблон, который делает недействительным неявноеRegExp
Патрик Робертс

Это отстой ... Я действительно не ожидал этого ...
Исмаэль Мигель
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.