国王+白鸦vs国王


16

这是另一场打得很好的国际象棋的结局。您是白人玩家,您仍然有一个菜鸟和您的国王。您的对手只剩下他的国王。

既然你是白人,轮到你了。创建一个程序来玩此国际象棋比赛。它的输出可以是一系列动作,gif动画,ASCII艺术或您喜欢的任何东西。

似乎很明显,但是我要明确指出:您必须赢得游戏(以有限的步数)。总是有可能从这个位置获胜。不要丢人。不要宽容。

您的程序可能会接受或可能不会接受人工输入的开始位置和每次黑棋操作(您可以放心地认为这是合法位置,即国王之间没有碰触)。如果不是这样,那么黑王的随机起始位置和随机移动就足够了。

得分了

您的分数将是代码长度+奖金的长度。允许使用任何语言,最低分获胜。

奖金

-50(如果您的程序允许人类定义的起始位置和随机的起始位置)。人们可以通过stdin,文件,GUI来输入...

-100如果您的程序允许人类和随机玩家同时移动黑王

+12345,如果您依靠外部象棋求解器或内置象棋库

祝好运!

更新!

附加规则:比赛必须进行直到比赛结束。布莱克不会辞职,不会跳到棋盘外面,也不会被外星人绑架。

暗示

您可能会在Chess.se从此问题获得帮助。


2
50平局抽奖规则适用吗?
Comintern 2014年

1
@Victor我走了很多路,但是还没有解决。蛮力显然太慢,alpha-beta也太慢,因为位置等级的情况相当平坦。并容易陷入循环。逆行分析会起作用,但前期非常慢。我的下一个尝试将使用Bratko的KRK算法,我避免使用该算法,因为这是一堆特殊情况,对高尔夫而言并不好。
bazzargh 2014年

1
@victor我也在看这个。正是因为它定义简单且难以执行,所以这很有趣。反过来,程序也不会很短,因此,代码高尔夫球标记使它看起来像加倍困难。如果我的程序有效,您很快就会看到。
级圣河

1
@Victor问题不在于试图达到最佳状态,任何在不考虑游戏历史的情况下选择“最佳”动作的尝试都会导致循环。需要测试的游戏从各个位置终止。最佳Bratko + variant,但可证明终止。刚才尝试进行逆向分析(即构建残局表),看起来很有希望,并且实际上最佳的,这很好。结果也很短。
bazzargh 2014年

2
如果有人需要灵感(或只是好奇),您可以在家里找到一个1433个字符的完整象棋引擎。hccnet.nl/ hgmuller / umax1_6.c
Comintern

Answers:


11

哈斯克尔1463-100 = 1363

只是得到一个答案。这找到了逆行方式的解决方案,从将死返回到我们所处的位置。它不同于对象棋编程进行逆行分析的描述-而不是从初始集合开始并通过向后移动来扩展它直到没有看到未移动的正方形为止,它以所有未使用的正方形开始,并通过尝试向前移动来减小该设置。这将比传统方式的时间效率更低,但是当我尝试使用它时,内存使用对我来说是爆炸性的。

ghc -O2为最终游戏表计算获得可接受的性能进行编译;第一步移动后即刻开始游戏。提供白国王,白嘴鸦,黑国王正方形作为论点。要移动,它只需要一个正方形,如果按回车键,它将为您选择一个正方形。会话示例:

$ 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
haskell代码有副作用吗?你怎么可能,异端!:p
Einacio 2014年

终于认真了!
izabera 2014年

这个难题是邪恶的@izabera!
bazzargh 2014年

真好!比我正在尝试的要好得多。我试图改善El Ajedrecista,以确保50个移动配合,但是就算法而言,这确实很糟糕。
Comintern

许多令人讨厌的表现来自我没有记住残局表(y)的情况。这很明显,因为当我们已经考虑了整个残局时,第二步动作并不快。今天晚上我要去酒吧,但是如果明天有机会,我会减少这种情况。
2014年

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

Bash,18岁(或-32?)

好吧,这是个笑话。由于布莱克是一位出色的国际象棋棋手,并且布莱克知道怀特也是一位出色的国际象棋棋手,因此他认为唯一明智的做法是:

echo Black resigns

这将导致赢得白金,这符合规范。

从技术上讲,您也可以输入当前位置作为参数,程序只会忽略它们,因此可以说有资格获得-50奖金。


有趣,但是我更新了规则。现在必须玩直到将死。
izabera 2014年

1
顺便说一句,最初的问题清楚地表明,您的程序可能允许人类或随机玩家使用黑色,而您的并非随机的。
izabera 2014年

2
使用标准符号,您可以输出1-0短一些的输出。
daniero 2014年

1
@Comintern失去最佳效果时通常意味着持续时间最长。
PyRulez 2014年

维基百科上的 @PyRulez说:“任何玩家都可以随时辞职,而对手则赢得了比赛。” 另外,这只是个玩笑,不要那么认真。
user12205 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.