逃离棋盘


23

您会像在棋盘上一样发现自己。您可以看到出口,但出口非常遥远,您宁愿不走一路。幸运的是,一些当地人为您提供了游览。骑士,白嘴鸦,主教和国王都愿意将您带到目的地,但看到这是一个棋盘,他们每个人都必须遵守前往目的地的棋牌规则。您想尽快离开这里,您接受谁的报价?

任务

给定任意形状和大小的棋盘,并在棋盘上指定两个点,输出可以在两个位置之间以尽可能少的移动进行移动的棋子。董事会不一定是连续的,这意味着董事会各部分之间可能存在差距。四个棋子(国王,白嘴鸦,骑士和主教)中的每一个都可以根据其在国际象棋中的标准规则移动。女王和典当兵被故意排除在这一挑战之外。

输入输出

您可以采用任何合理的格式输入,也可以以您选择的任何格式输出。您的输入和输出必须自洽。如果多个零件可以相同的移动次数到达目的地,则必须输出所有可以在最短时间内到达目的地的零件。如果这四个部分都不能奏效,则可以输出任何东西,只要它与所有其他可能的输出都不同即可。这可能包括不输出任何内容或引发错误。

测试用例

正方形表示起点,圆形表示终点。


测试1

主教


测试2

骑士


测试3

国王


测试4


测试5

国王,骑士


测试6

没有


真好 我喜欢这样,但是“形状和大小任意的棋盘”不是一个实体吗?我真的没有看到示例2和6如何适合这个,因为它们看起来像是两个分开的板,只有骑士可以在它们之间跳跃。也许我缺少什么?
ElPedro

2
@ElPedro他们仍然是一个单一的董事会,只是不是一个连续的董事会。任意形状的一部分是板可以是不连续的。
小麦巫师

例如3,不是“国王,皇后”吗?
Kritixi Lithos

谢谢@Wheat。不确定从问题中是否清楚,但我现在明白了。
ElPedro

2
@KritixiLithos为使事情有趣,没有皇后或典当。
小麦巫师

Answers:


8

哈斯克尔447个 439 437 432字节

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

呼叫使用g <board> <start> <end>,其中<board> :: [(Int, Int)]<start> :: (Int, Int)<end> :: (Int, Int)。返回一个列表,其中包含1Knight,2Rook,3Bishop和4King。如果未找到解决方案,它将持续使堆栈溢出(这算是引发错误,对吗?)。

从LYAH关于Monads的章节中汲取的灵感(%)只是笼统和打高尔夫inMany(&)等同于(Control.Monad.<=<)。其他所有内容或多或少都是不言自明的。

这可能可以打更多的球,但是我现在玩得很开心。

取消高尔夫:

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

很好地使用函数式编程!
小麦巫师

5

Mathematica,326 325字节

感谢masterX224指出节省了一个字节!

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

定义一个函数f取三个参数:g是有序对占板整数的列表,wx下令代表开始和结束的广场对。输出是{b, k, n, r}(分别代表Bishop,King,Knight和Rook)的子集,它们在两个方块之间具有最小移动路径。例如,第三个测试用例由调用f[{{0, 0}, {1, 1}, {1, 2}, {0, 3}}, {0, 0}, {0, 3}]并返回{k};最后两个测试用例分别返回{k, n}{}

该策略是将木板的正方形转换为图形的顶点,然后由内置的图形例程将图形的顶点确定为各边的移动。

代码的用户更友好版本:

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

在第3行中,Select[g~Tuples~2, # - #2 & @@ # == d找到所有差值是向量的顶点的有序对de然后将每个这样的有序对转换为无向图边。因此a,一个函数会在所有以固定向量相差的顶点之间创建边。

这足以来定义,在管路4和5,王的曲线图y@k(其取得由矢量生成边缘的并集{1, 1}{-1, 1}{0, 1},和{-1, 0})和骑士的曲线图y@n(其不具有相同的{1, 2}{2, 1}{-2, 1},和{-1, 2})。

在第7行中,ConnectedComponents@a@#获取这些邻居图之一,然后找到其连接的组件;这相当于将给定方向上的所有顶点线段分组(这样一来,车子或主教就不必一一穿过它们了)。然后,e@t@#在第6行中,将边线放在同一连接组件中的每对顶点之间,然后将其Flatten编辑为单个图形。因此,第6至8行定义了车队图y@r和主教图y@b

最后,第9到11行选择{b, k, n, r}在两个目标顶点之间产生最短路径的元素。FindShortestPath如果目标顶点未出现在图形中,将抛出错误并返回未评估的值(如果没有顶点出现,则发生),因此我们在这些情况下使用h@__ -> 0return 0来代替。有效顶点之间缺少路径会返回一个length列表0,因此可以Min[z~Complement~{0}]计算出实际存在的最小路径的长度,而无需考虑上述不良情况。


函数名称中有双倍f的原因是什么?还是数学极限?
masterX244

哦,哈哈,不,这对我来说是一个精神上的限制:)我已经f在该会话中进行过,但是我应该在提交前进行更改!
格雷格·马丁
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.