10
\$\begingroup\$

Challenge:
Given a 2D array (8x8) of characters, is the enemy king in check?
The program should return true if and only if the king is currently in check.
Standard chess rules apply.

Specifications:
The game is provided using a 2D array,
where blank space means there is not a piece on that square.
You may assume that the only characters that will appear
in the array are one of: [SPACE]kpnbrqKPNBRQ

The 2D array is given as a y-major array
that starts from the bottom left (a1 square),
which means accessing the array would be: board[row][column].
For example: board[0][0] is a1, and board[3][5] is f4

Character set:
King - K
Pawn - P
Knight - N
Bishop - B
Rook - R
Queen - Q

Your own pieces are in uppercase, and the enemy pieces are in lowercase.
Also, enemy pieces only serve the purpose of blocking a check, so they are essentially the same as pawns.

Test case:
Game 1
In this game, the given array would be:

r bqk  r
p pp Bpp
 pn  n  
    p  Q
 b PP   
  P     
PP   PPP
RNB K NR

and should return true. Note that even if the bishop on f7 wasn't protected, it should still return true.

Happy golfing!

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Related: Is it checkmate?. \$\endgroup\$
    – Jonah
    Commented Feb 24 at 15:08
  • \$\begingroup\$ Why this input instead of FEN? \$\endgroup\$
    – qwr
    Commented Feb 24 at 22:45
  • 1
    \$\begingroup\$ @qwr I think this input needs a lot less explanation (and possibly less code) than Forsyth Edwards Notation, which is generally known only by chess fans. \$\endgroup\$ Commented Feb 25 at 1:45
  • \$\begingroup\$ Because the board orientation matters for pawn moves, I think you should clarify how the black and white pieces are arranged, rather than just mentioning your own pieces and enemy pieces. \$\endgroup\$
    – Arnauld
    Commented Feb 28 at 11:45
  • \$\begingroup\$ @Arnauld It is specified that the a1 square is the first entry of the array. I thought it would've implied the orientation of the board. \$\endgroup\$
    – Mickey
    Commented Feb 28 at 13:04

5 Answers 5

4
\$\begingroup\$

Charcoal, 95 bytes

≔⪫EθS¶η¿⊙⌕AηN⁼⁵ΣX⟦⁻÷ι⁹÷⌕ηk⁹⁻﹪ι⁹﹪⌕ηk⁹⟧²-«Pη…η⌕ηk≔E⁸KD⁹✳ιζ⎚∨⊙✂ζ⁵χ²⁼§ι¹P⊙ζ№⊞O⟦Q⟧§RBκ§Φι⊙…ι⊕μ∧π⁻ ν¹

Try it online! Link is to verbose version of code. Outputs a Charcoal boolean, i.e. - for in check, nothing if not. Explanation:

≔⪫EθS¶η

Input the board and join it on newlines.

¿⊙⌕AηN⁼⁵ΣX⟦⁻÷ι⁹÷⌕ηk⁹⁻﹪ι⁹﹪⌕ηk⁹⟧²-«

If there is a knight a knight's move away from the king, then output a -, otherwise:

Pη…η⌕ηk≔E⁸KD⁹✳ιζ⎚

Temporarily write the board to the canvas so that all of the ranks, files and diagonals to the king can be extracted.

∨⊙✂ζ⁵χ²⁼§ι¹P⊙ζ№⊞O⟦Q⟧§RBκ§Φι⊙…ι⊕μ∧π⁻ ν¹

Check for pawns that are checking the king and check the first piece in every direction for a piece that can check the king from that direction.

\$\endgroup\$
4
\$\begingroup\$

JavaScript (ES6), 188 bytes

Expects a matrix of characters and returns a Boolean value.

f=(m,P,X,Y)=>m.some((r,y)=>r.some((p,x)=>P?{N:(H=x-X,H*=H)+(v=y-Y)*v-5,B:B=H-v*v|(g=A=>u=(c=m[A+=-(A>Y)|A<Y][x+=-(x>X)|x<X])!=P&&c>g|g(A))(y),R:u|=H*=v,Q:B*u,P:H-1}[p]+P=='0k':f(m,p,x,y)))

Try it online!

Position editor

f=(m,P,X,Y)=>m.some((r,y)=>r.some((p,x)=>P?{N:(H=x-X,H*=H)+(v=y-Y)*v-5,B:B=H-v*v|(g=A=>u=(c=m[A+=-(A>Y)|A<Y][x+=-(x>X)|x<X])!=P&&c>g|g(A))(y),R:u|=H*=v,Q:B*u,P:H-1}[p]+P=='0k':f(m,p,x,y)))
let board = document.getElementById("board"), pce = document.getElementById("pce"), b = [...Array(8)].map(_ => Array(8).fill(-1)), sel = 5; b[1][2] = 6; b[2][4] = 4; setClick(board, (x, y) => { b[y][x] = b[y][x] == sel ? -1 : sel; draw(); }); setClick(pce, (x, y) => { sel = x * 6 + 5 - y; draw(); }); draw(); function setClick(el, callback) { el.addEventListener("click", e => { let o = el.getBoundingClientRect(); callback(e.clientX - o.left >> 5, e.clientY - o.top >> 5); }) } function draw() { let html = "", m = []; for(let y = 0; y < 8; y++) for(let x = 0; x < 8; x++) { html += `<div class="sq ${x + y & 1 ? "dark" : "light"}">${~b[y][x] ? `&#${9812 + b[y][x]};` : ""}</div>`; (m[y] = m[y] || [])[x] = " KQRBNPkqrbnp"[b[y][x] + 1]; } document.getElementById("res").innerHTML = f(m) ? "YES" : "NO"; board.innerHTML = html; html = ""; for(let y = 0; y < 6; y++) for(let x = 0; x < 2; x++) html += `<div class="sq ${x * 6 + 5 - y == sel ? "sel" : x + y & 1 ? "dark" : "light"}">&#${9817 + x * 6 - y};</div>`; pce.innerHTML = html; }
body { font-family:Arial; } #board { float:left; width:256px; height:256px; } #pce { float:left; margin-left:32px; width:64px; height:192px; } .sq { float:left; width:32px; height:32px; line-height:32px; text-align:center; font-size:30px; cursor:pointer; } .dark { background-color:#7ab; } .light { background-color:#9cd; } .sel { background-color:#fd4; }
<p>Black king in check: <span id="res"></span></p><div id="board"></div><div id="pce"></div>

Commented

Main function

f = (               // f is a recursive function taking:
  m,                //   m[] = input matrix
  P,                //   P = piece on the target square
  X, Y              //   (X, Y) = position of the target square
) =>                //
m.some((r, y) =>    // for each row r[] at index y in m[]:
  r.some((p, x) =>  //   for each piece p at index x in r[]:
    P ?             //     if P is defined (2nd pass):
      {             //
        N: ...,     //       \    test whether the target square is
        B: ...,     //        |   attacked by the source square (x, y)
        R: ...,     //        |-- according to the piece p
        Q: ...,     //        |   these tests (which are detailed below)
        P: ...      //       /    return 0 for truthy
      }[p]          //
      + P           //       append P to the result
      == '0k'       //       if we get '0k', the enemy king is attacked
    :               //     else (1st pass):
      f(m, p, x, y) //       do a recursive call, using the current
                    //       square as the target square
  )                 //   end of inner some()
)                   // end of outer some()

Knight test

(                   // define:
  H = x - X,        //   H = (x - X)²
  H *= H            //
) + (               // and:
  v = y - Y         //   v = y - Y
) * v               //
- 5                 // test whether H + v² = 5, which may only happen
                    // if we have H = 4, v² = 1 or H = 1, v² = 4

Bishop test

B =                 // save the result in B
H - v * v |         // we must have H = v²
( g = A =>          // g is a recursive function
  u =               // which sets a flag u if the path from the source
  (                 // square to the target square is blocked:
    c =             //   c is the value of the square that is reached
    m[              //   when we move towards the target square
      A += -(A > Y) //   by adding the sign of Y - A to A
           | A < Y  //
    ][              //   
      x += -(x > X) //   and adding the sign of X - x to x
           | x < X  //
    ]               //
  ) != P &&         //   stop when c = P
  c > g |           //   set u if c is greater than or equal to 'B'
                    //   (i.e. the square is not empty)
  g(A)              //   recursive call
)(y)                // initial call to g with A = y

Rook test

u |= H *= v         // we must have u = 0 and either H = 0 or v = 0

Queen test

B * u               // queen = bishop * rook

Pawn test

H - 1               // we must have (x - X)² = 1 and y - Y = 1
                    // and H is now (x - X)² * (y - Y)

King test

Assuming that the position is valid, a king may not be attacked by the other king. Therefore, there is no test at all for king attacks.

\$\endgroup\$
3
\$\begingroup\$

Ruby, 192 170 167 166 bytes

->s{z=!k=/k/=~s+=$/*9
72.times{|i|i%8<1?(C=D=i/8%3+i/24*9-10;J=k):" "!=W&&D=0
z||="RQBP"[~C%2,C/8>i%16?3:2]=~/#{W=s[J+=D]}/||s[i]*((i%9-k%9)*(i/9-k/9))**2==?N*4}
!!z}

Try it online!

Function taking a newline separated string as an argument. See meta for discussion of equivalence of strings and arrays, and use of sentinel values as separators/terminators. A final newline is required (It would cost 1 byte $/*9->$/*10 to remove this requirement.) The two !! at the end ensure a clean true/false value is returned. Deleting them would save 2 bytes but return only a truthy/falsy output.

Explanation

Take a 72-character argument consisting of 8 lines each of 9 characters including the final newline. Add 9 newlines $/to the end. The newlines will act as non-space characters and prevent scanning from circling round to the other side of the board. Assign k to the index of the king by regex /k/. Assign false=!k to z. Iterate 72 times (scan up to 8 squares from the king in 9 possible directions. Simultaneously scan all 72 characters for knights.)

->s{z=!k=/k/=~s+=$/*9
72.times{|i|

If i%8==0 set J to the location of the king and select a new direction C=D to scan in. Offsets for horizontal scanning are i/8%3-1=[-1,0,1] Offsets for vertical scanning are i/8/3*9-9=[-9,0,9]. Combining these gives diagonal scanning +/-10 and +/-8. 0+0=0 is also generated as a ninth possible offset but makes no difference to the result. If i%8>0 check if the last character scanned W was a space, and if not set D=0 to prevent further scanning in that direction.

i%8<1?(C=D=i/8%3+i/24*9-10;J=k):" "!=W&&D=0

Check the character at s[J+=D] advancing J in direction D. Depending on the value of C select the values of character that would threaten the king. Where C is odd +/-1,+/-9 they start at index 0: RQ. Where C is even +/-8,+/-10 they start at index 1: QBP. Pawns should only be checked for on the first step in a diagonal direction i%16=0 and where the y direction is positive C/8>0.

z||="RQBP"[~C%2,C/8>i%16?3:2]=~/#{W=s[J+=D]}/

Check also for knights, which threaten the king on squares s[i] where (i%9-k%9)*(i/9-k/9) is +/-2. Squaring this gives 4. We take the character at s[i] and multiply by the numerical value giving a string of identical characters, then check if equal to the string ?N*4="NNNN". This is fewer bytes than separate string and numerical checks.

||s[i]*((i%9-k%9)*(i/9-k/9))**2==?N*4

Close the iteration loop and clean up the final value z from truthy/falsy to true/false by inverting twice.

}
!!z}
\$\endgroup\$
1
\$\begingroup\$

Python 3, 258 bytes

I thought I'd try my own challenge cuz why not. It's not terrible but it could be better. I'll try to post an explanation when I have time.

Takes a 2D array (just like the challenge description).

f=lambda b:any(['PN'[(p:=z-60)>0]in(g:=f"{(' '*16).join(map(''.join,b)):^576}")[(k:=g.find('k'))-p]+(p>0)*g[k+p]for z in b'%#mkVR']+[((t:=g[k-8*n:k+8*n:n])[9+(i<0)*i:8+(i>0)*i].split()==[])*(t[i+8]in'RBQQ'[n<0::2])for n in[1,24,-23,-25]for i in range(-8,8)])
\$\endgroup\$
1
\$\begingroup\$

Perl 5 -MList::Util=pairmap, 316 286 bytes

sub{map{map{($r,$c)=($t,$_)if($l=$_[$t][$_])eq'k';push@n,$t,$_ if'N'eq$l}0..7;++$t}@_;map{$v.=$_[$u=$r+$_][$c];$x.=$c+$_>=0&&$_[$u][$c+$_];$y.=$c-$_>=0&&$_[$u][$c-$_];}-$r..7-$r;(pairgrep{abs(($r-$a)*($c-$b))==2}@n)|(grep/[QR] *k|k *[QR]/,$v,"@{$_[$r]}")|grep/kP|[QB] *k|k *[QB]/,$x,$y}

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.