三次元チェス


26

誰かの不可解な決定を守るために、人々はしばしば、その人は誰の頭をも越えて「3次元チェス」をしていると言います。さあ、3次元のチェスをするチャンスです!

ルール

3Dチェスには多くのバリエーションがありますが、この課題のために私は自分で作成しました。私のバージョンは通常のチェスと同じですが、ピースが正方形ではなく立方体の中にあり、動きの次元が増えている点が異なります。この挑戦を簡単にするために、ポーンキャスティングがありません

ピースムーブメント

(コンパスの方向は、標準のチェス盤で発生する動きを指し、上下は3Dチェス盤で垂直に動くことを指します)。

  • キング -与えられたターンに行くことができる26の正方形を持っています:N、NE、E、SE、S、SW、W、NW。上、下、上/下+コンパス方向の1つ。
  • 女王 -王と同じ方向に移動できますが、彼女が望む方向に移動できます。
  • ルーク -6方向に移動できます:N、E、S、W、上、下、
  • ビショップ -8つの三角形の移動方向:NE +上/下、SE +上/下、SW +上/下、NW +上/下
  • ナイト -2つのスペースを1つの軸に移動し、次に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(黒)。または、異なる値に数字を使用します。
    • 3Dチェスボードは、複数のボードが積み重ねられ、上から下にリストされていると考えてください。次に、個々のボードは左から右へ、後ろから前へ(黒側から白側へ)表記されます。
    • 3D配列として与えられたこの2x2x2の場合を想像してください:
 [
[[bq] [##]]
[[bn] [KQ]]
]

「トップ」ボード:ここに画像の説明を入力してください「ボトム」ボード:ここに画像の説明を入力してください

出力

  • ブール値(真偽値)-ホワイトキングがチェックメイトにいる場合はtrue、そうでない場合はfalse。

チェックメイト

白の王は、黒の駒が黒の次のターンでそれを捕らえると脅すどうかチェックしています。チェックから抜け出すために、ホワイトは彼の王を安全に移動するか、別のピースでそれを守るか、脅迫的なピースを捕獲する必要があります。ホワイトにチェックアウトする方法がない場合、ホワイトキングはチェックメイトになります。白がチェックされていないが、チェックされずに移動できない場合は、チェックメイトではない膠着状態であることに注意してください。

仕様

  • 黒の王が白の王を「チェック」しようとしているボードや、両方の王がチェックされているボード(不可能なシナリオ)は与えられません。

テストケース

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

    ここに画像の説明を入力してください(III)ここに画像の説明を入力してください(II)ここに画像の説明を入力してください(I)

    出力:true

    説明:王は最上階のルークから小切手を受け取っています。白いルークは攻撃をブロックしたり、脅迫的なルークを捕まえたりすることはできないため、王は邪魔にならないようにしなければなりません。王の移動オプションを考えてみましょう。

    1. c2(I)-b3(II)の司教によって保護されています
    2. b2(I)-a2(III)でナイトによってガード
    3. c1(II)-c1(III)のルークによって保護
    4. b1(II)-b1(III)のルークによってガード
    5. c2(II)-a2(III)でナイトによってガード
    6. b2(II)-a1(I)で司教によって守られている

王は小切手を逃れることができないので、チェックメイトです!

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

    ここに画像の説明を入力してください(III)ここに画像の説明を入力してください(II)ここに画像の説明を入力してください(I)

    出力:false説明:王は女王から小切手を受け取っており、脱出またはブロックする動きはありません。ただし、騎士は女王を捕まえることができます。

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

    ここに画像の説明を入力してください(III)ここに画像の説明を入力してください(II)ここに画像の説明を入力してください(I)

出力:false説明:白には、脅迫の女王を捕まえたり、王を安全な場所に移動させる方法がありません。ただし、司教をb2(II)に移動することで、ホワイトは女王の脅威をブロックできます。

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

    ここに画像の説明を入力してください(IV)ここに画像の説明を入力してください(III)ここに画像の説明を入力してください(II)ここに画像の説明を入力してください(I)

    出力:true説明:この場合、王は騎士の1人と女王から小切手を受け取ります。Whiteはチェックピースの1つをキャプチャ/ブロックできますが、両方をキャプチャ/ブロックすることはできません。したがって、ホワイトは王をチェックから外そうとする必要がありますが、選択肢はありません。

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

    ここに画像の説明を入力してください(III)ここに画像の説明を入力してください(II)ここに画像の説明を入力してください(I)

出力:false説明:白はチェックされていませんが、チェックされずに移動する方法はありません。したがって、それは行き詰まりですが、チェックメイトではありません。

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

    ここに画像の説明を入力してください(III)ここに画像の説明を入力してください(II)ここに画像の説明を入力してください(I)

出力:true説明:白は王を守るために女王と一緒に急降下しようとしますが、彼の騎士は道を塞いでいます。

  1. n = 3、 [###,###,##q],[###,###,###],[#k#,###,rNK]

    ここに画像の説明を入力してください(III)ここに画像の説明を入力してください(II)ここに画像の説明を入力してください(I)

出力:true説明:ルークがホワイトのキングをチェックするため、ホワイトはナイトでクイーンを連れて行くことができません。

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

    ここに画像の説明を入力してください(II)ここに画像の説明を入力してください(I)

出力:false説明:白は王と女王を捕らえることができます。

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

    ここに画像の説明を入力してください(II)ここに画像の説明を入力してください(I)

出力:true説明:今回はルークがガードしているため、王は女王を捕まえられません。

  1. n = 3、 [###,###,#q#],[###,###,###],[#k#,###,BKn]

    ここに画像の説明を入力してください(III)ここに画像の説明を入力してください(II)ここに画像の説明を入力してください(I)

出力:false説明:白い王は騎士を捕まえることで脱出できます。


細部だけcell.className = (i + j)%2 == 0 ? "black" : "white"ですが、スニペットでは良くないでしょうか?
アーナウルド

@Arnauld笑、最も明白なものを修正するのを忘れていました。
-geokavel

サポートする必要がある最大のボードサイズは何ですか?
ウェイジュン周

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}

オンラインでお試しください!すべてのテストケースでチェックするようになりました。ケース5(膠着状態のケース)のバグを修正するためにコードが1バイト増加しました。

以下に示す形式の文字列としての入力を必要とするLlambda関数。オプションの2番目のパラメーターを指定して、次の移動で32個のASCIIコードのグループを考慮することを指定できます(デフォルトでは、この2は大文字/白文字に対応しますが、関数は小文字/黒文字に対応する3を使用して再帰的に呼び出します。 )

再帰レベル1:白(キューブからキューブへ)のすべての可能な動きを試行し、すべての有効な動きをステップスルーします。再帰レベル2:それぞれの場合、それはそれ自体を呼び出して、黒のすべての可能な動きをステップスルーします。これは、白のキングがすべての可能な黒の動きを生き延びた場合にtrueを返します。再帰レベル1:すべての可能な白の動きが、白のキングがすべての可能な黒の動きに耐えられない状況につながる場合、true(そうでない場合はfalse)を返します。

一般的に、駒は友好的な駒で占められた正方形に移動できません。白がまったく動かない場合(したがって、チェックメイトが膠着状態ではない場合)を考慮するために、王がすでにいる広場に「移動する」場合も許可されます。短いコードの理由から、他の白い部分も白い王がいる正方形に移動することが許可されています。これはナンセンスな動きですが、許可しても結果には影響しないため、問題はありません。

次のテストは、動きが各ピースに対して有効かどうかを確認するために使用されます。 x,y,z各軸で移動した距離の二乗です。eはこれらの合計であり(したがってユークリッド距離の2乗)d、最大値です。ピースタイプは、小文字のASCII値を大文字に変換するために95とANDされます。

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}

1桁のASCII値を使用してこれをゴルフできませんでしたか?また、3番目の段落で「チェックメイトではなく膠着状態」を意味しましたか?
ジオカベル

@geokavel Rubyでの単一のASCII値の最短表現は ?A(コードに例があります)、したがって2バイトのままです。を必要とするいくつかの言語よりも優れています"A"。文字よりもASCII値のほうがうまくいく特定の操作がありました(特にo^k>31、ピースが空いている正方形または友好的なピースではなく、敵対的な正方形に移動できることを保証します)
Level River St

つまり、チェックメイトは膠着状態ではありません。膠着状態は、プレイヤーが動いた場合に王が脅かされる状況です。チェックメイトとは、プレイヤーが動けばキングも脅かされ、そうでなければキン​​グが脅かされる状況です。
レベルリバーセント

ASCII値の代わりにint値(つまり、文字列ではなくintの配列)を使用した場合はどうなりますか?
ジオカベル

@geokavel intsはおそらく短くなるでしょう。仕様で明確に許可されているので、後で修正するかもしれません。しかし、選択したフォーマットを採用した理由の1つは、人間が読みやすいため(開発が容易)、また1つは、私の考えに大きな影響を与えたこの答えに触発されたためです:codegolf.stackexchange.com/a/45544/15599
Levelリバーセント
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.