Mouse dengan Dynamite


23

Kamu adalah tikus. Teman-teman mouse Anda semuanya telah ditangkap, dan tidak sadar dan terjebak dalam labirin yang hanya memiliki satu pintu masuk / keluar. Anda memiliki peta labirin yang sempurna, sehingga Anda dapat merencanakan solusi untuk bergegas masuk dan membawa semuanya ke tempat yang aman. Namun, labirin dijaga dengan sistem keamanan yang akan memicu peringatan jika ambang batas 1000tercapai, menyebabkan Anda ditangkap dan gagal dalam misi penyelamatan Anda.

Dari penyelidikan sebelumnya pada labirin, setiap kotak yang Anda langkahkan (yaitu, setiap gerakan horizontal atau vertikal - tikus tidak dapat bergerak secara diagonal ) menambah 1penghitung sistem keamanan. Namun, jika Anda membawa beban (baik blok dinamit atau teman mouse yang tidak sadar), itu malah menambah 2karena mendeteksi tekanan tambahan. Alur masuk / keluar tidak memiliki sistem keamanan ini, jadi tidak menambah konter.

Anda memiliki persediaan dinamit yang tidak terbatas yang telah Anda bawa ke pintu masuk, sehingga Anda dapat meledakkan semua dinding untuk membebaskan teman-teman Anda. Tetapi Anda harus berhati-hati dengan melakukannya, karena setiap ledakan menambah 50konter dari tekanan concussive. Selain itu, Anda hanya dapat membawa satu barang pada satu waktu, baik satu mouse atau satu blok dinamit. Karena setiap blok dinamit hanya dapat meledakkan satu ruang dinding, ini berarti bahwa jika ada beberapa dinding berturut-turut, Anda perlu melakukan perjalanan dengan tangan kosong kembali ke pintu masuk untuk mengambil lebih banyak.

Contoh yang dikerjakan

Misalkan labirin kita terlihat seperti berikut:

######
#M# E#
######

Saya akan gunakan cuntuk konter. Kami mulai di Entrance, bergerak satu persegi kiri sambil membawa dinamit c=2,. Kami meledakkan dinamit untuk meledakkan tembok c=52,. Kami memindahkan dua kotak ke kiri, dengan tangan kosong, untuk mendapatkan c=54, dan kami sekarang berdiri di atas kotak mouse. Kami menjemput teman kami, dan memindahkan 3 kotak kembali ke Exit, tetapi kotak terakhir tidak dihitung karena tidak memiliki sensor, jadi itu hanya 2 kotak dengan sesuatu di punggung kami. Itu berarti bahwa ketika kita mencapai pintu keluar dengan mouse terakhir c=58, yang kurang dari 1000, dan karenanya misi berhasil.

Tantangan

Diberikan labirin input, hasilkan apakah Anda, pahlawan mouse, dapat berhasil menyelamatkan semua tikus yang terperangkap dalam batasan yang diuraikan di atas, atau apakah misinya gagal.

Memasukkan

  • Labirin 2D dalam format apa pun yang dapat diterima (string multiline, array string, dll.).
  • Untuk tantangan ini, saya akan menggunakan #untuk dinding interior dan eksterior, Muntuk teman-teman mouse, dan Euntuk pintu masuk.
  • Pintu masuk tidak akan pernah berbatasan langsung dengan dinding interior (akan selalu ada setidaknya satu ruang untuk bergerak bebas).
  • Anda dapat mengganti karakter ASCII yang dapat dicetak yang Anda inginkan asalkan konsisten. Ini tidak memungkinkan Anda untuk menggunakan dua simbol yang berbeda untuk dinding interior vs dinding eksterior, asalkan Anda menjaga konsistensi (misalnya, jika Anda memilih untuk menggunakan @untuk dinding interior sebaliknya, dan cuti #untuk eksterior, setiap dinding interior harus @dan setiap dinding eksterior #).
  • Labirin akan selalu dibatasi sepenuhnya oleh dinding, tetapi tidak harus persegi panjang. Jika diinginkan, Anda dapat mengasumsikan bahwa labirin diisi dengan spasi untuk membuat input persegi panjang (opsional).
  • Labirin mungkin memiliki bagian yang tidak dapat dijangkau tanpa dinamit.
  • Anda tidak dapat mengubah dinding eksterior labirin.

Keluaran

Nilai kebenaran / kepalsuan . Sejujurnya untuk "Ya, mouse dapat menyelamatkan setiap mouse lainnya" atau Falsey untuk "Tidak, sistem alarm akan tersandung."

Aturan

  • Program lengkap atau fungsi dapat diterima.
  • Celah standar dilarang.
  • Ini adalah sehingga semua aturan golf biasa berlaku, dan kode terpendek (dalam byte) menang.

Contohnya

Contoh yang benar, dipisahkan oleh garis kosong.

#####
#M E#
#####

######
#M# E#
######

########
#E  # M#
#   #  #
#   #  #
#      #
########

#############################
#    ##      #       #      #
#  M ## M    #       #      #
#    ##      #   M   #    E #
#M   ##      #       #      #
#############################

###############
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMM MM#
#MMMMMMMMMMMME#
###############

Contoh Falsey, dipisahkan oleh garis kosong

#############################
#M   ##      ##      ##     #
#  M ## M    ##      ##     #
#    ##      ##  M   ##   E #
#M   ##      ##      ##     #
#############################
#############################
                     ########
                     ########
                     #   #  #
                     # M # M#
                     ########

              #####
              # M #
              #####
              #####
              #####
              #####
###################
# # # ##   ## # # #
#M#M#M## E ##M#M#M#
# # # ##   ## # # #
###################
#######
######
#####
####
# M#
####

###############
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMME#
###############

3
Tikus-tikus itu geram (spoiler ringan)
Luis Mendo

3
@AlexA. Maaf Anda harus mempelajarinya dari orang asing di Internet. ;-)
AdmBorkBork

2
Saya kira sebagian besar orang akan kesulitan menyelesaikan ini dengan kode biasa, apalagi bermain golf. Ini adalah tantangan besar yang sayangnya saya tidak punya waktu untuk dikerjakan.
Moshe Katz

2
Apakah dapat diterima untuk memiliki char yang berbeda untuk dinding eksternal (karena mereka tidak dapat dirusak)?
Tensibai

2
@ Moshe Katz , yakin Anda tidak punya waktu. Anda hanya tidak ingin menyimpan Mäuse.
msh210

Jawaban:


19

Perl, 216 215 byte

Termasuk +2 untuk -0p

Berikan masukan pada STDIN. Gunakan %untuk dinding eksternal, #untuk dinding internal, 0untuk ruang kosong, 8untuk tikus dan runtuk posisi awal. Seluruh papan harus empuk sehingga membentuk persegi panjang. Anda dapat mengubah dan menjalankan contoh sebagai:

cat dynamite.txt | perl -p0e 's/.+/$a^=$&/egr;s//sprintf"%-*s",length$a,$&/eg;1while/\n/,s/^ *\K#|#(?= *$)|^ *.{@{-}}\K#|\A[^\n]*\K#|#(?=[^\n]*\n\z)|#(?=.{@{-}} *$)/%/sm;y/ EM/0x2/' | dynamite.pl

dynamite.pl:

#!/usr/bin/perl -0p
sub f{@a{@_}||=push@{$%+($&?$1?50:$&=~8?0:$&&"5"?2:1:0)},@_}f$_;for(@{$%}){f y/xr|/ytx/r;{f s/\pL\d/$&^(E&$&)x2/er}{f s/(q|s|y)#/$&^"\x01\x13"/er}my$o;{$\|=/x/>/2/;$o.="
"while s/.$/$o.=$&,""/meg}f$o}$%++>999|$\||redo}{

Ganti \xhhlolos untuk skor yang diklaim.

Program tidak dapat secara realistis menangani kasus rumit. Khususnya itu tidak dapat menangani salah satu kasus kegagalan. Ini karena ada terlalu banyak cara yang berbeda untuk meledakkan dinding internal atau mengambil tikus sehingga pencarian menjadi terlalu luas dan menggunakan memori terlalu banyak meskipun itu setidaknya cukup pintar untuk tidak pernah memproses keadaan yang sama beberapa kali. Batas tekanan harus diturunkan ke 100atau lebih untuk runtime yang lumayan dan penggunaan memori.

Penjelasan

Saya menggunakan pola bit karakter untuk mewakili keadaan bidang:

contains victim: 0000 0010
has hero:        0100 0000
carry dynamite   0000 0001
carry mouse      0000 0100
home             0000 1000
walkable         0001 0000 (not really needed but results in shorter regexes)
make printable   0010 0000
wall             0010 xxxx (bit patterns not very important,
permawall        0010 xxxx  just avoid letters and digits)

Misalnya pahlawan ( 01000000) yang membawa dinamit ( 00000001) harus berada di tempat yang dapat ia jalani ( 00010000) dan kami ingin semua nilai dicetak ASCII ( 00100000). Mengambil bitwise ordari semua bitmasks ini memberi 01110001yang merupakan kode ASCII untuk q. Totalnya menjadi ::

p: hero                     r hero on victim
q: hero carrying dynamite   s hero carrying dynamite on victim
t: hero carrying mouse      v hero carrying mouse on victim

x : hero at home
y : hero at home carrying dynamite
| : hero at home carrying mouse

0: empty  without hero
8: home   without hero
2: victim without hero

%: permanent wall
#: normal wall

Program hanya akan mempertimbangkan pahlawan bergerak ke kanan (rotasi yang dijelaskan nanti akan mengurus arah lainnya). Bitmasks dipilih dengan hati-hati sedemikian rupa sehingga pahlawan selalu diwakili oleh surat dan tempat ia bisa bergerak dengan digit (kecuali pahlawan di rumah membawa korban, tapi sekali lagi ini disengaja sehingga satu-satunya langkah pahlawan adalah untuk menjatuhkan korban). Jadi pahlawan yang bisa bergerak maju dicocokkan dengan /\pL\d/. Substring yang cocok harus dimodifikasi sehingga pahlawan dan apa yang dibawanya dihapus dari karakter pertama dan ditambahkan ke karakter kedua, yang dapat dilakukan dengan bitwise xordengan nilai yang sama untuk kedua karakter. Nilai xor terdiri dari bit pahlawan ( 01000000), bit dinamit ( 00000001) dan carry mouse bit ( 00000100). Bersama-sama mereka ormelakukannya01000101yaitu ASCII E. Jadi menggerakkan pahlawan menjadi:

s/\pL\d/$&^(E&$&)x2/e

Pahlawan dapat meledakkan dinding jika dia berdiri tepat di depannya dan membawa dinamit ( q, satau y). Pahlawan akan kehilangan dinamitnya ( xordengan 00000001) dan dinding #akan berubah menjadi sebuah bagian 0(xor dengan 00010011), jadi

s/(q|s|y)#/$&^"\x01\x13"/e

Untuk menangani arah lain seluruh papan diputar (papan diputar berakhir di $o):

my$o;$o.="\n"while s/.$/$o.=$&,""/meg

Selain menggerakkan pahlawan juga memiliki sejumlah pilihan lain yang dapat ia buat:

When at home, pick up dynamite:                   x -> y
When on victim not carrying anything pick him up: r -> t
When at home carrying a victim, drop him off:     | -> x

Ini dilakukan oleh

y/xr|/ytx/

Papan selesai jika pahlawan di rumah tidak membawa apa-apa ( x) dan tidak ada lagi korban untuk diselamatkan (tidak 2). Ini dapat dengan mudah diuji menggunakan

/x/>/2/

Setelah papan diselesaikan, saya ingin mengingat keadaan ini dan pada akhirnya mencetaknya. Untuk itu saya membawa bendera "diselesaikan" $\dan mencetaknya di akhir program tanpa mencetak $_, jadi

$\|=/x/>/2/  ...   }{

Keadaan yang akan diproses pada tekanan 0 disimpan @0, pada tekanan 1 pada @1dll. Tekanan saat ini disimpan $%. Menggunakan $natau sesuatu seperti itu akan lebih pendek tetapi kode tidak berfungsi jika variabel tidak diinisialisasi ke sesuatu karena autovivification sebaliknya akan berubah $nmenjadi referensi ARRAY. Melompati keadaan pada tekanan tertentu dilakukan menggunakan a fordan bukan mapkarena dengan forAnda dapat memperpanjang array saat itu masih diulang dan akan mengambil elemen baru. Ini diperlukan karena rotasi dan pilihan bidang tunggal dari pahlawan terjadi dalam waktu 0 dan berakhir di array tekanan yang sama. Jadi loop untuk tekanan yang diberikan dilakukan oleh

for(@{$%}){...}

Ini diulang sampai tekanan mencapai 1000 atau solusi ditemukan:

$%++>999|$\||redo

Yang tersisa adalah menambahkan status yang baru ditemukan ke masing-masing bar tekanan. Itu akan dilakukan dengan subrutin f. Kami hanya ingin menambahkan keadaan jika belum terlihat. Negara yang telah dilihat sebelumnya disimpan di %a:

sub f{@a{@_}||=push@$n, @_}

$nmewakili tekanan baru untuk suatu negara. Saya akan mendapatkan bahwa dari negara bahwa variabel regex masih ada sebagai akibat dari tindakan pahlawan yang mengarah ke panggilan ini:

if $1 is set it was from s/(q|s|y)#/$&^"\x01\x13"/e which blows a wall -> 50
else if $& is set it was from s/\pL\d/$&^(E&$&)x2/e, so the hero moves.
    if $& contains 8 the hero went home -> 0
    else if the hero has carry bits (5) -> 2
    else                                   1
else ($& was not set) it was from y/xr|/ytx/r -> 0

Ini mengarah ke rumus berikut untuk $n:

$%+($&?$1?50:$&=~8?0:$&&"5"?2:1:0)

Semua substitusi mendapatkan rpengubah sehingga mereka mengembalikan keadaan yang diubah dan membiarkan keadaan saat ini $_sendirian. fkemudian dipanggil pada kondisi berubah ini, sehingga Anda mendapatkan kode seperti

f y/xr|/ytx/r;

Karena perhitungan $nkebutuhan variabel regex mereka harus default untuk tidak disetel jika penggantian tidak mengubah (karena kondisi untuk memicu mereka tidak terpenuhi). Saya juga tidak harus mengambil variabel regex dari loop sebelumnya. Oleh karena itu substitusi dibungkus dalam {}blok untuk menyimpan dan mengembalikan negara regex. Seperti itulah Anda mendapatkan pernyataan seperti

{f s/\pL\d/$&^(E&$&)x2/er}

Khususnya rotasi begitu dibungkus sehingga ia memanggil ftanpa variabel regex dan mendapat kontribusi tekanan 0.

Satu-satunya hal yang masih harus dilakukan adalah prima @0dengan keadaan awal di awal

f$_

Ini berada di loop utama sehingga juga mencoba untuk menambah $_array tekanan kemudian, tetapi karena keadaan awal sudah %atidak akan terjadi apa-apa.

Bersama-sama semua ini pada dasarnya menemukan solusi terpendek menggunakan algoritma Dijkstra . Namun ada potensi masalah. Kode saat ini tidak akan menambah status jika ditemukan kembali dengan tekanan lebih rendah daripada penemuan pertama. Saya tidak dapat membuat papan yang akan memicu ini, tetapi juga tidak dapat membuktikan bahwa itu tidak mungkin.


3
Ooo, membangkitkan minat. Ini jauh lebih singkat daripada yang saya harapkan. Bisakah Anda menambahkan sedikit penjelasan? Saya tidak benar-benar grok Perl.
AdmBorkBork

3
@TimmyD Ok, penjelasan ditambahkan dengan cukup detail sehingga bahkan seorang programmer non perl harus mendapatkan setidaknya kesan tentang cara kerjanya
Ton Hospel

1
Sangat mengesankan!
Emigna

Golf yang luar biasa, itu sangat mengesankan ... Ketika saya pikir saya tidak seburuk itu bermain golf dengan Perl, saya melihat golf Anda, dan perasaan itu hilang dengan cepat!
Dada

13

JavaScript, 863 834 785 781 byte

Menyimpan 29 byte berkat ETHproduk
Disimpan 53 byte berkat Jordan

L=[]
f=(S,r="",R="",p=0,s=S.replace(RegExp(r),R),l=`((.|\n){${s.split`
`[0].length}})`,q=p+1,o=p+2,n=p+50)=>s in L|p>999?1e3:!/M/.test(s,L[s]=0)&/E/.test(s)?p:Math.min(...[[/ E/,"me",q],[/ E/,"de",o],[/ME/,"ce",q],[/E /,"em",q],[/E /,"ed",o],[/EM/,"ec",q],[`E${l} `,"e$1m",q],[`E${l} `,"e$1d",o],[`E${l}M`,"e$1c",q],[` ${l}E`,"m$1e",q],[` ${l}E`,"d$1e",o],[`M${l}E`,"c$1e",q],[/ m/,"m ",q],[/m /," m",q],[`m${l} `," $1m",q],[` ${l}m`,"m$1 ",q],[/ (d|c)/,"$1 ",o],[/(d|c) /," $1",o],[`(d|c)${l} `," $2$1",o],[` ${l}(d|c)`,"$3$1 ",o],[/d#/,"m ",n],[/#d/," m",n],[`#${l}d`," $1m",n],[`d${l}#`,"m$1 ",n],[/mM/," c",q],[/Mm/,"c ",q],[`M${l}m`,"c$1 ",q],[`m${l}M`," $1c",q],[/[mc]e/," E",p],[/e[mc]/,"E ",p],[`e${l}[mc]`,"E$1 ",p],[`[mc]${l}e`," $1E",p]].map(a=>f(s,...a)))
F=s=>f(s)<1e3

Mengambil input sebagai string multiline.

Ini mendefinisikan fungsi anonim yang menggunakan fungsi rekursif funtuk menentukan apakah Anda mematikan alarm sebelum mengambil semua tikus. fkembali 1000jika tekanan di atas 1000 (untuk menghindari rekursi tak berujung), mengembalikan tekanan jika tidak ada lagi tikus untuk menyelamatkan dan mouse di pintu keluar, dan mengembalikan tekanan minimum dari semua gerakan yang mungkin dari keadaan saat ini sebaliknya. Ini menggunakan array Luntuk melacak posisi yang sudah dikunjungi di mana L[pos]==0jika dikunjungi, dan tidak terdefinisi jika tidak. Ini mungkin tidak perlu, tetapi itu mencegah mouse dari melakukan gerakan tidak berguna dan melemparkan kesalahan rekursi setidaknya. (Ini berarti Anda harus mendefinisikan ulang Ljika Anda menguji ini beberapa kali)

Ini menggunakan format dalam pertanyaan selain yang mengharuskan Anda menggunakan karakter yang berbeda untuk dinding eksternal. (Apa pun selain # MEmecd)

Versi yang lebih mudah dibaca:

stateList = []
f=(s,regex="",replacement="",pressure=0,state=s.replace(regexp(regex),replacement),line=`((.|\n){${state.split("\n")[0].length}})`)=>{
    if (state in stateList || pressure > 999) return 1e3
    if (!/M/.test(state) && /E/.test(state)) return pressure

    stateList[state] = 0

    return [
        [/ E/,"me",pressure+1],
        [/ E/,"de",pressure+2],
        [/ME/,"ce",pressure+1],
        [/E /,"em",pressure+1],
        [/E /,"ed",pressure+2],
        [/EM/,"ec",pressure+1],
        [`E${line} `,"e$1m",pressure+1],
        [`E${line} `,"e$1d",pressure+2],
        [`E${line}M`,"e$1c",pressure+1],
        [` ${line}E`,"m$1e",pressure+1],
        [` ${line}E`,"d$1e",pressure+2],
        [`M${line}E`,"c$1e",pressure+1],
        [/ m/,"m ",pressure+1],
        [/m /," m",pressure+1],
        [`m${line} `," $1m",pressure+1],
        [` ${line}m`,"m$1 ",pressure+1],
        [/ ([dc])/,"$1 ",pressure+2],
        [/([dc]) /," $1",pressure+2],
        [`([dc])${line} `," $2$1",pressure+2],
        [` ${line}([dc])`,"$3$1 ",pressure+2],
        [/d#/,"m ",pressure+50],
        [/#d/," m",pressure+50],
        [`#${line}d`," $1m",pressure+50],
        [`d${line}#`,"m$1 ",pressure+50],
        [/mM/," c",pressure+1],
        [/Mm/,"c ",pressure+1],
        [`M${line}m`,"c$1 ",pressure+1],
        [`m${line}M`," $1c",pressure+1],
        [/[mc]e/," E",pressure],
        [/e[mc]/,"E ",pressure],
        [`e${line}[mc]`,"E$1 ",pressure],
        [`[mc]${line}e`," $1E",pressure]
    ].map(a=>f(state,...a)).reduce((a,b)=>a-b<0?a:b) //reduce used for support in more browsers.
}
s=>f(s)>1e3

Ruang putih tidak berguna di s in L|p > 999.
Yytsi

@ TuukkaX Terima kasih telah mengingatkan saya tentang hal itu, bytecount adalah untuk versi tanpa spasi.
DanTheMan

Lihat apakah Anda dapat menyimpan byte dengan membungkus kode evaldan mengganti @dengan $1(tidak yakin apakah ini akan berhasil, tetapi Anda $1banyak menulis )
Cyoce

Saya pikir Anda bisa menyelamatkan banyak dengan membuat fsatu-liner:f=(...)=>s in L|p>999?1e3:!/M/.test(s,L[s]=0)&/E/.test(s)?p:Math.min(...
ETHproduksi

@Cyoce Saya menggunakan $118 kali, dan .replace("@","$1")18 byte. Saya tidak melihat cara untuk melakukannya.
DanTheMan
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.