왕 + 루크 대 왕


16

잘 플레이 된 다른 체스 게임의 결말입니다. 당신은 백인 플레이어이고 여전히 루크와 왕이 있습니다. 상대는 자신의 왕만 남았습니다.

당신이 백인이기 때문에 당신 차례입니다. 이 체스 경기를하는 프로그램을 만드십시오. 출력은 일련의 동작, gif 애니메이션, ASCII 아트 또는 원하는대로 될 수 있습니다.

꽤 분명해 보이지만, 명시 적으로 언급 할 것입니다 : 당신은 게임을 승리해야합니다 (유한 한 수의 움직임으로). 이 위치에서 항상 이길 수 있습니다. 잃어버린 것을 잃지 마십시오. 발화하지 마십시오.

당신의 프로그램은 시작 위치와 각각의 검은 움직임에 대한 인간의 의견을 받아 들일 수도 있고받지 않을 수도 있습니다 (이것은 합법적 인 입장이라고 가정 할 수 있습니다. 즉, 왕이 서로 접촉하지 않습니다). 그렇지 않다면, 블랙 킹의 무작위 시작 위치와 무작위 움직임으로 충분합니다.

점수

점수는 코드의 바이트 + 보너스 길이입니다. 모든 언어가 허용되며 가장 낮은 점수를 얻습니다.

보너스

프로그램에서 사람이 정의한 시작 위치와 임의의 시작 위치를 모두 허용하면 -50입니다. 인간은 stdin, file, GUI를 통해 입력 할 수 있습니다 ...

만약 당신의 프로그램이 인간과 무작위 플레이어 모두에게 검은 왕을 움직일 수있게한다면 -100

외부 체스 솔버 또는 내장 체스 라이브러리를 사용하는 경우 +12345

행운을 빕니다!

최신 정보!

추가 규칙 : 경기는 체크 메이트까지 진행해야합니다. 블랙은 사임하지 않고 체스 판 밖으로 뛰어 내리지 않으며 외계인에게 납치되지 않습니다.

힌트

chess.se 에서이 질문대한 도움을 얻을 수 있습니다 .


2
50 무승부 규칙이 적용됩니까?
Comintern

1
@ 빅터 나는 몇 번 갔지만 아직 해결되지 않았습니다. 위치 등급의 지형이 매우 평평하기 때문에 무차별 대입은 분명히 너무 느리고 알파-베타도 마찬가지입니다. 루프에 빠지는 경향이 있습니다. 역행 분석은 효과가 있지만 초기 속도는 매우 느립니다. 다음 시도는 KRK에 Bratko의 알고리즘을 사용할 것입니다. 골프에는 좋지 않은 특별한 경우이기 때문에 피했습니다.
bazzargh

1
@ 빅터 나도 이것을보고있다. 정의하기 쉽고 어렵 기 때문에 이것은 매우 흥미 롭습니다. 결과적으로 프로그램이 짧지 않기 때문에 code-golf 태그로 인해 프로그램이 두 배로 힘들어졌습니다. 내 프로그램이 작동하면 곧 볼 수 있습니다.
Level River St

1
@ 빅터 문제는 최적의 시도가 아니라 게임 기록을 고려하지 않고 '최상의'동작을 선택하려는 시도가 반복되었습니다. 모든 위치에서 게임 종료를 테스트해야합니다. Bratko + 변형은 최적은 아니지만 종료됩니다. 지금 막 역행 분석을 시도하면 (즉, 최종 게임 테이블 작성) 유망 해 보이고 실제로 최적입니다. 또한 합리적으로 짧습니다.
bazzargh

2
누군가 영감이 필요하거나 궁금한 점이 있다면 home.hccnet.nl/hgmuller/umax1_6.c 에서 1433 자의 완전한 체스 엔진 을 찾을 수 있습니다.
Comintern

답변:


11

하스켈 1463-100 = 1363

답을 얻는 것입니다. 이것은 체크 메이트에서 현재 위치로 되돌아가는 솔루션을 역행 방식으로 찾습니다. 초기 설정으로 시작하여 후진 이동으로 확장하는 대신 체스 프로그래밍 에 대한 역행 분석 설명과 다릅니다. 이동 한 사각형이 보이지 않을 때까지 사용되지 않은 모든 사각형으로 시작하고 앞으로 이동하여 해당 사각형을 줄입니다. 이것은 전통적인 방법보다 시간 효율적이지 않지만 메모리 사용은 내가 시도했을 때 나에게 폭발했습니다.

ghc -O2최종 게임 테이블 계산에 적합한 성능으로 컴파일하십시오 . 첫 번째 이동 후 즉시 재생됩니다. 하얀 왕, 루크, 검은 왕 광장을 인수로 제공하십시오. 이동의 경우 정사각형 만 원하며 Return 키를 누르면 하나를 선택합니다. 세션 예 :

$ 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

암호:

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}

편집 : 엔드 게임 테이블을 기억하고 인수를 사용하도록 코드가 수정되어 반복적으로 테스트하는 것이 훨씬 덜 고통 스럽습니다.


2
부작용이있는 하스켈 코드? 어떻게 이단적일 수 있니! : p
Einacio

마침내 진지한 것!
izabera

그 퍼즐은 사악한 @izabera였습니다!
bazzargh

좋은! 내가 시도했던 것보다 훨씬 낫습니다. El Ajedrecista를 개선하여 50 명의 이동 메이트를 확보하려고했지만 알고리즘이 진행되는 한 실제로는 나쁩니다.
Comintern

최종 게임 테이블 ( y)을 기억하지 않는 많은 짜증나는 성능이 나옵니다 . 우리가 이미 전체 최종 게임을 고려했을 때 두 번째 움직임이 빠르지 않다는 점에서 이것은 분명합니다. 나는 오늘 저녁에 술집을 떠난다. 그러나 만일 내일 기회를 얻는다면 나는 이것을 덜 끔찍하게 만들 것이다.
bazzargh

7

C, 현재 2552 개의 비 주석 비 공백 문자

이 숫자는 총 2552 문자 이하로 골프를 줄 수 있다고 알려주지 만 이미 작은 대답 (이기는 것이 힘들 것입니다)이 주어지면 그것을 고려하기 전에 신중하게 고려할 것입니다. 보드를 표시하는 데 약 200 개의 문자가 있고 시작 위치와 이동 모두의 사용자 입력을 확인하기 위해 각각 200 개의 문자가 있습니다 (테스트가 필요하지만 제거 할 수 있음).

여기에는 게임 트리가없고 하드 코딩 된 알고리즘만으로 즉시 이동합니다.

시작 위치는 오른쪽 상단에서 번호가 지정된 행 (1-8) 열 (1-8)로 입력되며 프로그램은 동일한 구성표에서 작동합니다. 따라서 화면을 시계 반대 방향으로 90도 돌리면 표준 통신 체스 숫자 사각형 표기법을 따릅니다. 흑인 왕이 이미 확인한 위치는 불법으로 거부됩니다.

검은 색 움직임은 0에서 7까지의 숫자로 입력되며, 0은 북쪽으로, 1은 북동쪽으로, 시계 방향으로 이동합니다.

백인 왕의 보호하에 루크를 독점적으로 사용하여 흑인 왕을 제한하는 일반적으로 알려진 알고리즘을 따르지 않습니다. 사기꾼은 수직적 의미로만 검은 왕을 제한하고 (쫓는 경우 가로로 도망 갈 것입니다.) 흰색 왕은 검은 왕을 가로로 움직입니다. 이것은 두 개의 흰색 조각이 서로의 방식으로 들어 가지 않음을 의미합니다.

나는 대부분의 버그와 가능한 무한 루프를 다룬 것처럼 보이며 지금은 잘 작동하고 있습니다. 나는 내일 다시 그것을 가지고 고칠 필요가 다른 것이 있는지 볼 것입니다.

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

}

일반적인 마감 처리는 다음과 같습니다 (메이트는 때때로 보드의 오른쪽 또는 왼쪽 가장자리에서 발생할 수 있습니다).

여기에 이미지 설명을 입력하십시오


6

배쉬, 18 (또는 -32?)

네, 이것은 농담입니다. Black은 좋은 체스 플레이어이고 Black은 White도 좋은 체스 플레이어라는 것을 알고 있기 때문에 할 수있는 유일한 방법은 다음과 같습니다.

echo Black resigns

이로 인해 사양에 맞는 흰색 당첨이 발생합니다.

인수가 너무, 프로그램은 단지 그들을 무시 기술적으로는, 현재의 위치를 입력 할 수 있도록 틀림 이는 -50 보너스를받을 수 있습니다.


웃기는하지만 규칙을 업데이트했습니다. checkmate까지 플레이하는 것이 이제 필수입니다.
izabera

1
원래의 질문과는 달리 프로그램은 인간이나 무작위 플레이어가 흑인을 허용 할 수 있으며 귀하의 것이 무작위가 아니라는 것을 분명히 나타냅니다.
izabera

2
표준 표기법을 사용 1-0하면 조금 더 짧은 출력 이 가능합니다.
daniero

1
@Comintern은 최적을 잃을 때 실제로 가장 오래 지속되는 것을 의미합니다.
PyRulez

wikipedia 에 따르면 @PyRulez 는 "언제든지 플레이어가 사임 할 수 있으며 상대방이 게임에서 승리합니다." 게다가, 이것은 단지 농담 답변입니다. 그렇게 심각하게 받아들이지 마십시오.
user12205
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.