Mendeteksi jendela seni ASCII yang terbuat dari karakter M dan S

28

Jendela adalah kotak ASCII-art square dengan panjang sisi ganjil minimal 3, dengan batas karakter tunggal di sekitar tepi serta goresan vertikal dan horizontal di tengah:

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

MS Window adalah jendela di mana perbatasan dibuat hanya dari karakter Mdan S. Tugas Anda adalah menulis sebuah program (atau fungsi) yang mengambil string dan menampilkan nilai kebenaran jika inputnya adalah MS Window yang valid, dan nilai falsey jika tidak.

Spesifikasi

  • Anda dapat mengambil input sebagai string yang dipisahkan baris baru atau array string yang mewakili setiap baris.
  • Batas MS Window mungkin berisi campuran karakter M dan S, tetapi bagian dalamnya akan selalu terdiri dari spasi.
  • Anda dapat memilih untuk mendeteksi hanya windows dengan trailing newlines, atau hanya windows tanpa trailing newlines, tetapi tidak keduanya.

Uji Kasus

Benar:

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
Buah Esolanging
sumber
3
Ini adalah twist besar pada seni ASCII, masalah keputusan untuk mendeteksi struktur tertentu.
xnor
4
@ xnor Saya merasa kami mungkin menginginkan tag lain untuk membalikkan seni ASCII seperti ini.
Buah Esolanging
2
sementara tidak khusus untuk ascii art, pencocokan pola mungkin menjadi pilihan yang baik untuk tag baru
Destructible Lemon
Bisakah Anda menambahkan satu atau dua test case di mana string tidak membentuk array persegi panjang?
Greg Martin
1
@Mast, Anda benar sekali! Mungkin tantangannya perlu diklarifikasi
Chris M

Jawaban:

1

Pyke, 34 31 byte

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

Coba di sini!

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")
Biru
sumber
8

Retina , 68 67 byte

Hitungan byte mengasumsikan penyandian ISO 8859-1.

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

Cobalah online!

Martin Ender
sumber
7

Grime , 39 38 byte

Terima kasih kepada Zgarb karena telah menghemat 1 byte.

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

Cobalah online!

Saya tidak yakin apakah ada cara yang lebih sederhana untuk menegakkan rasio aspek kuadrat dari masing-masing komponen jendela daripada menggunakan rekursif nonterminal, tetapi ini tampaknya bekerja dengan cukup baik.

Penjelasan

Yang terbaik adalah membaca program dari bawah ke atas.

W=[MS

Ini hanya mendefinisikan nonterminal (yang dapat Anda anggap sebagai subrutin yang cocok dengan persegi panjang) Wyang cocok dengan salah satu Matau S(ada yang tersirat ]di akhir baris).

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

Ini mendefinisikan non-terminal Byang cocok dengan sekitar seperempat dari output, yaitu satu panel jendela dengan batas kiri dan atas. Sesuatu seperti ini:

MSM
S  
M  

Untuk memastikan bahwa panel jendela ini berbentuk persegi, kami mendefinisikan Bsecara rekursif. Entah itu karakter jendela W, atau B/W\ * W/\ /*yang menambahkan satu lapisan ke kanan dan ke bawah. Untuk melihat bagaimana hal ini dilakukan, mari kita hapus beberapa gula sintaksis:

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

Ini sama, karena konkatasi horizontal dapat ditulis salah satu ABatau A B, tetapi yang terakhir memiliki prioritas lebih rendah daripada konkatasi vertikal /sedangkan untuk yang sebelumnya memiliki yang lebih tinggi. Begitu B/W[ ]*juga Bdengan karakter jendela dan deretan spasi di bawah. Dan kemudian kita menambahkan secara horizontal W/[ ]/*yang merupakan karakter jendela dengan kolom spasi.

Akhirnya, kami mengumpulkan nonterminal ini ke dalam bentuk jendela terakhir:

BB/BB/W+ W/+

Itu empat panel jendela Bdiikuti oleh deretan karakter jendela dan kolom karakter jendela. Perhatikan bahwa kami tidak membuat pernyataan eksplisit bahwa keempat panel jendela berukuran sama, tetapi jika tidak, tidak mungkin untuk menggabungkannya menjadi persegi panjang.

Akhirnya e`pada awalnya hanyalah sebuah konfigurasi yang memberitahu Grime untuk memeriksa bahwa seluruh input dapat dicocokkan dengan pola ini (dan itu mencetak 0atau 1sesuai).

Martin Ender
sumber
5

JavaScript (ES6), 115 113 byte

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

Mengambil input sebagai array array karakter (tambahkan 5 byte untuk array string) dan mengembalikan 1atau 0. Setelah memverifikasi bahwa ketinggiannya ganjil, setiap baris diperiksa untuk memastikan lariknya berbentuk bujur sangkar, dan setiap karakter diverifikasi menjadi salah satu karakter yang kami harapkan di posisi tertentu. Sunting: Disimpan 2 byte berkat @PatrickRoberts.

Neil
sumber
Anda dapat mengubah (...).includes(c)ke ~(...).search(c)untuk menyimpan 1 byte
Patrick Roberts
1
Sebenarnya, bahkan lebih baik Anda dapat mengubahnya (...?/ /:/[MS]/).test(c)untuk menghemat 2 byte, bukan hanya 1.
Patrick Roberts
@ PatrickRoberts Lucu, terima kasih!
Neil
5

Perl, 124 123 119 95 93 84

Script Perl berikut membaca satu kandidat MS Window dari input standar. Itu kemudian keluar dengan status keluar nol jika kandidat adalah MS Window dan dengan status keluar non-nol jika tidak.

Ia bekerja dengan menghasilkan dua ekspresi reguler, satu untuk garis atas, tengah dan bawah dan satu untuk setiap baris lainnya, dan memeriksa input terhadapnya.

Terima kasih, @Dada. Dan lagi.

map{$s=$"x(($.-3)/2);$m="[MS]";($c++%($#a/2)?/^$m$s$m$s$m$/:/^${m}{$.}$/)||die}@a=<>
nwk
sumber
Saya tidak yakin memberikan hasilnya karena status keluar diizinkan (saya tidak punya waktu untuk mencari meta post yang relevan). Apapun, Anda dapat menyimpan beberapa byte:@a=<>;$s=$"x(($.-3)/2);$m="[MS]";map{$a[$_]!~($_%($./2)?"$m$s$m$s$m":"$m${m}{$.}")&&die}0..--$.
Dada
@Dada: Terima kasih! Itu peningkatan yang mengesankan: 24 karakter. (Ada "$ m" yang tersesat dalam kode Anda, jadi itu bahkan lebih pendek daripada yang terlihat pada awalnya.) Saya tidak yakin apakah melaporkan hasil dengan kode keluar diizinkan secara umum, tetapi saya mengambil "tulis sebuah program ( atau fungsi) "memungkinkan seseorang menjadi fleksibel dengan bagaimana hasilnya dikembalikan dalam kasus khusus ini; kode keluar praktis adalah nilai-nilai fungsi pengembalian lingkungan * nix. :-)
nwk
Buat 26 karakter.
nwk
1
Sebenarnya, saya mengurangi $.pada akhirnya untuk menghindari menggunakan dua kali $.-1(terutama sejak pertama kali itu ($.-1)/2jadi diperlukan beberapa tanda kurung tambahan), jadi $mdalam $m${m}{$.}bukan kesalahan. Juga, saya baru sadar sekarang, tetapi regex harus dikelilingi dengan ^...$(jadi karakter tambahan di akhir atau awal membuat mereka gagal), atau lebih pendek: gunakan nebukan !~.
Dada
Nevermind, jelas Anda tidak bisa menggunakan nebukan !~(saya tidak harus menulis pesan ketika saya sudah bangun hanya 15 menit!). Jadi Anda harus menggunakan ^...$di kedua regex, saya khawatir.
Dada
2

Mathematica, 166 byte

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

Fungsi tanpa nama mengambil daftar daftar karakter sebagai input dan pengembalian Trueatau False. Ini versi yang kurang golf:

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

Baris pertama mendefinisikan fungsi t, yang memisahkan daftar panjang dmenjadi dua bagian, yang pertama adalah entri pertama, tengah, dan terakhir dari daftar, dan yang kedua adalah sisanya. Baris kedua memeriksa apakah input adalah array persegi di tempat pertama. Baris keempat menggunakan tdua kali, sekali pada input itu sendiri dan sekali pada semua * string dalam input, untuk memisahkan karakter yang seharusnya "M"atau "S"dari karakter yang seharusnya spasi; kemudian baris kelima dan ketujuh memeriksa apakah mereka benar-benar seharusnya.

Greg Martin
sumber
2

JavaScript (ES6), 108 106 byte

Input: array string / Output: 0atau1

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)

Uji kasus

Arnauld
sumber
2

JavaScript (ES6), 140 138 141 140 byte

Saya tahu ini bukan hitungan byte yang menang (walaupun terima kasih kepada Patrick Roberts untuk -3, dan saya menyadari itu melemparkan positif palsu untuk 1 bukannya M / S: +3), tetapi saya melakukannya dengan cara yang sedikit berbeda, saya ' Saya baru dengan ini, dan itu menyenangkan ...

Menerima serangkaian string, satu untuk setiap baris dan mengembalikan benar atau salah. Baris baru ditambahkan untuk kejelasan (tidak termasuk dalam jumlah byte).

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

Alih-alih memeriksa input terhadap pola umum, saya membangun jendela 'M' dengan ukuran yang sama, mengganti S dengan M pada input, dan membandingkan keduanya.

Tidak disatukan

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

Uji kasus

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
sumber
1
Untuk referensi di masa mendatang, kecuali fungsinya rekursif, f=tidak perlu dimasukkan dalam jumlah byte, jadi ini sebenarnya adalah pengiriman 138 byte.
Patrick Roberts
Anda dapat menggantinya z=-1+s/2|0dengan z=(s-3)/2menyimpan 1 byte
Patrick Roberts
Anda juga dapat mengganti e.replace(/S/g,'M')==...dengan e.split`S`.join`M`==...untuk menyimpan byte lain
Patrick Roberts
Terima kasih! z=-1+s/2|0ada untuk mengembalikan bilangan bulat positif untuk s == 1 dan bahkan s, yaitu fungsi mengembalikan false tanpa Array () menabraknya. Kalau tidak, logika yang diperlukan membuatnya lebih lama. Tip luar biasa untuk split / gabung, terima kasih
Chris M
Tangkapan yang bagus, saya tidak mempertimbangkan s=1kasusnya, karena regex saya yang tidak valid hanya diam-diam gagal.
Patrick Roberts
1

JavaScript (ES6), 109 107 106 105 99 byte

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

Sunting : Whoa, Arnauld menyelamatkan saya 6 byte dengan mengubah s.split`\n`.lengthke s.search`\n`! Terima kasih!

Ini mengambil string multiline tunggal dan membangun RegExpvalidasi berbasis menggunakan panjang string input. Pengembalian trueatau false. Mengasumsikan jendela valid memiliki tidak memiliki baris baru trailing.

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
sumber
Pendekatan yang bagus! Bisakah Anda menggunakan r=s.search('\n')bukan split / length?
Arnauld
@Arnauld saran yang luar biasa, terima kasih!
Patrick Roberts
Tanda kurung aktif s=>!s.split`S`.join`M`.search([...])dapat dihapus, tanpa menyebabkan kesalahan sintaksis.
Ismael Miguel
@ IsmaelMiguel benar, tetapi kemudian string dilewatkan sebagai templat, yang membatalkan implisitRegExp
Patrick Roberts
Itu menyebalkan ... Aku benar-benar tidak mengharapkan itu ...
Ismael Miguel