Escapar de un tablero de ajedrez

23

Te encuentras en un tablero de ajedrez, como uno lo hace. Puedes ver la salida pero está muy lejos y prefieres no caminar todo el camino. Afortunadamente, algunos lugareños te han ofrecido un aventón. Un Caballero, una Torre, un Obispo y un Rey están dispuestos a llevarte a tu destino, pero al ver que este es un tablero de ajedrez, cada uno debe cumplir con las reglas del ajedrez en el camino a tu destino. Te gustaría salir de aquí lo antes posible, ¿qué oferta aceptas?

Tarea

Dado un tablero de ajedrez de tamaño y forma arbitraria y dos puntos en el tablero de ajedrez, saca la pieza de ajedrez que puede moverse entre las dos ubicaciones en el menor número de movimientos posible. Los tableros no serán necesariamente continuos, lo que significa que podría haber brechas entre las secciones del tablero. Cada una de las cuatro piezas (Rey, Torre, Caballero y Obispo) puede moverse de acuerdo con sus reglas estándar en ajedrez. La Reina y las piezas de peón se han dejado intencionalmente fuera de este desafío.

I / O

Puede tomar la entrada en cualquier formato razonable y también puede generar la salida en el formato que elija. Su entrada y salida debe ser autoconsistente. Si varias piezas pueden llegar al destino en el mismo número de movimientos, debe generar todas las piezas que pueden llegar allí en el mínimo tiempo posible. Si ninguna de las cuatro piezas puede llegar al final, puede generar algo siempre que sea distinto de todas las demás salidas posibles. Esto podría incluir no generar nada o arrojar un error.

Casos de prueba

Un cuadrado indica el punto de partida y un círculo indica el punto final.


Prueba 1

obispo


Prueba 2

Caballero


Prueba 3

Rey


Prueba 4

Torre


Prueba 5

Rey, caballero


Prueba 6

Ninguna

Asistente de trigo
fuente
Agradable. Me gusta esto, pero ¿no es un "tablero de ajedrez de forma y tamaño arbitrario" una sola entidad? Realmente no veo cómo los ejemplos 2 y 6 encajan con esto, ya que parece que son dos tableros separados entre los que solo el caballero puede saltar. Tal vez me estoy perdiendo algo?
ElPedro
2
@ElPedro Todavía son un solo tablero, simplemente no es un tablero continuo. Parte de la forma arbitraria es que los tableros pueden ser no continuos.
Wheat Wizard
Por ejemplo 3, ¿no debería ser "rey, reina"?
Kritixi Lithos
Gracias @ trigo. No estoy seguro de que eso quede claro a partir de la pregunta, pero ahora entiendo.
ElPedro
2
@ KritixiLithos Para mantener las cosas interesantes, no hay reina ni peón.
Wheat Wizard

Respuestas:

8

Haskell , 447 439 437 432 bytes

t=[1,2]
p=[(+),(-)]
f=filter
a=abs
(#)=elem
x?y=[min x y..max x y]
f&g=(\x->g x>>=f)
(b!1)(c,r)=f(#b)[(c!d,r#s)|(!)<-p,(#)<-p,d<-t,s<-t,d/=s]
(b!2)(c,r)=f(\(d,s)->(c==d&&all(#b)[(c,z)|z<-r?s])||r==s&&all(#b)[(z,r)|z<-c?d])b
(b!3)(c,r)=f(\(d,s)->a(c-d)==a(r-s)&&all(#b)(zip(c?d)$r?s))b
(b!4)(c,r)=f(\(d,s)->a(c-d)<2&&a(r-s)<2)b
(b%p)n s=[s]>>=foldr(&)(:[])(replicate n$b!p)
g b s e=head$f(not.null)[[p|p<-[1..4],e#(b%p)n s]|n<-[0..]]

Llamar usando g <board> <start> <end>donde <board> :: [(Int, Int)], <start> :: (Int, Int)y <end> :: (Int, Int). Devuelve una lista que contiene 1Knight, 2Rook, 3Bishop y 4King. Si no se encuentran soluciones, desborda constantemente la pila (eso cuenta como arrojar un error, ¿verdad?).

La inspiración tomada de los capítulos de LYAH sobre Mónadas (%)es simplemente generalizada y de golf inMany, con (&)equivalente a (Control.Monad.<=<). Todo lo demás debería ser más o menos autoexplicativo.

Probablemente se pueda jugar mucho más al golf, pero por ahora me he divertido lo suficiente.

Sin golf:

data Piece = Knight | Rook | Bishop | King deriving (Show)
type Place = (Int, Int)
type Board = [Place]

x `to` y = [min x y..max x y]

f <=< g = (\x -> g x >>= f)

moves
    :: Board    -- available spaces
    -> Piece    -- type of piece
    -> Place    -- starting place
    -> [Place]  -- places available in one move
moves b Knight (c,r) =
    filter (`elem` b) [(c!d, r#s) |
                       (!) <- [(+),(-)], (#) <- [(+),(-)],
                       d <- [1,2], s <- [1,2], d/=s]
moves b Rook   (c,r) =
    filter (\(d,s) -> (c == d && all (`elem` b) [(c,z) | z <- r `to` s])
                    || r == s && all (`elem` b) [(z,r) | z <- c `to` d]) b
moves b Bishop (c,r) =
    filter (\(d,s) -> abs(c-d) == abs(r-s)
                && all (`elem` b) (zip (c `to` d) (r `to` s))) b
moves b King   (c,r) =
    filter (\(d,s) -> abs(c-d) <= 1 && abs(r-s) <= 1) b

canGoIn
    :: Board    -- available spaces
    -> Piece    -- type of piece
    -> Int      -- number of moves
    -> Place    -- starting place
    -> [Place]  -- places available in the specified number of moves
canGoIn b p n s =
    return s >>= foldr (<=<) return (replicate n (moves b p))

quickestPieces
    :: Board    -- available spaces
    -> Place    -- starting place
    -> Place    -- ending place
    -> [Piece]  -- quickest pieces
quickestPieces b s e =
        head $ filter (not.null)
            [[p | p <- [Knight, Rook, Bishop, King], elem e (canGoIn b p n s)] |
                n<-[0..]]
Julian Wolf
fuente
Buen uso de la programación funcional!
Wheat Wizard
5

Mathematica, 326 325 bytes

¡Gracias a masterX224 por señalar un ahorro de bytes!

f[g_,w_,x_]:=(c={{1,1},{-1,1}};s=c.c/2;e=#<->#2&@@@#&;j=Join;h=FindShortestPath;t=#~Tuples~2&;a@d_:=e@Select[t@g,#-#2&@@#==d&];y@k=j@@(a/@j[s,c]);y@n=j@@(a/@{{1,2},{2,1},{-2,1},{-1,2}});v=Flatten[e@t@#&/@ConnectedComponents@a@#&/@#]&;y@r=v@s;y@b=v@c;Pick[p={b,k,n,r},z=Length[h[y@#,w,x]/.h@__->0]&/@p,Min[z~Complement~{0}]]);

Define una función fde tomar tres argumentos: ges una lista de pares ordenados de números enteros que representan el tablero, y wy xlos pares ordenados que representan los cuadrados de inicio y fin. La salida es el subconjunto de {b, k, n, r}(que representa obispo, rey, caballero y torre, respectivamente) que tienen una trayectoria de movimientos mínimos entre los dos cuadrados. Por ejemplo, el tercer caso de prueba es invocado por f[{{0, 0}, {1, 1}, {1, 2}, {0, 3}}, {0, 0}, {0, 3}]y devuelve {k}; los dos últimos casos de prueba regresan {k, n}y {}, respectivamente.

La estrategia es traducir los cuadrados del tablero en vértices de un gráfico, donde los bordes están determinados por los movimientos de cada pieza, y luego usar rutinas de gráficos incorporados.

Versión más fácil de usar del código:

 1  f[g_, w_, x_] := ( c = {{1, 1}, {-1, 1}}; s = c.c/2;
 2    e = # <-> #2 & @@@ # &; j = Join; h = FindShortestPath; t = #~Tuples~2 &; 
 3    a@d_ := e@Select[t@g, # - #2 & @@ # == d &]; 
 4    y@k = j @@ (a /@ j[s, c]); 
 5    y@n = j @@ (a /@ {{1, 2}, {2, 1}, {-2, 1}, {-1, 2}}); 
 6    v = Flatten[e@t@# & /@
 7         ConnectedComponents@a@# & /@ #] &;
 8    y@r = v@s; y@b = v@c; 
 9    Pick[p = {b, k, n, r}, 
10      z = Length[h[y@#, w, x] /. h@__ -> 0] & /@ p, 
11      Min[z~Complement~{0}]]
12  );

En la línea 3, Select[g~Tuples~2, # - #2 & @@ # == dencuentra todos los pares de vértices ordenados cuya diferencia es el vector d; eluego transforma cada par ordenado en un borde de gráfico no dirigido. Entonces, aes una función que crea bordes entre todos los vértices que difieren en un vector fijo.

Eso es suficiente para definir, en las líneas 4 y 5, el gráfico del rey y@k(que toma la unión de bordes generados por los vectores {1, 1}, {-1, 1}, {0, 1}, y {-1, 0}) y gráfico del caballero y@n(que hace lo mismo con {1, 2}, {2, 1}, {-2, 1}, y {-1, 2}).

En la línea 7, ConnectedComponents@a@#toma uno de estos gráficos vecinos y luego encuentra sus componentes conectados; esto corresponde a agrupar todos los segmentos de línea de vértices en la dirección dada (para que una torre u alfil no tenga que moverse a través de ellos uno por uno). Luego, e@t@#en la línea 6 coloca bordes entre cada par de vértices en el mismo componente conectado, que luego se Flatteneditan en un solo gráfico. Así, las líneas 6 a 8 definen la gráfica de la torre y la gráfica y@rdel alfil y@b.

Finalmente, las líneas 9 a 11 eligen los elementos {b, k, n, r}que producen el camino más corto entre los dos vértices objetivo. FindShortestPatharrojará un error y volverá sin evaluar si un vértice objetivo no aparece en el gráfico (lo que sucederá si no emanan bordes), por lo h@__ -> 0que 0en su lugar usamos para regresar en esos casos. Y la falta de una ruta entre vértices válidos devuelve una lista de longitud 0, por lo que Min[z~Complement~{0}]calcula la longitud de la ruta más pequeña que realmente existe, ignorando los casos negativos anteriores.

Greg Martin
fuente
alguna razón para doble f en el nombre de la función? o es un límite matemático?
masterX244
oh, jaja, no, es un límite mental de mi parte :) Ya tuve una fen esa sesión, ¡pero debería haberla cambiado antes de enviarla!
Greg Martin