rey + torre contra rey

16

Es el final de otro juego de ajedrez bien jugado. Eres el jugador blanco, y todavía tienes una torre y tu rey. A tu oponente solo le queda su rey.

Como eres blanco, es tu turno. Crea un programa para jugar esta partida de ajedrez. Su salida puede ser una secuencia de movimientos, una animación gif, arte ASCII o lo que quieras.

Parece bastante obvio, pero lo declararé explícitamente: tienes que ganar el juego (en un número finito de movimientos). Siempre es posible ganar desde esta posición. No pierdas ese libro. NO ESTEALMATE.

Su programa puede o no aceptar una entrada humana para la posición inicial y para cada movimiento negro (puede asumir con seguridad que esta es una posición legal, es decir, los reyes no se tocan entre sí). Si no es así, una posición inicial aleatoria y movimientos aleatorios para el rey negro serán suficientes.

Puntuación

Su puntaje será la longitud en bytes de su código + bono. Se permite cualquier idioma, gana la puntuación más baja.

Prima

-50 si su programa permite tanto una posición inicial definida por el ser humano como una aleatoria. Los humanos pueden ingresar a través de stdin, archivo, GUI ...

-100 si su programa permite que un jugador humano y uno aleatorio muevan al rey negro

+12345 si confía en un solucionador de ajedrez externo o una biblioteca de ajedrez incorporada

¡Buena suerte!

¡Actualizar!

Regla adicional: el partido debe jugarse hasta el jaque mate. Black no renuncia, no salta fuera del tablero de ajedrez y no es secuestrado por extraterrestres.

Insinuación

Probablemente pueda obtener ayuda de esta pregunta en chess.se .

izabera
fuente
2
¿Se aplica la regla de 50 movimientos?
Comintern
1
@Victor He tenido un par de intentos, pero aún no ha funcionado. La fuerza bruta es obviamente demasiado lenta, alfa-beta también porque el panorama de las clasificaciones de posición es bastante plano; y tiende a atascarse en un bucle. El análisis retrógrado funcionaría, pero muy lento por adelantado. Mi próximo intento usará el algoritmo de Bratko para KRK, que he evitado porque es un montón de casos especiales, no muy buenos para el golf.
bazzargh
1
@victor Estoy mirando esto también. Esto es interesante precisamente porque es simple de definir y difícil de hacer. A su vez, el programa no será corto, por lo que la etiqueta de código de golf lo hizo parecer doblemente difícil. Si mi programa funciona, lo verás pronto.
Level River St
1
@Victor, el problema no era tratar de ser óptimo, cualquier intento de elegir una 'mejor' jugada sin considerar el historial del juego condujo a bucles. La necesidad de probar el juego termina desde cualquier posición. Las variantes de Bratko + no son óptimas, sino que terminan probablemente. Intentar un análisis retrógrado en este momento (es decir, crear una tabla de final de juego) parece prometedor y en realidad es óptimo, lo cual es bueno. También resulta razonablemente corto.
bazzargh
2
Si alguien necesita inspiración (o simplemente tiene curiosidad), puede encontrar un motor de ajedrez completo de 1433 personajes en casa.hccnet.nl/hgmuller/umax1_6.c
Comintern

Respuestas:

11

Haskell 1463-100 = 1363

Solo obtengo una respuesta. Esto encuentra la solución de manera retrógrada, trabajando desde jaque mate hasta la posición en la que estamos. Difiere de la descripción del análisis retrógrado en la programación de ajedrez , en lugar de comenzar con un conjunto inicial y expandirlo con movimientos hacia atrás hasta que no se haya visto ningún cuadro movido, comienza con todos los cuadrados no utilizados y reduce ese conjunto al intentar avanzar. Esto será menos eficiente en tiempo que la forma tradicional, pero el uso de memoria explotó cuando lo probé.

Compilar con ghc -O2un rendimiento aceptable para el cálculo de la tabla final; El juego es instantáneo después del primer movimiento. Proporcione rey blanco, torre, cuadrados de rey negro como argumentos. Para un movimiento, solo quiere un cuadrado, y elegirá uno para ti si presionas Intro. Sesión de ejemplo:

$ 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

Código:

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}

Editado: Se corrigió el código para recordar la tabla del final del juego y usar argumentos, mucho menos doloroso para probar repetidamente.

bazzargh
fuente
2
código haskell con efectos secundarios? ¿Cómo pudiste, hereje? : p
Einacio
finalmente uno serio!
izabera
ese acertijo era malvado @izabera!
bazzargh
¡Agradable! Mucho mejor que el intento en el que estaba trabajando. Estaba tratando de mejorar el Ajedrecista lo suficiente como para asegurar 50 compañeros de movimiento, pero en lo que respecta a un algoritmo, es realmente malo.
Comintern
Gran parte del rendimiento desafortunado proviene de que no recuerdo la tabla final ( y). Esto es realmente obvio porque el segundo movimiento no es rápido cuando ya hemos considerado todo el final del juego. Esta noche voy al pub, pero si tengo la oportunidad mañana, lo haré menos terrible.
bazzargh
7

C, actualmente 2552 caracteres sin espacios en blanco sin comentarios

El conteo me indica que podría jugar menos de 2552 caracteres en total, pero dado que ya hay una respuesta más pequeña (que será difícil de superar), lo consideraré cuidadosamente antes de molestarme en hacerlo. Es cierto que hay alrededor de 200 caracteres para mostrar el tablero y otros 200 para verificar la entrada del usuario tanto de la posición inicial como del movimiento (que necesito para probar, pero podría eliminar).

No hay árbol de juego aquí, solo algoritmo codificado, por lo que se mueve al instante.

Las posiciones de inicio se ingresan como fila (1-8) columna (1-8) numeradas desde la parte superior derecha y el programa funciona en el mismo esquema. Entonces, si giró la pantalla 90 grados en sentido antihorario, seguiría la notación cuadrática numérica del Ajedrez por correspondencia estándar. Las posiciones donde el rey negro ya está bajo control son rechazadas como ilegales.

Los movimientos negros se ingresan como un número del 0 al 7, siendo 0 un movimiento hacia el norte, 1 hacia el noreste y así sucesivamente en sentido horario.

No sigue el algoritmo comúnmente conocido que usa exclusivamente la torre bajo la protección del rey blanco para restringir al rey negro. La torre solo restringe al rey negro en sentido vertical (y se escapará horizontalmente si es perseguido). El rey blanco restringe al rey negro en movimiento horizontal. Esto significa que las dos piezas blancas no se interponen entre sí.

Parece que he solucionado la mayoría de los errores y posibles bucles infinitos, ahora funciona bastante bien. Mañana volveré a jugar con él y veré si hay algo más que deba arreglarse.

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

}

Aquí hay un acabado típico (el mate a veces puede ocurrir en cualquier lugar en el borde derecho o izquierdo del tablero).

ingrese la descripción de la imagen aquí

Level River St
fuente
6

Bash, 18 (o -32?)

Bien, esta es una respuesta de broma. Como el negro es un buen jugador de ajedrez, y el negro sabe que el blanco también es un buen jugador de ajedrez, decide que lo único sensato es:

echo Black resigns

Esto da como resultado la victoria blanca, que cumple con las especificaciones.

Técnicamente, también puede ingresar las posiciones actuales como argumentos, el programa simplemente los ignora, por lo que podría decirse que esto puede calificar para el bono de -50.

usuario12205
fuente
Es gracioso, pero actualicé las reglas. Jugar hasta el jaque mate ahora es obligatorio.
izabera
1
Por cierto, la pregunta original establece claramente que su programa puede permitir un humano o un jugador aleatorio para negro, y el suyo no es aleatorio.
izabera
2
Usando la notación estándar, puede generar 1-0un resultado un poco más corto.
daniero
1
@Comintern bien real cuando la pérdida óptima suele significar durar más tiempo.
PyRulez
@PyRulez, según Wikipedia , "cualquiera de los jugadores puede renunciar en cualquier momento y su oponente gana el juego". Además, esta es solo una respuesta de broma, no lo tomes tan en serio.
user12205