Трехмерные шахматы

26

Чтобы отстаивать чье-то непонятное решение, люди часто говорят, что этот человек проходит через все головы и играет в «трехмерные шахматы». Теперь у вас есть шанс сыграть в трехмерные шахматы!

правила

Существует много вариантов 3D-шахмат , но для этого испытания я создал свой собственный. Моя версия похожа на обычные шахматы, за исключением того, что фигуры находятся внутри кубов, а не квадратов, и теперь имеют дополнительное измерение движения. Чтобы упростить эту задачу, здесь нет пешек и рокировки .

Движение части

(Направления компаса относятся к движению, которое происходит на стандартной шахматной доске, вверх и вниз - к вертикальному движению на трехмерной шахматной доске).

  • Король - имеет 26 квадратов, на которые он может пойти в данный ход: N, NE, E, SE, S, SW, W, NW; а также вверх, вниз и вверх / вниз + одно из направлений компаса.
  • Королева - может двигаться в том же направлении, что и король, но так далеко, как она хочет в этих направлениях.
  • Ладья - может двигаться в 6 направлениях: N, E, S, W, Вверх и Вниз,
  • Епископ - имеет 8 трехугольных направлений движения: NE + Вверх / Вниз, SE + Вверх / Вниз, SW + Вверх / Вниз, NW + Вверх / Вниз
  • Рыцарь - перемещает 2 пробела по одной оси, затем 1 пробел по другой. Как и обычные шахматы, рыцарь - единственная фигура, которая может перепрыгивать через другие фигуры.

Тестер

Используйте этот фрагмент, чтобы увидеть, как различные фигуры движутся на 3D-доске ( совет : посмотрите *Testфункции в JS, чтобы быстро определить, является ли квадрат правильным ходом, просто на основе его абсолютного расстояния от фигуры.):

const color = "Black";
const pieces = ["N","B","R","Q","K"];
const urls = ["https://image.ibb.co/gyS9Cx/Black_N.png","https://image.ibb.co/dknnzc/Black_B.png","https://image.ibb.co/kb3hXx/Black_R.png","https://image.ibb.co/hGO5kH/Black_Q.png","https://image.ibb.co/jApd5H/Black_K.png"];
var dragPiece;
var size = 3;
var index = 0;
function start() {
Array.prototype.add = function(a) {return [this[0]+a[0],this[1]+a[1],this[2]+a[2]]};

document.getElementById("n").onchange=function() {
	size = parseInt(this.value);
	var s = document.getElementsByClassName("selected");
	var pos;
	if(s.length > 0) {
		pos = s[0].pos;
	}
	document.body.removeChild(document.body.firstChild);
	createBoards();
	if(pos != null && valid(...pos)) {
	cellAt(...pos).click();
	}
};
createBoards();
}

function createBoards() {
var boards = document.createElement("div");
boards.style.counterReset = "board-count "+(size+1);
boards.name=size;
for(var x = 0;x<size;x++) {
var t = document.createElement("table");
for(var i = 0;i<size;i++) {
  var row = document.createElement("tr");
  row.className="row";
  for(var j = 0;j<size;j++) {
  	var cell = document.createElement("td");
    cell.className = (size+i+j)%2 == 1 ? "black" : "white";
    var im = document.createElement("img");
    im.draggable = true;
    im.ondragstart = function(e) {dragPiece = this;e.dataTransfer.setData("piece",this.parentElement.name);
    this.parentElement.classList.add("start");
    this.classList.add("dragged");
    };
    im.ondragend = function(e) {this.parentElement.classList.remove("start");this.classList.remove("dragged");};
    im.hidden = true;
    cell.appendChild(im);
    cell.pos = [j,i,x];
    cell.ondragover = function(e) {e.preventDefault();};
    cell.ondragenter = function(e) {this.classList.add("drag");};
    cell.ondragleave = function(e) {this.classList.remove("drag");};
    cell.ondrop = function(e) { e.preventDefault();this.classList.remove("drag");
    if(this != dragPiece.parentElement && this.firstChild.hidden ){
    dragPiece.hidden=true;
    setPiece(this,e.dataTransfer.getData("piece"));
    }
    
    };
    cell.onclick = function() {
    if(this.firstChild.hidden == false && this.classList.contains("selected")) {
		index++;
    	if(index == pieces.length) index = 0;
    }
     	setPiece(this,pieces[index]);
    };
  
    
    row.appendChild(cell);
  }
  t.appendChild(row);
  }
  boards.appendChild(t);
  }
  document.body.insertBefore(boards,document.body.firstChild);
}



function clearHighlighted() {
	var sel =  document.getElementsByClassName("highlighted");
     while(sel.length > 0) {
     	sel[0].classList.remove("highlighted");
     }
}

function setPiece(cell,piece) {
var s=document.getElementsByClassName("selected");
if(s.length > 0){ s[0].firstChild.hidden=true;s[0].classList.remove("selected");}
cell.classList.add("selected");
cell.firstChild.hidden = false;
cell.name = piece;
     	cell.firstChild.src = urls[index];
     clearHighlighted();
     	showMoves(cell,piece);
}

function showMoves(cell,piece) {
	if(piece=="K") selector(cell,kingTest)
	else if(piece=="N") selector(cell,knightTest);
	else if(piece=="Q") selector(cell,queenTest);
	else if(piece=="R") selector(cell,rookTest);
	else if(piece=="B") selector(cell,bishopTest);
}

function cellAt(col,row,board) {
	return document.body.firstChild.children[board].children[row].children[col];
}

function valid(col,row,board) {
	return 0<=col && col<size && 0<=row && row<size && 0<=board && board<size;
}

function select(cell) {
if(cell != null && cell.firstChild.hidden) cell.classList.add("highlighted");
}



function rookTest(dist) {
	var d = [].concat(dist).sort();
	return d[0] == 0 && d[1] == 0;
}

function knightTest(dist) {
	var d = [].concat(dist).sort();
	return d[0] == 0 && d[1] == 1 && d[2] == 2;
}

function kingTest(dist) {
	return dist[0] <= 1 && dist[1] <= 1 && dist[2] <= 1;
}

function bishopTest(dist) {
	return dist[0]==dist[1] && dist[1]==dist[2];
}

function queenTest(dist) {
	var d = [].concat(dist).sort();
	return rookTest(dist) || bishopTest(dist) || (d[0]==0 && d[1]==d[2]) ;
}

function dist(cell,x,y,z) {
	return [Math.abs(cell.pos[0]-x),Math.abs(cell.pos[1]-y),Math.abs(cell.pos[2]-z)];
}

function selector(cell,test) {
	for(var i = 0;i<size;i++) {
		for(var j = 0;j<size;j++) {
			for(var k = 0;k<size;k++) {
			if(test(dist(cell,k,j,i))) {
				var c = cellAt(k,j,i);
				if(c != cell) select(c);
			}
			}
			}
			}
	
}
table
{
	padding: 10px;
  display:inline-block;
}

table:after
{
  counter-increment: board-count -1;
  content: "("counter(board-count,upper-roman)")";
  float:right;
}

td
{
  width:28px;
  height:28px;
  border: 1px solid;
  cursor: pointer;
}

.black
{
  background-color: rgba(127,127,127,0.6);
}

.white
{
  background-color: white;
}


.start {
background-color: rgba(0,204,0,0.6);
}

.highlighted {
background-color: rgba(0,255,0,0.6);
}

.drag
{
background-color: rgba(0,204,255,0.6);
}


.selected {
background-color: green;
cursor: grab;
}

.selected img
{
  display:block;
}

.dragged {
  cursor: grabbing;
}
<body data-size=3 onload="start()"
<label for="n">Size: </label><select id="n">
<option>2</option>
<option selected>3</option>
<option>4</option>
<option>5</option>
<option>6</option>
<option>7</option>
<option>8</option>
<option>9</option>
<option>10</option>
</select>
<div>Click or drag to place the piece. Click on the piece to change its type.</div>
</body>

Вызов

По заданной доске n x n x n определите, находится ли белый король в мате.

вход

  • (Необязательно) n ≥ 2 - размер платы
  • Игровая доска
    • Может быть в виде 1d-2d- или 3d-массива или другого аналогичного формата. Запись может быть в любом простом формате. Например, KQRBN (белый) и kqrbn (черный) с # для пустых кубов. Или используйте числа для разных значений.
    • Думайте о трехмерной шахматной доске как о нескольких досках, сложенных друг на друге и перечисленных сверху вниз. Затем каждая отдельная доска записывается слева направо, сзади вперед (с черной стороны на белую).
    • Представьте себе этот случай 2x2x2 в виде трехмерного массива:
 [
[[Бк] [##]]
[[Млрд] [Ко]]
]

«верхняя» доска: введите описание изображения здесь«нижняя» доска:введите описание изображения здесь

Выход

  • логическое (значение истина / ложь) - истина, если белый король находится в мате, ложь в противном случае.

Мат

Белый король проверяет, угрожает ли чёрная фигура захватить его в следующий ход черных. Чтобы выйти из-под контроля, белым нужно перевести своего короля в безопасное место, защитить его другой частью или захватить угрожающую часть. Если у белых нет возможности выйти из-под контроля, то белый король в мате . Помните, что если белые не контролируются, но не могут двигаться без проверки, то это тупик , который не является матом.

Спецификация

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

Тестовые случаи

  1. п = 3, [###,n##,#rr],[#b#,###,###],[###,###,bRK]

    введите описание изображения здесь(III) введите описание изображения здесь(II) введите описание изображения здесь(I)

    Вывод: правда

    Пояснение: Король получает чек от ладьи на верхнем этаже. Белая ладья не может блокировать атаку или захватить угрожающую ладью, поэтому король должен попытаться уйти с дороги. Давайте рассмотрим варианты хода короля:

    1. c2 (I) - охраняется епископом на b3 (II)
    2. b2 (I) - охраняется рыцарем в a2 (III)
    3. c1 (II) - охраняется ладьей в c1 (III)
    4. b1 (II) - охраняется ладьей на b1 (III)
    5. с2 (II) - охраняется рыцарем на а2 (III)
    6. b2 (II) - охраняется епископом в a1 (I)

Так как король не может избежать проверки, это мат!

  1. п = 3, [b#b,###,###],[###,###,RNR],[#q#,###,#K#]

    введите описание изображения здесь(III) введите описание изображения здесь(II) введите описание изображения здесь(I)

    Вывод: false Объяснение: Король получает чек от королевы и не имеет ходов, чтобы сбежать или заблокировать. Тем не менее, рыцарь может захватить королеву.

  2. п = 3, [#q#,#b#,###],[n##,###,###],[#k#,###,#KB]

    введите описание изображения здесь(III) введите описание изображения здесь(II) введите описание изображения здесь(I)

Вывод: false Объяснение: у белых нет способа захватить угрожающую королеву или переместить своего короля в безопасное место. Однако, переместив своего слона в b2 (II), белые могут блокировать угрозу королевы.

  1. п = 4, [####,####,r###,####],[####,#q##,####,####],[##r#,###b,####,BRnn],[####,####,#N##,#KQ#]

    введите описание изображения здесь(IV) введите описание изображения здесь(III) введите описание изображения здесь(II) введите описание изображения здесь(I)

    Выходные данные: true Объяснение: В этом случае король получает чек от одного из рыцарей и королевы. Несмотря на то, что белые могут захватить / заблокировать одну из контрольных фигур, они не могут захватить / заблокировать обе. Поэтому белые должны попытаться вывести своего короля из-под контроля, но у него нет других вариантов.

  2. п = 3, [###,##b,r#r],[###,###,###],[#k#,###,#K#]

    введите описание изображения здесь(III) введите описание изображения здесь(II) введите описание изображения здесь(I)

Вывод: false Объяснение: Белые не контролируются, но не могут двигаться без проверки. Следовательно, это тупик, а не мат.

  1. п = 3, [##k,###,r#K],[###,n##,#N#],[###,###,#Q#]

    введите описание изображения здесь(III) введите описание изображения здесь(II) введите описание изображения здесь(I)

Вывод: верно Объяснение: Белые хотели бы напасть со своей королевой, чтобы защитить своего короля, но их рыцарь блокирует путь.

  1. п = 3, [###,###,##q],[###,###,###],[#k#,###,rNK]

    введите описание изображения здесь(III) введите описание изображения здесь(II) введите описание изображения здесь(I)

Вывод: верно Объяснение: Белые не могут взять королеву со своим рыцарем, потому что тогда ладья будет проверять короля белых.

  1. п = 2, [#q,##],[##,K#]

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

Вывод: false Объяснение: белые могут захватить королеву вместе со своим королем.

  1. п = 2, [rq,##],[##,K#]

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

Вывод: true Объяснение: На этот раз ладья охраняет, поэтому король не может захватить королеву.

  1. п = 3, [###,###,#q#],[###,###,###],[#k#,###,BKn]

    введите описание изображения здесь(III) введите описание изображения здесь(II) введите описание изображения здесь(I)

Вывод: false Объяснение: Белый король может сбежать, захватив рыцаря.

geokavel
источник
Просто деталь, но не cell.className = (i + j)%2 == 0 ? "black" : "white"будет ли лучше во фрагменте?
Арнаулд
@Arnauld lol, забыл исправить самое очевидное.
геокавель
Какой самый большой размер платы нам нужно поддерживать?
Вейцзюнь Чжоу
1
@WeijunZhou По сути, вы должны быть в состоянии выполнить тесты за разумное количество времени, чтобы увидеть, работает ли ваш код. Для больших чисел просто необходимо теоретически работать с учетом бесконечного времени и памяти.
геокавел

Ответы:

5

Рубин , 412 413 байт

->b,w=2{n=b=~/\n/
g=->h{h[0]-~n*(h[1]-~n*h[2])} 
r=1
(n**6).times{|i|a=b*1     
m=[]
9.times{|j|m<<(j<6?i/n**j%n:m[j-6]-m[j-3])}
x,y,z=v=m[6,3].map{|j|j*j}
d=v.max
e=x+y+z
q=95&o=(t=a[p=g[m[3,3]]]).ord
k=a[s=g[m]].ord
o/32==w&&(o^k>31||k==75)&&((q%8==2&&q%9*d==e||q==81&&x%d+y%d+z%d<1)&&((1...c=d**0.5).map{|j|a[p+g[m[6,3]]/c*j]}+[?#]).max<?A||q==78&&e==5||q==75&&e<4)&&(a[p]=?0;a[s]=t;r&&=w>2?a=~/K/:!f[a,3])}
r}

Попробуйте онлайн! Сейчас проверено на всех тестовых случаях. Код увеличен на 1 байт, чтобы исправить ошибку в случае 5 (тупик).

Функция лямбда, требующая ввода в виде строки в формате, показанном ниже. Можно указать необязательный второй параметр, указывающий, какая группа из 32 кодов ASCII должна учитываться при следующем перемещении (по умолчанию это 2 соответствует заглавным / белым символам, но функция вызывает себя рекурсивно, используя 3, соответствующие строчным / черным символам. )

Уровень рекурсии 1: пробует все возможные ходы для белых (от любого куба до любого куба) и перебирает все легальные. Уровень рекурсии 2: В каждом случае он призывает себя пройти все возможные ходы для черных. Это возвращает истину, если белый король пережил все возможные ходы черных. Уровень рекурсии 1: Если все возможные ходы белых приводят к ситуации, когда белый король НЕ переживает все возможные ходы черных, верните true (иначе false).

В общем, фигура не может двигаться на квадрат, занятый дружественной фигурой. Чтобы рассмотреть случай, когда белые вообще не двигаются (следовательно, мат не находится в тупике), также допускается случай, когда король «движется» к клетке, на которой он уже находится. Из-за короткого кода другим белым фигурам также разрешается перемещаться на площадь, занятую белым королем. Это бессмысленный ход, но его разрешение не влияет на результат, поэтому это не проблема.

Следующие тесты используются, чтобы проверить, является ли ход действительным для каждой фигуры. x,y,zквадраты расстояний, пройденных по каждой оси. eявляется их суммой (отсюда квадрат евклидова расстояния) и dявляется максимумом. Тип фигуры - AND с 95, чтобы преобразовать строчные ASCII-значения в прописные.

Bishop and Rook (ASCII 66 and 82) For the rook e=1*d. For the bishop e=3*d. 
The same code is used for both with q%9 giving 1 and 3 respectively.

Queen (ASCII 81) x%d+y%d+z%d<1 Each axis must be 0 or d, so this sum must be 0.

For the above pieces, any cubes crossed must be checked to ensure they are empty.

Knight (ASCII 78) e=5

King (ASCII 75) e<4

Код комментария

->b,w=2{                                                        #board, colour to move (default upcase/white)
  n=b=~/\n/                                                     #n=board size (index of first newline.)
  g=->h{h[0]-~n*(h[1]-~n*h[2])}                                 #Function to calculate position in string based on array of 3d coordinates.
  r=1                                                           #Return value = truthy.
  (n**6).times{|i|                                              #Iterate through n**6 moves (n**3 start cubes and n**3 end cubes.)
    a=b*1      
    m=[]                                                        #Make an empty array for coordinates.                                             
    9.times{|j|m<<(j<6?i/n**j%n:m[j-6]-m[j-3])}                 #Split i into six base n digits for the start and end coordinates. also derive 3 relative move distances.
    x,y,z=v=m[6,3].map{|j|j*j}                                  #v=array of relative distances squared. x,y,z are the 3 individual relative distances squared.
    d=v.max                                                     #Max of x,y,z                                     
    e=x+y+z                                                     #Square of euclidean distance
    q=95&o=(t=a[p=g[m[3,3]]]).ord                               #t=contents of cube to move from. o=ascii value, q=uppercase of o.
    k=a[s=g[m]].ord                                             #k=ascii value of contents of cube to move to.
    o/32==w&&(o^k>31||k==75)&&                                  #If o is in the right 32 byte range (uppercase or lowercase) AND the destination contains the white king or a character not in the same 32 byte range AND...
      ((q%8==2&&q%9*d==e||q==81&&x%d+y%d+z%d<1)&&               #the piece is a rook, bishop or queen with a valid move (as described in the text) AND..
      ((1...c=d**0.5).map{|j|a[p+g[m[6,3]]/c*j]}+[?#]).max<?A|| #the intervening squares are all empty, OR..
      q==78&&e==5||                                             #the piece is a knight and the move has euclidean distance sqrt(5) OR..
      q==75&&e<4)&&                                             #the piece is a king and the move has euclidean distance <4 THEN
      (a[p]=?0;a[s]=t;r&&=w>2?a=~/K/:!f[a,3])                   #put a 0 in the start cube and put the piece in the end cube. If moved piece is black, is the white king still there? AND with return value.
  }                                                             #If moved piece is white, recursively call the f to carry out the black moves. Does the white king NOT survive some black moves? AND with return value.
r}
Уровень реки St
источник
Не могли бы вы сыграть в гольф, используя однозначные значения ascii? Кроме того, вы имели в виду «тупик, а не мат» в третьем абзаце?
геокавель
@geokavel Самое короткое представление единственного значения ascii в Ruby - это ?A(есть пример в коде), поэтому оно по-прежнему составляет 2 байта. Все еще лучше, чем некоторые языки, которые требуют "A". Были определенные манипуляции, которые лучше выполнялись со значениями ASCII, а не с персонажами (в частности, o^k>31которые гарантируют, что фигура может переместиться на незанятую клетку или на площадь, занятую дружественной фигурой, но не враждебную.)
Level River St
Я имею в виду мат не тупик. Патовая ситуация - это ситуация, когда королю угрожает опасность, если игрок движется. Мат - это ситуация, когда королю угрожают, если игрок движется, а также если он не делает.
Уровень Река Сен
Что если вы использовали значения int вместо значений ascii (т.е. массив целых вместо строки)?
геокавель
@geokavel ints, вероятно, будет короче, и я могу пересмотреть его позже, поскольку это специально разрешено спецификацией. Но я выбрал выбранный формат отчасти потому, что он был более читабельным (а следовательно, более легким для развития) и отчасти потому, что меня вдохновил мой ответ, который сильно повлиял на мое мышление: codegolf.stackexchange.com/a/45544/15599
Уровень Река Санкт-