キング+ルークvsキング


16

それは別のよくプレイされるチェスゲームの終わりです。あなたは白人プレイヤーであり、まだルークとあなたの王がいます。対戦相手のキングは残っています。

あなたは白人なので、あなたの番です。このチェスの試合をプレイするプログラムを作成します。その出力は、一連の動き、gifアニメーション、ASCIIアートなど、何でもかまいません。

当たり前のように思えますが、明示的に述べます。ゲームに勝つ必要があります(限られた数の動きで)。このポジションから勝つことは常に可能です。そのルークを失うことはありません。遅らせないでください。

プログラムは、開始位置と各黒の動きに対して人間の入力を受け入れる場合と受け入れない場合があります(これは正当な位置、つまりキングが互いに触れていないことを安全に想定できます)。そうでない場合、黒王のランダムな開始位置とランダムな動きで十分です。

スコア

スコアは、コードのバイト単位の長さ+ボーナスになります。すべての言語が許可され、最低スコアが勝ちます。

ボーナス

プログラムで人間が定義した開始位置とランダムな位置の両方を許可する場合は-50。人間は標準入力、ファイル、GUIから入力できます...

プログラムで人間とランダムの両方のプレイヤーが黒王を動かすことができる場合は-100

外部チェスソルバーまたは組み込みのチェスライブラリに依存している場合は+12345

幸運を!

更新!

追加ルール:試合はチェックメイトまでプレイする必要があります。黒は辞任せず、チェス盤の外に飛び出さず、エイリアンに誘getされません。

ヒント

あなたは、おそらくから助けを得ることができ、この質問chess.se


2
50ムーブドロールールは適用されますか?
コミンテルン14

1
@Victor何度か行ったことがありますが、まだ解決していません。ポジションレーティングのランドスケープは非常にフラットであるため、ブルートフォースは明らかに遅すぎ、アルファベータも遅すぎます。ループに陥りがちです。逆行解析は機能しますが、非常に時間がかかります。私の次の試みでは、KRKにBratkoのアルゴリズムを使用します。これは、ゴルフには向いていない特殊なケースの山であるため、回避しました。
バザーグ14年

1
@victor私もこれを見ています。定義が簡単で、実行が難しいため、これはまさに興味深いことです。その結果、プログラムは短くなりません。そのため、code-golfタグにより、プログラムは二重に困難に見えました。私のプログラムが動作する場合、すぐに表示されます。
レベルリバーセント14年

1
@Victorの問題は最適化を試みることではなく、ゲームの履歴を考慮せずに「最良の」動きを選択しようとすると、ループが発生しました。ゲームをテストする必要はすべての位置から終了します。Bratko + variantsは最適ではありませんが、確実に終了します。今すぐ逆行解析を試してみてください(つまり、エンドゲームテーブルを構築します)。有望に見え、実際に最適です。これは素晴らしいことです。また、かなり短いことが判明しました。
バザーグ14年

2
誰もがインスピレーションを必要とする(あるいは単に好奇心である)場合は、1433文字を見つけることができ、完全なチェスエンジンをhome.hccnet.nl/hgmuller/umax1_6.c
コミンテルン

回答:


11

Haskell 1463-100 = 1363

。ただ、中に答えを得ることはこれが私たちがいる位置までチェックメイトから戻って作業、解決に逆行する道を見つけることがで逆行分析の説明とは異なります。chessprogramming -代わりの初期セットから始まり、後方に移動すると、それを拡大します移動する正方形が見えなくなるまで、すべての未使用の正方形から開始し、前方への移動を試みてそのセットを減らします。これは、従来の方法よりも時間効率が悪くなりますが、試したときにメモリ使用量が爆発的に増えました。

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
副作用を持つhaskellコード?異端者よ!:p
Einacio 14年

最後に深刻なもの!
izabera 14年

そのパズルは邪悪な@izaberaでした!
バザーグ14年

いいね!私が取り組んでいた試みよりもはるかに良い。El Ajedrecistaを50の移動仲間を確保するのに十分なだけ改善しようとしていましたが、アルゴリズムに関して言えば、それは本当に悪いことです。
コミンテルン14年

嫌なパフォーマンスの多くは、エンドゲームテーブル(y)を覚えていないからです。これは、エンドゲーム全体をすでに検討したときに、2番目の動きが速くないという点で非常に明白です。今夜はパブに行きますが、明日チャンスがあれば、これをもっとひどくします。
バザーグ14年

7

C、現在2552の非コメント非空白文字

カウントは、合計で2552チャー以下にゴルフできることを示していますが、すでに小さな回答(ビートするのは難しい)があるので、気にする前に慎重に検討します。ボードを表示するのに約200文字、開始位置と移動の両方のユーザー入力を確認するのにそれぞれ200文字あることは事実です(これはテストする必要がありますが、除去できます)。

ここにはゲームツリーはなく、ハードコーディングされたアルゴリズムなので、即座に動きます。

開始位置は、行(1-8)列(1-8)として右上から入力され、プログラムは同じスキームで動作します。したがって、画面を反時計回りに90度回転させた場合、標準の通信チェスの数値平方表記に従います。黒王がすでにチェックされているポジションは違法として拒否されます。

黒の動きは0から7までの数字として入力され、0は北への動き、1は北東への動き、というように時計回りの意味で入力されます。

それは、黒王を制限するために白王の保護の下でルークのみを使用する一般的に知られているアルゴリズムに従いません。ルークは、垂直方向の黒王のみを制限します(追いかけられた場合、水平方向に逃げます)。これは、2つの白い部分が互いの邪魔にならないことを意味します。

バグと無限ループの可能性のほとんどを解決したようですが、今はかなり順調に動作しています。明日もう一度試して、修正が必要なものがあるかどうかを確認します。

#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 14

1
ところで、元の質問では、あなたのプログラムは黒人を人間またはランダムなプレーヤーに許可する可能性があり、あなたのプログラムはランダムではないことを明確に述べています。
izabera 14

2
標準表記を使用1-0すると、出力が少し短くなります。
ダニエロ14

1
@Cominternは、最適を失ったときに実際に最も長くなることを意味します。
PyRulez 14年

ウィキペディアによると、@ PyRulezは、「どちらのプレイヤーもいつでも辞任でき、対戦相手がゲームに勝ちます。」さらに、これは単なる冗談の答えです。それほど真剣に受け止めないでください。
user12205 14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.