Các chương trình xây dựng mê cung chuột


15

Bạn đã được thuê làm trợ lý nghiên cứu, và được yêu cầu tạo ra một chương trình nhỏ sẽ xây dựng mê cung chuột. Hộp chuột luôn là 62x22 và có lối vào (a) và lối ra (A) cho chuột, như thế này (đầu vào 1):

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

Chương trình của bạn phải điền vào ô với các khối (#) để lại đường dẫn cho chuột, như thế này (đầu ra 1):

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

Điều này thật dễ dàng bạn nghĩ! Bạn bắt đầu viết một chương trình nhỏ, đầy tự tin. Tuy nhiên, Nhà khoa học nguyên lý đã có một ý tưởng mới - ông muốn hai con chuột điều hướng mê cung cùng một lúc. Tiến sĩ Rattanshnorter giải thích rằng họ có những cánh cửa khác nhau và lối thoát khác nhau (đầu vào 2):

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

Những con chuột đã được huấn luyện để di chuyển thẳng qua các giao lộ chéo nhưng giao lộ T khiến chúng bối rối vô vọng và sẽ làm mất hiệu lực thí nghiệm. Bạn bắt đầu nhiệm vụ phức tạp mới của mình khi Bác sĩ giỏi giải thích một yêu cầu cuối cùng: lũ chuột dã man với nhau vì vậy nếu chúng gặp nhau bất cứ lúc nào, một cuộc chiến chuột sẽ nổ ra và cả hai bạn sẽ ở trước hội đồng đạo đức. Bây giờ bạn nhận ra chương trình của bạn sẽ xuất ra một mê cung giống như thế này (đầu ra 2):

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

Đến khi chuột B đến ngã tư, chuột A sẽ đi dọc hành lang để thoát khỏi A và cuộc chiến chuột sẽ tránh được.

Quy tắc:

  • Chương trình của bạn nên đọc trong (STDIN hoặc tệp) một đầu vào như ở trên và đầu ra (STDOUT hoặc tệp) cùng một dữ liệu ngoại trừ nhiều khoảng trắng sẽ bị băm (#). Bạn có thể thay thế bất kỳ ký tự đơn nào (chẳng hạn như ;) thay vì \ntrong chuỗi đầu vào nhưng chuỗi đầu ra vẫn yêu cầu các \nký tự. CẬP NHẬT

  • Một con đường chuột phải có chiều rộng một ký tự, ngoại trừ các giao điểm chéo (mỗi không gian phải có 0 hoặc hai #ký tự liền kề trực giao ). Mỗi con chuột phải có một con đường duy nhất rõ ràng, ngoại trừ các giao lộ chéo. Không có giao lộ T được phép.

  • Chuột được thả ra đồng thời và di chuyển với tốc độ không đổi. Không bao giờ nên có hai hoặc nhiều chuột nhìn thấy nhau (ở cùng một cột hoặc hàng mà không có một trong nhiều #ký tự ở giữa).

  • Nếu không có giải pháp nào khả thi (như các điểm vào liền kề), hãy in Impossible\nvà thoát.

  • Lối vào và lối ra có thể ở bất kỳ phía nào, tuy nhiên chúng sẽ không bao giờ nằm ​​ở các góc.

  • Nếu lối vào và lối ra phù hợp liền kề (ví dụ ##aA##:), chuột không thể đi trực tiếp từ ađến A. Phải có một phần hành lang 2 không gian nhỏ bên trong khu vực mê cung.

  • Ở ngã rẽ nơi một con chuột đến điểm thoát của nó (hoặc bất cứ lúc nào sau đó), nó không còn nhìn thấy được đối với những con chuột khác.

  • Chương trình của bạn có thể được thiết kế để tính toán mê cung cho 1, 2, tối đa 26 con chuột.

  • Sơ hở tiêu chuẩn là không được phép.

Ghi bàn:

Với giải pháp của bạn, hãy chỉ định số lượng chuột trên mỗi mê cung (N) mà chương trình của bạn có thể giải quyết. Điểm của bạn là độ dài mã của bạn tính bằng byte chia cho số N này.

Vui lòng bao gồm một đầu ra mẫu trong câu trả lời của bạn để chúng tôi có thể xem chương trình của bạn tạo ra những gì.


Là sự khác biệt duy nhất trong các đầu vào có thể là vị trí của a, A, b, B?
xnor

Đối với phiên bản 2 chuột, có. Nếu chương trình của bạn được thiết kế cho tối đa 3 con chuột, bạn sẽ cần phải đối phó với tất cả các vị trí có thể có của a, b, c, A, B, C.
Logic Knight

Các giao lộ T có được phép không nếu chuột sẽ chỉ đi dọc theo phần ngang của T?
orlp

Không, những con chuột này dễ bị nhầm lẫn. Chỉ cho phép các đường thẳng, uốn cong khuỷu tay và đường chéo.
Logic Knight

@CarpetPython Một lối vào / lối ra có thể ở bất cứ đâu dọc theo rìa mê cung không? Họ có thể liền kề nhau không?
orlp

Câu trả lời:


2

Haskell, 26 con chuột?, ~ 5000 byte

Về mặt lý thuyết, mã này sẽ hoạt động với bất kỳ số lượng chuột nào, nhưng tôi không đảm bảo rằng nó sẽ chấm dứt trước cái chết nhiệt của vũ trụ. Nó dựa trên thuật toán quay lui cố gắng đi theo đường thẳng trước, sau đó chuyển đường dẫn nếu đường dẫn không hoạt động. Số lượng các lựa chọn thay thế là theo cấp số nhân liên quan đến chiều dài của đường dẫn và số lượng chuột.

Tôi chưa bận tâm đến việc chơi golf, vì nó quá lớn và vì tôi muốn làm cho nó nhanh hơn trước.

{-# 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)

Sản lượng mẫu, 6 con chuột:

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

2
Khi bđến ngã tư eb, anh ta không nhìn thấy e? bdường như để đạt được điều đó t = 11, mà sẽ đặt evẫn trong hành lang đó. Tui bỏ lỡ điều gì vậy?
BrainSteel

@BrainSteel Vâng, đúng vậy. Câu trả lời của tôi không hợp lệ. Trước đây tôi cũng lưu ý rằng tôi cũng cần kiểm tra các va chạm "ngược thời gian" (sau khi băng qua các con đường chuột khác), nhưng vì một số lý do tôi đã quyết định rằng nó không cần thiết. : P
Hjulle

@BrainSteel Tôi tin rằng tôi đã sửa lỗi đó ngay bây giờ.
Hjulle

1

Haskell, 1 con chuột, 681 Nhân vật

Vấn đề có thể được giải quyết tầm thường cho tất cả các mê cung chỉ với một con chuột. Mã này cũng "hoạt động" cho bất kỳ số lượng chuột, nhưng không tuân theo bất kỳ ràng buộc nào về sự tương tác giữa nhiều chuột và đường dẫn.

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)

Đầu ra mẫu:

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

Tôi dự định hỗ trợ nhiều con chuột, vì vậy tôi đã viết mã chung, nhưng tôi chưa tìm thấy một thuật toán tốt cho điều đó.

  • parse trích xuất một danh sách tất cả các lối vào và lối ra, với tọa độ của chúng
  • rats lấy danh sách đó và chuyển đổi nó thành các cặp tọa độ cho mỗi con chuột.
  • bnds lấy tọa độ trên một cạnh và di chuyển nó đến tọa độ gần nhất bên trong mê cung.
  • naive có một vị trí bắt đầu và kết thúc và trả về một đường dẫn đơn giản giữa chúng.
  • main sau đó thay thế tất cả các khoảng trắng không nằm trong một đường dẫn bằng '#'

@ edc65 "... ràng buộc giữa nhiều con chuột". Đây là một câu trả lời cho chỉ 1 con chuột, được cho phép theo câu hỏi.
Hjulle

OK lỗi của tôi. Chỉ cần nghĩ rằng với 1 con chuột thì đó là một thử thách khác. Tôi sẽ xóa các bình luận trước đây của tôi
edc65
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.