Haskell Quiz/Sokoban/Solution Anton
From HaskellWiki
(Difference between revisions)
(New page: <haskell> -- Rubyquiz Nr. 5 - Sokoban - Haskell solution -- Copyright (C) 2011 Anton Pirogov module Main where import System (getArgs) import System.IO import System.IO.Unsafe (unsafePerf...) |
|||
| Line 1: | Line 1: | ||
| + | [[Category:Haskell Quiz solutions|Sokoban]] | ||
<haskell> | <haskell> | ||
-- Rubyquiz Nr. 5 - Sokoban - Haskell solution | -- Rubyquiz Nr. 5 - Sokoban - Haskell solution | ||
Revision as of 00:07, 12 August 2011
-- Rubyquiz Nr. 5 - Sokoban - Haskell solution -- Copyright (C) 2011 Anton Pirogov module Main where import System (getArgs) import System.IO import System.IO.Unsafe (unsafePerformIO) import Data.Char (chr) import Data.Function (on) import Data.List (groupBy, findIndex) import Data.Maybe (fromJust, isJust, isNothing) main = do args <- getArgs let startlevel = if not $ null args then (-1)+read (args !! 0) else 0 hSetBuffering stdin NoBuffering hSetBuffering stdout NoBuffering playFrame [] startlevel (0,0) 0 playFrame l n p m = do -- check whether a new level is started (moves = 0) and init if neccessary let lvl = if m == 0 then levels !! n else l let pos@(x,y) = if m == 0 then findPlayer lvl else p if m == 0 then clearScr else return () outputFrame lvl n pos m c <- getChar case () of _ | c=='q' -> do clearScr; cursorTo (0,0); return () -- quit game | c=='r' -> playFrame lvl n pos 0 | c=='n' -> if n<maxlevel then playFrame lvl (n+1) pos 0 else playFrame lvl n pos m | c=='p' -> if n>0 then playFrame lvl (n-1) pos 0 else playFrame lvl n pos m | any (==c) "wasd" -> let nlvl = tryMove lvl pos $ getDirection c in if lvl == nlvl then playFrame lvl n pos m else playFrame nlvl n (findPlayer nlvl) (m+1) | otherwise -> playFrame lvl n pos m tryMove lvl (x,y) (dx,dy) = if a==' ' then apply '@' b else if a=='.' then apply '+' b else if a=='o' && b==' ' then apply '@' 'o' else if a=='o' && b=='.' then apply '@' '*' else if a=='*' && b=='.' then apply '+' '*' else if a=='*' && b==' ' then apply '+' 'o' else lvl where apply n m = setPos (x,y) (if p=='@' then ' ' else '.') $ setPos (x+dx,y+dy) n $ setPos (x+2*dx,y+2*dy) m lvl p = getPos lvl (x,y) a = getPos lvl (x+dx,y+dy) b = getPos lvl (x+2*dx,y+2*dy) getDirection c = case c of 'w' -> (0,-1) 'a' -> (-1,0) 's' -> (0,1) 'd' -> (1,0) -- Not portable, relying on ANSI escape sequences clearScr = putStr (chr 27:"[2J") cursorTo (x,y) = putStr (chr 27:"["++show (y+1)++";"++show (x+1)++"H") ---- levels = unsafePerformIO $ do fmap split $ readFile "sokoban_levels.txt" where split = filter (/=[""]) . groupBy ((==) `on` (=="")) . lines maxlevel = (length levels) - 1 setPos (v,w) n lvl = a ++ (x++n:z):c where (a,b:c) = splitAt w lvl (x,y:z) = splitAt v b getPos lvl (x,y) = lvl !! y !! x findPlayer lvl = foldl findpos (-1,0) lvl where findpos (x,y) l = let r = findIndex (\a -> a=='@' || a=='+') l in if isNothing r then if x == -1 then (-1,y+1) else (x,y) else (fromJust r,y) checkSolved lvl = foldl (\a b -> if isJust $ findIndex (=='.') b then False else a) True lvl outputFrame level nr pos moves = do cursorTo (0,0) putStr $ unlines level putStrLn $ "\nSokoban Level Nr.:\t"++show (nr+1) putStrLn $ "Number of moves:\t"++show moves putStrLn "Help: W,A,S,D -> Movement, R -> restart level, Q -> Quit game" putStrLn "N -> next level, P -> previous level\n" let solved = checkSolved level if solved then if nr<maxlevel then putStrLn "Level solved! Press N for next level!" else putStrLn "YOU ARE THE ULTIMATE SOKOBAN MASTER! CONGRATULATIONS!" else return () cursorTo pos
You need the sokoban_levels.txt in the same directory as the program to play the game. You can get it here.
Because I'm using ANSI escape sequences, it won't work under windows, but should work under any unixoid system..
