15
\$\begingroup\$

Atomic chess is a (very fun) variant of chess in which every capture causes an "explosion," destroying the captured piece, the piece doing the capturing, and all non-pawns in a 1-square radius. The goal of this challenge is not to play an entire game of atomic chess, but simply to simulate what happens when a certain move is made.

Disclaimer: Explosion sound effects not included.

Input

Board position will be given in Forsyth-Edwards Notation (commonly known as FEN), but with only the first field. For example, an input of:

rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR

represents the starting position:

Chess starting position.

This must be taken as a string or your language's equivalent. It is guaranteed to be valid; for example, you don't have to care if there are ten kings, or if there is no king at all.

You will also be given the move that you are to simulate, which is represented as two square names: the square on which the piece to be moved is, and the square it is moving to. For example, moving the king's pawn two spaces forwards on the above picture would be represented as:

e2e4

This must also be taken as a string. The move will always be valid, and you do not need to support castling. You do need to support en passant, which will be explained in further detail in the next section.

Output

The output of your program should be in the same partial-FEN notation as the input, with the specified move made (and any pieces exploded if necessary).

The exact rules for explosions are—when a piece is captured:

  • Remove the piece being captured (this will always be the piece on the second square named in the input, except for when the capture is an en passant).

  • Remove the piece that is doing the capturing (this will always be the piece on the first square named in the input).

  • Remove every piece that is:

    • located on one of the 8 squares surrounding the one where the capture took place (for en passant, this is the square that the capturing pawn would be on, if it didn't explode).

    • not a pawn.

Quick overview of en passant rules, for those who are unfamiliar: if a pawn moves two spaces forwards from its starting rank, and there is a pawn that could have captured it if it only moved one square forward, it may capture it anyway, but only on the subsequent move. This capture is said to be done "in passing" (or in French: "en passant").

Test cases

In the pictures, the green arrows represet the move about to be made, and the green circles represents pieces that are exploded.

Input: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR, g1f3
Output: rnbqkbnr/pppppppp/8/8/8/5N2/PPPPPPPP/RNBQKB1R
Test case 1.


Input: 3kBb1r/pp5p/3p4/4pn2/P7/1P2P1pP/2rP1P2/R1B3RK, f2g3
Output: 3kBb1r/pp5p/3p4/4pn2/P7/1P2P2P/2rP4/R1B3RK
Test case 2.
(stolen from http://en.lichess.org/ocoSfS5I/white#36)


Input: rnbqk1nr/1pp5/p2pp1pp/5p2/1bN5/2P1PQ1N/PP1P1PPP/R1B1KB1R, f3b7
Output: 3qk1nr/2p5/p2pp1pp/5p2/1bN5/2P1P2N/PP1P1PPP/R1B1KB1R
Test case 3.
(stolen from http://en.lichess.org/NCUnA6LV/white#14)


Input: rnbqk2r/pp2p2p/2p3pb/3pP3/5P2/2N5/PPPP2P1/R1BQKB1R, e5d6
Output: rnbqk2r/pp2p2p/2p3pb/8/5P2/2N5/PPPP2P1/R1BQKB1R
Test case 4.
(stolen from http://en.lichess.org/AvgU4Skq/white#16; this wasn't the actual move, but I couldn't be bothered to find an atomic game that actually had en passant :P)


Input: 5r2/2k5/p1B5/1pP1p3/1P4P1/3P4/P7/1K3R1q, c6h1
Output: 5r2/2k5/p7/1pP1p3/1P4P1/3P4/P7/1K3R2
Test case 5.
(stolen from http://en.lichess.org/l77efXEb/white#58)

Scoring

This is , so the shortest code in bytes wins.

\$\endgroup\$
8
  • \$\begingroup\$ So... taking a piece with your king is a bad idea? \$\endgroup\$
    – mbomb007
    Jun 23, 2015 at 18:26
  • \$\begingroup\$ @mbomb007 You are not allowed to take pieces with your king. :P \$\endgroup\$
    – Doorknob
    Jun 23, 2015 at 21:07
  • \$\begingroup\$ I might be mistaken, but how are we supposed to know if the en passant capture is possible? In your example the last move is denoted by the empty green square, so the black pawned can be captured en passant. But this information is not contained in the inputs we are given, so it is possible to have the exact same board but where the en passant capture is not possible, because the last move was not a two spaces forward move. \$\endgroup\$
    – Fatalize
    Jun 24, 2015 at 12:50
  • 1
    \$\begingroup\$ @Fatalize "The move will always be valid" - from the "Input" section. \$\endgroup\$
    – Doorknob
    Jun 24, 2015 at 12:51
  • \$\begingroup\$ Complete program or function? \$\endgroup\$
    – edc65
    Jun 24, 2015 at 13:39

3 Answers 3

5
\$\begingroup\$

JavaScript (ES6) 305 310 321

As a function with 2 real parameters (and a lot more with default values, used as a quick and dirty way to define locals)

Test running the snippet below (being EcmaScript 6, Firefox only)

F=(g,m,b=[...g.replace(/\d/g,c=>'0'.repeat(c))],P=p=>p=='p'|p=='P',n=parseInt(m,32),
  s=n>>5&31,u=n>>15,x=b[y=u+62-(n>>10&31)*9])=>(
  b[b[y]=0,z=s+62-(n&31)*9]<1&!(s!=u&P(x))?b[z]=x:
  [-1,1,8,9,10,-8,-9,-10].map(i=>b[i+=z]>'/'&&!P(b[i])?b[i]=0:0,b[b[z]<1?z>y?z-9:z+9:z]=0),
  b.join('').replace(/0+/g,x=>x.length)
)

//TEST
out=x=>O.innerHTML+=x+'\n'

test=[
 ['rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR', 'g1f3'
  ,'rnbqkbnr/pppppppp/8/8/8/5N2/PPPPPPPP/RNBQKB1R']
,['3kBb1r/pp5p/3p4/4pn2/P7/1P2P1pP/2rP1P2/R1B3RK', 'f2g3'
  ,'3kBb1r/pp5p/3p4/4pn2/P7/1P2P2P/2rP4/R1B3RK']  
,['rnbqk1nr/1pp5/p2pp1pp/5p2/1bN5/2P1PQ1N/PP1P1PPP/R1B1KB1R', 'f3b7'
  ,'3qk1nr/2p5/p2pp1pp/5p2/1bN5/2P1P2N/PP1P1PPP/R1B1KB1R']
,['rnbqk2r/pp2p2p/2p3pb/3pP3/5P2/2N5/PPPP2P1/R1BQKB1R', 'e5d6'
  ,'rnbqk2r/pp2p2p/2p3pb/8/5P2/2N5/PPPP2P1/R1BQKB1R']
,['5r2/2k5/p1B5/1pP1p3/1P4P1/3P4/P7/1K3R1q', 'c6h1'
  ,'5r2/2k5/p7/1pP1p3/1P4P1/3P4/P7/1K3R2']
]

test.forEach(t=>( 
  r=F(t[0],t[1]), 
  out('Test '+(r==t[2]?'Ok':'Fail!')+'  '+t[0]+' move '+t[1]
     +'\nResult   '+r+'\nExpected '+t[2]+'\n')))
<pre id=O></pre>

Ungolfed

B=(b,m)=>{
  P=p=>p=='p'|p=='P'
  m=parseInt(m,32) 
  r=m&31 // arrival row
  s=(m/32&31)-10 // arrival column
  z=s+(8-r)*9 // arrival position
  t=m/32/32&31 // start row
  u=(m/32/32/32&31)-10 // start column
  y=u+(8-t)*9 // start position
  b=[...b.replace(/\d/g,c=>'0'.repeat(c))] // board to array, empty squares as 0
  x=b[y] // moving piece
  b[y]=0 
  C=s!=u&P(x) // pawn capture
  if (b[z]<1 && !C)
  {  // no capture
    b[z]=x
  }
  else
  {
    // capture and boom!
    if (b[z]<1) // arrival empty: en passant
      b[z>y?z-9:z+9]=0;
    else
      b[z]=0;
    // zero to nearest 8 squares
    [-1,1,8,9,10,-8,-9,-10].forEach(i=>b[i+=z]>'/'&&!P(b[i])?b[i]=0:0)
  }
  return b.join('').replace(/0+/g,x=>x.length)
}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Wow, that looks a lot simpler than my solution... Nice job! \$\endgroup\$
    – cmxu
    Jun 24, 2015 at 17:39
3
\$\begingroup\$

APL (Dyalog Extended), 240 bytes

e←' '@(p/⍨~(x←∊∘'Pp')(b←↑⊃¨,/¨{⍵∊⎕D:' '/⍨⍎⍵⋄⍵}¨¨'/'(≠⊆⊢)⎕)[p←p/⍨{∧/⍵∊⍳8}¨p←(⊃⌽c←{(9-⍎⊃⌽⍵),⎕A⍳⊃⍵}¨↓2 2⍴1⎕C⎕)∘+¨,∘.,⍨2-⍳3])
1↓⊃,/{'/',⊃,/{' '∊⍵:⍕≢⍵⋄⍵}¨⍵⊂⍨1,2≠/⍵}¨↓{(x⊃n)∧(≠/⊃¨⌽¨c)∧' '=⊃⌽n←b[c]:e' '@((⊃c)+0,1↓⊃-⍨/c)⊢b⋄' '∊n:(⌽n)@c⊢b⋄e' '@c⊢b}0

Try it online!

A full program which takes the FEN first, and the move second.

Explanation

First Line: Explode. Function

e←' '@(p/⍨~(x←∊∘'Pp')(b←↑⊃¨,/¨{⍵∊⎕D:' '/⍨⍎⍵⋄⍵}¨¨'/'(≠⊆⊢)⎕)[p←p/⍨{∧/⍵∊⍳8}¨p←(⊃⌽c←{(9-⍎⊃⌽⍵),⎕A⍳⊃⍵}¨↓2 2⍴1⎕C⎕)∘+¨,∘.,⍨2-⍳3])

1⎕C⎕ capitalize the first input

↓2 2⍴ cut into halves

{(9-⍎⊃⌽⍵),⎕A⍳⊃⍵}¨ apply the following to each square name: ,⎕A⍳⊃⍵ get position of first char in the alphabet(column) (9-⍎⊃⌽⍵) get 9-int(second char) (row number)

c← store that in c

⊃⌽ take the last element

∘+¨ and add that to:

,∘.,⍨2-⍳3 the cartesian product of (-1,0,1), getting the von neumann neighborhood

p← assign the explosion indices to p

p←p/⍨{∧/⍵∊⍳8}¨ remove the ones outside the board, and reassign to p

(...)[...] and index into the following:

'/'(≠⊆⊢)⎕ split the second input on slashes

⊃¨,/¨{⍵∊⎕D:' '/⍨⍎⍵⋄⍵}¨¨ convert each digit to the appropriate amount of spaces

b←↑ and convert it to a matrix, storing in b. This is the chess board.

x←∊∘'Pp' store function to check for pawns in x

p/⍨~(x←∊∘'Pp') now, remove the points which map to pawns

' '@ and insert a space at those indices in the right argument.

Second line: capture calculator

1↓⊃,/{'/',⊃,/{' '∊⍵:⍕≢⍵⋄⍵}¨⍵⊂⍨1,2≠/⍵}¨↓{(x⊃n)∧(≠/⊃¨⌽¨c)∧' '=⊃⌽n←b[c]:e' '@((⊃c)+0,1↓⊃-⍨/c)⊢b⋄' '∊n:(⌽n)@c⊢b⋄e' '@c⊢b}0

n←b[c] assign the starting and ending characters to n

' '=⊃⌽n←b[c] If ending square is empty,

(≠/⊃¨⌽¨c)∧ start column and end column are not equal,

(x⊃n)∧ and capturing piece is a pawn(en passant):

0,1↓⊃-⍨/c get the direction of capture

(⊃c)+ add that to the start index

' '@((⊃c)+0,1↓⊃-⍨/c)⊢b insert a space at that point

e and explode.

' '∊n else, if ending point is a space(no capture):

(⌽n)@c⊢b⋄ switch the places of the space and the piece

' '@c⊢b Otherwise, insert a space at the start and end index(normal capture)

e and explode.

¨↓ then, take each row,

⍵⊂⍨1,2≠/⍵ group it into equal runs

{' '∊⍵:⍕≢⍵⋄⍵}¨ replace runs of spaces with their length.

'/',⊃,/ join everything together and prepend a /

1↓⊃,/ join the rows together and drop the first /.

\$\endgroup\$
2
\$\begingroup\$

Java, (946 777 776 chars)

1 char thanks to @edc65

Note: Chars counted without test cases put in.

Code

class E{public static void main(String[]a){String i=a[0];char[]n=a[1].toCharArray();int j,k,z,w,y,u=56-n[3];char q,p,e ='e';char[][]b=new char[8][8];String[]r=i.split("/");for(j=0;j<8;j++){z=0;for(k=0;k<r[j].length();k++){p=r[j].charAt(k);if(Character.isDigit(p)){for(int l=k+z;l<k+z+p-48;l++)b[j][l]=e;z+=p-49;}else b[j][k+z]=p;}}z=n[0]-97;w=56-n[1];y=n[2]-97;p=b[w][z];q=b[u][y];b[w][z]=e;if(q!=e||((p|32)=='p'&&(w-u<0?u-w:w-u)==1&&(z-y<0?y-z:z-y)==1)){if(q!=e)b[u][y]=e;else b[w][y]=e;for(j=y-(y==0?0:1);j<y+(y==8?0:y==7?1:2);j++){for(k=u-(u==0?0:1);k<u+(u==8?0:u==7?1:2);k++)if((b[k][j]|32)!='p')b[k][j]=e;}}else b[u][y]=p;i="";for(j=0;j<8;j++){z=0;for(k=0;k<8;k++){if(b[j][k]==e)z++;else {if(z>0){i+=z;z=0;}i+=b[j][k];}}if(z>0)i+=z;i+=j!=7?"/":"";}System.out.print(i);}}

I'm not sure if this solution is optimal, but I'm working on golfing it more, any suggestions are welcome. I can comment all the code too if anyone would like, but I think it's mostly self explanatory, except for the confusing variable enumeration.

Explanation

  • Unpack the board string into a char matrix
  • Calculate the effect of the move
  • Repack the board into a string

Expanded

class ExplosionChess{
    public static void main(String[]a){
        String i=a[0];
        //"rnbqk1nr/1pp5/p2pp1pp/5p2/1bN5/2P1PQ1N/PP1P1PPP/R1B1KB1R";
        //"f3b7";
        char[]n=a[1].toCharArray();
        int j,k,z,w,y,u=56-n[3];
        char q,p,e ='e';
        char[][]b=new char[8][8];
        String[]r=i.split("/");
        for(j=0;j<8;j++){
            z=0;
            for(k=0;k<r[j].length();k++){
                p=r[j].charAt(k);
                if(Character.isDigit(p)){
                    for(int l=k+z;l<k+z+p-48;l++)
                        b[j][l]=e;
                    z+=p-49;
                }else 
                    b[j][k+z]=p;
            }
        }
        z=n[0]-97;
        w=56-n[1];
        y=n[2]-97;
        p=b[w][z];
        q=b[u][y];
        b[w][z]=e;
        if(q!=e||((p|32)=='p'&&(w-u<0?u-w:w-u)==1&&(z-y<0?y-z:z-y)==1)){
            if(q!=e)
                b[u][y]=e;
            else
                b[w][y]=e;
            for(j=y-(y==0?0:1);j<y+(y==8?0:y==7?1:2);j++){
                for(k=u-(u==0?0:1);k<u+(u==8?0:u==7?1:2);k++)
                    if((b[k][j]|32)!='p')
                        b[k][j]=e;
            }
        }else 
            b[u][y]=p;
        i="";
        for(j=0;j<8;j++){
            z=0;
            for(k=0;k<8;k++){
                if(b[j][k]==e)
                    z++;
                else {
                    if(z>0){
                        i+=z;
                        z=0;
                    }
                    i+=b[j][k];
                }
            }
            if(z>0)
                i+=z;
            i+=j!=7?"/":"";
        }
        System.out.print(i);
    }
}

Old

class E{public static void main(String[]a){String m,i="rnbqk1nr/1pp5/p2pp1pp/5p2/1bN5/2P1PQ1N/PP1P1PPP/R1B1KB1R";m="f3b7";char[]n=m.toCharArray();int z,w,y,u=56-n[3];z=n[0]-97;w=56-n[1];y=n[2]-97;char e='e';char[][]b=new char[8][8];String[]r=i.split("/");for(int j=0;j<8;j++){int o=0;for(int k=0;k<r[j].length();k++){char q=r[j].charAt(k);if(Character.isDigit(q)){for(int l=k+o;l<k+o+q-48;l++){b[j][l]=e;}o+=q-49;}else b[j][k+o]=q;}}char q,p=b[w][z];q=b[u][y];b[w][z]=e;if(q==e){if((p|32)=='p'&&(w-u<0?u-w:w-u)==1&&(z-y<0?y-z:z-y)==1){b[w][y]=e;for(int j=y-(y==0?0:1);j<y+(y==8?0:y==7?1:2);j++){for(int k=u-(u==0?0:1);k<u+(u==8?0:u==7?1:2);k++){if((b[k][j]|32)!='p')b[k][j]=e;}}}else{b[u][y]=p;}}else{b[u][y]=e;for(int j=y-(y==0?0:1);j<y+(y==8?0:y==7?1:2);j++){for(int k=u-(u==0?0:1);k<u+(u==8?0:u==7?1:2);k++){if((b[k][j]|32)!='p')b[k][j]=e;}}}i="";for(int j=0;j<8;j++){int x=0;for(int k=0;k<8;k++){if(b[j][k]==e)x++;else{if(x>0){i+=x;x=0;}i+=b[j][k];}}if(x>0)i+=x;i+=j!=7?"/":"";}System.out.println(i);}}
\$\endgroup\$
2
  • \$\begingroup\$ String m,i="";m="";char[]n=m.toCharArray() --> String i=a[0];char[]n=a[1].toCharArray() is shorter and so you get the parameters from outside (as you should anyway) \$\endgroup\$
    – edc65
    Jun 24, 2015 at 17:41
  • \$\begingroup\$ Ahh, okay I'll change that, thanks! \$\endgroup\$
    – cmxu
    Jun 24, 2015 at 17:44

Your Answer

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

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