raja + benteng vs raja

16

Ini adalah akhir dari permainan catur yang dimainkan dengan baik. Anda adalah pemain kulit putih, dan Anda masih memiliki benteng dan rajamu. Lawanmu hanya memiliki rajanya.

Karena Anda berkulit putih, giliran Anda. Buat program untuk memainkan pertandingan catur ini. Outputnya dapat berupa urutan gerakan, animasi gif, seni ASCII atau apa pun yang Anda suka.

Tampaknya cukup jelas, tetapi saya akan menyatakannya secara eksplisit: Anda harus memenangkan permainan (dalam jumlah gerakan terbatas). Selalu mungkin untuk menang dari posisi ini. JANGAN KEHILANGAN BAHWA ROOK. JANGAN MENCERMATAKAN.

Program Anda mungkin atau mungkin tidak menerima input manusia untuk posisi awal dan untuk setiap gerakan hitam (Anda dapat dengan aman menganggap ini adalah posisi hukum, yaitu para raja tidak saling bersentuhan). Jika tidak, posisi awal acak dan gerakan acak untuk raja hitam akan cukup.

Skor

Skor Anda akan panjang dalam byte kode + bonus Anda. Bahasa apa pun diizinkan, skor terendah akan menang.

Bonus

-50 jika program Anda memungkinkan posisi awal yang ditentukan manusia dan yang acak. Manusia dapat memasukkannya melalui stdin, file, GUI ...

-100 jika program Anda memungkinkan manusia dan pemain acak untuk memindahkan raja hitam

+12345 jika Anda mengandalkan pemecah catur eksternal atau perpustakaan catur bawaan

Semoga berhasil!

Memperbarui!

Aturan tambahan: Pertandingan harus dimainkan sampai skakmat. Hitam tidak mengundurkan diri, tidak melompat keluar dari papan catur dan tidak diculik oleh alien.

Petunjuk

Anda mungkin bisa mendapatkan bantuan dari pertanyaan ini di chess.se .

izabera
sumber
2
Apakah aturan 50 langkah menggambar berlaku?
Comintern
1
@ Viktor Saya sudah beberapa kali pergi, tetapi belum berhasil. Brute force jelas terlalu lambat, alpha-beta juga karena lanskap peringkat posisi cukup datar; dan cenderung terjebak dalam satu lingkaran. Analisis retrograde akan bekerja tetapi sangat lambat di muka. Usaha saya berikutnya akan menggunakan algoritma Bratko untuk KRK, yang saya hindari karena tumpukan kasus khusus, tidak bagus untuk golf.
bazzargh
1
@victor Saya melihat ini juga. Ini menarik justru karena mudah didefinisikan dan sulit dilakukan. Pada gilirannya program ini tidak akan pendek, sehingga tag kode-golf membuatnya tampak sangat sulit. Jika program saya berfungsi, Anda akan segera melihatnya.
Level River St
1
@ Viktor masalahnya bukan tentang mencoba menjadi optimal, setiap upaya untuk mengambil langkah 'terbaik' tanpa mempertimbangkan riwayat permainan menyebabkan loop. Perlu untuk menguji permainan berakhir dari setiap posisi. Varian Bratko + tidak optimal tetapi dapat dihentikan. Mencoba analisis retrograde sekarang (yaitu tabel membangun Endgame), terlihat menjanjikan dan benar-benar adalah yang optimal, yang bagus. Ternyata juga cukup pendek.
bazzargh
2
Jika ada yang membutuhkan inspirasi (atau hanya ingin tahu), Anda dapat menemukan mesin catur 1433 karakter lengkap di home.hccnet.nl/hgmuller/umax1_6.c
Comintern

Jawaban:

11

Haskell 1463-100 = 1363

Hanya mendapatkan jawaban. Ini menemukan solusi cara retrograde, bekerja kembali dari sekakmat ke posisi kita. Ini berbeda dari deskripsi analisis retrograde pada pemrograman catur - bukannya memulai dengan set awal dan memperluasnya dengan gerakan mundur sampai tidak ada kotak yang dipindahkan ke belum terlihat, itu dimulai dengan semua kotak yang tidak digunakan dan mengurangi set itu dengan mencoba bergerak maju. Ini akan menjadi kurang efisien waktu daripada cara tradisional, tetapi penggunaan memori meledak bagi saya ketika saya mencobanya.

Kompilasi dengan ghc -O2untuk kinerja yang dapat diterima untuk perhitungan tabel endgame; bermain instan setelah langkah pertama. Pasokan raja putih, benteng, kotak raja hitam sebagai argumen. Untuk suatu langkah, ia hanya ingin sebuah kotak, dan akan memilih satu untuk Anda jika Anda menekan kembali. Sesi contoh:

$ time  printf "\n\n\n\n\n\n\n\n"|./rook8 e1 a1 e8
("e1","a7","e8")[d8]?
("d2","a7","d8")[c8]?
("d2","h7","c8")[b8]?
("c3","h7","b8")[a8]?
("b4","h7","a8")[b8]?
("c5","h7","b8")[a8]?
("b6","h7","a8")[b8]?
("b6","h8","b8") mate

real    0m8.885s
user    0m8.817s
sys 0m0.067s

Kode:

import System.Environment
import qualified Data.Set as S
sp=S.partition
sf=S.fromList
fl=['a'..'h']
rk=[0..7]
lf=filter
m=map
ln=notElem
lh=head
pl=putStrLn
pa[a,b]=(lh[i|(i,x)<-zip[0..]fl,a==x],read[b]-1)
pr(r,c)=fl!!r:show(c+1)
t f(w,r,b)=(f w,f r,f b)
km(a,b)=[(c,d)|c<-[a-1..a+1],d<-[b-1..b+1],0<=c,c<=7,0<=d,d<=7]
vd (w,r,b)=b`ln`km w&&w/=r&&b/=w&&b/=r
vw p@(_,r,b)=vd p&&r`ln`km b&&(ck p||[]/=bm p)
vb p=vd p&&(not.ck)p
rm (w@(c,d),(j,k),b@(u,x))=[(w,(j,z),b)|z<-rk,z/=k,j/=c||(k<d&&z<d)||(d<k&&d<z),j/=u||(k<x&&z<x)||(x<k&&x<z)]
kw(w,r,b)=m(\q->(q,r,b))$km w
xb(w,r,_)b=(w,r,b)
wm p=lf(\q->q/=p&&vw q)$rm p++(m(t f)$rm$t f p)++kw p
bm p@(_,_,b)=lf(\q->q/=p&&vb q)$m(xb p)$km b
c1((c,d),(j,k),(u,x))=j==u&&(c/=j||(k<x&&d<k)||(k>x&&d>k))
ck p=c1 p||(c1$t f p)
mt p=ck p&&[]==bm p
h(x,y)=(7-x,y)
v=f.h.f
f(x,y)=(y,x)
n p@((c,d),_,_)|c>3=n$t h p|d>3=n$t v p|c>d=n$t f p|True=p
ap=[((c,d),(j,k),(u,x))|c<-[0..3],d<-[c..3],j<-rk,k<-rk,u<-rk,x<-rk]
fr s p=S.member(n p)s
eg p=ef(sp mt$sf$lf vw ap)(sf$lf vb ap)
ps w mv b0=sp(\r->any(fr b0)$mv r)w
ef(b0,b1)w=let(w1,w2)=ps w wm b0 in(w1,b0):(if S.null w2 then[]else ef(f$ps b1 bm w2)w2)
lu((w1,b0):xs)p=if fr w1 p then lh$lf(fr b0)$wm p else lu xs p
th(_,_,b)=b
cd tb q=do{let{p=lu tb q};putStr$show$t pr p;if mt p then do{pl" mate";return()}else do{let{b1=pr$th$lh$bm p};pl("["++b1++"]?");mv<-getLine;cd tb$xb p (pa$if""==mv then b1 else mv)}}
main=do{p1<-getArgs;let{p2=m pa p1;p=(p2!!0,p2!!1,p2!!2)};cd(eg p)p}

Diedit: Memperbaiki kode untuk mengingat tabel endgame dan menggunakan argumen, sehingga tidak terlalu menyakitkan untuk diuji berulang kali.

bazzargh
sumber
2
kode haskell dengan efek samping? bagaimana mungkin kamu, bidat! : p
Einacio
akhirnya yang serius!
izabera
Teka-teki itu jahat @izabera!
bazzargh
Bagus! Jauh lebih baik daripada upaya yang saya kerjakan. Saya mencoba untuk meningkatkan El Ajedrecista cukup untuk memastikan 50 pasangan bergerak, tetapi sejauh algoritma berjalan itu benar-benar buruk.
Comintern
Banyak kinerja sial berasal dari saya yang tidak memafit tabel endgame ( y). Ini sangat jelas karena langkah kedua tidak cepat ketika kita sudah mempertimbangkan keseluruhan endgame. Saya pergi ke pub malam ini tetapi jika saya mendapat kesempatan besok saya akan membuat ini kurang mengerikan.
bazzargh
7

C, Saat ini 2552 karakter non-spasi bukan komentar

Hitungan menunjukkan kepada saya bahwa saya bisa golf di bawah total 2552 karakter, tetapi mengingat sudah ada jawaban yang lebih kecil (yang akan sulit dikalahkan) Saya akan mempertimbangkan itu dengan hati-hati sebelum repot-repot melakukannya. Memang benar ada sekitar 200 karakter untuk menampilkan papan dan 200 lainnya masing-masing untuk memeriksa input pengguna dari posisi awal dan bergerak (yang saya butuhkan untuk pengujian, tetapi bisa menghilangkan.)

Tidak ada pohon permainan di sini, hanya algoritma hardcoded, sehingga bergerak secara instan.

Posisi awal dimasukkan sebagai baris (1-8) kolom (1-8) bernomor dari kanan atas dan program bekerja pada skema yang sama. Jadi, jika Anda memutar layar 90 derajat berlawanan arah jarum jam, itu akan mengikuti notasi numerik Catur Korespondensi standar. Posisi di mana raja hitam sudah di cek ditolak sebagai ilegal.

Bergerak hitam dimasukkan sebagai angka dari 0 hingga 7, dengan 0 sebagai pindah ke utara, 1 ke timur laut dan seterusnya dalam arti searah jarum jam.

Itu tidak mengikuti algoritma yang dikenal umum yang secara eksklusif menggunakan benteng di bawah perlindungan raja putih untuk membatasi raja hitam. Benteng hanya membatasi raja hitam dalam arti vertikal (dan akan lari secara horizontal jika dikejar.) Raja putih membatasi raja hitam dalam gerakan horizontal. Ini berarti dua keping putih tidak saling menghalangi.

Saya sepertinya telah mengatasi sebagian besar bug dan kemungkinan loop tak terbatas, sekarang berjalan cukup baik. Saya akan bermain dengannya lagi besok dan melihat apakah ada hal lain yang perlu diperbaiki.

#include "stdafx.h"
#include "stdlib.h"
#include "string.h"

int b[2], w[2], r[2], n[2],s,t,i,nomate;
int v[2][8] = { {-1,-1,0,1,1,1,0,-1}, {0,1,1,1,0,-1,-1,-1} };
int u[5] = { 0, 1, -1, 2, -2 };
char empty[82] = "        \n--------\n--------\n--------\n--------\n--------\n--------\n--------\n--------\n";
char board[82];

int distance(int p[2], int q[2]){
    return __max(abs(p[0]-q[0]),abs(p[1]-q[1]));
}

int sign(int n){
    return (n>0)-(0>n); 
}

// from parameters p for white king and q for rook, determines if rook is/will be safe
int rsafe(int p[2],int q[2]){
    return  distance(p, q)<2 | distance(q,b)>1;
}

void umove(){
    t=0;
    while (t != 100){
        printf("Enter number 0 to 7 \n");
        scanf_s("%d", &t); t %= 8;
        n[0] = b[0] + v[0][t];
        n[1] = b[1] + v[1][t];
        if (distance(w, n) < 2 | (n[0] == r[0] & (n[1]-w[1])*(r[1]-w[1])>0) 
            | ((n[1] == r[1]) & (n[0]-w[0])*(r[0]-w[0])>0) | n[0] % 9 == 0 | n[1] % 9 == 0)
            printf("illegal move");
        else{ b[0] = n[0]; b[1] = n[1]; t = 100; };
    }
}

void imove(){
    t=0;
    // mate if possible
    if (distance(b, w) == 2 & b[0] == w[0] & (b[1] == 1 | b[1] == 8) & r[0]!=w[0]){
        n[0] = r[0]; n[1] = b[1];
        if (rsafe(w, n)){
            r[1] = n[1]; 
            printf("R to %d %d mate!\n", r[0], r[1]);
            nomate=0;
            return;
        }
    }

    //avoid stalemate
    if ((b[0] == 1 | b[0] == 8) & (b[1] == 1 | b[1] == 8) & abs(b[0] - r[0]) < 2 & abs(b[0]-w[0])<2){
        r[0] = b[0]==1? 3:6;
        printf("R to %d %d \n", r[0], r[1]);
        return;
    }

    // dont let the rook be captured. 
    if(!rsafe(w,r)) 
    {
        if (w[0] == r[0]) r[1] = w[1] + sign(r[1]-w[1]);
        else r[1] = r[1]>3? 2:7;
        printf("R to %d %d \n", r[0], r[1]);
        return;
    }

    // if there's a gap between the kings and the rook, move rook towards them. we only want to do this when kings on same side of rook, and not if the black king is already on last row.
    if (abs(w[0]-r[0])>1 & abs(b[0] - r[0]) > 1 & (b[0]-r[0])*(w[0]-r[0])>0 & b[0]!=1 & b[0]!=8){
        n[0] = r[0] + sign(b[0] - r[0]); n[1] = r[1];
        if (rsafe(w, n)) r[0] = n[0]; 
        else r[1] = r[1]>3? 2:7;
        printf("R to %d %d \n", r[0], r[1]);
        return;

    }
    // if kings are far apart, or if they not on the same row (except if b 1 row from r and w 2 rows from r), move king
    if ((w[0]-r[0])!=2*(b[0]-r[0]) | abs(b[0]-w[0])>1 | distance(w,b)>2){
        for (i = 0; i<8; i++) if (v[0][i] == sign(b[0] - w[0]) & v[1][i] == sign(b[1] - w[1])) t = i;
        s = 1 - 2 * (w[0]>3 ^ w[1] > 3);
        for (i = 0; i < 5; i++){
            n[0] = w[0] + v[0][(t + s*u[i] + 8) % 8];
            n[1] = w[1] + v[1][(t + s*u[i] + 8) % 8] *(1-2*(abs(w[0]-b[0])==2));
            if (distance (n,b)>1 & distance(n, r)>0 & rsafe(n,r) & n[0]%9!=0 & n[1]%9!=0
                & !(n[0]==r[0] & (w[0]-r[0])*(b[0]-r[0])>0)) i = 5;
        }
        if (i == 6) {
            w[0] = n[0]; w[1] = n[1]; printf("K to %d %d \n", w[0], w[1]); return;
        }
    }

    //if nothing else to do, perform a waiting move with the rook. Black is forced to move his king.
    t = r[1]>3? -1:1;
    for (i = 1; i < 5; i++){
        n[0] = r[0]; n[1] = r[1] + t*i;
        if (rsafe(w, n)){ r[1] = n[1]; i=5; }
    }
    printf("R to %d %d \n", r[0], r[1]);
}

int _tmain(){

    do{ 
        t=0;
        printf("enter the row and col of the black king ");
        scanf_s("%d%d", &b[0], &b[1]);
        printf("enter the row and col of the white king ");
        scanf_s("%d%d", &w[0], &w[1]);
        printf("enter the row and col of the rook");
        scanf_s("%d%d", &r[0], &r[1]);
        for (i = 0; i < 2; i++) if (b[i]<1 | b[i]>8 | w[i]<1 | w[i]>8 | w[i]<1 | w[i]>8)t=1;
        if (distance(b,w)<2)t+=2;
        if ((b[0] == r[0] & (b[1]-w[1])*(r[1]-w[1])>0) | ((b[1] == r[1]) & (b[0]-w[0])*(r[0]-w[0])>0)) t+=4;
        printf("error code (0 if OK) %d \n",t);
    } while(t);

    nomate=1;
    while (nomate){
        imove();
        strncpy_s(board, empty, 82);
        board[b[0] * 9 + b[1] - 1] = 'B'; board[w[0] * 9 + w[1] - 1] = 'W'; board[r[0] * 9 + r[1] - 1] = 'R'; printf("%s", board);      
        if(nomate)umove();
    }
    getchar(); getchar();

}

Inilah hasil akhir yang khas (pasangan terkadang dapat terjadi di mana saja di tepi kanan atau kiri papan.)

masukkan deskripsi gambar di sini

Level River St
sumber
6

Bash, 18 (atau -32?)

Oke, ini jawaban yang lucu. Karena Black adalah pemain catur yang baik, dan Black tahu bahwa White juga pemain catur yang baik, ia memutuskan bahwa satu-satunya hal yang masuk akal untuk dilakukan adalah:

echo Black resigns

Ini menghasilkan kemenangan putih, yang memenuhi spesifikasi.

Secara teknis Anda dapat memasukkan posisi saat ini sebagai argumen juga, program hanya mengabaikannya, jadi bisa dibilang ini dapat memenuhi syarat untuk bonus -50.

pengguna12205
sumber
Lucu, tapi saya perbarui aturannya. Bermain sampai skakmat sekarang wajib.
izabera
1
Namun, pertanyaan awal dengan jelas menyatakan bahwa program Anda dapat mengizinkan manusia atau pemain acak untuk yang hitam, dan milik Anda tidak acak.
izabera
2
Menggunakan notasi standar, Anda bisa mengeluarkan 1-0yang sedikit lebih pendek.
daniero
1
@Comintern juga aktual ketika kehilangan optimal biasanya berarti bertahan paling lama.
PyRulez
@PyRulez menurut wikipedia , "Salah satu pemain dapat mengundurkan diri kapan saja dan lawan mereka memenangkan permainan." Plus, ini hanya jawaban lelucon, jangan menganggapnya serius.
user12205