König + Turm gegen König

16

Es ist das Ende eines weiteren gut gespielten Schachspiels. Du bist der weiße Spieler, und du hast immer noch einen Turm und deinen König. Dein Gegner hat nur noch seinen König übrig.

Da du weiß bist, bist du dran. Erstellen Sie ein Programm, um dieses Schachspiel zu spielen. Die Ausgabe kann eine Folge von Zügen sein, eine GIF-Animation, ASCII-Grafiken oder was auch immer Sie möchten.

Es scheint ganz offensichtlich, aber ich sage es ausdrücklich: Sie müssen das Spiel gewinnen (in einer begrenzten Anzahl von Zügen). Es ist immer möglich, von dieser Position aus zu gewinnen. VERLIERE DIESEN ROOK NICHT. NICHT EINSTELLEN.

Ihr Programm akzeptiert möglicherweise eine menschliche Eingabe für die Startposition und für jede schwarze Bewegung (Sie können sicher annehmen, dass dies eine legale Position ist, dh die Könige berühren sich nicht). Ist dies nicht der Fall, genügen eine zufällige Startposition und zufällige Bewegungen für den schwarzen König.

Ergebnis

Ihre Punktzahl ergibt sich aus der Länge Ihres Codes + Bonus in Byte. Jede Sprache ist erlaubt, die niedrigste Punktzahl gewinnt.

Bonus

-50, wenn Ihr Programm sowohl eine vom Menschen definierte als auch eine zufällige Startposition zulässt. Menschen können es über stdin, Datei, GUI eingeben ...

-100, wenn Ihr Programm es sowohl einem Menschen als auch einem zufälligen Spieler erlaubt, den schwarzen König zu bewegen

+12345, wenn Sie sich auf einen externen Schachlöser oder eine integrierte Schachbibliothek verlassen

Viel Glück!

Aktualisieren!

Extra Regel: Das Match muss bis zum Schachmatt gespielt werden. Schwarz tritt nicht zurück, springt nicht aus dem Schachbrett und wird nicht von Außerirdischen entführt.

Hinweis

Sie können wahrscheinlich Hilfe von dieser Frage auf chess.se bekommen .

Izabera
quelle
2
Gilt die 50-Züge-Ziehungsregel?
Komintern
1
@ Victor Ich hatte ein paar Versuche, aber es hat noch nicht geklappt. Brute Force ist offensichtlich zu langsam, Alpha-Beta auch, weil die Landschaft der Positionsbewertungen ziemlich flach ist; und neigt dazu, in einer Schleife stecken zu bleiben. Eine retrograde Analyse würde funktionieren, aber im Voraus sehr langsam. Mein nächster Versuch wird Bratkos Algorithmus für KRK verwenden, was ich vermieden habe, weil es sich um einen Haufen von Sonderfällen handelt, die für das Golfen nicht gut sind.
Bazzargh
1
@ Victor Ich schaue das auch an. Das ist gerade deshalb interessant, weil es einfach zu definieren und schwierig zu tun ist. Im Gegenzug wird das Programm nicht kurz sein, so dass der Code-Golf-Tag es doppelt schwer erscheinen ließ. Wenn mein Programm funktioniert, werden Sie es bald sehen.
Level River St
1
Bei Victor ging es nicht darum, optimal zu sein. Jeder Versuch, einen "besten" Zug zu wählen, ohne den Spielverlauf zu berücksichtigen, führte zu Schleifen. Zum Testen muss das Spiel von jeder Position beendet werden. Bratko + -Varianten sind nicht optimal, aber nachweislich zu beenden. Gerade jetzt rückläufige Analyse Der Versuch (dh Build endgame Tabelle), sieht vielversprechend aus und tatsächlich ist optimal, das ist schön. Fällt auch einigermaßen kurz aus.
Bazzargh
2
Wenn jemand Inspiration braucht (oder nur neugierig ist), finden Sie eine 1433 Zeichen umfassende Schachengine bei home.hccnet.nl/hgmuller/umax1_6.c
Comintern

Antworten:

11

Haskell 1463-100 = 1363

Ich bekomme nur eine Antwort. Dies findet die Lösung auf retrograde Weise und arbeitet vom Schachmatt zur Position zurück, in der wir uns befinden. Es unterscheidet sich von der Beschreibung der retrograden Analyse beim Schachprogrammieren - anstatt mit einer ersten Menge zu beginnen und sie mit Rückwärtsbewegungen zu erweitern Solange sich keine Quadrate bewegt haben, um nicht gesehen zu werden, beginnt es mit allen nicht verwendeten Quadraten und reduziert diese Menge, indem es Vorwärtsbewegungen versucht. Dies wird weniger zeiteffizient sein als die herkömmliche Methode, aber die Speichernutzung ist für mich explodiert, als ich es ausprobiert habe.

Kompilieren Sie mit ghc -O2für eine akzeptable Leistung für die Berechnung der Endspieltabelle. Das Spiel beginnt sofort nach dem ersten Zug. Liefern Sie weiße Königs-, Turm- und schwarze Königsfelder als Argumente. Für einen Zug möchte es nur ein Quadrat und wählt eines für Sie aus, wenn Sie die Eingabetaste drücken. Beispielsitzung:

$ 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

Code:

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}

Bearbeitet: Ein Fehler wurde behoben, durch den sich der Code an die Endspieltabelle erinnert und Argumente verwendet wurden, sodass das wiederholte Testen weniger schmerzhaft war.

bazzargh
quelle
2
haskell code mit nebenwirkungen? Wie konntest du, Ketzer? : p
Einacio
endlich eine ernste!
Izabera
Dieses Rätsel war böse @izabera!
bazzargh
Nett! Viel besser als der Versuch, an dem ich gearbeitet habe. Ich habe versucht, den El Ajedrecista nur so weit zu verbessern, dass 50 Mitspieler erreicht werden, aber was den Algorithmus angeht, ist er wirklich schlecht.
Comintern
Ein Großteil der verdammten Leistung kommt von mir, dass ich mir die Endspieltabelle nicht gemerkt habe ( y). Dies ist wirklich offensichtlich, da der zweite Zug nicht schnell ist, wenn wir bereits über das gesamte Endspiel nachgedacht haben. Ich bin heute Abend in der Kneipe, aber wenn ich morgen die Chance bekomme, werde ich das weniger schrecklich machen.
bazzargh
7

C, Derzeit 2552 noncomment nonwhitespace Zeichen

Die Zählung zeigt mir, dass ich unter 2552 Zeichen Golf spielen könnte, aber da es bereits eine kleinere Antwort gibt (die schwer zu schlagen sein wird), werde ich dies sorgfältig prüfen, bevor ich mir die Mühe mache, es zu tun. Es ist wahr, es gibt ungefähr 200 Zeichen für die Anzeige der Tafel und jeweils weitere 200 für die Überprüfung der Benutzereingaben von Startposition und Bewegung (was ich zum Testen brauche, aber beseitigen könnte.)

Hier gibt es keinen Spielbaum, nur einen fest codierten Algorithmus, sodass er sich sofort bewegt.

Startpositionen werden als Reihe (1-8), Spalte (1-8) von rechts oben nummeriert eingegeben und das Programm arbeitet nach demselben Schema. Wenn Sie Ihren Bildschirm also um 90 Grad gegen den Uhrzeigersinn drehen, folgt er der standardmäßigen numerischen Quadratnotation für Korrespondenzschach. Positionen, bei denen der schwarze König bereits in Schach ist, werden als illegal zurückgewiesen.

Schwarze Züge werden als Zahl von 0 bis 7 eingegeben, wobei 0 ein Zug nach Norden, 1 nach Nordosten usw. im Uhrzeigersinn ist.

Es folgt nicht dem allgemein bekannten Algorithmus, der ausschließlich den Turm unter dem Schutz des weißen Königs verwendet, um den schwarzen König einzuschränken. Der Turm schränkt den schwarzen König nur in vertikaler Richtung ein (und rennt horizontal davon, wenn er verfolgt wird). Der weiße König schränkt den schwarzen König in horizontaler Richtung ein. Dies bedeutet, dass sich die beiden weißen Teile nicht gegenseitig behindern.

Ich habe die meisten Bugs und möglichen Endlosschleifen ausgebügelt, es läuft jetzt ziemlich gut. Ich werde morgen wieder damit spielen und sehen, ob es noch etwas gibt, das repariert werden muss.

#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();

}

Hier ist ein typisches Finish (Mate kann manchmal irgendwo am rechten oder linken Rand des Bretts auftreten.)

Bildbeschreibung hier eingeben

Level River St
quelle
6

Bash, 18 (oder -32?)

Okay, das ist eine Scherzantwort. Da Schwarz ein guter Schachspieler ist und Schwarz weiß, dass Weiß auch ein guter Schachspieler ist, entscheidet er, dass das einzig Vernünftige ist:

echo Black resigns

Dies führt zu einem Weißgewinn, der der Spezifikation entspricht.

Technisch gesehen können Sie die aktuellen Positionen auch als Argumente eingeben, das Programm ignoriert sie einfach. Dies kann sich also möglicherweise für den Bonus von -50 qualifizieren.

user12205
quelle
Komisch, aber ich habe die Regeln aktualisiert. Spielen bis Schachmatt ist jetzt Pflicht.
Izabera
1
Übrigens, die ursprüngliche Frage besagt eindeutig, dass Ihr Programm möglicherweise einen menschlichen oder zufälligen Spieler für Schwarz zulässt und Ihre Frage nicht zufällig ist.
Izabera
2
Mit der Standardnotation können Sie 1-0etwas kürzere Ausgaben machen .
Daniero
1
@Comintern gut aktuell, wenn man optimal verliert, bedeutet in der Regel, dass man am längsten durchhält.
PyRulez
@PyRulez laut Wikipedia : "Jeder Spieler kann jederzeit zurücktreten und sein Gegner gewinnt das Spiel." Plus, das ist nur eine Scherzantwort, nimm es nicht so ernst.
user12205