ラットの迷路を構築するプログラム


15

あなたは研究助手として雇われ、ラットの迷路を構築する小さなプログラムを作成するように依頼されました。ラットボックスは常に62x22で、次のように、ラットの入り口(a)と出口(A)があります(入力1)。

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

次のように、プログラムはボックスをブロック(#)で満たし、ラット用のパスを残さなければなりません(出力1)。

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

これは簡単だと思います!あなたは自信に満ちた小さなプログラムを書き始めます。しかし、原理科学者は新しいアイデアを持っていました-彼は2匹のネズミが同時に迷路をナビゲートすることを望んでいます。Rattanshnorter博士は、ドアと出口が異なることを説明しています(入力2)。

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

ラットは交差点を直進するように訓練されていますが、T交差点ではラットが混乱し、実験が無効になります。良い医者が最後の要件を説明すると、より複雑な新しいタスクを開始します:ラットはお互いに野であるため、ラットがいつでも会うとラットの戦いが勃発し、両方が倫理委員会の前にいます。これで、プログラムが次のような迷路を出力するはずであることがわかります(出力2)。

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

ラットBが交差点に到達するまでに、ラットAは廊下を下りてAを出て、ラットの戦いは回避されます。

ルール:

  • プログラムは、上記のような入力を(STDINまたはファイル)入力し、多くのスペースがハッシュ(#)になることを除いて、同じデータを出力(STDOUTまたはファイル)する必要があります。入力文字列の;代わりに任意の単一文字(など)を使用でき\nますが、出力文字列には文字が必要\nです。更新しました

  • ラットの経路は、交差点を除き、1文字幅でなければなりません(すべてのスペースには、直交する隣接する#文字が0個または2個必要です)。交差点を除き、各ラットには明確な単一の経路が必要です。T交差点は許可されていません。

  • ラットは同時に解放され、一定の速度で動きます。決して2匹以上のラットがお互いを見ることはありません(#間に1つ以上の文字がなくても同じ列または行にいる)。

  • 解決策がない場合(隣接する入口点など)、印刷 Impossible\nして終了します。

  • 入口と出口はどの側面にあってもかまいませんが、角には決してありません。

  • 一致する入口と出口が隣接している場合(例##aA##:)、ラットはからaに直接移動できませんA。迷路エリア内に2つの小さな廊下セクションが必要です。

  • ラットが出口ポイントに到達するターン(またはその後)は、他のラットには見えなくなります。

  • プログラムは、1、2、最大26匹のラットの迷路を計算するように設計されている場合があります。

  • 標準の抜け穴は許可されていません。

スコア:

ソリューションを使用して、プログラムが解決できる迷路(N)あたりのラット数を指定します。スコアは、バイト単位のコード長をこの数値Nで割ったものです。

あなたのプログラムが生成するものを見ることができるように、回答にサンプル出力を含めてください。


可能な入力の唯一の違いは、a、A、b、Bの位置ですか?
xnor

2ラットバージョンの場合、はい。プログラムが最大3匹のラット用に設計されている場合、a、b、c、A、B、Cのすべての可能な場所に対処する必要があります。
ロジックナイト

ラットがTの水平部分に沿ってのみ歩く場合、T交差は許可されますか?
orlp

いいえ、これらのラットは簡単に混同されます。まっすぐな道、肘の曲がり、および交差道路のみが許可されます。
ロジックナイト

@CarpetPython入口/出口は迷路の端に沿ったどこにでも配置できますか?それらは隣接できますか?
orlp

回答:


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の交点になるeb、彼はで見られませんかebそこでやっているt = 11置くことになる、eその廊下にはまだ。何か不足していますか?
BrainSteel

@BrainSteelはい、それは正しいです。私の答えは無効です。以前に「他のネズミの道を渡った後」にも「時間的に後方に」衝突をチェックする必要があることに気づきましたが、何らかの理由でそれは必要ないと判断しました。:P
Hjulle

@BrainSteel私は今そのバグを修正したと信じています。
ユール

1

Haskell、1ラット、681キャラクター

この問題は、1匹のネズミですべての迷路で簡単に解決できます。このコードは、任意の数のラットに対しても「機能」しますが、複数のラットとパスの間の相互作用に対する制約には従いません。

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「... 複数のラット間の制約」。これは1匹のネズミに対する答えであり、質問に従って許可されています。
ユール

OK 1匹のネズミにとってそれは別の挑戦だと思っているだけです。以前のコメント
-edc65
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.