Capture on the Pawn Chessboard

17

1

You should write a program or function which receives a string representing a chessboard with only pawns as input and outputs or returns whether any capture is possible on the board.

Input is in a FEN-like notation describing positions of white and black pawns with no other pieces present. You should decide if there is a pawn which can capture an enemy one.

Each rank is described, starting with rank 8 and ending with rank 1; within each rank, the contents of each square are described from file "a" through file "h". Each pawn is identified by a single letter (white pawn = "P", black pawn = "p", ). Empty squares are noted using digits 1 through 8 (the number of empty squares), and "/" separates ranks. (partially taken from Wikipedia)

For example

8/pppppppp/8/8/4P3/8/PPPP1PPP/8

describes the board

--------

pppppppp


    P   

PPPP PPP

--------

A white pawn can capture a black one if the black one is positioned diagonally up from it (black is up-left or up-right) and a black pawn can capture a white one if the white one is diagonally below from it (white is down-left or down-right). No other capture move (en passant) should be considered.

Input

  • A FEN-like string consisting of the characters 12345678pP/.
  • The input describes the pawns of a valid chess game position. This means (among other more complex constraints) there will be at most 8 pawns for each side and no pawns on ranks 1 and 8.

Output

  • If there is a possible capture for either side you should output a truthy value and a falsy value otherwise.

Examples

Inputs with truthy output (one per line)

8/7p/6P1/8/8/8/8/8
8/8/p7/1P6/3P3p/8/8/8
8/2P5/8/4P1p1/2p2P2/3p4/3p1P2/8
8/P7/8/5P2/2pp4/3P2p1/3pP3/8
8/P7/p7/p1P1P3/1P3p2/8/1p6/8
8/4p1P1/2P2P1P/2p1pPpp/8/6P1/pP1p4/8

Inputs with falsy output (one per line)

8/8/8/8/8/8/8/8
8/7P/6p1/8/8/8/8/8
8/7p/7P/8/8/8/8/8
8/pppppppp/8/8/8/8/PPPPPPPP/8
8/p7/8/1p6/5P2/8/8/8
8/p7/P7/2P1p1p1/2p5/8/PP6/8

This is code golf so the shortest entry wins.

randomra

Posted 2015-09-30T12:59:35.637

Reputation: 19 909

Shouldn't the example board be described by 8/pppppppp/8/8/8/7P/PPPP1PPP/8 ? – TheNumberOne – 2015-09-30T14:24:53.503

@TheNumberOne No, 7P would mean the pawn is on the last, 8th file. (The diagram was incorrect though, I corrected that.) – randomra – 2015-09-30T14:47:04.660

1I feel removing en passant makes this a less interesting puzzle. – corsiKa – 2015-09-30T21:03:18.730

Answers

6

Pyth, 25 bytes

/smC,>JsXz`M9*LN9dJ,8T"Pp

Test suite

Steps:

Transform the input by replacing the digits with the equivalent number of quote marks (N). This is saved in J. We then slice off the first 8 or 10 characters, and zip the result with the original. Any capturing pair will be transformed into "Pp", so we then find the count of that string in the resultant list. This is the output.

As a bonus, this actually counts the number of captures possible in the input.

isaacg

Posted 2015-09-30T12:59:35.637

Reputation: 39 268

Another 25 solution: :sXz`M9*LN9"p.{7}(..)?P"1 Sadly the last parameter of : is not optional (I think it should be). – Jakube – 2015-09-30T18:16:53.830

3@Jakube Will do. – isaacg – 2015-09-30T18:49:37.377

12

Retina, 33 29 bytes

T`d`w
)`\d
$0.
_

p.{7}(..)?P

To run the code from a single file, use the -s flag.

Should be easily beatable by something like Perl where the expansion of digits into strings of spaces (or other characters) doesn't take up 17 bytes.

The output is positive (truthy) if there is a possible capture and zero (falsy) if there isn't.

Explanation

T`d`w
)`\d
$0.

This is a loop of two stages. The first one is a transliteration stage which decrements each digit, and turns zeroes to underscores. Why? Because d and w expand to the following two lines:

0123456789
_0123456789AB...YZab...yz

If the target set of a transliteration stage is longer than the source set, the extraneous characters are ignored, hence the decrementing behaviour (honestly, it was just luck that I decided to put the underscore in front of the digits when expanding the w character class).

Then the second stage is a replacement, which appends a . to each digit. That means that for each digit n, n periods are added before that digit is turned into an underscore.

_
<empty>

This just gets rid of the underscores.

p.{7}(..)?P

Finally, we find the matches. Since we're ignoring en passant, captures are only possible if there is a p and then a P diagonally below it. In the linear string, this simply means that there must be 7 or 9 characters between the two pawns. This matched with .{7}(..)? (i.e. match 7 characters and then, optionally, match another two).

Such a match stage returns the number of matches it found.

Martin Ender

Posted 2015-09-30T12:59:35.637

Reputation: 184 808

Re "Should be easily beatable by something like Perl where the expansion of digits into strings of spaces (or other characters) doesn't take up 17 bytes.": I can't get Perl to even tie your score, let alone beat it. (My Perl answer.) But maybe someone else can....

– msh210 – 2016-06-22T23:06:32.273

3

Javascript, 272 characters

function h(t){b=[[]];for(i=-1;i++<7;){c=0;b.push(l=[]);for(j=-1;j++<7;){o=t.split('/')[i][j];switch(o){case'P':l[c++]=-1;break;case'p':l[c++]=1;break;default:c+=parseInt(o);}}}b.push([]);for(i=1;i<9;i++)for(j=0;j<8;j++)if((p=b[i][j])&&(b[i+p][j-1]||b[i+p][j+1]))return 1;}

There's probably a lot of room for improvement.

Najkin

Posted 2015-09-30T12:59:35.637

Reputation: 171

3

Ruby, 145 123 46 bytes

->b{b.gsub(/\d/){|x|?.*x.to_i}=~/p.{7}(..)?P/}

I don't know why I didn't think about this in the first place. It's much shorter and also pretty readable.

Here's the test: http://ideone.com/Gzav8N


The old approach:

->b{l={}
r=p
b.split(?/).map{|s|c={}
i=0
s.chars.map{|x|n=x.to_i;c[i]=x;i+=n<1?1:n;x==?P&&r||=l[i-2]==?p||l[i]==?p}
l=c}
r}

Online test: http://ideone.com/9L01lf, version before golfing: http://ideone.com/CSmqlW

A history of modifications is available here.

Cristian Lupascu

Posted 2015-09-30T12:59:35.637

Reputation: 8 369

2

ES6, 64 bytes

An appropriate byte count, if it lasts!

f=s=>/p.{7}(..)?P/.test(s.replace(/\d/g,n=>"        ".slice(-n)))

I actually thought of this solution without reading the other answers first, but I won't mind if you don't believe me.

Neil

Posted 2015-09-30T12:59:35.637

Reputation: 95 035

0

Perl 5, 30 bytes

29, plus 1 for -pe instead of -e

s/\d/1x$&/ge;$_=/p.{7}(..)?P/

A Perl copy of w0lf's Ruby answer.

msh210

Posted 2015-09-30T12:59:35.637

Reputation: 3 094

0

PHP, 94 87 80 bytes

for(;$i++<8;)$t[$i]=$s.=" ";echo preg_match("#p.{7}(..)?P#",strtr($argv[1],$t));

That loop+strtr is a lot shorter than preg_replace_callback with str_pad.

Titus

Posted 2015-09-30T12:59:35.637

Reputation: 13 814

0

Jelly, 88 84 79 72 69 65 64 63 60 bytes

Definitely room for improvement. Noncompeting because Jelly was created before the question. Thanks to @lirtosiast for telling me that!

ØDṖḊ
”_x
e1£¬
1£iЀ2Ŀ€
x"3Ŀ€;"ÇFṣ”/
w€⁾pPn0
5ĿUŒDÇ
5ĿŒD6ĿoÇS

Zacharý

Posted 2015-09-30T12:59:35.637

Reputation: 5 710