Scappa da una scacchiera


23

Ti ritrovi su una scacchiera, come si fa. Puoi vedere l'uscita ma è terribilmente lontana e preferiresti non camminare fino in fondo. Fortunatamente alcuni locali ti hanno offerto un passaggio. Un cavaliere, un corvo, un vescovo e un re sono tutti disposti a portarti a destinazione, ma vedendo come questa è una scacchiera devono rispettare le regole degli scacchi sulla strada verso la tua destinazione. Vorresti uscire di qui al più presto, di chi accetti l'offerta?

Compito

Data una scacchiera di dimensioni e forma arbitrariamente e due punti sulla scacchiera, genera il pezzo degli scacchi che può spostarsi tra le due posizioni nel minor numero di mosse possibile. Le schede non saranno necessariamente continue, il che significa che potrebbero esserci delle lacune tra le sezioni della scheda. Ognuno dei quattro pezzi (Re, Torre, Cavaliere e Vescovo) può muoversi secondo le loro regole standard negli scacchi. I pezzi della pedina e della regina sono stati intenzionalmente esclusi da questa sfida.

I / O

Puoi accettare input in qualsiasi formato ragionevole e puoi anche output in qualunque formato tu scelga. L'input e l'output devono essere coerenti. Se più pezzi riescono a raggiungere la destinazione con lo stesso numero di mosse, devi produrre tutti i pezzi che possono arrivare lì nel minor tempo possibile. Se nessuno dei quattro pezzi riesce a raggiungere la fine, è possibile produrre qualsiasi cosa purché sia ​​distinta da tutte le altre possibili uscite. Ciò potrebbe includere la mancata emissione o la generazione di un errore.

Casi test

Un quadrato indica il punto iniziale e un cerchio indica il punto finale.


Test 1

vescovo


Test 2

Cavaliere


Test 3

re


Test 4

Torre


Test 5

Re, cavaliere


Test 6

Nessuna


Bello. Mi piace, ma una "scacchiera di dimensioni e forme arbitrarie" non è una singola entità? Non vedo davvero come gli esempi 2 e 6 si adattino a questo in quanto sembra che siano due schede separate tra cui solo il cavaliere può saltare. Forse mi manca qualcosa?
ElPedro

2
@ElPedro Sono ancora una scheda singola, non è una scheda continua. Parte della forma arbitraria è che le schede possono essere non continue.
Wheat Wizard

Ad esempio 3, non dovrebbe essere "re, regina"?
Kritixi Lithos,

Grazie @Wheat. Non sono sicuro che fosse chiaro dalla domanda, ma ora capisco.
ElPedro

2
@KritixiLithos Per mantenere le cose interessanti non c'è regina o pedone.
Wheat Wizard

Risposte:


8

Haskell , 447 439 437 432 byte

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

Chiamare utilizzando g <board> <start> <end>dove <board> :: [(Int, Int)], <start> :: (Int, Int)e <end> :: (Int, Int). Restituisce un elenco contenente 1per Knight, 2Rook, 3Bishop e 4King. Se non viene trovata alcuna soluzione, trabocca costantemente lo stack (che viene considerato un errore, giusto?).

L'ispirazione presa dai capitoli di LYAH su Monads - (%)è solo una generalizzata e giocata a golf inMany, con (&)equivalenti a (Control.Monad.<=<). Tutto il resto dovrebbe essere più o meno autoesplicativo.

Probabilmente questo può essere giocato a golf molto di più, ma per ora mi sono divertito abbastanza.

Ungolfed:

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

Buon uso della programmazione funzionale!
Wheat Wizard

5

Mathematica, 326 325 byte

Grazie a masterX224 per aver sottolineato un risparmio di byte!

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}]]);

Definisce una funzione che faccetta tre argomenti: gè un elenco di coppie ordinate di numeri interi che rappresentano la scheda we xcoppie ordinate che rappresentano i quadrati iniziale e finale. L'output è il sottoinsieme di {b, k, n, r}(che rappresentano rispettivamente vescovo, re, cavaliere e torre) che hanno un percorso di mosse minime tra i due quadrati. Ad esempio, il terzo caso di test viene richiamato f[{{0, 0}, {1, 1}, {1, 2}, {0, 3}}, {0, 0}, {0, 3}]e restituisce {k}; gli ultimi due casi di test ritornano {k, n}e {}, rispettivamente.

La strategia è quella di tradurre i quadrati della scacchiera in vertici di un grafico, in cui i bordi sono determinati dalle mosse di ciascun pezzo e quindi utilizzare routine di grafici incorporate.

Versione più intuitiva del codice:

 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  );

Nella riga 3, Select[g~Tuples~2, # - #2 & @@ # == dtrova tutte le coppie ordinate di vertici la cui differenza è il vettore d; equindi trasforma ciascuna di queste coppie ordinate in un bordo grafico non orientato. Quindi aè una funzione che crea bordi tra tutti i vertici che differiscono per un vettore fisso.

Questo è sufficiente per definire, in linee 4 e 5, il grafico del re y@k(che prende l'unione dei bordi generati dai vettori {1, 1}, {-1, 1}, {0, 1}, e {-1, 0}) ed il grafico del cavaliere y@n(che fa lo stesso con {1, 2}, {2, 1}, {-2, 1}e {-1, 2}).

Nella riga 7, ConnectedComponents@a@#prende uno di questi grafici vicini e poi trova i suoi componenti collegati; questo corrisponde al raggruppamento di tutti i segmenti di linea dei vertici nella direzione data (in modo che un torre o un vescovo non debbano spostarsi uno per uno). Quindi e@t@#sulla linea 6 inserisce i bordi tra ogni coppia di vertici nello stesso componente collegato, che vengono quindi Flatteninseriti in un singolo grafico. Pertanto le linee da 6 a 8 definiscono il grafico della torre y@re il grafico del vescovo y@b.

Infine, le linee da 9 a 11 scelgono gli elementi {b, k, n, r}che producono il percorso più breve tra i due vertici target. FindShortestPathgenererà un errore e tornerà non valutato se un vertice di destinazione non appare nel grafico (cosa che succederà se non emanano spigoli da esso), quindi usiamo invece h@__ -> 0tornare 0in quei casi. E la mancanza di un percorso tra vertici validi restituisce un elenco di lunghezza 0, quindi Min[z~Complement~{0}]calcola la lunghezza del percorso più piccolo che esiste effettivamente, ignorando i casi negativi sopra.


qualche motivo per la doppia f nel nome della funzione? o è un limite di matematica?
masterX244

oh, ahah, no è un limite mentale da parte mia :) Ne avevo fgià fatto uno in quella sessione, ma avrei dovuto cambiarlo prima dell'invio!
Greg Martin,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.