Sıçan labirentini oluşturmak için programlar


15

Bir araştırma görevlisi olarak işe alındınız ve sıçan labirentine inşa edecek küçük bir program oluşturmanız istendi. Sıçan kutusu her zaman 62x22'dir ve sıçan için şu şekilde bir giriş (a) ve çıkışa (A) sahiptir (giriş 1):

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

Programınız, sıçan için aşağıdaki gibi bir yol bırakarak (çıkış 1) bloklarla (#) kutuyu doldurmalıdır:

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

Düşündüğün bu kolay! Güvenle dolu küçük bir program yazmaya başlıyorsunuz. Ancak, İlke Bilimcisi'nin yeni bir fikri vardı - iki farenin aynı anda labirentte gezinmesini istiyor. Dr. Rattanshnorter, farklı kapıları ve farklı çıkışları olduğunu açıklar (giriş 2):

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

Sıçanlar, doğrudan çapraz kavşaklarda hareket etmek üzere eğitilmiştir, ancak T kavşakları onları umutsuzca karıştırır ve deneyi geçersiz kılar. İyi Doktor son bir şartı açıkladığında yeni daha karmaşık görevinize başlarsınız: sıçanlar birbirlerine karşı vahşi olurlar, bu yüzden birbirlerini herhangi bir noktada görürlerse, bir sıçan dövüşü bozulur ve her ikisi de etik kurulundan önce olacaksınız. Artık programınızın aşağıdaki gibi bir labirent çıktısı gerektiğini fark ediyorsunuz (çıktı 2):

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

Sıçan B kavşağa ulaştığında, sıçan A, A'dan çıkmak için koridordan aşağı doğru hareket edecek ve sıçan kavgasından kaçınılacaktır.

Kurallar:

  • Programınız yukarıdaki gibi bir girişi okumalı (STDIN veya dosya) ve birçok boşluk dışında aynı verileri şimdi karma (#) olarak vermelidir (STDOUT veya dosya). Giriş dizesi ;yerine herhangi bir tek karakteri (örneğin ) kullanabilirsiniz \n, ancak çıkış dizesi yine de \nkarakter gerektirir . GÜNCELLENMİŞ

  • Sıçan yolu, çapraz kavşaklar hariç bir karakter genişliğinde olmalıdır (her boşluk sıfır veya iki dikey bitişik #karaktere sahip olmalıdır ). Her bir sıçan, çapraz kavşaklar hariç, net bir tek yola sahip olmalıdır. T kavşaklarına izin verilmez.

  • Sıçanlar aynı anda salınır ve sabit bir oranda hareket eder. Hiçbir zaman iki veya daha fazla sıçan birbirini görmemelidir (arada birden fazla #karakter olmadan aynı sütunda veya sırada olmalıdır ).

  • Çözüm yoksa (bitişik giriş noktaları gibi), yazdırın Impossible\nve çıkın.

  • Girişler ve çıkışlar herhangi bir tarafta olabilir, ancak asla köşelerde olmazlar.

  • (Örneğin, bir eşleştirilmiş giriş ve çıkış bitişik olan ##aA##), sıçan şirketinden gidemez aiçin A. Labirent alanında küçük bir 2 boşluk koridor bölümü olmalıdır.

  • Sıçanın çıkış noktasına ulaştığı dönüşte (veya bundan sonraki herhangi bir zamanda), diğer fareler tarafından artık görülemez.

  • Programınız 1, 2, 26 fareye kadar labirentine hesaplamak için tasarlanabilir.

  • Standart boşluklara izin verilmez.

Puan:

Çözümünüzle, programınızın labirent başına kaç fareyi (N) çözebileceğini belirleyin. Puanınız bayt cinsinden kod uzunluğunuzun bu N sayısına bölünmesiyle elde edilir.

Programınızın ne ürettiğini görebilmemiz için lütfen yanıtınıza örnek bir çıktı ekleyin.


Olası girişlerdeki tek fark a, A, b, B'nin konumları mıdır?
xnor

2 sıçan versiyonu için evet. Programınız 3 fareye kadar tasarlanmışsa, a, b, c, A, B, C'nin tüm olası yerleriyle başa çıkmanız gerekir.
Mantık Şövalyesi

Sıçan sadece T'nin yatay kısmının yanında yürüyecekse T kavşaklarına izin veriliyor mu?
orlp

Hayır, bu sıçanlar kolayca karışır. Sadece düz yollara, dirsek kıvrımlarına ve çapraz yollara izin verilir.
Mantık Şövalyesi

@CarpetPython Bir giriş / çıkış labirentin kenarı boyunca herhangi bir yerde olabilir mi? Bitişik olabilirler mi?
orlp

Yanıtlar:


2

Haskell, 26 sıçan ?, ~ 5000 bayt

Teorik olarak, bu kod herhangi bir sayıda sıçan için çalışmalıdır, ancak evrenin ısı ölümünden önce sona ereceğine dair hiçbir garanti sunmuyorum. İlk önce düz yola gitmeye ve ardından yol çalışmazsa yolu değiştirmeye çalışan bir geri izleme algoritmasına dayanır. Alternatif sayısı, yolun uzunluğuna ve sıçan sayısına göre üsteldir.

Henüz golf oynamak için uğraşmadım, çünkü çok büyük ve önce daha hızlı yapmak istiyorum.

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

Örnek çıktı, 6 sıçan:

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

2
Ne zaman bkesişme noktasına ulaşır eve b, tarafından görülmez emi? hala o koridorda koymak biçin oraya gibi görünüyor . Bir şey mi kaçırıyorum? t = 11e
BrainSteel

@BrainSteel Evet, doğru. Cevabım geçersiz. Daha önce kendime (diğer sıçan yollarını geçtikten sonra) "zamanda geriye doğru" çarpışmaları kontrol etmem gerektiğini de belirtmiştim, ama nedense bunun gerekli olmadığına karar verdim. : P
Hjulle

@BrainSteel Bu hatayı şimdi düzelttiğime inanıyorum.
Hjulle

1

Haskell, 1 sıçan, 681 Karakter

Sorun sadece bir sıçan ile tüm labirentine çözülebilir. Bu kod aynı zamanda herhangi bir sayıda sıçan için "çalışır", ancak birden çok sıçan ve yol arasındaki etkileşimlerdeki kısıtlamalara uymaz .

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)

Örnek çıktı:

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

Birçok fareyi desteklemeyi planlıyorum, bu yüzden genel kod yazdım, ama bunun için iyi bir algoritma bulamadım.

  • parse koordinatları ile tüm giriş ve çıkışların bir listesini çıkarır
  • rats bu listeyi alır ve her bir sıçan için koordinat çiftlerine dönüştürür.
  • bnds bir kenarda bir koordinat alır ve labirent içindeki en yakın koordinatlara taşır.
  • naive başlangıç ​​ve bitiş konumlarını alır ve aralarında basit bir yol döndürür.
  • main daha sonra yoldaki tüm beyaz boşlukları '#' ile değiştirir

@ edc65 "... birden fazla sıçan arasındaki kısıtlamalar ". Bu soruya izin verilen sadece 1 sıçan için bir cevaptır.
Hjulle

Tamam benim hatam. Sadece 1 sıçan için farklı bir meydan okuma olduğunu düşünüyoruz. Önceki yorumlarımı sileceğim
edc65
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.