¿Es esta cadena válida FEN?

12

El reto

Escriba un programa o función que tome una entrada de cadena como parámetro de función o desde stdin y determine si es una cadena FEN válida .

Entrada

Puede suponer que la entrada solo incluirá los siguientes caracteres (mayúsculas y minúsculas)
pkqrbnPKQRBN12345678/
La longitud de la entrada siempre será un mínimo de 1 carácter y un máximo de 100 caracteres.

Salida

La salida debe ser un valor verdadero / falso. Estos pueden ser los valores que desee siempre que sean consistentes (todos los resultados verdaderos tienen la misma salida, todos los resultados falsey tienen la misma salida). Debe tener exactamente dos salidas posibles distintas.

Lo que cuenta como válido

Las letras minúsculas representan piezas negras, las letras mayúsculas representan piezas blancas.
Debes asegurarte de que es posible en un juego de ajedrez que existan las piezas en la posición actual.
Cada jugador siempre tendrá exactamente 1 rey (k / K)
Cada jugador puede tener no más de 8 peones (p / P)
Cada jugador generalmente no tendrá más de 1 * reina (q / Q)
Cada jugador generalmente no tendrá más de 2 * torres (r / R)
Cada jugador generalmente no tendrá más de 2 * caballeros (n / N)
Cada jugador generalmente no tendrá más de 2 * obispos (b / B)
* Es legal que un jugador ' promueve 'un peón a cualquiera de estas cuatro piezas.
El total de peones, reinas, torres, caballeros y obispos para cada jugador nunca será más de 15

El número total de piezas más los cuadrados vacíos (indicados por números) siempre debe sumar exactamente 8 para cada rango. Y siempre debe haber exactamente 8 rangos, separados por una barra diagonal.

Cosas que puedes ignorar

No necesita preocuparse por si es posible o no jugar en la posición indicada, o si la posición es legal, solo si las piezas pueden existir en las cantidades indicadas.
Puede ignorar otras complejidades de las cadenas FEN, como el turno de jugador, los derechos de enroque y al pasar.

Este es el código de golf. El programa más corto en bytes gana. Se aplican las lagunas y reglas habituales.

Casos de prueba

Entrada rnbqkbnr / pppppppp / 8/8/8/8 / PPPPPPPP / RNBQKBNR
Salida Verdadero

Entrada 2br2k1 / 1p2n1q1 / p2p2p1 / P1bP1pNp / 1BP2PnP / 1Q1B2P1 / 8 / 3NR2K
Salida Verdadero

Entrada r2r2k1 / p3q2p / ppR3pr / rP4bp / 3p4 / 5B1P / P4PP1 / 3Q1RK1
Salida False
(el negro tiene 7 peones y 4 torres - imposible)

Entrada 6k1 / pp3ppp / 4p3 / 2P3b1 / bPP3P1 / 3K4 / P3Q1q1
Salida False (solo 7 rangos)

Entrada 3r1rk1 / 1pp1bpp1 / 6p1 / pP1npqPn / 8 / 4N2P / P2PP3 / 1B2BP2 / R2QK2R
Salida False (9 rangos)

Entrada 5n1k / 1p3r1qp / p3p3 / 2p1N2Q / 2P1R3 / 2P5 / P2r1PP1 / 4R1K1
Salida False (2do rango tiene 9 cuadrados / piezas)

Entrada rnbqkbnr / pppppppp / 8/35/8/8 / PPPPPPPP / RNBQKBNR
Salida Verdadero
Gracias a Feersum y Arnauld por aclarar este caso (3 + 5 = 8)

¿Qué es el FEN?

FEN es una notación estándar para registrar la posición de las piezas en un tablero de ajedrez. Crédito de imagen http://www.chessgames.comingrese la descripción de la imagen aquí

Darren H
fuente
"Cada jugador generalmente no tendrá más de 1 * reina", aclare lo que cuenta como válido, ya que supongo que no importa lo que cuenta como "habitual". ¿Es válido que las blancas tengan nueve reinas? ¿Diez reinas? ¿Ocho peones y dos reinas? ¿Cero reyes? ¿Un peón no promocionado en el primer o último rango?
Anders Kaseorg 01 de
@AndersKaseorg * It is legal for a player to 'promote' a pawn to any of these four pieces.El jugador puede tener hasta 9 reinas siempre que se reduzca el número de peones para compensar. No necesita preocuparse de que la posición de las piezas sea legal o ilegal, solo la cantidad de piezas.
Darren H
1
En su tercer caso de prueba, el negro tiene 6 peones, no 7, por lo que es un 'Verdadero' (?)
pizzapants184
1
@DarrenH La posición de FEN propuesta por feersum es válida de acuerdo con sus reglas actuales. 35es solo una forma inusual de describir 8 cuadrados vacíos.
Arnauld
1
Los peones de @PatrickRoberts en primer o último rango son válidos para este desafío. No es necesario tener en cuenta la legalidad de una posición, solo la cantidad de piezas. Tener en cuenta la legalidad de una posición (como que ambos jugadores estén bajo control) agrega mucha complejidad, así que pensé que un manto de 'posición no importa' es más claro que un debate sobre dónde trazar la línea de lo que las necesidades explicaron. y que no.
Darren H

Respuestas:

5

Retina , 105 bytes

[1-8]
$*
^
/
iG`^(/[1KQRBNP]{8}){8}$
G`K
G`k
A`K.*K|k.*k
{2`N

2`B

2`R

1`Q

K

T`L`P
8`P

A`P
}T`l`L
^.

Pruébalo en línea! El enlace incluye casos de prueba. Explicación:

[1-8]
$*

Expande los dígitos a cuadrados vacíos, que denotamos usando 1s.

^
/
iG`^(/[1KQRBNP]{8}){8}$

Elimine la entrada si no coincide con 8 conjuntos de 8 cuadrados válidos unidos con /s. (Se agrega un /prefijo adicional para simplificar la verificación).

G`K
G`k
A`K.*K|k.*k

Elimine la entrada si no tiene un rey blanco o negro, o si tiene dos de cualquiera.

{2`N

2`B

2`R

1`Q

K

Elimina las piezas iniciales de las blancas, si todavía están allí.

T`L`P

Degradar las piezas blancas restantes a los peones.

8`P

Eliminar los peones blancos válidos.

A`P

Elimine la entrada si queda algún peón blanco.

}T`l`L

Verifique nuevamente pero con las piezas negras.

^.

Salida de un valor verdadero a menos que se elimine la línea.

Neil
fuente
6

JavaScript (ES6), 168 174 ... 155

Esta respuesta ha sido editada una cantidad embarazosa de veces. Con suerte, la versión actual es confiable y de golf decente.


Devuelve un booleano.

s=>[...s].map(c=>++n%9?+c?n+=--c:a[i='pP/KkQqRrBbNn'.search(c),i&=i>4&a[i]>(i>6)||i]=-~a[i]:x+=c=='/',a=[x=n=0])&&!([p,P,s,k,K]=a,n-71|x-7|s|k*K-1|p>8|P>8)

Formateado y comentado

s => [...s].map(c =>                  // for each character 'c' in the FEN string 's':
  ++n % 9 ?                           //   if we haven't reached the end of a rank:
    +c ?                              //     if the character is a digit:
      n += --c                        //       advance the board pointer by c - 1 squares
    :                                 //     else:
      a[                              //       update the piece counter array:
        i =                           //         i = piece identifier (0 to 12)
          'pP/KkQqRrBbNn'.search(c),  //             with special case: '/' --> 2
        i &=                          //         we count it as a promoted pawn instead if:
          i > 4 &                     //           it's a Q, R, B or N and we already have
          a[i] > (i > 6) ||           //           2 of them for R, B, N or just 1 for Q
          i                           //           else, we keep the identifier unchanged
      ] = -~a[i]                      //         '-~' allows to increment 'undefined'
  :                                   //   else:
    x += c == '/',                    //     check that the expected '/' is there
  a = [                               //   initialize the piece counter array 'a'
    x =                               //   initialize the '/' counter 'x',
    n = 0 ]                           //   initialize the board pointer 'n'
) &&                                  // end of map()
!(                                    // now it's time to perform all sanity checks:
  [p, P, s, K, k] = a,                //   copy the 5 first entries of 'a' to new variables
  n - 71 |                            //   have we reached exactly the end of the board?
  x - 7 |                             //   have we identified exactly 7 ends of rank?
  s |                                 //   have we encountered any unexpected '/' character?
  k * K - 1 |                         //   do we have exactly one king on each side?
  p > 8 |                             //   no more than 8 black pawns, including promotions?
  P > 8)                              //   no more than 8 white pawns, including promotions?

Casos de prueba

Arnauld
fuente
3

Python 3, 284 259 236 225 247 234 bytes

import re
s=input()
t,c=s.split("/"),s.count;P=p=9;o=0
for x in"pqrnb":p-=max(0,c(x)-o);P-=max(0,c(x.upper())-o);o+=o<2
v=8==len(t)and all(8==sum(int(x)for x in re.sub("[A-z]","1",p))for p in t)and p>0<P and c('k')==c('K')==1
print(v)

Pruébalo en línea!

¡Pruébelo en línea con todos los casos de prueba!

-11 bytes gracias al Sr. Xcoder

-13 bytes gracias a Jonathan Allen

+22 Olvidé que los reyes existían.

Semi-no golfista con alguna explicación:

import re
string = input()
split = string.split("/")
count = string.count # find # of occurences of char in string
pawns = 9 # represents the # of pawns a player has out of the game... plus one, e.g. 1 is all in game, 2 is one out, 0 is invalid
PAWNS = 9 # would be 8, but then I would need >= instead of >
offset = 0 # default for pawns
for char in "pqrnb": # for each pawn, each queen over 1, and each rook/knight/bishop over 2 for each player
    # subtract one from the players 'pawns' var, which must end up 1 or greater to be valid
    # otherwise too many pawns/queens/etc of that player are on the board
    pawns -= max(0,count(char)-offset)
    PAWNS -= max(0,count(char.upper())-offset)
    offset += (offset 0 and PAWNS>0 and \ # make sure each player does not have an invalid number of pawns/q/n/b/r
    count('k')==count('K')==1 # correct # of kings
print(valid)
pizzapants184
fuente
1
234 bytes . Reemplacé ,p,P=9,9con ;P=p=9.
Sr. Xcoder
1
230 bytes . ¿Por qué tenía espacios innecesarios en for-loop: /
Mr. Xcoder
1
225 bytes : también puede usar en p>0<Plugar de p>0and P>0guardar 5 bytes. Alternativamente, podría haber usado p and P(para -3 bytes), no necesita el >0, porque los valores distintos de cero son verdaderos en Python
Mr. Xcoder
1
Los peones se pueden actualizar, la especificación dice que hay 7 peones en minúsculas y 4 torres, mientras que mis ojos solo ven 6 'p' en minúsculas.
pizzapants184
1
Puede guardar 13 bytes inicializando con o=0antes del ciclo e incrementando con o+=o<2al final del cuerpo del ciclo.
Jonathan Allan
2

PHP , 269 bytes

$t=($o=count_chars($a="$argn/"))[47]==8&$o[107]==1&$o[75]==1&9>($w=$u=$o[80])&9>$b=$l=$o[112];foreach([81,82,78,66]as$k=>$v){$z=$k?11:10;$b+=$x=$o[32+$v];$t&=$l+$x<$z;$w+=$x=$o[$v];$t&=$u+$x<$z;}$t&=$b<16&$w<16;for(;$c=$a[$n++];)$c<A?$c>0?$s+=$c:$t&=!$s-=8:++$s;echo$t;

Pruébalo en línea!

Jörg Hülsermann
fuente
2

JavaScript (ES6), 181 172 174 bytes

f=([c,...s],n=1,o={p:0,P:0})=>c?c=='/'&&n%9?0:f(s,n+(+c||1),(o[c]=(o[c]||0)+(/[qrbn]/i.test(c)&&o[c]>1-/q/i.test(c)?!o[c>'a'?'p':'P']++:1),o)):o.p<9&o.P<9&n==72&o.k==1&o.K==1

Sin golf:

f=
  ([c,...s],                 //c is current character
   n=1,                      //n is current square, range [1-72] (board is 9x8 due to slashes)
   o={p:0,P:0}               //o holds piece counts
  )=>
  c?
    c=='/'&&n%9?0:           //ensure 8 squares per row
    f(s,
      n+(+c||1),             //increment n by the correct number of squares
      (o[c]=(o[c]||0)+(/[qrbn]/i.test(c)&&o[c]>1-/q/i.test(c)?!o[c>'a'?'p':'P']++:1),o)
                             //"depromote" extra queens, rooks, bishops, or knights
     ):
  o.p<9&o.P<9&               //no more than 8 pawns per side (accounting for promotions)
  o.k==1&o.K==1&             //each side has one and only one king  
  n==72                      //correct number of squares

Rick Hitchcock
fuente
1

Python 3 , 263 bytes

s=input()
n=0
for a in s.split('/'):n+=sum([int(c)if c in"123456789"else 1for c in a])
m=lambda k:{c:s.count(c)for c in s}.get(k,0)
p=[m("p"),m("P")]
for c in"rnbqRNGQ":b=c in"qQ";p[c<"Z"]+=m(c)+b-2if m(c)>2-b else 0
print((n==64)&(p[0]<9>p[1])&(m("K")>0<m("k")))

Pruébalo en línea!

No es la presentación más pequeña de Python, pero creo que todavía tiene alguna promesa.

MooseOnTheRocks
fuente