roi + tour vs roi

16

C'est la fin d'un autre jeu d'échecs bien joué. Vous êtes le joueur blanc et vous avez toujours une tour et votre roi. Votre adversaire n'a plus que son roi.

Puisque tu es blanc, c'est ton tour. Créez un programme pour jouer ce match d'échecs. Sa sortie peut être une séquence de mouvements, une animation gif, de l'art ASCII ou tout ce que vous voulez.

Cela semble assez évident, mais je vais le dire explicitement: vous devez gagner le jeu (en un nombre fini de coups). Il est toujours possible de gagner de cette position. NE PERDEZ PAS CE ROOK. NE PAS STALEMATE.

Votre programme peut accepter ou non une entrée humaine pour la position de départ et pour chaque mouvement noir (vous pouvez supposer en toute sécurité qu'il s'agit d'une position légale, c'est-à-dire que les rois ne se touchent pas). Si ce n'est pas le cas, une position de départ aléatoire et des mouvements aléatoires pour le roi noir suffiront.

But

Votre score sera la longueur en octet de votre code + bonus. N'importe quelle langue est autorisée, le score le plus bas l'emporte.

Prime

-50 si votre programme permet à la fois une position de départ définie par l'homme et une position aléatoire. Les humains peuvent y accéder via stdin, fichier, GUI ...

-100 si votre programme permet à un humain et à un joueur aléatoire de déplacer le roi noir

+12345 si vous comptez sur un solveur d'échecs externe ou une bibliothèque d'échecs intégrée

Bonne chance!

Mise à jour!

Règle supplémentaire: le match doit être joué jusqu'à l'échec et mat. Les noirs ne démissionnent pas, ne sautent pas hors de l'échiquier et ne sont pas kidnappés par des extraterrestres.

Allusion

Vous pouvez probablement obtenir de l'aide sur cette question sur chess.se .

izabera
la source
2
La règle du tirage de 50 coups s'applique-t-elle?
Comintern
1
@Victor J'ai eu quelques essais, mais cela n'a pas encore fonctionné. La force brute est évidemment trop lente, alpha-bêta aussi parce que le paysage des notes de position est assez plat; et a tendance à rester coincé dans une boucle. L'analyse rétrograde fonctionnerait mais très lentement à l'avance. Ma prochaine tentative utilisera l'algorithme de Bratko pour KRK, que j'ai évité car c'est une pile de cas spéciaux, pas génial pour le golf.
bazzargh
1
@victor Je regarde ça aussi. C'est intéressant précisément parce qu'il est simple à définir et difficile à faire. À son tour, le programme ne sera pas court, donc la balise code-golf l'a fait paraître doublement difficile. Si mon programme fonctionne, vous le verrez bientôt.
Level River St
1
@Victor, le problème n'était pas d'essayer d'être optimal, toute tentative de choisir un «meilleur» coup sans tenir compte de l'historique du jeu a conduit à des boucles. Le besoin de tester le jeu se termine à chaque position. Les variantes de Bratko + ne sont pas optimales mais se terminent de façon prouvée. Essayer l'analyse rétrograde en ce moment (c'est-à-dire construire une table de fin de partie), semble prometteur et est en fait optimal, ce qui est bien. Se révèle également assez court.
bazzargh
2
Si quelqu'un a besoin d'inspiration (ou est simplement curieux), vous pouvez trouver un moteur d'échecs complet de 1433 caractères à home.hccnet.nl/hgmuller/umax1_6.c
Comintern

Réponses:

11

Haskell 1463-100 = 1363

Je reçois juste une réponse. Cela trouve la solution de manière rétrograde, en remontant du mat à la position dans laquelle nous nous trouvons. Elle diffère de la description de l'analyse rétrograde sur la programmation d' échecs - au lieu de commencer avec un ensemble initial et de l'étendre avec des mouvements vers l'arrière jusqu'à ce qu'aucun carré déplacé ne soit visible, il commence par tous les carrés inutilisés et réduit cet ensemble en essayant les mouvements en avant. Cela va être moins efficace en temps que la méthode traditionnelle, mais l'utilisation de la mémoire a explosé pour moi lorsque je l'ai essayée.

Compiler avec ghc -O2pour des performances acceptables pour le calcul de la table de fin de partie; le jeu est instantané après le premier coup. Fournissez des carrés de roi blanc, de tour, de roi noir comme arguments. Pour un coup, il veut juste un carré, et en choisira un pour vous si vous appuyez sur Entrée. Exemple de session:

$ 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}

Modifié: code fixe pour se souvenir de la table de fin de partie et utiliser des arguments, donc beaucoup moins pénible à tester à plusieurs reprises.

bazzargh
la source
2
code haskell avec effets secondaires? comment pourriez-vous, hérétique! : p
Einacio
enfin un sérieux!
izabera
ce puzzle était mauvais @izabera!
bazzargh
Agréable! Bien mieux que la tentative sur laquelle je travaillais. J'essayais juste d'améliorer suffisamment El Ajedrecista pour assurer 50 compagnons de mouvement, mais en ce qui concerne un algorithme, c'est vraiment mauvais.
Komintern
Une grande partie de la performance sucky vient de moi de ne pas mémoriser la table de fin de partie ( y). C'est vraiment évident dans la mesure où le deuxième mouvement n'est pas rapide alors que nous avons déjà considéré l'ensemble de la phase finale. Je pars au pub ce soir mais si j'en ai l'occasion demain je vais rendre ça moins terrible.
bazzargh
7

C, actuellement 2552 caractères non blancs non commentés

Le décompte m'indique que je pourrais jouer au golf en dessous de 2552 caractères totaux, mais étant donné qu'il y a déjà une réponse plus petite (qui sera difficile à battre), j'y réfléchirai attentivement avant de prendre la peine de le faire. Il est vrai qu'il y a environ 200 caractères pour afficher la carte et 200 autres pour vérifier l'entrée utilisateur de la position de départ et du mouvement (dont j'ai besoin pour les tests, mais qui pourrait être éliminé.)

Pas d'arbre de jeu ici, juste un algorithme codé en dur, il se déplace donc instantanément.

Les positions de départ sont entrées dans la colonne (1-8) de la ligne (1-8) numérotée en haut à droite et le programme fonctionne sur le même schéma. Donc, si vous tourniez votre écran de 90 degrés dans le sens inverse des aiguilles d'une montre, il suivrait la notation carrée numérique standard des échecs par correspondance. Les positions où le roi noir est déjà en échec sont rejetées comme illégales.

Les mouvements noirs sont entrés sous la forme d'un nombre de 0 à 7, 0 étant un déplacement vers le nord, 1 vers le nord-est et ainsi de suite dans le sens des aiguilles d'une montre.

Il ne suit pas l'algorithme communément utilisé qui utilise exclusivement la tour sous la protection du roi blanc pour restreindre le roi noir. La tour restreint seulement le roi noir dans un sens vertical (et s'enfuira horizontalement si elle est poursuivie.) Le roi blanc restreint le roi noir en mouvement horizontal. Cela signifie que les deux pièces blanches ne se gênent pas mutuellement.

Il semble que j'ai résolu la plupart des bugs et des boucles infinies possibles, cela fonctionne plutôt bien maintenant. Je jouerai à nouveau avec lui demain et je verrai s'il y a autre chose à réparer.

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

}

Voici une finition typique (l'accouplement peut parfois se produire n'importe où sur le bord droit ou gauche de la planche.)

entrez la description de l'image ici

Level River St
la source
6

Bash, 18 (ou -32?)

D'accord, c'est une réponse de plaisanterie. Puisque Black est un bon joueur d'échecs et que Black sait que White est aussi un bon joueur d'échecs, il décide que la seule chose sensée à faire est:

echo Black resigns

Il en résulte un gain blanc, qui répond aux spécifications.

Techniquement, vous pouvez également saisir les positions actuelles en tant qu'arguments, le programme les ignore simplement, donc cela peut sans doute bénéficier du bonus de -50.

user12205
la source
C'est drôle, mais j'ai mis à jour les règles. Jouer jusqu'à échec et mat est désormais obligatoire.
izabera
1
Btw la question d'origine indique clairement que votre programme peut autoriser un humain ou un joueur aléatoire pour le noir, et le vôtre n'est pas aléatoire.
izabera
2
En utilisant la notation standard, vous pouvez produire 1-0un résultat un peu plus court.
daniero
1
@Comintern bien réel lorsque perdre optimal signifie généralement durer plus longtemps.
PyRulez
@PyRulez selon wikipedia , "Chaque joueur peut démissionner à tout moment et son adversaire gagne la partie." De plus, ce n'est qu'une réponse de plaisanterie, ne le prenez pas si au sérieux.
user12205