dig maze
Table of Contents
dig for maze
別の迷路作成を探してみたら、 【C言語】穴掘り法で「迷路」を作成する なんてのが出てきた。べったり配列を使いまくりだ。これをhaskell語に変換 したら、またリスト表現を使うのだろうか? それに、digの中のswitchは、パ ターンマッチに変換されるのだろうか? 興味津々。
その前に、digって言う穴掘り関数って、unixコマンドにも有ったぞ。DNS。
dig command
[sakae@deb ~]$ dig @8.8.8.8 www.yahoo.co.jp ; <<>> DiG 9.16.42-Debian <<>> @8.8.8.8 www.yahoo.co.jp : ;; QUESTION SECTION: ;www.yahoo.co.jp. IN A ;; ANSWER SECTION: www.yahoo.co.jp. 215 IN CNAME edge12.g.yimg.jp. edge12.g.yimg.jp. 40 IN A 182.22.24.124 ;; Query time: 19 msec ;; SERVER: 8.8.8.8#53(8.8.8.8)
Uum .. ロードバランサーで一手に引受してて、ホットスタンバイ構成になっ てるんだろうね。と、素人が想像してますよ。
vbox$ host www.yahoo.co.jp www.yahoo.co.jp is an alias for edge12.g.yimg.jp. edge12.g.yimg.jp has address 182.22.24.124 vbox$ nslookup www.yahoo.co.jp Server: 8.8.8.8 Address: 8.8.8.8#53 Non-authoritative answer: www.yahoo.co.jp canonical name = edge12.g.yimg.jp. Name: edge12.g.yimg.jp Address: 183.79.249.124
ソースをちら見してたら、アイリアスっぽいコマンドが出てた。懐かしいやっ ちゃ! OpenBSDはソースも簡単に閲覧できるので、三文の得。
error
話を戻して迷路のdig問題。相変わらずのエラー。
/tmp/digmaze/app/Main.hs:129:15-18: error:
• Couldn't match expected type: UArray (Int, Int) Int
with actual type: STUArray RealWorld (Int, Int) Int
• In the first argument of ‘printMaze’, namely ‘maze’
In a stmt of a 'do' block: printMaze maze
In the expression:
do initRand
maze <- stToIO createMaze
putStrLn "Create MAZE"
printMaze maze
|
129 | printMaze maze
| ^^^^
見た事ないな。何度か修正依頼をしてみたけど、解決に至らず。堂々巡りして る。奴に取っても未知の世界なんだろうね。関係すると思われる所を検索して みた。
go ahead
難しいのは置いておいて、他にコンパイルエラーが無いか確認する。先へ進め の無線用語 go aheadを何十年ぶりぐらいに使ってみた。問題になってたmain を下記の様に縮小。
こんな事に気付いたのは、コンパイルに段階が有ると思うから。最初は全体を 見渡して、型の矛盾が無いか確認。それがOKなら、個々の関数の中に突入するっ ぽいからだ。まあ、永遠にコンパイルエラーに付き纏わる感じがするけど。
main :: IO ()
main = do
initRand
maze <- stToIO createMaze
return ()
こんな風に、しといてコンパイルを実施すると
app/Main.hs:49:14: error:
• No instance for (Random Direction)
arising from a use of ‘randomRIO’
:
新らしい種類のエラーに遭遇した。
こんな修正方法が有るとな。もう知らない事ばかり。
data Direction = UP | DOWN | LEFT | RIGHT deriving (Enum, Bounded, Show)
instance Random Direction where
randomR (lo, hi) g =
case randomR (fromEnum lo, fromEnum hi) g of
(r, g') -> (toEnum r, g')
random g = randomR (minBound, maxBound) g
Arrayの種類
Arrayと言っても色々な切り口から分類できる
IArray MArray
簡単に言うと、ROMかRAMぐらいになるか。
Array UArray
haskellなデータなら何でも入いるBox型と、マシン寄りの特定な型のデータし か入れられないUBox型。
ArrayIO ArrayST
例を頂いてきた。
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.ST
import Data.Array.ST
import Data.Array.IO
import Data.Array.MArray
makeSTArray :: ST s (STArray s Int Int)
makeSTArray = make
makeSTUArray :: ST s (STUArray s Int Int)
makeSTUArray = make
makeIOArray :: IO (IOArray Int Int)
makeIOArray = make
makeIOUArray :: IO (IOUArray Int Int)
makeIOUArray = make
make :: MArray a Int m => m (a Int Int)
make = do
arr <- newArray (5,11) 8
val <- readArray arr 7
writeArray arr 7 (negate val)
return arr
目出度いアドレスを read modfy write してみる。
ghci> runSTArray $ makeSTArray array (5,11) [(5,8),(6,8),(7,-8),(8,8),(9,8),(10,8),(11,8)]
STArrayはSTの中に巣篭りしてるんで、runSTArrayを使って取り出した。
ghci> xx <- makeIOArray ghci> getBounds xx (5,11) ghci> readArray xx 7 -8
IOArrayの方は、ghciがdoの中で動いているんで、<- で、リンクしてから利用 してみた。結構難しいな。
use only Array.IO
一番易しいと思われる、Array.IOだけを利用してみて、って指示したら、一発 でコンパイル出来るコードを提示してきた。で、実行すると、
ghci> :main
*** Exception: Error in array index
ghci> :set -fbreak-on-exception
ghci> :trace main
Stopped in <exception thrown>, <unknown>
_exception :: e = GHC.Exception.Type.SomeException
(GHC.Exception.ErrorCallWithLocation "Error in array index" [])
Unable to list source for <unknown>
Try :back then :list
[<unknown>] ghci> :back
Logged breakpoint at app/Main.hs:51:22-26
_result :: Int
x :: Int
50 move DOWN (x, y) = (x, y + 2)
51 move LEFT (x, y) = (x - 2, y)
^^^^^
52 move RIGHT (x, y) = (x + 2, y)
[-1: app/Main.hs:51:22-26] ghci> x
1
確かに、範囲外になるな。
Debug.Trace
埋め込みのprintを利用してみる。import Debug.Traceを思い出せ。
その前に、どこの仕込むかだな。そりゃ、一次受注のdigだろう。肝心のMaze は、天下り的に更に上位から壁だけの配列が用意されて渡ってくる。
天下りと言えば、今世間で話題になってる、こういう問題が有る。 シン大阪・関西万博へ ヘドロの埋立地を貸してあげるから、注目をあびる興業をやってみてって言う 大阪の金儲け算段。
今回の穴ほり問題も、同じ構図っぽい。
dig :: Maze -> (Int, Int) -> StdGen -> IO ()
dig maze (x, y) gen
| null directions = return ()
| otherwise = do
let (dir, gen') = randomDirection directions gen
let nextCell = move dir (x, y)
let wallCell = move (opposite dir) (x, y)
writeArray maze nextCell 0
writeArray maze wallCell 0
dig maze nextCell gen'
where
directions = shuffle [minBound..maxBound] gen
再帰関数なので、終了条件を確認。方向リストが空になったら終了。返り値は 無いけど。そりゃ、加工されたMazeがそれにあたる。
それ以外の時はMazeを更新しつつ、再帰する。Mazeに書き込む0は、通路って 意味だ。
再帰の停止性を確認しつつ、方向と座標を表示してみるか。traceの引数に変 数も指定した場合、括弧で包む。元の引数は削除する。何故なら、traceの返 値が第二引数になるから。
let nextCell = move
(trace ("dir: -> " ++ show dir) dir)
(trace (" (x,y) = " ++ show (x,y)) (x,y))
9x9の盤面で、中央の(5,5)から穴を堀り始めてみた。
ghci> :main dir: -> LEFT (x,y) = (5,5) dir: -> LEFT (x,y) = (3,5) dir: -> RIGHT (x,y) = (1,5) *** Exception: Error in array index
確かにエリア外に出てしまうな。更にwriteArrayをコメントにして例外しない ようにすると、永久ループになってしまう。方向リストがnullって条件で再帰 が終了なんだけど、そうなっていない。それを指摘したら、随分複雑なコード を提示してきた。
そのおかげで、コンパイルすら成功しない堂々巡りな世界に突入してしまった。 いわゆる自爆ってやつだ。地雷があちこちに仕掛けてあるからね。
丁度、万博会場と一緒。
reset and try
オイラーが追従出来る所まで戻って再度作戦開始。新たなセッションを始めて みたのさ。
このHaskellコードは、永久ループに陥る問題を持っています。永久ループの原因は、dig関数でランダムな方向を選択するときに、すべての方向が既に探索済みの場合にも再帰呼び出しを続けてしまうことです。
この問題を解決するために、次のような修正が必要です:
dig関数内で、ランダムに選択した方向をリストから削除する必要があります。これにより、同じ方向を無限に選択し続ける問題が解消されます。
方向を選択する前に、各セルの周りの未探索の方向をチェックし、ランダムに選択できる方向があることを確認する必要があります。すべての方向が既に探索済みの場合、再帰呼び出しを終了します。
と、まっとうな指針を提示して来たけど、内容を伴っていない。ここからは自 分でガンバレ かな。なんだか、万博のゆく末を暗示してるぞ。
code
どろ沼に嵌って脱出不可能なコード。
import System.Random
import Data.Array.IO
import Control.Monad
data Direction = UP | DOWN | LEFT | RIGHT
deriving (Show, Enum, Bounded)
type Maze = IOArray (Int, Int) Int
meiroWidth :: Int
meiroWidth = 20
meiroHeight :: Int
meiroHeight = 10
main :: IO ()
main = do
maze <- createMaze
printMaze maze
createMaze :: IO Maze
createMaze = do
maze <- newArray ((0, 0), (meiroWidth - 1, meiroHeight - 1)) 1
gen <- newStdGen
let startCell = (1, 1)
writeArray maze startCell 0
dig maze startCell gen
return maze
dig :: Maze -> (Int, Int) -> StdGen -> IO ()
dig maze (x, y) gen
| null directions = return ()
| otherwise = do
let (dir, gen') = randomDirection directions gen
let nextCell = move dir (x, y)
let wallCell = move (opposite dir) nextCell
writeArray maze nextCell 0
writeArray maze wallCell 0
dig maze nextCell gen'
where
directions = shuffle [minBound..maxBound] gen
randomDirection :: [Direction] -> StdGen -> (Direction, StdGen)
randomDirection dirs gen =
let (index, gen') = randomR (0, length dirs - 1) gen
in (dirs !! index, gen')
move :: Direction -> (Int, Int) -> (Int, Int)
move UP (x, y) = (x, max 0 (y - 2))
move DOWN (x, y) = (x, min (meiroHeight - 1) (y + 2))
move LEFT (x, y) = (max 0 (x - 2), y)
move RIGHT (x, y) = (min (meiroWidth - 1) (x + 2), y)
opposite :: Direction -> Direction
opposite UP = DOWN
opposite DOWN = UP
opposite LEFT = RIGHT
opposite RIGHT = LEFT
shuffle :: [a] -> StdGen -> [a]
shuffle xs gen = shuffle' xs [] gen
where
shuffle' [] ys _ = ys
shuffle' zs ys g =
let (index, g') = randomR (0, length zs - 1) g
elem = zs !! index
zs' = take index zs ++ drop (index + 1) zs
in shuffle' zs' (elem : ys) g'
printMaze :: Maze -> IO ()
printMaze maze = do
(_, (maxX, maxY)) <- getBounds maze
forM_ [0..maxY] $ \y -> do
forM_ [0..maxX] $ \x -> do
cell <- readArray maze (x, y)
putStr $ if cell == 1 then "■" else " "
putStrLn ""