Essa string é válida FEN?

12

O desafio

Escreva um programa ou função que utilize uma entrada de string como parâmetro de função ou de stdin e determine se é uma string FEN válida .

Entrada

Você pode assumir que a entrada incluirá apenas os seguintes caracteres (diferencia maiúsculas de minúsculas).
pkqrbnPKQRBN12345678/
O comprimento da entrada sempre será no mínimo 1 caractere e no máximo 100 caracteres

Resultado

A saída deve ser um valor de verdade / falsey. Podem ser quaisquer valores que você desejar, desde que sejam consistentes (todos os resultados verdadeiros têm a mesma saída, todos os resultados falsey têm a mesma saída). Você deve ter exatamente duas saídas possíveis distintas.

O que conta como válido

Letras minúsculas representam peças pretas, letras maiúsculas representam peças brancas.
Você deve garantir que, em um jogo de xadrez, é possível que as peças na posição atual existam.
Cada jogador sempre terá exatamente 1 rei (k / K).
Cada jogador pode ter no máximo 8 peões (p / P).
Cada jogador normalmente não terá mais que 1 * rainha (q / Q).
Cada jogador normalmente não terá mais de 2 * rooks (r / R)
Cada jogador normalmente não terá mais que 2 * cavaleiros (n / N)
Cada jogador normalmente não terá mais que 2 * bispos (b / B)
* É legal para um jogador ' promova 'um peão para qualquer uma dessas quatro peças.
O total de peões, rainhas, gralhas, cavaleiros e bispos para cada jogador nunca será superior a 15

O número total de peças mais quadrados vazios (indicados por números) sempre deve somar exatamente 8 para cada classificação. E sempre deve haver exatamente 8 fileiras, separadas por uma barra.

Coisas que você pode ignorar

Você não precisa se preocupar se é possível ou não participar da posição indicada ou se a posição é legal, apenas que as peças podem existir nas quantidades indicadas.
Você pode ignorar outras complexidades das seqüências de caracteres FEN, como turno do jogador, direitos de castling e passantes.

Isso é código de golfe. O programa mais curto em bytes vence. Aplicam-se brechas e regras usuais.

Casos de teste

Entrada rnbqkbnr / pppppppp / 8/8/8/8 / PPPPPPPP / RNBQKBNR
Saída True

Entrada 2br2k1 / 1p2n1q1 / p2p2p1 / P1bP1pNp / 1BP2PnP / 1Q1B2P1 / 8 / 3NR2K
Saída True

Entrada r2r2k1 / p3q2p / ppR3pr / rP4bp / 3p4 / 5B1P / P4PP1 / 3Q1RK1
Saída falsa
(o preto tem 7 peões e 4 torres - impossível)

Entrada 6k1 / pp3ppp / 4p3 / 2P3b1 / bPP3P1 / 3K4 / P3Q1q1
Saída falsa (apenas 7 classificações)

Entrada 3r1rk1 / 1pp1bpp1 / 6p1 / pP1npqPn / 8 / 4N2P / P2PP3 / 1B2BP2 / R2QK2R
Saída falsa (9 classificações)

Entrada 5n1k / 1p3r1qp / p3p3 / 2p1N2Q / 2P1R3 / 2P5 / P2r1PP1 / 4R1K1
Saída falsa (a segunda classificação tem 9 quadrados / peças)

Entrada rnbqkbnr / pppppppp / 8/35/8/8 / PPPPPPPP / RNBQKBNR
Saída Verdadeiro
Agradecimentos a Feersum e Arnauld por esclarecer este caso (3 + 5 = 8)

O que é FEN?

FEN é uma notação padrão para registrar a posição das peças em um tabuleiro de xadrez. Crédito de imagem http://www.chessgames.cominsira a descrição da imagem aqui

Darren H
fonte
“Cada jogador geralmente não terá mais que 1 * rainha” - esclareça o que conta como válido, pois presumo que não importa o que conta como “usual”. É válido que o branco tenha nove rainhas? Dez rainhas? Oito peões e duas rainhas? Zero reis? Um peão não promovido na primeira ou na última fila?
Anders Kaseorg 01/07
@AndersKaseorg * It is legal for a player to 'promote' a pawn to any of these four pieces.O jogador pode ter até 9 rainhas, desde que o número de peões seja reduzido para compensar. Você não precisa se preocupar com a posição das peças como legal ou ilegal, apenas com o número de peças.
Darren H
1
Em seu teste de caso de terceiro, preto tem 6 peões, e não 7, tornando-se um 'True' (?)
pizzapants184
1
@DarrenH A posição FEN proposta por feersum é válida de acordo com as regras atuais. 35é apenas uma maneira incomum de descrever 8 quadrados vazios.
Arnauld
1
Os peões da @PatrickRoberts na primeira ou na última classificação são válidos para o objetivo deste desafio. Você não precisa levar em consideração a legalidade de uma posição, apenas a quantidade de peças. Contabilizar a legalidade de uma posição (como ambos os jogadores estão sob controle) acrescenta muita complexidade, então eu achei que um cobertor de 'posição não importa' é mais claro do que um debate sobre onde traçar a linha do que é necessário e o que não.
Darren H

Respostas:

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
^.

Experimente online! O link inclui casos de teste. Explicação:

[1-8]
$*

Expanda dígitos para quadrados vazios, que denotamos usando 1s.

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

Exclua a entrada se ela não corresponder a 8 conjuntos de 8 quadrados válidos unidos a /s. (Um extra /é prefixado para simplificar a verificação.)

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

Exclua a entrada se não houver rei branco ou preto ou se houver dois.

{2`N

2`B

2`R

1`Q

K

Exclua as peças iniciais das brancas, se elas ainda estiverem lá.

T`L`P

Rebaixe as peças brancas restantes em peões.

8`P

Exclua os peões brancos válidos.

A`P

Exclua a entrada se houver algum peão branco sobrando.

}T`l`L

Verifique novamente, mas com as peças pretas.

^.

Emita um valor verdadeiro, a menos que a linha tenha sido excluída.

Neil
fonte
6

JavaScript (ES6), 168 174 ... 155

Esta resposta foi editada um número embaraçoso de vezes. Felizmente, a versão atual é confiável e decentemente jogada.


Retorna um 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)

Formatado e 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 teste

Arnauld
fonte
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)

Experimente Online!

Experimente on-line com todos os casos de teste!

-11 bytes graças ao Sr. Xcoder

-13 bytes graças a Jonathan Allen

+22 Eu esqueci que os reis existiam.

Semi-ungolfed com alguma explicação:

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
fonte
1
234 bytes . Eu substituí ,p,P=9,9por ;P=p=9.
Mr. Xcoder
1
230 bytes . Por que você tem espaços desnecessários no for-loop: /
Sr. Xcoder
1
225 bytes : você pode usar em p>0<Pvez de p>0and P>0salvar também 5 bytes. Alternativamente, você poderia ter usado p and P(para -3 bytes), você não precisa o >0, porque diferentes de zero valores são truthy em Python
Mr. Xcoder
1
Os peões podem ser atualizados, a especificação diz que existem 7 peões em minúsculas e 4 torres, enquanto meus olhos veem apenas 6 'p' minúsculos.
precisa saber é o seguinte
1
Você pode salvar 13 bytes, inicializando com o=0antes do loop e incrementando com o+=o<2no final do corpo do loop.
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;

Experimente online!

Jörg Hülsermann
fonte
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

Ungolfed:

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
fonte
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")))

Experimente online!

Não é o menor envio de Python, mas acho que ainda tem alguma promessa.

MooseOnTheRocks
fonte