re + torre vs re


16

È la fine di un'altra partita di scacchi ben giocata. Sei il giocatore bianco e hai ancora una torre e il tuo re. Al tuo avversario è rimasto solo il suo re.

Dato che sei bianco, tocca a te. Crea un programma per giocare a questa partita di scacchi. Il suo output può essere una sequenza di mosse, un'animazione gif, un'arte ASCII o qualunque cosa tu voglia.

Sembra abbastanza ovvio, ma lo dichiarerò esplicitamente: devi vincere la partita (in un numero finito di mosse). È sempre possibile vincere da questa posizione. NON PERDERE QUELLO ROOK. NON STALEMARE.

Il tuo programma potrebbe accettare o meno un input umano per la posizione di partenza e per ogni mossa nera (puoi tranquillamente supporre che questa sia una posizione legale, cioè i re non si toccano). In caso contrario, saranno sufficienti una posizione iniziale casuale e movimenti casuali per il re nero.

Punto

Il tuo punteggio sarà la lunghezza in byte del tuo codice + bonus. È consentita qualsiasi lingua, il punteggio più basso vince.

indennità

-50 se il programma consente sia una posizione iniziale definita umana sia una posizione casuale. Gli umani possono accedervi tramite stdin, file, GUI ...

-100 se il tuo programma consente sia a un giocatore umano che a uno casuale di muovere il re nero

+12345 se si fa affidamento su un risolutore di scacchi esterno o su una libreria di scacchi incorporata

In bocca al lupo!

Aggiornare!

Regola extra: la partita deve essere giocata fino allo scacco matto. Il nero non si dimette, non salta fuori dalla scacchiera e non viene rapito dagli alieni.

Suggerimento

Probabilmente puoi ottenere aiuto da questa domanda su chess.se .


2
Si applica la regola di estrazione a 50 mosse?
Comintern,

1
@Victor Ho avuto un paio di tentativi, ma non ha ancora funzionato. La forza bruta è ovviamente troppo lenta, anche l'alfa-beta perché il panorama dei rating di posizione è piuttosto piatto; e tende a rimanere bloccato in un ciclo. L'analisi retrograda funzionerebbe ma molto lentamente in anticipo. Il mio prossimo tentativo utilizzerà l'algoritmo di Bratko per KRK, che ho evitato perché è un mucchio di casi speciali, non eccezionale per il golf.
bazzargh,

1
@victor Sto guardando anche questo. Questo è interessante proprio perché è semplice da definire e difficile da fare. A sua volta, il programma non sarà breve, quindi il tag code-golf lo ha reso doppiamente difficile. Se il mio programma funziona, lo vedrai presto.
Level River St

1
@Victor il problema non riguardava il tentativo di essere ottimale, qualsiasi tentativo di scegliere una mossa "migliore" senza considerare la cronologia del gioco ha portato a dei cicli. È necessario testare il gioco termina da ogni posizione. Le varianti di Bratko + non sono ottimali ma si risolvono in modo dimostrabile. Provare l'analisi retrograda in questo momento (ad esempio costruire una tabella dei giochi finali) sembra promettente e in realtà è ottimale, il che è bello. Inoltre risulta ragionevolmente breve.
bazzargh,

2
Se qualcuno ha bisogno di ispirazione (o è solo curioso), puoi trovare un motore di scacchi completo da 1433 caratteri a casa.hccnet.nl/hgmuller/umax1_6.c
Comintern

Risposte:


11

Haskell 1463-100 = 1363

Sta solo ricevendo una risposta. Questa trova la soluzione nel modo retrogrado, passando da scacco matto alla posizione in cui ci troviamo. Si differenzia dalla descrizione dell'analisi retrograda sulla programmazione degli scacchi - invece di iniziare con un set iniziale e di espanderlo con mosse all'indietro fino a quando non sono stati visti quadrati non spostati, inizia con tutti i quadrati inutilizzati e riduce quel set provando le mosse in avanti. Questo sarà meno efficiente in termini di tempo rispetto al modo tradizionale, ma l'utilizzo della memoria è esploso per me quando l'ho provato.

Compilare con ghc -O2per prestazioni accettabili per il calcolo della tabella endgame; il gioco è istantaneo dopo la prima mossa. Fornisci i re bianchi, i corvi, i quadrati neri come argomenti. Per una mossa, vuole solo un quadrato e ne sceglierà uno per te se premi Invio. Sessione di esempio:

$ 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

Codice:

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}

Modificato: risolto il codice per ricordare la tabella di endgame e usare gli argomenti, molto meno doloroso da testare ripetutamente.


2
codice haskell con effetti collaterali? come hai potuto, eretico! : p
Einacio

finalmente serio!
Izabera,

quel puzzle era malvagio @izabera!
bazzargh,

Bello! Molto meglio del tentativo a cui stavo lavorando. Stavo cercando di migliorare El Ajedrecista abbastanza da garantire 50 compagni di movimento, ma per quanto riguarda un algoritmo va davvero male.
Comintern

Molte delle prestazioni sucky provengono da me non memoizing la tabella endgame ( y). Questo è davvero ovvio in quanto la seconda mossa non è veloce quando abbiamo già considerato l'intero endgame. Stasera vado al pub ma se avrò l'occasione domani lo renderò meno terribile.
bazzargh,

7

C, Attualmente 2552 caratteri non bianchi non spaziali

Il conteggio mi indica che avrei potuto giocare a golf al di sotto di 2552 caratteri totali, ma dato che esiste già una risposta più piccola (che sarà difficile da battere) lo terrò attentamente in considerazione prima di preoccuparmi di farlo. È vero che ci sono circa 200 caratteri per visualizzare la scheda e altri 200 ciascuno per controllare l'input dell'utente sia della posizione iniziale che della mossa (cosa di cui ho bisogno per i test, ma che potrei eliminare).

Nessun albero di gioco qui, solo algoritmo hardcoded, quindi si muove all'istante.

Le posizioni iniziali vengono inserite come colonne (1-8) di colonna (1-8) numerate in alto a destra e il programma funziona sullo stesso schema. Quindi, se ruotassi lo schermo di 90 gradi in senso antiorario, seguirà la notazione quadrata numerica standard degli scacchi per corrispondenza. Le posizioni in cui il re nero è già sotto controllo sono respinte come illegali.

Le mosse nere vengono inserite come un numero compreso tra 0 e 7, con 0 che corrisponde a una mossa verso nord, 1 a nord-est e così via in senso orario.

Non segue l'algoritmo comunemente noto che usa esclusivamente la torre sotto la protezione del re bianco per limitare il re nero. Il corvo limita il re nero in senso verticale (e scapperà in orizzontale se inseguito.) Il re bianco limita il re nero in movimento orizzontale. Ciò significa che i due pezzi bianchi non si intralciano l'un l'altro.

Mi sembra di aver risolto la maggior parte dei bug e dei possibili loop infiniti, ora funziona abbastanza bene. Ci giocherò di nuovo domani e vedrò se c'è qualcos'altro che deve essere riparato.

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

}

Ecco una finitura tipica (a volte il compagno può verificarsi ovunque sul bordo destro o sinistro della tavola).

inserisci qui la descrizione dell'immagine


6

Bash, 18 (o -32?)

Ok, questa è una risposta scherzosa. Poiché Black è un buon giocatore di scacchi e Black sa che anche White è un buon giocatore di scacchi, decide che l'unica cosa sensata da fare è:

echo Black resigns

Ciò si traduce in una vincita bianca, che soddisfa le specifiche.

Tecnicamente puoi inserire anche le posizioni attuali come argomenti, il programma le ignora, quindi probabilmente questo può qualificarsi per il bonus di -50.


Divertente, ma ho aggiornato le regole. Giocare fino a che il checkmate non è ora obbligatorio.
Izabera,

1
A proposito, la domanda originale afferma chiaramente che il tuo programma potrebbe consentire a un giocatore umano o casuale di giocare in nero, e il tuo non è casuale.
Izabera,

2
Usando la notazione standard, è possibile ottenere 1-0un output leggermente più breve.
daniero,

1
@Comintern ben reale quando perdere ottimale di solito significa durare più a lungo.
PyRulez

@PyRulez secondo wikipedia , "Entrambi i giocatori possono dimettersi in qualsiasi momento e il loro avversario vince la partita." Inoltre, questa è solo una risposta scherzosa, non prenderla così sul serio.
user12205
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.