Advertisement
Guest User

Untitled

a guest
Nov 18th, 2018
112
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Haskell 11.38 KB | None | 0 0
  1. import System.Environment (getArgs)
  2. import Data.Maybe (fromJust)
  3. import Debug.Trace (trace)
  4. import Data.List (isPrefixOf)
  5.  
  6. main :: IO ()
  7. main = do (boardFile:steps) <- getArgs
  8.           boardString       <- readFile boardFile
  9.           if null steps
  10.             then putStrLn "Spelletjes spelen en zo"
  11.           else
  12.             putStr . unlines . map printBoard $ scanl (flip walk) (stringToBoard boardString) (map parseDir steps)
  13.               where stringToBoard bs = parseBoard (splitLines bs) (Board [] [] (Player (0, 0) (0, 0) up ) Playing) 0
  14.  
  15. splitLines :: String -> [String]
  16. splitLines str = foldr f [[]] str
  17.   where f c l@(x:xs)
  18.           | c == '\n'      = []    : l
  19.           | otherwise      = (c:x) : xs
  20.  
  21. -- define necessary types/datatypes
  22.  
  23. data Board = Board [Surface] [Sausage] Player Status
  24.  
  25. data Surface =  Surface { surfaceType :: SurfaceType
  26.                         , surfacePos  :: Coordinate }
  27.  
  28. data SurfaceType = Grass | Water | Grill
  29.  
  30. data Sausage = Sausage { fstHalf  :: SausageHalf
  31.                        , sndHalf  :: SausageHalf
  32.                        , orient   :: Orientation }
  33.  
  34. data SausageHalf  = SausageHalf { topState    :: SausageState
  35.                                 , bottomState :: SausageState
  36.                                 , sausagePos  :: Coordinate }
  37.  
  38. sausageUpper :: Char
  39. sausageUpper = '\8745'
  40.  
  41. sausageLower :: Char
  42. sausageLower = '\8746'
  43.  
  44. sausageLeft :: Char
  45. sausageLeft = '\8834'
  46.  
  47. sausageRight :: Char
  48. sausageRight = '\8835'
  49.  
  50. data Orientation = Vertical | Horizontal deriving (Eq)
  51.  
  52. data SausageState = Raw | Cooked | Burned
  53.  
  54. data Player = Player { bodyPos   :: Coordinate
  55.                      , forkPos   :: Coordinate
  56.                      , direction :: Direction }
  57.  
  58. data Status = Won | Lost | Playing
  59.  
  60. type Coordinate = (Int, Int)
  61.  
  62. type Direction = (Int, Int)
  63.  
  64. up :: Direction
  65. up    =  (-1, 0)
  66.  
  67. down :: Direction
  68. down  =  (1, 0)
  69.  
  70. left :: Direction
  71. left  =  (0, -1)
  72.  
  73. right :: Direction
  74. right =  (0, 1)
  75.  
  76. moveCoord :: Coordinate -> Direction -> Coordinate
  77. moveCoord (row, col) (dy, dx) = (row + dy, col + dx)
  78.  
  79. -- parsing and printing
  80.  
  81. parseDir :: String -> Direction
  82. parseDir dir
  83.   | dir == "N" = up
  84.   | dir == "W" = left
  85.   | dir == "S" = down
  86.   | dir == "E" = right
  87.  
  88.  
  89. parseBoard :: [String] -> Board -> Int -> Board
  90. parseBoard [] board _                  = board
  91. parseBoard (line:rest) board rowNumber = parseBoard rest board' (rowNumber+1)
  92.  where board' = parseBoardLine line board rowNumber 0
  93.  
  94.  
  95. parseBoardLine :: String -> Board -> Int -> Int -> Board
  96. parseBoardLine "" board _ _ = board
  97. parseBoardLine (s:m:rest) board row col = parseBoardLine rest board' row (col+1)
  98.  where board'  = parseBoardCell (s, m) row col board
  99.  
  100.  
  101. parseBoardCell :: (Char, Char) -> Int -> Int -> Board -> Board
  102. parseBoardCell (s, m) row col (Board surfaces sausages player st)
  103.   | m `elem` [sausageUpper,sausageLeft] = Board surfaces' (sausage : sausages) player st
  104.  | m `elem` ['N', 'E', 'S', 'W']       = Board surfaces' sausages player' st
  105.  | otherwise                           = Board surfaces' sausages player st
  106.   where surface
  107.           | s == '~'  = Surface Water (row, col)
  108.           | s == '+'  = Surface Grass (row, col)
  109.           | s == '#'  = Surface Grill (row, col)
  110.         surfaces' = (surface : surfaces)
  111.        sausage = Sausage firstHalf secondHalf orientation
  112.          where (dY, dX, orientation)
  113.                  | m == sausageUpper = (1, 0, Vertical)
  114.                  | m == sausageLeft  = (0, 1, Horizontal)
  115.                firstHalf   = SausageHalf Raw Raw (row, col)
  116.                secondHalf  = SausageHalf Raw Raw (row + dY, col + dX)
  117.        player' = Player body fork (dY, dX)
  118.           where (dY, dX)   = parseDir [m]
  119.                 body    = (row, col)
  120.                 fork    = (row + dY, col + dX)
  121.  
  122.  
  123. printBoard :: Board -> String
  124. printBoard (Board _ _ _ Won)            = "Opgelost!\n"
  125. printBoard board@(Board surfaces _ _ _) = helper (reverse surfaces) board ""
  126.   where
  127.     helper :: [Surface] -> Board -> String -> String
  128.     helper [] _ str                   = str
  129.     helper [surf] board str           = helper [] board (str ++ (surface surf) ++ (movable $ surfacePos surf)) ++ "\n"
  130.     helper (surf:next:rest) board str = helper (next:rest) board (str ++ (surface surf) ++ (movable $ surfacePos surf) ++ newLine)
  131.       where newLine
  132.               | ((fst $ surfacePos surf) + 1) == (fst $ surfacePos next) = "\n"
  133.               | otherwise                                                = ""
  134.     movable pos
  135.       | sausageSymbol board pos   /= Nothing = fromJust $ sausageSymbol board pos
  136.       | playerSymbol board pos    /= Nothing = fromJust $ playerSymbol board pos
  137.       | hasFork board pos                    = "x"
  138.       | otherwise                            = " "
  139.     surface (Surface Grass _) = "+"
  140.     surface (Surface Water _) = "~"
  141.     surface (Surface Grill _) = "#"
  142.  
  143.  
  144.  
  145. sausageSymbol :: Board -> Coordinate -> Maybe String
  146. sausageSymbol (Board _ sausages _ _) pos = findSausage sausages
  147.   where findSausage []     = Nothing
  148.         findSausage ((Sausage fstHalf sndHalf ori):xs)
  149.           | sausagePos fstHalf == pos = Just $ fstHalfSymbol fstHalf
  150.           | sausagePos sndHalf == pos = Just $ sndHalfSymbol sndHalf
  151.           | otherwise                 = findSausage xs
  152.           where fstHalfSymbol (SausageHalf Raw _ _)
  153.                   | ori == Vertical = "∩"
  154.                   | otherwise       = "⊂"
  155.                 fstHalfSymbol (SausageHalf Cooked _ _)
  156.                   | ori == Vertical = "∧"
  157.                   | otherwise       = "<"
  158.                 fstHalfSymbol (SausageHalf Burned _ _)
  159.                   | ori == Vertical = "⊓"
  160.                   | otherwise       = "⊏"
  161.                 sndHalfSymbol (SausageHalf Raw _ _)
  162.                   | ori == Vertical = "∪"
  163.                   | otherwise       = "⊃"
  164.                 sndHalfSymbol (SausageHalf Cooked _ _)
  165.                   | ori == Vertical = "∨"
  166.                   | otherwise       = ">"
  167.                 sndHalfSymbol (SausageHalf Burned _ _)
  168.                   | ori == Vertical = "⊔"
  169.                   | otherwise       = "⊐"
  170.  
  171.  
  172. playerSymbol :: Board -> Coordinate -> Maybe String
  173. playerSymbol (Board _ _ player _) pos
  174.   | bodyPos player == pos = Just $ (dirSymbol (direction player))
  175.   | otherwise             = Nothing
  176.       where dirSymbol dir
  177.               | dir == up    = "N"
  178.               | dir == down  = "S"
  179.               | dir == left  = "W"
  180.               | dir == right = "E"
  181.  
  182.  
  183. hasFork :: Board -> Coordinate -> Bool
  184. hasFork (Board _ _ player _) pos = (forkPos player) == pos
  185.  
  186. -- moving player
  187.  
  188. walk :: Direction -> Board -> Board
  189. walk dir board
  190.   | isRotation dir board = checkIfWon . modifySausages . (rotatePlayer dir) $ board
  191.   | canMove    dir board = checkIfWon . modifySausages . (movePlayer dir) $ board
  192.   | otherwise            = board
  193.   where isRotation mdir (Board _ _ (Player _ _ pdir) _)
  194.           =   (mdir `elem` [left, right] && pdir `elem` [up, down])
  195.            || (mdir `elem` [up, down]    && pdir `elem` [left, right])
  196.  
  197.  
  198. canMove :: Direction -> Board -> Bool
  199. canMove dir (Board surfaces _ player _) = walkable neighborSurface
  200.   where pdir = direction player
  201.         bpos = bodyPos player
  202.         hasPos (Surface _ spos) = spos == moveCoord bpos dir
  203.         neighborSurface =  head $ filter hasPos surfaces
  204.         walkable (Surface Grass _) = True
  205.         walkable _                 = False
  206.  
  207.  
  208. rotatePlayer :: Direction -> Board -> Board
  209. rotatePlayer dir (Board surf saus player st) = Board surf saus' player' st
  210.   where body    = bodyPos player
  211.         fork    = forkPos player
  212.         fork'   = moveCoord body dir
  213.        player' = Player body fork' dir
  214.        -- when rotating, the player can potentially
  215.        -- move a sausage at two positions, we'll call those positions
  216.         -- pos1 and pos2
  217.         pos1  = moveCoord fork dir
  218.         pos2  = fork'
  219.        (dy, dx) = direction player
  220.        oppositeDir = (dy * (-1), dx * (-1))
  221.        saus' = (moveSausage pos2 oppositeDir) . (moveSausage pos1 dir) $ saus
  222.  
  223. movePlayer :: Direction -> Board -> Board
  224. movePlayer dir (Board surf saus (Player b f pdir) st)
  225.   = Board surf saus' (Player (moveCoord b dir) (moveCoord f dir) pdir) st
  226.  where b'    = moveCoord b dir
  227.         f'    = moveCoord f dir
  228.        reverseMovement = dir /= pdir
  229.        saus'
  230.         -- if the player is walking backwards, then sausages behind him should be moved
  231.           | reverseMovement = moveSausage b' dir saus
  232.          | otherwise       = moveSausage f' dir saus
  233.  
  234. moveSausage :: Coordinate -> Direction -> [Sausage] -> [Sausage]
  235. moveSausage pos dir sausages
  236.   | null $ filter (hasPos pos)  sausages = sausages
  237.   -- if there are no sausages at this position, do nothing
  238.   | null $ filter (hasPos pos') sausages = map (moveAtPos pos) sausages
  239.  -- if there are no sausages next to the sausages at this position, only move this sauges
  240.  | otherwise                            = map (moveAtPos pos) (moveSausage pos' dir sausages)
  241.   -- otherwise, move the sausages next to this sausage before moving this saugage
  242.   where
  243.     pos' = moveCoord pos dir
  244.    hasPos p (Sausage fh sh _) = (sausagePos fh) == p || (sausagePos sh) == p
  245.    moveAtPos p s@(Sausage _ _ ori)
  246.      | not (hasPos p s)   = s
  247.      | fromSide           = rolled s
  248.      | otherwise          = shoved s
  249.      where
  250.        ori = orient s
  251.        fromSide =   (ori == Vertical && dir `elem` [left, right])
  252.                  || (ori == Horizontal && dir `elem` [up, down])
  253.    rolledHalf (SausageHalf t b c) = SausageHalf b t (moveCoord c dir)
  254.    -- rolledHalf returns a sausage half where the top and bottom are switched
  255.    rolled (Sausage fh sh ori) = Sausage (rolledHalf fh) (rolledHalf sh) ori
  256.    shovedHalf (SausageHalf t b c) = SausageHalf t b (moveCoord c dir)
  257.    shoved (Sausage fh sh ori) = Sausage (shovedHalf fh) (shovedHalf sh) ori
  258.  
  259. modifySausages :: Board -> Board
  260. -- checks if any sausages are on a grill or in the water and
  261. -- modifies them accordingly
  262. modifySausages (Board surfaces sausages pl st) = Board surfaces sausages' pl st'
  263.  where nextState Raw    = Cooked
  264.        nextState Cooked = Burned
  265.        nextState Burned = Burned
  266.        surfAtPos pos = filter (\(Surface _ p) -> p == pos) surfaces
  267.        hasGrill (Surface Grill _) = True
  268.        hasGrill _                 = False
  269.        grillHalf s@(SausageHalf t b pos)
  270.          -- if there are no grills at the half's position, don't grill it
  271.          -- otherwise grill it
  272.          | null $ filter hasGrill (surfAtPos pos)  = s
  273.          | otherwise                               = SausageHalf t (nextState b) pos
  274.        grill (Sausage fh sh ori) = Sausage (grillHalf fh) (grillHalf sh) ori
  275.        hasWater (Surface Water _) = True
  276.        hasWater _                 = False
  277.        halfUnsunken (SausageHalf _ _ pos) = (null $ filter hasWater (surfAtPos pos)) && (not $ null (surfAtPos pos))
  278.        ssgUnsunken  (Sausage fh sh _) = halfUnsunken fh || halfUnsunken sh
  279.        unsunken = filter ssgUnsunken sausages
  280.  
  281.        st'
  282.         -- if a sausage has sunk, the game is lost
  283.           | (length unsunken) == (length sausages) = st
  284.           | otherwise                              = Lost
  285.          
  286.         sausages' = map grill unsunken
  287.  
  288.  
  289. checkIfWon :: Board -> Board
  290. checkIfWon b@(Board _ _ _ Lost) = b
  291. checkIfWon (Board surfaces sausages _ _) =
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement