In order to defend someone’s baffling decision, people often say that that person is going over everyone’s heads and playing “3-dimensional chess”. Now it’s your chance to play 3-dimensional chess!
Rules
There are many variants of 3D Chess, but for this challenge I’ve made up my own. My version is just like regular chess except that the pieces are inside cubes instead of squares, and now have an additional dimension of movement. To make this challenge simple there are no pawns and no castling.
Piece Movement
(Compass directions refer to movement that would occur on a standard chessboard, Up and Down refer to moving vertically on the 3D chess board).
- King - has 26 squares it can go to on a given turn: N,NE,E,SE,S,SW,W,NW; as well as up, down, and up/down + one of the compass directions.
- Queen - can move in the same directions as the King, but as far as she wants in those directions.
- Rook - can move in 6 directions: N,E,S,W, Up, and Down,
- Bishop - has 8 triagonal directions of travel: NE + Up/Down, SE + Up/Down, SW + Up/Down, NW + Up/Down
- Knight - moves 2 spaces one axis, then 1 space on another. Just like regular chess, the knight is the only piece that can hop over other pieces.
Piece Tester
Use this snippet to see how the different pieces move on the 3D board (tip: check out the *Test
functions in the JS for quick ways to determine if a square is a valid move, simply based on its absolute distance from the piece.):
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>
Challenge
Given an nxnxn board, determine if the white king is in checkmate.
Input
- (Optional) n ≥ 2 - the size of the board
- The game board
- Can be in the form of 1d- 2d- or 3d- array, or other similar format. Notation can be in any simple format. For example, KQRBN (White) and kqrbn (Black) with # for empty cubes. Or, use numbers for the different values.
- Think of the 3D chess board as multiple boards stacked on top of each other and listed from top to bottom. Then, each individual board is notated from left to right, back to front (Black side to White side).
- Imagine this 2x2x2 case given as a 3D array:
[ [[bq][##]] [[bn][KQ]] ]
Output
- boolean (truthy/falsy value) - true if white king is in checkmate, false otherwise.
Checkmate
The white king is in check if a black piece threatens to capture it on Black's next turn. To get out of check, White needs to move his king to safety, defend it with another piece, or capture the threatening piece. If White has no way to get out of check, then the white king is in checkmate . Remember, if White is not in check, but can not move without getting into check, then it is a stalemate, which is not a checkmate.
Specification
- You won't be given a board where the black king is trying to "check" the white king, or a board where both kings are in check (impossible scenarios).
Test Cases
n=3,
[###,n##,#rr],[#b#,###,###],[###,###,bRK]
Output: true
Explanation: The king is receiving a check from the rook on the top floor. The white rook is unable to block the attack or capture the threatening rook, so the king must try to move out of the way. Let's consider the king's move options:
- c2(I) - guarded by bishop at b3(II)
- b2(I) - guarded by knight at a2(III)
- c1(II) - guarded by rook at c1(III)
- b1(II) - guarded by rook at b1(III)
- c2(II) - guarded by knight at a2(III)
- b2(II) - guarded by bishop at a1(I)
Since the king cannot escape check, it's a checkmate!
n=3,
[b#b,###,###],[###,###,RNR],[#q#,###,#K#]
Output: false Explanation: The king is receiving a check from the queen, and has no moves to escape or block with. However, the knight can capture the queen.
n=3,
[#q#,#b#,###],[n##,###,###],[#k#,###,#KB]
Output: false Explanation: White has no way of capturing the threatening queen or moving his king to safety. However, by moving his bishop to b2(II), White can block the queen's threat.
n=4,
[####,####,r###,####],[####,#q##,####,####],[##r#,###b,####,BRnn],[####,####,#N##,#KQ#]
Output: true Explanation: In this case the king is receiving a check from one of the knights and a queen. Even though White can capture/block one of the checking pieces, he can't capture/block both. Therefore, White must try to move his king out of check, but he has no options.
n=3,
[###,##b,r#r],[###,###,###],[#k#,###,#K#]
Output: false Explanation: White is not in check, but has no way of moving without getting into check. Therefore, it is a stalemate, but not a checkmate.
Output: true Explanation: White would like to swoop in with his queen to defend his king, but his knight is blocking the path.
Output: true Explanation: White can not take the queen with his knight, because then the rook will be checking White's king.
Output: false Explanation: White can capture the queen with his king.
Output: true Explanation: This time the rook is guarding, so the king can't capture the queen.
Output: false Explanation: The white king can escape by capturing the knight.
cell.className = (i + j)%2 == 0 ? "black" : "white"
be better in the snippet? \$\endgroup\$