король + ладья против короля

16

Это конец другой хорошо сыгранной шахматной игры. Ты белый игрок, и у тебя все еще есть ладья и твой король. У вашего противника остался только его король.

Так как ты белый, твоя очередь. Создайте программу для игры в этот шахматный матч. Его вывод может быть последовательностью ходов, анимацией GIF, ASCII-изображением или чем угодно.

Это кажется вполне очевидным, но я прямо скажу: вы должны выиграть игру (за конечное число ходов). С этой позиции всегда можно выиграть. НЕ ПОТЕРЯЙТЕ ЭТУ ЛАДУ. НЕ УБИРАЙТЕ.

Ваша программа может принимать или не принимать вводимые человеком данные для начальной позиции и для каждого хода черных (вы можете смело предполагать, что это допустимая позиция, т.е. короли не соприкасаются друг с другом). Если этого не произойдет, будет достаточно случайной стартовой позиции и случайных движений для черного короля.

Гол

Ваша оценка будет длиной в байтах вашего кода + бонус. Разрешен любой язык, выигрывает самый низкий балл.

бонус

-50, если ваша программа допускает как определенную человеком начальную позицию, так и случайную. Люди могут войти в него через стандартный ввод, файл, графический интерфейс ...

-100, если ваша программа позволяет и черному, и обычному игроку перемещать черного короля

+12345, если вы полагаетесь на сторонний шахматный решатель или встроенную шахматную библиотеку

Удачи!

Обновить!

Дополнительное правило: Матч должен быть сыгран до матов. Черные не сдаются, не выпрыгивают из шахматной доски и не похищаются инопланетянами.

намек

Вы можете получить помощь по этому вопросу на chess.se .

izabera
источник
2
Применяется ли правило розыгрыша 50 ходов?
Коминтерн
1
@Victor У меня было пару ходов, но это еще не сработало. Грубая сила, очевидно, слишком медленная, и альфа-бета тоже, потому что ландшафт рейтингов позиции довольно плоский; и имеет тенденцию застрять в петле. Ретроградный анализ будет работать, но очень медленно. Моя следующая попытка будет использовать алгоритм Братко для KRK, которого я избегал, потому что это куча особых случаев, не очень подходящих для гольфа.
Bazzargh
1
@victor Я тоже смотрю на это. Это интересно именно потому, что его легко определить и сложно сделать. В свою очередь программа не будет короткой, поэтому тег code-golf сделал ее вдвойне сложной. Если моя программа сработает, вы скоро это увидите.
Уровень Река St
1
@Victor проблема заключалась не в том, чтобы пытаться быть оптимальным, любая попытка выбрать «лучший» ход без учета истории игры приводила к петлям. Необходимость тестирования игры заканчивается с каждой позиции. Варианты Bratko + не оптимальны, но доказуемо заканчиваются. Попытка ретроградного анализа только сейчас (т.е. составить таблицу финалов) выглядит многообещающе и на самом деле является оптимальной, что приятно. Также получается достаточно коротким.
Bazzargh
2
Если кому -то нужно вдохновение (или просто любопытно), вы можете найти 1433 символов в полный шахматный движок на home.hccnet.nl/hgmuller/umax1_6.c
Коминтерн

Ответы:

11

Haskell 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}

Отредактировано: исправлен код для запоминания таблицы эндшпиля и использования аргументов, поэтому гораздо менее болезненный для повторного тестирования.

bazzargh
источник
2
код haskell с побочными эффектами? как ты мог, еретик! : p
Einacio
наконец серьезный!
Изабера
эта головоломка была злой @izabera!
Bazzargh
Ницца! Намного лучше, чем попытка, над которой я работал. Я пытался просто улучшить El Ajedrecista, чтобы обеспечить 50 товарищей по ходу, но с точки зрения алгоритма это действительно плохо.
Коминтерн
Большая часть отстойной работы исходит от того, что я не запомнил таблицу финальных игр ( 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();

}

Вот типичное завершение (мат может иногда происходить в любом месте на правом или левом краю доски.)

введите описание изображения здесь

Уровень реки St
источник
6

Баш, 18 (или -32?)

Хорошо, это ответ на шутку. Поскольку черные - хороший шахматист, и черные знают, что белые также являются хорошими шахматистами, он решает, что единственное разумное, что нужно сделать, это:

echo Black resigns

Это приводит к победе белых, что соответствует спецификации.

Технически вы также можете вводить текущие позиции в качестве аргументов, программа просто игнорирует их, поэтому, возможно, это может претендовать на бонус -50.

user12205
источник
Забавно, но я обновил правила. Игра до матов теперь обязательна.
Изабера
1
Кстати, в первоначальном вопросе четко говорится, что ваша программа может позволить черных или случайного игрока, а ваша - не случайная.
Изабера
2
Используя стандартную запись, вы можете вывести 1-0немного короче.
Даниеро
1
@Comintern вполне актуально, когда теряет оптимальное значение, обычно означает, что оно длится дольше всего.
PyRulez
@PyRulez согласно википедии : «Любой игрок может подать в отставку в любое время, и его противник выигрывает игру». Плюс, это просто шутка, не принимайте это всерьез.
user12205