Melarikan papan catur

23

Anda menemukan diri Anda di papan catur, seperti yang dilakukan orang. Anda dapat melihat pintu keluar tetapi sangat jauh dan Anda lebih suka tidak berjalan jauh-jauh. Untungnya beberapa penduduk setempat menawarkan tumpangan kepada Anda. Seorang Ksatria, seorang Benteng, seorang Uskup dan seorang Raja semua bersedia membawa Anda ke tujuan Anda, tetapi melihat bagaimana ini adalah papan catur mereka masing-masing harus mematuhi aturan catur dalam perjalanan ke tujuan Anda. Anda ingin keluar dari sini sesegera mungkin, penawaran siapa yang Anda terima?

Tugas

Dengan papan catur berbentuk dan berukuran sewenang-wenang dan dua titik di papan catur, hasilkan bidak catur yang dapat bergerak di antara dua lokasi dalam gerakan sesedikit mungkin. Papan tidak harus berarti terus menerus bahwa mungkin ada kesenjangan antara bagian-bagian papan. Masing-masing dari empat bagian (Raja, Benteng, Ksatria, dan Uskup) dapat bergerak sesuai dengan aturan standar mereka dalam catur. Sang Ratu dan bidak telah sengaja ditinggalkan dari tantangan ini.

I / O

Anda dapat mengambil input dalam format apa pun yang masuk akal dan Anda dapat menampilkan dalam format apa pun yang Anda pilih. Input dan output Anda harus konsisten. Jika beberapa bagian dapat mencapai tujuan dalam jumlah gerakan yang sama, Anda harus mengeluarkan semua bagian yang bisa sampai di sana dalam jumlah waktu minimum. Jika tidak satu pun dari empat bagian yang dapat mencapai akhir, Anda dapat mengeluarkan apa saja selama berbeda dari semua kemungkinan hasil lainnya. Ini bisa termasuk mengeluarkan apa-apa atau melempar kesalahan.

Uji Kasus

Kotak menunjukkan titik awal dan sebuah lingkaran menunjukkan titik akhir.


Tes 1

Uskup


Tes 2

Ksatria


Tes 3

Raja


Tes 4

Benteng


Tes 5

Raja, Ksatria


Tes 6

Tidak ada

Wisaya Gandum
sumber
Bagus. Saya suka ini, tetapi bukankah "papan catur ukuran dan ukurannya berubah-ubah" adalah entitas tunggal? Saya tidak benar-benar melihat bagaimana contoh 2 dan 6 cocok dengan ini karena sepertinya mereka adalah dua papan terpisah yang hanya bisa dilewati oleh ksatria. Mungkin saya kehilangan sesuatu?
ElPedro
2
@ ElPedro Mereka masih satu papan, hanya saja bukan papan kontinu. Bagian dari bentuk sewenang-wenang adalah bahwa papan bisa tidak kontinu.
Wheat Wizard
Misalnya 3, bukankah seharusnya "raja, ratu"?
Kritixi Lithos
Terima kasih @ Panas. Tidak yakin itu jelas dari pertanyaan tapi saya mengerti sekarang.
ElPedro
2
@KritixiLithos Untuk membuat hal-hal menarik tidak ada Ratu atau Pion.
Wheat Wizard

Jawaban:

8

Haskell , 447 439 437 432 byte

t=[1,2]
p=[(+),(-)]
f=filter
a=abs
(#)=elem
x?y=[min x y..max x y]
f&g=(\x->g x>>=f)
(b!1)(c,r)=f(#b)[(c!d,r#s)|(!)<-p,(#)<-p,d<-t,s<-t,d/=s]
(b!2)(c,r)=f(\(d,s)->(c==d&&all(#b)[(c,z)|z<-r?s])||r==s&&all(#b)[(z,r)|z<-c?d])b
(b!3)(c,r)=f(\(d,s)->a(c-d)==a(r-s)&&all(#b)(zip(c?d)$r?s))b
(b!4)(c,r)=f(\(d,s)->a(c-d)<2&&a(r-s)<2)b
(b%p)n s=[s]>>=foldr(&)(:[])(replicate n$b!p)
g b s e=head$f(not.null)[[p|p<-[1..4],e#(b%p)n s]|n<-[0..]]

Panggilan menggunakan di g <board> <start> <end>mana <board> :: [(Int, Int)], <start> :: (Int, Int)dan <end> :: (Int, Int). Mengembalikan daftar yang berisi 1untuk Ksatria, 2untuk Benteng, 3untuk Uskup, dan 4untuk Raja. Jika tidak ada solusi yang ditemukan, itu secara konsisten meluap tumpukan (yang dianggap sebagai melemparkan kesalahan, kan?).

Inspirasi yang diambil dari bab-bab LYAH tentang Monad - (%)hanyalah sebuah generalisasi dan golf inMany, dengan (&)setara dengan (Control.Monad.<=<). Segala sesuatu yang lain harus lebih atau kurang jelas.

Ini mungkin bisa bermain golf lebih banyak, tetapi saya sudah cukup bersenang-senang untuk saat ini.

Tidak Disatukan:

data Piece = Knight | Rook | Bishop | King deriving (Show)
type Place = (Int, Int)
type Board = [Place]

x `to` y = [min x y..max x y]

f <=< g = (\x -> g x >>= f)

moves
    :: Board    -- available spaces
    -> Piece    -- type of piece
    -> Place    -- starting place
    -> [Place]  -- places available in one move
moves b Knight (c,r) =
    filter (`elem` b) [(c!d, r#s) |
                       (!) <- [(+),(-)], (#) <- [(+),(-)],
                       d <- [1,2], s <- [1,2], d/=s]
moves b Rook   (c,r) =
    filter (\(d,s) -> (c == d && all (`elem` b) [(c,z) | z <- r `to` s])
                    || r == s && all (`elem` b) [(z,r) | z <- c `to` d]) b
moves b Bishop (c,r) =
    filter (\(d,s) -> abs(c-d) == abs(r-s)
                && all (`elem` b) (zip (c `to` d) (r `to` s))) b
moves b King   (c,r) =
    filter (\(d,s) -> abs(c-d) <= 1 && abs(r-s) <= 1) b

canGoIn
    :: Board    -- available spaces
    -> Piece    -- type of piece
    -> Int      -- number of moves
    -> Place    -- starting place
    -> [Place]  -- places available in the specified number of moves
canGoIn b p n s =
    return s >>= foldr (<=<) return (replicate n (moves b p))

quickestPieces
    :: Board    -- available spaces
    -> Place    -- starting place
    -> Place    -- ending place
    -> [Piece]  -- quickest pieces
quickestPieces b s e =
        head $ filter (not.null)
            [[p | p <- [Knight, Rook, Bishop, King], elem e (canGoIn b p n s)] |
                n<-[0..]]
Julian Wolf
sumber
Penggunaan pemrograman fungsional yang bagus!
Wheat Wizard
5

Mathematica, 326 325 byte

Terima kasih kepada masterX224 karena menunjukkan penghematan byte!

f[g_,w_,x_]:=(c={{1,1},{-1,1}};s=c.c/2;e=#<->#2&@@@#&;j=Join;h=FindShortestPath;t=#~Tuples~2&;a@d_:=e@Select[t@g,#-#2&@@#==d&];y@k=j@@(a/@j[s,c]);y@n=j@@(a/@{{1,2},{2,1},{-2,1},{-1,2}});v=Flatten[e@t@#&/@ConnectedComponents@a@#&/@#]&;y@r=v@s;y@b=v@c;Pick[p={b,k,n,r},z=Length[h[y@#,w,x]/.h@__->0]&/@p,Min[z~Complement~{0}]]);

Mendefinisikan fungsi yang fmengambil tiga argumen: gadalah daftar pasangan integer yang diurutkan yang mewakili dewan, dan wdan xpasangan yang diperintahkan mewakili kotak awal dan akhir. Outputnya adalah himpunan bagian {b, k, n, r}(mewakili masing-masing uskup, raja, ksatria, dan benteng) yang memiliki jalur gerakan minimal antara dua kotak. Sebagai contoh, test case ketiga dipanggil oleh f[{{0, 0}, {1, 1}, {1, 2}, {0, 3}}, {0, 0}, {0, 3}]dan kembali {k}; dua kasus uji terakhir kembali {k, n}dan {}masing-masing.

Strateginya adalah menerjemahkan kuadrat papan ke dalam simpul grafik, di mana ujung-ujungnya ditentukan oleh gerakan masing-masing bagian, dan kemudian menggunakan rutinitas grafik bawaan.

Versi kode yang lebih mudah digunakan:

 1  f[g_, w_, x_] := ( c = {{1, 1}, {-1, 1}}; s = c.c/2;
 2    e = # <-> #2 & @@@ # &; j = Join; h = FindShortestPath; t = #~Tuples~2 &; 
 3    a@d_ := e@Select[t@g, # - #2 & @@ # == d &]; 
 4    y@k = j @@ (a /@ j[s, c]); 
 5    y@n = j @@ (a /@ {{1, 2}, {2, 1}, {-2, 1}, {-1, 2}}); 
 6    v = Flatten[e@t@# & /@
 7         ConnectedComponents@a@# & /@ #] &;
 8    y@r = v@s; y@b = v@c; 
 9    Pick[p = {b, k, n, r}, 
10      z = Length[h[y@#, w, x] /. h@__ -> 0] & /@ p, 
11      Min[z~Complement~{0}]]
12  );

Pada baris 3, Select[g~Tuples~2, # - #2 & @@ # == dtemukan semua pasangan simpul berurutan yang perbedaannya adalah vektor d; ekemudian mengubah setiap pasangan yang dipesan tersebut menjadi tepi grafik yang tidak terarah. Jadi aadalah fungsi yang menciptakan tepi antara semua simpul yang berbeda dengan vektor tetap.

Itu cukup untuk mendefinisikan, di jalur 4 dan 5, grafik raja y@k(yang mengambil penyatuan tepi yang dihasilkan oleh vektor {1, 1}, {-1, 1}, {0, 1}, dan {-1, 0}) dan grafik ksatria y@n(yang melakukan hal yang sama dengan {1, 2}, {2, 1}, {-2, 1}, dan {-1, 2}).

Pada baris 7, ConnectedComponents@a@#ambil salah satu dari grafik tetangga ini dan kemudian temukan komponen yang terhubung; ini sesuai dengan pengelompokan semua segmen garis simpul dalam arah yang diberikan (sehingga benteng atau uskup tidak harus bergerak melewatinya satu per satu). Kemudian e@t@#pada baris 6 menempatkan tepi antara setiap pasangan simpul dalam komponen terhubung yang sama, yang kemudian Flattendiedit menjadi satu grafik. Dengan demikian, baris 6 hingga 8 menentukan grafik rook y@rdan grafik uskup y@b.

Akhirnya, garis 9 hingga 11 memilih elemen-elemen {b, k, n, r}yang menghasilkan jalur terpendek antara dua simpul target. FindShortestPathakan melempar kesalahan dan mengembalikan yang tidak dievaluasi jika titik target tidak muncul dalam grafik (yang akan terjadi jika tidak ada tepi yang keluar dari itu), jadi kami gunakan h@__ -> 0untuk kembali 0dalam kasus tersebut sebagai gantinya. Dan kurangnya jalur antara simpul yang valid mengembalikan daftar panjang 0, jadi Min[z~Complement~{0}]hitung panjang jalur terkecil yang benar-benar ada, abaikan kasus buruk di atas.

Greg Martin
sumber
ada alasan untuk double f dalam nama fungsi? atau apakah itu sebuah Mathematica limit?
masterX244
oh, haha, tidak, ini adalah batasan mental saya :) Saya sudah punya fdi sesi itu, tapi saya harus mengubahnya sebelum dikirim!
Greg Martin