建立老鼠迷宫的程序


15

您已被聘为研究助理,并被要求创建一个小程序来构建老鼠迷宫。老鼠盒始终为62x22,并具有老鼠的入口(a)和出口(A),如下所示(输入1):

#######a######################################################
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#################################################A############

您的程序必须在方框中填充块(#),为老鼠留下路径,如下所示(输出1):

#######a######################################################
####### ######################################################
####### ######################################################
####### ######################################################
####### ######################################################
#######                                           ############
################################################# ############
################################################# ############
################################################# ############
################################################# ############
################################################# ############
################################################# ############
################################################# ############
################################################# ############
################################################# ############
################################################# ############
################################################# ############
################################################# ############
################################################# ############
################################################# ############
################################################# ############
#################################################A############

您认为这很容易!您开始写一个充满信心的小程序。但是,原理科学家有了一个新主意-他希望两只老鼠同时导航迷宫。Rattanshnorter博士解释说,它们有不同的门和不同的出口(输入2):

#b#####a######################################################
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            #
#                                                            B
#                                                            #
#################################################A############

对大鼠进行了训练,使其可以直穿过交叉路口,但T型交叉路口使它们毫无希望地感到困惑,从而使实验无效。当好医生解释一个最终要求时,您就开始了新的更复杂的任务:老鼠彼此野蛮,因此,如果它们在任何时候都可以看到对方,那么老鼠之争就会爆发,你们俩都将进入道德委员会。现在,您意识到程序应该输出如下所示的迷宫(输出2):

#b#####a######################################################
# ##### ######################################################
# ##### ######################################################
# ##### #######################################           ####
# ##### ####################################### ######### ####
# #####                                           ####### ####
# ############################################# # ####### ####
# ############################################# # ####### ####
# ############################################# # ####### ####
# ############################################# # ####### ####
#                                               # ####### ####
################################################# ####### ####
################################################# ####### ####
################################################# ####### ####
################################################# ####### ####
################################################# ####### ####
################################################# ####### ####
################################################# ####### ####
################################################# ####### ####
################################################# #######    B
################################################# ############
#################################################A############

到老鼠B到达交叉路口时,老鼠A会沿着走廊走到出口A,从而避免了老鼠打架。

规则:

  • 您的程序应该读入(STDIN或文件)类似上述的输入,并输出(STDOUT或文件)相同的数据,但现在很多空格将变成哈希(#)。您可以用任何单个字符(例如;)代替\n输入字符串,但输出字符串仍需要\n字符。更新

  • 除交叉点外,大鼠路径的宽度必须为一个字符宽度(每个空间必须具有零个或两个正交相邻的#字符)。除交叉路口外,每只大鼠都必须有一条清晰的单一路径。禁止T型交叉路口。

  • 同时释放大鼠并以恒定的速度移动。任何时候都不应有两只或更多只老鼠见到对方(在同一列或同一行#中,中间没有其他字符之一)。

  • 如果无法解决(例如相邻的入口点),请打印 Impossible\n并退出。

  • 入口和出口可以在任何一侧,但是永远不会在拐角处。

  • 如果匹配的入口和出口相邻(例如:)##aA##,则鼠不能直接从a转到A。迷宫区域内必须有一个小的2空间走廊部分。

  • 在老鼠到达其出口点(或之后的任何时间)的转弯处,其他老鼠不再可见。

  • 您的程序可能旨在计算1、2,最多26只老鼠的迷宫。

  • 不允许出现标准漏洞。

得分了:

使用您的解决方案,指定您的程序可以解决的每个迷宫(N)有多少只老鼠。您的分数是您的代码长度(以字节为单位)除以该数字N。

请在您的答案中包含示例输出,以便我们查看您的程序产生了什么。


可能的输入中唯一的区别是a,A,b,B的位置吗?
xnor 2015年

对于2鼠版,是的。如果您的程序最多可容纳3只大鼠,则需要处理a,b,c,A,B,C的所有可能位置。
逻辑骑士

如果老鼠只能沿着T的水平部分行走,是否允许T交叉?
orlp 2015年

不,这些老鼠很容易混淆。仅允许直线路径,弯头和交叉路。
逻辑骑士

@CarpetPython入口/出口可以位于迷宫边缘的任何地方吗?他们可以相邻吗?
orlp 2015年

Answers:


2

Haskell,26只老鼠?,〜5000字节

从理论上讲,该代码适用于任何数量的老鼠,但我不保证它将在宇宙热死之前终止。它基于回溯算法,该算法先尝试走直线路径,然后在路径不起作用时切换路径。备选方案的数量相对于路径的长度和老鼠的数量是指数的。

因为它很大,而且我想先使其更快,所以我还没有打高尔夫球。

{-# LANGUAGE FlexibleContexts #-}
module Main (main) where

import Control.Lens
import Control.Monad
import Data.Char
import Data.Function
import Data.List
import Data.Maybe

type Pos = (Int,Int)
type Path = [Pos]
type Maze = String
type Input = [(Pos,Char)]
type MazeState = [(Path,Path)]

type ChoiceMonad a = [a]


instance (Num a, Num b) => Num (a,b) where
  (x,y)+(x',y')=(x+x',y+y')
  (x,y)-(x',y')=(x-x',y-y')
  fromInteger n = (fromInteger n,fromInteger n)


parseMaze :: Maze -> Input
parseMaze maze = maze ^@.. inner . filtered (`notElem` "# ")

inner :: IndexedTraversal' Pos Maze Char
inner = lined <.> traversed

main :: IO ()
main = do
    maze <- readFile "Sample2.in"
    putStrLn $ solveAndShow maze

fillMaze :: Maze -> Maze
fillMaze = inner.filtered(==' ').~'#'

updateMaze :: Path -> Maze -> Maze
updateMaze path = inner.indices (`elem` path).filtered(=='#') .~ ' '

isDone :: MazeState -> Bool
isDone = all (null . snd)

showMaze :: Maze -> MazeState -> Maze
showMaze maze path = updateMaze (fst =<< path) $ fillMaze maze

showSolution :: Maze -> ChoiceMonad MazeState -> String
showSolution _    []    = "Impossible"
showSolution maze (x:_) = showMaze maze x


stopCondition :: ChoiceMonad MazeState ->  Bool
stopCondition x = not $ null x || isDone (head x)

solveAndShow :: Maze -> String
solveAndShow maze = showSolution maze . solve $ mazeToState maze

solve :: ChoiceMonad MazeState -> ChoiceMonad MazeState
solve = fromJust . find (not.stopCondition) . iterate fullStep

mazeToState :: Maze -> ChoiceMonad MazeState
mazeToState maze = do
    let startsEnds = paths $ parseMaze maze
        return $ startsEnds & traverse.both %~ (:[])


fullStep :: ChoiceMonad MazeState -> ChoiceMonad MazeState
fullStep = (>>= stepAll)

stepAll :: MazeState -> ChoiceMonad MazeState
stepAll input = do
    pths <- mapM goStep input
    guard $ iall (checkVisible pths) $ map fst pths
    return $ pths
  where
    goStep :: (Path,Path) -> ChoiceMonad (Path,Path)
    goStep (curr:rest,[]) = return (curr:curr:rest,[])
    goStep (curr:these,end:ends)
       | distance curr end == 1 = return (end:curr:these,ends)

       | curr == end = goStep (curr:these,ends)
    goStep (path,end) = do
      next <- twoSteps (head end) path
      prev <- twoSteps next end
      return $ (next:path,prev:end)
    inMaze = inMazeWith input

    twoSteps :: Pos -> Path -> ChoiceMonad Pos
    twoSteps goal path = do
      next <- oneStep goal path inMaze
      guard $ not.null $ oneStep goal (next:path) (\x -> x==next || inMaze x)
      return next

checkVisible :: MazeState -> Int -> Path -> Bool
checkVisible _    _ [] = True
checkVisible pths i xs@(x:_) = checkBack && checkNow
  where
    nBack = 1 + visibleBackwards xs
    --checkBack = none (any (==x).take nBack .fst) pths
    checkBack = hasn't (folded.indices (/=i)._1.taking nBack folded.filtered (==x)) pths
    checkNow  = inone (\i' (x':_,_) -> (i/=i') && (==x') `any` take nBack xs ) pths

-- How long have you stayed on a line
visibleBackwards :: Path -> Int
visibleBackwards as = length . takeWhile ((==headDiff as) .headDiff). filter ((>=2).length) $ tails as
      where headDiff (a:a1:_) = a-a1
            headDiff x        = error $ "Bug: Too short list " ++ show x


inMazeWith :: [(Path, Path)] -> Pos -> Bool
inMazeWith = flip elem . concatMap (\x->snd x ++ fst x)

oneStep :: MonadPlus m => Pos -> Path -> (Pos -> Bool)  -> m Pos
oneStep end (curr:prev:_) inMaze =
  if distance curr end <= 1
     then return end
     else do
    let distance' :: Pos -> Double
        distance' x = fromIntegral (distance x end) + if curr - prev == x - curr then 0 else 0.4
    next <- msum . map return $ sortBy (compare`on`distance') $ neighbors curr

    -- Don't go back
    guard $ next /= prev

    -- Stay in bounds
    guard $ isInBounds next

    let dir = (next - curr)
    let lr = neighbors next \\ [curr,next+dir,end]

    -- If next is blocked, check that the one after that is free
    if inMaze next
      then do
        guard $ not . (\x->(x/=end)&&inMaze x) $ next + dir
        -- Both sides should be blocked as well
        guard $ (==2). length . filter inMaze $ lr
      else do
        -- No neighbors if empty
        guard $ null . filter inMaze $ lr

    -- All neighbors of 'curr', including 'next'
    let neigh' = filter (\i -> inMaze i || i == next) $ neighbors curr
        -- should be an even number
        guard $ even $ length neigh'

    return next
oneStep _ [start] _ = return $ inBounds start
oneStep _ _ _ = error "Too short path given"


toBounds :: (Num a, Eq a) => (a,a) -> a -> a
toBounds (low, high) x
    | x == low  = x + 1
    | x == high = x - 1
    | otherwise = x

distance :: Pos -> Pos -> Int
distance (x1,y1) (x2,y2) = abs(x1-x2)+abs(y1-y2)

-- Moves a pos to the closest one inside the bounds
inBounds :: Pos -> Pos
inBounds = bimap (toBounds (0,21)) (toBounds (0,61))

isInBounds :: Pos -> Bool
isInBounds x = x == inBounds x

neighbors :: Pos -> [Pos]
neighbors pos = [ pos & l %~ p| l <- [_1,_2], p <- [succ,pred]]

paths :: Input -> [(Pos,Pos)]
paths pos = flip unfoldr 'a' $ \x ->
  do (y,_) <- find ((==x).snd) pos
     (z,_) <- find ((==toUpper x).snd) pos
     return ((y,z),succ x)

样品输出,6只大鼠:

##c###B#####b#######C#######F######################f##########
##   #       #       #######                        ##########
####  ######## ###############################################
#####          ###############################################
##############################################################
##############################################################
##############################################################
##############################################################
##############################################################
##############################################################
##############################################################
##############################################################
##############################################################
##############################################################
#############       ##########################################
############# #####  #########################################
D             #    #     #####################################
##############  ## ##### #####################################
#########      #                 #############################
######### ###### # ##### ####### #############################
####      #      #     # #######                        ######
####E######a##########e#d##############################A######

2
b到达e和时b,他不被看见e吗?b似乎到达的位置t = 11,该位置仍会e停留在该走廊中。我想念什么吗?
BrainSteel 2015年

@BrainSteel是的,这是正确的。我的答案无效。我以前对自己说过,我还需要“及时向后”检查碰撞(在穿过其他老鼠的路径之​​后),但是由于某种原因,我决定不需要它。:P
Hjulle

@BrainSteel我相信我已经修复了该错误。
Hjulle

1

Haskell,1只老鼠,681个字符

只需一只老鼠,所有迷宫都可以轻松解决该问题。该代码也可以对任何数量的老鼠“起作用”,但不遵循对只老鼠和路径之间相互作用的任何约束。

module Main where
import Control.Lens
import Data.List(unfoldr,find)
import Data.Char(toUpper)
parse=(^@..lined<.>folded.filtered(`notElem`"# "))
main=interact$do i<-(naive=<<).rats.parse;(lined<.>traversed).filtered(==' ').indices (`notElem`i).~'#'
    naive(start,(ex,ey))=start':unfoldr go start' where
     start'=bnds start
     (ex',ey')=bnds(ex,ey)
     go(x,y)
      |(x,y)==(ex',ey')=Nothing
      |x== ex'=ok(x,y`t`ey')
      |otherwise=ok(x`t`ex',y)
     ok z=Just(z,z)
     t x y=if x>y then x-1 else x+1
    bnd(l,h)x |x==l=x+1 |x==h=x-1 |True=x
    bnds=bimap(bnd(0,21))(bnd(0,61))
    rats pos=(`unfoldr`'a')$ \x->
  do (y,_)<-find((==x).snd)pos
     (z,_)<-find((==toUpper x).snd)pos
     return((y,z),succ x)

样本输出:

#######a######################################################
####### ######################################################
####### ######################################################
####### ######################################################
####### ######################################################
####### ######################################################
####### ######################################################
####### ######################################################
####### ######################################################
####### ######################################################
####### ######################################################
####### ######################################################
####### ######################################################
####### ######################################################
####### ######################################################
####### ######################################################
####### ######################################################
####### ######################################################
####### ######################################################
####### ######################################################
#######                                           ############
#################################################A############

我打算支持很多老鼠,所以我写了通用代码,但是我还没有找到一个好的算法。

  • parse 提取所有出入口及其坐标的列表
  • rats 获取该列表并将其转换为每只大鼠的坐标对。
  • bnds 在边缘上获取坐标,并将其移动到迷宫内最近的坐标。
  • naive 取得开始和结束位置,并返回它们之间的简单路径。
  • main 然后用“#”替换不在路径中的所有空白

@ edc65“ ... 只老鼠之间的约束”。这仅是一只老鼠的答案,根据问题允许。
Hjulle

好的,我的错。只是认为对于一只老鼠来说,这是一个不同的挑战。我将删除以前的评论
edc65
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.