король + грак проти короля


16

Це закінчення чергової добре зіграної гри в шахи. Ти гравець білого кольору, а ти ще грак і твій король. У вашого суперника залишився лише його король.

Оскільки ти білий, на тебе черга. Створіть програму для гри в цей шаховий матч. Її результатом може бути послідовність рухів, gif-анімація, мистецтво ASCII або все, що завгодно.

Це здається цілком очевидним, але я це чітко зазначу: ви повинні виграти гру (у обмеженій кількості рухів). Завжди можна виграти з цієї позиції. НЕ ВІДТВОРЮЄТЬСЯ, ЩО РОКУТ. НЕ СТАЛЕМАТИ.

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

Оцінка

Ваш рахунок буде довжиною в байті вашого коду + бонус. Будь-яка мова дозволена, виграє найнижчий бал.

Бонус

-50, якщо ваша програма дозволяє визначати людину як початкову позицію, так і випадкову. Люди можуть ввести його через stdin, файл, GUI ...

-100, якщо ваша програма дозволяє людям і випадковим гравцям переміщати чорного короля

+12345, якщо ви покладаєтесь на зовнішній шаховий вирішувач або вбудовану шахову бібліотеку

Удачі!

Оновлення!

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

Підказка

Можливо, ви можете отримати допомогу з цього питання на chess.se .


2
Чи застосовується правило 50 розіграшу ходу?
Комінтерн

1
@Victor У мене було кілька кроків, але це ще не вийшло. Груба сила очевидно занадто повільна, альфа-бета теж, оскільки пейзаж рейтингів позицій досить рівний; і прагне застрягнути в петлі. Ретроградний аналіз спрацював би, але дуже повільно. Наступна моя спроба використовуватиме алгоритм Братко для KRK, якого я уникав, оскільки це купа особливих випадків, не велика для гольфу.
bazzargh

1
@victor Я також на це дивлюсь. Це цікаво саме тим, що його легко визначити і важко зробити. У свою чергу програма не буде короткою, тому тег коду-гольфу зробив це вдвічі важким. Якщо моя програма працює, ви побачите її незабаром.
Річка рівня Св.

1
@Victor проблема полягала не в тому, щоб намагатися бути оптимальними, будь-яка спроба вибрати найкращий крок без врахування історії ігор, що вела до циклів. Необхідність перевірити гру закінчується з кожної позиції. Варіанти Bratko + не є оптимальними, але доказливо припиняються. Пробуючи ретроградний аналіз саме зараз (тобто будувати таблицю з ендграми), виглядає багатообіцяюче і насправді є оптимальним, що приємно. Також виходить досить коротким.
bazzargh

2
Якщо комусь потрібне натхнення (або просто цікаво), ви можете знайти повний шаховий двигун на 1433 символів удома.hccnet.nl/hgmuller/umax1_6.c
Комінтерн

Відповіді:


11

Haskell 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
код haskell з побічними ефектами? як ти міг, єретик! : p
Ейнасіо

нарешті серйозний!
izabera

що головоломка була зла @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();

}

Ось типова обробка (парення іноді можуть виникати в будь-якому місці правого або лівого краю дошки.)

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


6

Баш, 18 (або -32?)

Гаразд, це жартівлива відповідь. Оскільки Блек - хороший шахіст, а Блек знає, що Білий також хороший шахіст, він вирішує, що єдине розумне - це:

echo Black resigns

Це призводить до виграшу білого кольору, що відповідає технічним умовам.

Технічно ви можете вводити поточні позиції і як аргументи, програма просто ігнорує їх, тому, можливо, це може претендувати на бонус -50.


Смішно, але я оновив правила. Грайте до мат-мату, тепер обов'язкові.
izabera

1
У первинному запитанні чітко зазначено, що ваша програма може дозволити людині чи випадковому гравцю чорних, а ваша не є випадковою.
izabera

2
Використовуючи стандартні позначення, ви можете вивести 1-0їх трохи коротше.
daniero

1
@Comintern добре актуальний при втраті оптимальних, як правило, означає, що триває найдовше.
PyRulez

@PyRulez згідно з wikipedia : "Будь-який гравець може піти у відставку в будь-який час, і його противник виграє гру". Плюс, це лише жартівлива відповідь, не сприймайте це так серйозно.
користувач12205
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.