Aku ingin melihatmu mati kehausan

12

Anda adalah seorang musafir yang melintasi padang pasir antara dua kota. Anda tidak dapat membawa air yang cukup untuk menyeberang tanpa berhenti. Ini adalah variasi dari teka-teki klasik.

Aturan

Gurun terlihat seperti ini: kotak WxH sebagian besar ruang kosong. Ruang yang ditandai Sadalah tempat Anda mulai, Etempat Anda ingin mengakhiri, dan kotak yang ditandai dengan angka N menampung N unit air. Kotak ditandai dengan .tahan nol air.

.....................................
........S............................
.....................................
.........7...........................
.....................................
.......................3.............
.....5...............................
................................2....
.....................................
.....................................
.....................................
...............................E.....
.....................................
....................7................
.....................................
.....................................

Anda mulai dari S dengan 5 unit air.

Anda dapat membawa paling banyak 5 unit air.

Setiap giliran Anda

  1. pindahkan satu kotak ke atas, Bawah, Kiri, atau Kanan,
  2. mengkonsumsi 1 unit air yang Anda bawa,
  3. mengambil atau menjatuhkan beberapa unit air.

Sebuah gilirannya dinotasikan demikian: (direction)(+|-)(units of water), +menunjukkan Anda mengambil air, -bahwa Anda menjatuhkannya.

Contoh berubah:

D+0        Move Down
R+0        Move Right
D+2        Move Down, pick up two units of water.
U-1        Move Up, drop one unit of water.

Jika Anda melakukan gerakan ini mulai dari S pada contoh di atas, gurun tampak seperti ini sesudahnya.

.....................................
........S............................
.........1...........................
.........5...........................
.....................................
.......................3.............
.....5...............................
................................2....
.....................................
.....................................
.....................................
...............................E.....
.....................................
....................7................
.....................................
.....................................

Anda tidak dapat mengambil lebih banyak air daripada yang sudah ada di alun-alun Anda. Saat Anda mengambil air, kurangi jumlah unit itu dari hitungan ubin.

Anda hanya dapat mengambil air untuk menampung maksimal 5 unit.

Tidak ada ubin yang dapat menampung lebih dari 9 unit, kecuali S yang menampung unit tanpa batas.

Anda hanya bisa menjatuhkan air sebanyak yang Anda pegang saat ini.

Air di tanah tetap tidak berubah sampai Anda mengambilnya lagi.

Jika Anda kembali ke S, Anda dapat mengambil sejumlah air tanpa mengurasnya.

Jika Anda mencapai E maka Anda menang . Anda masih menang jika Anda mengkonsumsi unit air terakhir Anda pada E.

Jika, setelah giliran Anda, Anda memiliki nol air dan Anda tidak berada di E, Anda mati .

Masukan dan keluaran

Program Anda akan menerima peta awal ukuran sewenang-wenang STDINsebagai seni ASCII dalam format di atas. Anda dapat menganggap itu adalah persegi panjang yaitu semua garis memiliki panjang yang sama, bahwa ada tepat satu Sdan satu Ekotak, semua garis diakhiri dengan \n, dan seluruh STDIN akan sesuai dengan regex ini:/^[SE1-9\.\n]+$/

Program Anda akan menulis output berikut ke STDOUT:

  1. daftar gerakan,
  2. keadaan akhir peta.

Anda dapat menampilkan daftar gerakan dalam format apa pun yang nyaman.

Status akhir peta akan dicetak dalam format yang sama dengan input kecuali bahwa itu juga akan menunjukkan rute yang Anda ambil melalui padang pasir dengan menandai semua ubin yang dikunjungi dengan #, jika ubin itu tidak mengandung air dan bukan S atau E (yaitu ini .).

CONTOH input:

.....S.
.......
.......
E......
....8..

CONTOH hasil kemenangan:

D+0
D+0
D+0
D+0
L+5
L+0
L+0
L+0
L+0
U+0
.....S.
.....#.
.....#.
E....#.
####3#.

Ketidaksenangan

Ketika Anda memposting kode Anda, memposting input peta sampel yang kode Anda menemukan solusi yang memenuhi kondisi non-sepele berikut:

  • S dan E setidaknya 10 langkah terpisah.
  • Setiap bujur sangkar yang awalnya berisi N unit air harus dikelilingi oleh perbatasan selebar-N di mana semua kotak .(tidak ada air, bukan S atau E)

CONTOH

........2.
..........
..........
S.1..2....
..........
..........
........1.
..3.......
.........E

Jika Anda menambah jumlah air pada ubin apa pun, hal di atas menjadi sepele.

Persyaratan

Agaknya program Anda akan menghadapi sejumlah upaya gagal sebelum menemukan solusi, jika ada.

  1. Program Anda pada akhirnya harus menyelesaikan setiap input yang dapat dipecahkan.
  2. Saya ingin melihat Anda mati - program Anda akan menampilkan gerakan dan peta akhir rute kematian untuk setiap upaya yang gagal untuk menemukan solusi.
  3. Jika Anda menemukan solusi yang menang, cetak output penuh untuk itu dan hentikan.
  4. Jalankan sampai solusi ditemukan, tetapi jangan mencoba solusi yang sama dua kali - semua kematian harus melalui rute yang berbeda.
  5. Gunakan input tes ini:

(membutuhkan setidaknya satu gerakan untuk menjatuhkan cache air di beberapa titik tengah).

 S........
 .........
 .........
 ........E

Kode terpendek yang diposting dengan input demonstrasi non-sepele yang dipecahkan menang.

Spraff
sumber
Ini perlu diklarifikasi untuk menentukan apakah program perlu dapat menyelesaikan peta yang dapat dipecahkan, atau apakah hanya perlu bekerja untuk satu peta. Saya pasti akan mendorong yang pertama; dalam kasus satu peta, akan lebih mudah untuk meng-hardcode solusi daripada menghitungnya.
Diedit untuk kejelasan. Ya, Anda menang jika Anda memiliki lebih dari nol air, dan ya program Anda harus menyelesaikan semua input yang dapat dipecahkan.
spraff
Apa yang menghentikan Anda untuk menggunakan algoritme seperti A * dan menjatuhkan jalur 5 unit pada setiap ubin yang Anda tuju dan kembali ke awal jika Anda melangkah ke ubin tanpa 5 air?
Biru
Tidak ada. Lanjutkan.
spraff
Strategi 'bawa semua air dari S' harus berhasil, meskipun akan sangat membosankan. Pertimbangkan S.,.,.,.,. E .... E di mana, dan e benar-benar titik. Koma adalah tempat Anda menyimpan simpanan Anda di sepanjang jalan, dan 'e' adalah di mana Anda perlu memiliki 5 air untuk berlari untuk E. 4 langkah untuk memindahkan 1 air ke koma pertama (E + 0 E-1 W + 0 W + 4). 16 langkah untuk memindahkan 1 air ke koma kedua. 52 ke yang ketiga, 160 ke yang keempat, 484 untuk menjatuhkan 1 air ke e dan kembali ke langkah S. 1926 dan Anda berada di e membawa 5 air, 5 lebih banyak untuk lari ke E, 1931 langkah. Setiap dua langkah lintasan efektif tiga kali lipat panjang solusi Anda.
Sparr

Jawaban:

12

Perl, 299 + 1 = 300 254 + 1 = 255 byte

Ini hampir pasti akan dikalahkan oleh salah satu bahasa golf ketika orang melihat algoritma saya :-)

Jalankan dengan -n(penalti 1 byte).

Versi sebelumnya tidak cukup sesuai dengan spesifikasi karena meninggalkan air ekstra di peta dan tidak menunjukkannya di versi final peta; saat mengubah algoritma untuk mengatasi situasi itu, saya berhasil golf sedikit lebih kecil dalam proses.

push@a,[split//];($s,$t)=(length$`,$.)if/S/;($e,$f)=(length$`,$.)if/E/}{$_.=$s<$e?($s++,R):$s>$e?($s--,L):$t<$f?($t++,D):($t--,U);$\=reverse$";$".=y/LRUD/RLDU/r.Y.reverse.($"=~y/Y/X/r);$a[$t-1][$s]=~y/./#/;$\.=$s-$e||$t-$f?redo:$_;print map{join'',@$_}@a

Contoh (Saya telah menambahkan jeda baris ke output untuk menghindari perlunya menggulir dan menampilkan struktur):

E .....
# .....
# .....
# .....
##### S
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLLUXDRRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLLUUXDDRRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLLUXDRRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLLUUUYDDDRRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLLUXDRRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUUYDDRRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLLUYDRRRRR
 LXR LLXRR LXR LLLXRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLYRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLYRRRR
 LXR LLXRR LXR LLLYRRR LXR LLYRR LYR LLLLLUUUU

Dalam notasi yang digunakan oleh program, gerakan diwakili melalui L, R, U, dan Duntuk kiri, atas, kanan, bawah masing-masing. Secara default, Anda mengambil 1 unit air setelah setiap gerakan, tetapi ini dapat dimodifikasi dengan menambahkan karakter:

  • X: jatuhkan 2 unit air, daripada mengambil 1
  • Y: jatuhkan 1 unit air, daripada mengambil 1
  • (yaitu ruang): sepenuhnya terisi air (hanya keluaran setelah pindah ke S; program ini juga keluaran dengan ruang terkemuka, yang masuk akal karena Anda mulai penuh di atas air)

Seperti yang dapat Anda lihat, mungkin saja untuk melintasi peta ini (agak mandul) tanpa menggunakan kolam tambahan sama sekali. Bahkan, dimungkinkan untuk mendapatkan jarak di bawah aturan ini, di peta mana pun , tanpa bantuan air yang disiapkan sebelumnya. Dengan demikian, algoritme ini mengabaikan air preplaced, artinya saya tidak perlu membuang byte yang mencoba menanganinya. Ini juga berarti Anda tidak akan melihat bot mati, maaf. Itu tidak pernah bergerak di luar rentang di mana ia tahu dijamin untuk bertahan hidup

Alasan kita membutuhkan keduanya Xdan Y(dan sedikit kode tambahan untuk memastikan kita memiliki Xsebagian besar strategi tetapi kadang-kadang Ydi akhir) adalah bahwa spesifikasi membutuhkan versi final peta untuk menjadi output. Cara termudah untuk mengimplementasikan ini adalah meninggalkan peta sama sekali tidak tersentuh (terlepas dari jalur kami melalui kotak yang awalnya kosong), terutama karena kotak yang dimulai dengan 9air akan berakhir dengan 10(melanggar seni ASCII) jika kebetulan berada di jalan dan kami hanya menggunakanX, dan dengan demikian kita perlu menemukan beberapa solusi yang menghindari menjatuhkan air tambahan di peta. Algoritme di sini akan "secara alami" menjatuhkan 1 unit air tambahan pada setiap kotak pada rute; dengan demikian, waktu terakhir kita mengunjungi setiap kotak, kita mengurangi jumlah air yang dijatuhkan oleh 1 melalui penggunaan Y daripada X, sehingga pada kunjungan terakhir kita, kita mengalirkan kembali kotak ke jumlah air aslinya, daripada meninggalkannya sedikit lebih basah daripada ketika kita mulai.

Saya akan merekomendasikan untuk tidak menjalankan ini pada peta besar, karena memiliki kinerja O (2 ^ n) (meskipun bot tidak pernah mati kehausan, masuk akal untuk berpikir itu akan mati karena kelaparan menggunakan strategi seperti ini.)

Versi yang dapat dibaca:

# implicit with -n: read a line of input into $_
push @a, [split //]; #/ split $_ into characters, store at the end of @a
($s,$t) = (length$`,$.) if /S/; # if we see S, store its coordinates
($e,$f) = (length$`,$.) if /E/  # if we see E, store its coordinates
}{ # Due to -n, loop back to start if there are more lines.

# From here onwards, $" stores the partial solution this iteration;
#                    $\ stores the partial solution last iteration;
#                    $_ stores the path from ($s,$t) to S.
# At the start of the program, $" is a space, $\ and $_ are empty.

$_ .=  # Work out the next step on the path:
  $s < $e ? ($s++,R) # if too far left, move right, record that in $_;
: $s > $e ? ($s--,L) # if too far right, move left, record that in $_;
: $t < $f ? ($t++,D) # if too far up,    move down, record that in $_;
: ($t--,U);          # in other cases we must be too far down.
$\ = reverse $";     # Store last iteration; $" is constructed backwards.
$" .=                # Extend $" by appending
  y/LRUD/RLDU/r .    # the path from ($s, $t) back to S;
  Y .                # a literal 'Y';
  reverse .          # $/ backwards (i.e. the path from S to ($s, $t);
  ($"=~y/Y/X/r);     # a copy of $" with all 'Y' changed to 'X'.
$a[$t-1][$s] =~      # At the current path coordinate,
  y/./#/;            # replace any '.' with '#'.
$\ .=                # Start appending to $\;
  $s-$e||$t-$f?redo  # if we're not at E, abort that and jump back to {{,
: $_;                # otherwise append $_ (the path from S to E).
print map            # For each element of some array
  {join'',@$_}       # output its concatenated elements
  @a                 # specifying that array as @a.
# Implicitly: print $\ (which specifies the sort of newline print uses).

sumber
Apakah banjir yang mengisi dunia dalam bentuk spiral lebih sedikit dari pada blok kondisi Anda untuk arah untuk mencari jalan keluar?
Sparr
1
Saya tidak berpikir itu akan terjadi (walaupun mungkin ada beberapa cara yang tidak saya lihat; itu tentu saja ide yang layak dipertimbangkan). Anda masih harus berurusan dengan empat arah, dan sekarang Anda juga harus berurusan dengan tepi peta, sesuatu yang bukan masalah dalam versi algoritma ini.
Poin bagus, ulangi tepi. Saya pikir itu bisa dilakukan pada peta yang tak terbatas.
Sparr