Add solutions for HW4.
This commit is contained in:
parent
2f5fc49117
commit
cf99539856
183
HW4.fedorind.hs
Normal file
183
HW4.fedorind.hs
Normal file
@ -0,0 +1,183 @@
|
||||
-- Authors: Danila Fedorin, Ryan Alder, Matthew Sessions
|
||||
-- ONIDs: fedorind, alderr, sessionm
|
||||
module HW3 where
|
||||
|
||||
import MiniMiniLogo
|
||||
import Render
|
||||
import System.Random
|
||||
import Data.Maybe
|
||||
import System.IO.Unsafe
|
||||
|
||||
--
|
||||
-- * Semantics of MiniMiniLogo
|
||||
--
|
||||
|
||||
-- NOTE:
|
||||
-- * MiniMiniLogo.hs defines the abstract syntax of MiniMiniLogo and some
|
||||
-- functions for generating MiniMiniLogo programs. It contains the type
|
||||
-- definitions for Mode, Cmd, and Prog.
|
||||
-- * Render.hs contains code for rendering the output of a MiniMiniLogo
|
||||
-- program in HTML5. It contains the types definitions for Point and Line.
|
||||
|
||||
-- | A type to represent the current state of the pen.
|
||||
type State = (Mode,Point)
|
||||
|
||||
-- | The initial state of the pen.
|
||||
start :: State
|
||||
start = (Up,(0,0))
|
||||
|
||||
-- | A function that renders the image to HTML. Only works after you have
|
||||
-- implemented `prog`. Applying `draw` to a MiniMiniLogo program will
|
||||
-- produce an HTML file named MiniMiniLogo.html, which you can load in
|
||||
-- your browswer to view the rendered image.
|
||||
draw :: Prog -> IO ()
|
||||
draw p = let (_,ls) = prog p start in toHTML ls
|
||||
|
||||
|
||||
-- Semantic domains:
|
||||
-- * Cmd: State -> (State, Maybe Line)
|
||||
-- * Prog: State -> (State, [Line])
|
||||
|
||||
|
||||
-- | Semantic function for Cmd.
|
||||
--
|
||||
-- >>> cmd (Pen Down) (Up,(2,3))
|
||||
-- ((Down,(2,3)),Nothing)
|
||||
--
|
||||
-- >>> cmd (Pen Up) (Down,(2,3))
|
||||
-- ((Up,(2,3)),Nothing)
|
||||
--
|
||||
-- >>> cmd (Move 4 5) (Up,(2,3))
|
||||
-- ((Up,(4,5)),Nothing)
|
||||
--
|
||||
-- >>> cmd (Move 4 5) (Down,(2,3))
|
||||
-- ((Down,(4,5)),Just ((2,3),(4,5)))
|
||||
--
|
||||
cmd :: Cmd -> State -> (State, Maybe Line)
|
||||
cmd (Move l r) s = case fst s of
|
||||
Up -> ((fst s, (l, r)), Nothing)
|
||||
Down -> ((fst s, (l, r)), Just (snd s, (l, r)))
|
||||
cmd (Pen m) s = ((m, snd s), Nothing)
|
||||
|
||||
-- | Semantic function for Prog.
|
||||
--
|
||||
-- >>> prog (nix 10 10 5 7) start
|
||||
-- ((Down,(15,10)),[((10,10),(15,17)),((10,17),(15,10))])
|
||||
--
|
||||
-- >>> prog (steps 2 0 0) start
|
||||
-- ((Down,(2,2)),[((0,0),(0,1)),((0,1),(1,1)),((1,1),(1,2)),((1,2),(2,2))])
|
||||
prog :: Prog -> State -> (State, [Line])
|
||||
prog cs is = (fs, catMaybes ls)
|
||||
where
|
||||
step (s, xs) c = let (ns, ml) = cmd c s in (ns, xs ++ [ml])
|
||||
(fs, ls) = foldl step (is, []) cs
|
||||
|
||||
|
||||
type Cell = (Bool, Bool, Bool, Bool, Bool)
|
||||
type Coord = (Int, Int)
|
||||
type Maze = [(Coord, Cell)]
|
||||
data Wall = WTop | WBottom | WLeft | WRight
|
||||
|
||||
visited :: Cell -> Bool
|
||||
visited (b, _, _, _, _) = b
|
||||
|
||||
neighbors :: Maze -> Coord -> [Coord]
|
||||
neighbors m (x, y) = [ c | c <- map fst m,
|
||||
abs (x - fst c) + abs (y - snd c) == 1,
|
||||
maybe False (not . visited) (lookup c m) ]
|
||||
|
||||
pickNeighbor :: Maze -> StdGen -> Coord -> (Maybe Coord, StdGen)
|
||||
pickNeighbor m sg c =
|
||||
case ns of
|
||||
[] -> (Nothing, sg)
|
||||
_ -> (Just (ns !! v), g)
|
||||
where
|
||||
(v, g) = randomR (0, length ns - 1) sg
|
||||
ns = neighbors m c
|
||||
|
||||
breakCellWall :: Wall -> Cell -> Cell
|
||||
breakCellWall WTop (b1, b2, b3, b4, b5) = (b1, False, b3, b4, b5)
|
||||
breakCellWall WBottom (b1, b2, b3, b4, b5) = (b1, b2, False, b4, b5)
|
||||
breakCellWall WLeft (b1, b2, b3, b4, b5) = (b1, b2, b3, False, b5)
|
||||
breakCellWall WRight (b1, b2, b3, b4, b5) = (b1, b2, b3, b4, False)
|
||||
|
||||
getWall :: Coord -> Coord -> Wall
|
||||
getWall (x, y) (x2, y2) = case (x - x2, y - y2) of
|
||||
(0, 1) -> WBottom
|
||||
(0, -1) -> WTop
|
||||
(1, 0) -> WLeft
|
||||
(-1, 0) -> WRight
|
||||
|
||||
update :: Eq a => (b -> b) -> a -> [(a, b)] -> [(a, b)]
|
||||
update f a = map changeFunc
|
||||
where
|
||||
changeFunc (a', b) = if a == a' then (a', f b) else (a', b)
|
||||
|
||||
breakWall :: Wall -> Coord -> Maze -> Maze
|
||||
breakWall w = update (breakCellWall w)
|
||||
|
||||
visit :: Coord -> Maze -> Maze
|
||||
visit = update (\(b1, b2, b3, b4, b5) -> (True, b2, b3, b4, b5))
|
||||
|
||||
generate :: Coord -> StdGen -> Maze -> (Maze, StdGen)
|
||||
generate c s m = maybe (nm, s) fixpointMove mc
|
||||
where
|
||||
nm = visit c m
|
||||
(mc, g) = pickNeighbor nm s c
|
||||
fixpointMove c2 = let (nm', g') = moveCell c2 in generate c g' nm'
|
||||
moveCell c2 = generate c2 g $
|
||||
breakWall (getWall c c2) c $
|
||||
breakWall (getWall c2 c) c2 nm
|
||||
|
||||
emptyMaze :: Int -> Int -> Maze
|
||||
emptyMaze w h = [ ((x, y), (False, True, True, True, True)) | x <- [0..w - 1], y <- [0..h - 1] ]
|
||||
|
||||
transform :: (Int -> Int) -> (Int -> Int) -> Coord -> Coord
|
||||
transform fx fy (x, y) = (fx x, fy y)
|
||||
|
||||
offset :: Coord -> Coord -> Coord
|
||||
offset (ox, oy) = transform (+ox) (+oy)
|
||||
|
||||
line :: Coord -> Coord -> Prog
|
||||
line (x1, y1) (x2, y2) = [ Pen Up, Move x1 y1, Pen Down, Move x2 y2 ]
|
||||
|
||||
spotToLogo :: (Coord, Cell) -> Prog
|
||||
spotToLogo (c, (_, top, bottom, left, right)) = lineMovements
|
||||
where
|
||||
ls =
|
||||
[ (offset (0, 1) c, offset (1, 1) c, top)
|
||||
, (c, offset (1, 0) c, bottom)
|
||||
, (c, offset (0, 1) c, left)
|
||||
, (offset (1, 0) c, offset(1, 1) c, right)
|
||||
]
|
||||
lineMovements = ls >>= (\(c1, c2, b) -> if b then line c1 c2 else [])
|
||||
|
||||
mazeToLogo :: Maze -> Prog
|
||||
mazeToLogo m = m >>= spotToLogo
|
||||
|
||||
drawFlower :: Int -> Coord -> Prog
|
||||
drawFlower n (xi, yi) = [ Pen Up, Move xi yi, Pen Down, Move xi (yi + n * 2) ] ++ linesFromCenter (xi, yi + n * 2)
|
||||
where
|
||||
pointsAround xs ys = [ (xs + x, ys + y) | x <- [-n..n], y <- [-n..n], abs x + abs y == n ]
|
||||
linesFromCenter c@(x, y) = pointsAround x y >>= line c
|
||||
--
|
||||
-- * Extra credit
|
||||
--
|
||||
|
||||
generateMaze :: Int -> Int -> Maze
|
||||
generateMaze w h = m
|
||||
where
|
||||
mi = emptyMaze w h
|
||||
r = mkStdGen 5
|
||||
(m, _) = generate (0, 0) r mi
|
||||
|
||||
-- | This should be a MiniMiniLogo program that draws an amazing picture.
|
||||
-- Add as many helper functions as you want.
|
||||
amazing :: Prog
|
||||
amazing =
|
||||
drawFlower 3 (5, 20) ++
|
||||
drawFlower 3 (12, 20) ++
|
||||
drawFlower 4 (27, 20) ++
|
||||
drawFlower 6 (40, 20) ++
|
||||
drawFlower 5 (65, 20) ++
|
||||
(mazeToLogo $ generateMaze 80 20)
|
63
MiniMiniLogo.hs
Normal file
63
MiniMiniLogo.hs
Normal file
@ -0,0 +1,63 @@
|
||||
-- | This module defines the syntax of MiniMiniLogo. It also provides
|
||||
-- functions to generate programs that draw some basic shapes.
|
||||
--
|
||||
-- NOTE: You should not change the definitions in this file!
|
||||
--
|
||||
module MiniMiniLogo where
|
||||
|
||||
--
|
||||
-- * Syntax
|
||||
--
|
||||
|
||||
-- | A program is a sequence of commands.
|
||||
type Prog = [Cmd]
|
||||
|
||||
-- | The mode of the pen.
|
||||
data Mode = Down | Up
|
||||
deriving (Eq,Show)
|
||||
|
||||
-- | Abstract syntax of commands.
|
||||
data Cmd = Pen Mode
|
||||
| Move Int Int
|
||||
deriving (Eq,Show)
|
||||
|
||||
-- | Generate a MiniMiniLogo program that draws a 2x2 box starting from the
|
||||
-- specified point. Conceptually, this program looks like the following, but
|
||||
-- the additions are carried out in Haskell rather than in MiniMiniLogo.
|
||||
--
|
||||
-- pen up; move (x,y);
|
||||
-- pen down; move (x+2,y); move (x+2,y+2);
|
||||
-- move (x,y+2); move (x,y);
|
||||
--
|
||||
-- >>> box 7 3
|
||||
-- [Pen Up,Move 7 3,Pen Down,Move 9 3,Move 9 5,Move 7 5,Move 7 3]
|
||||
--
|
||||
box :: Int -> Int -> Prog
|
||||
box x y = [Pen Up, Move x y, Pen Down,
|
||||
Move (x+2) y, Move (x+2) (y+2), Move x (y+2), Move x y]
|
||||
|
||||
-- | Generate an 'X' from (x,y) to (x+w,y+h).
|
||||
--
|
||||
-- >>> nix 10 10 5 7
|
||||
-- [Pen Up,Move 10 10,Pen Down,Move 15 17,Pen Up,Move 10 17,Pen Down,Move 15 10]
|
||||
--
|
||||
nix :: Int -> Int -> Int -> Int -> Prog
|
||||
nix x y w h = [Pen Up, Move x y, Pen Down, Move (x+w) (y+h),
|
||||
Pen Up, Move x (y+h), Pen Down, Move (x+w) y]
|
||||
|
||||
-- | Generate a MiniMiniLogo program that draws n steps starting from
|
||||
-- point (x,y).
|
||||
--
|
||||
-- >>> steps 3 2 4
|
||||
-- [Pen Up,Move 2 4,Pen Down,Move 2 5,Move 3 5,Move 3 6,Move 4 6,Move 4 7,Move 5 7]
|
||||
--
|
||||
steps :: Int -> Int -> Int -> Prog
|
||||
steps n x y = [Pen Up, Move x y, Pen Down] ++ step n
|
||||
where
|
||||
step 0 = []
|
||||
step n = step (n-1) ++ [Move (x+n-1) (y+n), Move (x+n) (y+n)]
|
||||
|
||||
-- | Draw an example picture. The expected output is given on the HW4
|
||||
-- description page.
|
||||
demo :: Prog
|
||||
demo = box 7 3 ++ nix 6 6 4 3 ++ steps 3 2 4
|
82
Render.hs
Normal file
82
Render.hs
Normal file
@ -0,0 +1,82 @@
|
||||
-- | A module for rendering lines as an HTML5 file containing an SVG image.
|
||||
-- This can be used to visualize the denotational semantics of a MiniLogo
|
||||
-- program.
|
||||
--
|
||||
-- NOTE: You should not change the definitions in this file!
|
||||
--
|
||||
module Render (Point,Line,toHTML,toGridHTML) where
|
||||
|
||||
import Data.List (intercalate)
|
||||
|
||||
|
||||
-- | A point is a cartesian pair (x,y).
|
||||
type Point = (Int,Int)
|
||||
|
||||
-- | A line is defined by its endpoints.
|
||||
type Line = (Point,Point)
|
||||
|
||||
-- | Output a list of lines as an HTML5 file containing an SVG image.
|
||||
toHTML :: [Line] -> IO ()
|
||||
toHTML ls = writeFile "MiniMiniLogo.html" (header ++ content ls ++ footer)
|
||||
|
||||
-- | Alternate version of 'toHTML' that adds a grid to the background.
|
||||
toGridHTML :: [Line] -> IO ()
|
||||
toGridHTML ls = writeFile "MiniMiniLogo.html" (header ++ grid ++ content ls ++ footer)
|
||||
|
||||
--
|
||||
-- Private definitions. All definitions below this point will not be visible
|
||||
-- from within a module that imports this module.
|
||||
--
|
||||
|
||||
scale, margin, width, height :: Int
|
||||
scale = 10
|
||||
margin = 10
|
||||
width = 800
|
||||
height = 400
|
||||
|
||||
gridStep = 5
|
||||
maxX = width `div` scale
|
||||
maxY = height `div` scale
|
||||
|
||||
gridStyle = "fill:none;stroke:lightgrey;stroke-width:1"
|
||||
drawStyle = "fill:none;stroke:red;stroke-width:2"
|
||||
|
||||
title = "<head><title>MiniLogo Semantics Viewer</title></head>"
|
||||
view = "<svg width='100%' viewBox='0 0 "
|
||||
++ show (width + 2*margin) ++ " "
|
||||
++ show (height + 2*margin) ++ "'>"
|
||||
border = "<rect x='" ++ show (margin-3) ++
|
||||
"' y='" ++ show (margin-3) ++
|
||||
"' width='" ++ show (width +6) ++
|
||||
"' height='" ++ show (height+5) ++
|
||||
"' style='fill:none;stroke:black;stroke-width:2'/>"
|
||||
|
||||
header = unlines ["<!DOCTYPE html>", "<html>", title, "<body>", view, border]
|
||||
footer = unlines ["</svg>","</body>","</html>"]
|
||||
|
||||
grid = unlines (map (poly gridStyle) lines)
|
||||
where lines = [ [(x,0), (x,maxY)] | x <- [0,gridStep..maxX] ]
|
||||
++ [ [(0,y), (maxX,y)] | y <- [0,gridStep..maxY] ]
|
||||
|
||||
content :: [Line] -> String
|
||||
content = unlines . map (poly drawStyle) . chunk
|
||||
|
||||
-- | A canvas-adjusted point as a string.
|
||||
point :: Point -> String
|
||||
point (x,y) = show xp ++ "," ++ show yp
|
||||
where xp = x*scale + margin
|
||||
yp = height - y*scale + margin
|
||||
|
||||
-- | Chunk a bunch of lines into sequences of connected points.
|
||||
chunk :: [Line] -> [[Point]]
|
||||
chunk [] = []
|
||||
chunk [(p,q)] = [[p,q]]
|
||||
chunk ((p,q):ls) | q == head ps = (p:ps) : pss
|
||||
| otherwise = [p,q] : ps : pss
|
||||
where (ps:pss) = chunk ls
|
||||
|
||||
-- | Draw a sequence of connected points.
|
||||
poly :: String -> [Point] -> String
|
||||
poly style ps = "<polyline points='"
|
||||
++ intercalate " " (map point ps)
|
||||
++ "' style='" ++ style ++ "'/>"
|
Loading…
Reference in New Issue
Block a user