import LSystem import Test.HUnit --1. join join :: [Command] -> Command join []= Sit join (a:az) = foldl (:#:) a az joinTests = test [ join [] ~?= Sit, (join [Go 10, Go(-10)]) `equivalent` (Go 10 :#: Go (-10)) ~?=True, join [Turn 90, Go 10] ~?= Turn 90 :#: Go 10, (join [Turn 90, Go 10, Turn 90, Go 10]) `equivalent` (Turn 90 :#: Go 10 :#: Turn 90 :#: Go 10) ~?= True ] --2. split split :: Command -> [Command] --split Sit = [Sit] --split (Turn x) = [Turn x] --split (Go x) = [Go x] split (a :#: az) = split a ++ split az split x = [x] splitTests = test [ ([] == split Sit || [Sit] == split Sit) ~?= True, [Go 10, Go(-10)] ~=? split (Go 10 :#: Go (-10)) ] copy :: Int -> Command -> Command copy n c = join $ replicate n c copyTests = test [ (copy 3 (Go 10 :#: Turn 30)) `equivalent` (Go 10 :#: Turn 30 :#: Go 10 :#: Turn 30 :#: Go 10 :#: Turn 30) ~?= True, (copy 0 (Go 10 :#: Turn 30)) `equivalent` Sit ~?= True, (copy 4 Sit) `equivalent` (Sit :#: Sit :#: Sit :#: Sit) ~?= True ] -- 4. pentagon pentagon :: Distance -> Command pentagon a = copy 5 (Go a :#: Turn 72) pentagonTests = test [(pentagon 7) `equivalent` (Go 7 :#: Turn 72.0 :#: Go 7 :#: Turn 72.0 :#: Go 7 :#: Turn 72.0 :#: Go 7 :#: Turn 72.0 :#: Go 7 :#: Turn 72.0) ~?= True] --5. polygon polygon :: Distance -> Int -> Command polygon a b = copy b (Go a :#: Turn (fromIntegral (360 `div` b))) polygonTests = test [ (polygon 10 3) `equivalent` (Go 10 :#: Turn 120 :#: Go 10 :#: Turn 120 :#: Go 10 :#: Turn 120) ~?= True, (polygon 10 8) `equivalent` (Go 10 :#: Turn 45 :#: Go 10 :#: Turn 45 :#: Go 10 :#: Turn 45 :#: Go 10 :#: Turn 45 :#: Go 10 :#: Turn 45 :#: Go 10 :#: Turn 45 :#: Go 10 :#: Turn 45 :#: Go 10 :#: Turn 45) ~?= True ] --6. spiral spiral :: Distance -> Int -> Distance -> Angle -> Command spiral _ 0 _ _ = Sit spiral 0 _ _ _ = Sit spiral seg n step angle = Go seg :#: (Turn angle) :#: spiral (seg-step) (n-1) step angle -- join [gi (seg-step) :#: turn angle | - <- [o..n-1]] spiralTests = test [ (spiral 100 30 1 10) `equivalent` (Go 100.0 :#: Turn 10.0 :#: Go 99.0 :#: Turn 10.0 :#: Go 98.0 :#: Turn 10.0 :#: Go 97.0 :#: Turn 10.0 :#: Go 96.0 :#: Turn 10.0 :#: Go 95.0 :#: Turn 10.0 :#: Go 94.0 :#: Turn 10.0 :#: Go 93.0 :#: Turn 10.0 :#: Go 92.0 :#: Turn 10.0 :#: Go 91.0 :#: Turn 10.0 :#: Go 90.0 :#: Turn 10.0 :#: Go 89.0 :#: Turn 10.0 :#: Go 88.0 :#: Turn 10.0 :#: Go 87.0 :#: Turn 10.0 :#: Go 86.0 :#: Turn 10.0 :#: Go 85.0 :#: Turn 10.0 :#: Go 84.0 :#: Turn 10.0 :#: Go 83.0 :#: Turn 10.0 :#: Go 82.0 :#: Turn 10.0 :#: Go 81.0 :#: Turn 10.0 :#: Go 80.0 :#: Turn 10.0 :#: Go 79.0 :#: Turn 10.0 :#: Go 78.0 :#: Turn 10.0 :#: Go 77.0 :#: Turn 10.0 :#: Go 76.0 :#: Turn 10.0 :#: Go 75.0 :#: Turn 10.0 :#: Go 74.0 :#: Turn 10.0 :#: Go 73.0 :#: Turn 10.0 :#: Go 72.0 :#: Turn 10.0 :#: Go 71.0 :#: Turn 10.0 :#: Sit) ~?= True ] --7. optimise --optimise :: Command -> Command optimise = undefined --noptimise x | ((optimised x) == x) = x -- | otherwise = optimise (join (opt x)) -- | opt :: [Command] -> [Command] opt [] = [] opt ((Turn x) : (Turn y) : xs ) = Turn (x+y) :opt xs opt ( (Go x) : (Go y) : xs) = Go (x+y) :opt xs opt (Sit : xs) =opt xs opt ((Turn 0) : xs) =opt xs opt ((Go 0): xs) =opt xs opt ((Turn x): xs) = Turn x :opt xs opt ((Go x) : xs) = Go x : opt xs -- opt (a :#: b) = (opt a ++ opt b) te = [ Turn 3 , Turn 4, Sit, Go 3, Go 4 , Go 10 , Turn 90 , Go 10 , Turn 90 , Go 10 , Turn 90 , Go 10 , Turn 90 , Go 10 , Go (-10) , Turn(-90) ] find b = fst (head [(x,y) | (x,y) <- (optimised b), x==y]) optimised xs = zip ( iterate opt xs) (iterate opt (opt xs)) --split --better Com _> Com --better (go x : go y : x) = go x+y : better xs --zip iterate better xs) iterate better better xs --find == ys optimiseTests = test [ optimise Sit ~?= Sit, optimise (Go 10 :#: Turn 90 :#: Go 10 :#: Turn 90 :#: Go 10 :#: Turn 90 :#: Go 10 :#: Turn 90 :#: Go 10 :#: Go (-10) :#: Turn(-90) :#: Go (-10) :#: Turn(-90) :#: Go (-10) :#: Turn(-90) :#: Go (-10) :#: Turn(-90) :#: Go (-10)) ~?= Sit, (optimise (Go 10 :#: Turn 90 :#: Go 10 :#: Turn 90 :#: Go 10 :#: Turn 90 :#: Go 10 :#: Turn 90 :#: Go 10 :#: Go (-10) :#: Turn(-90) :#: Go (-10) :#: Turn(-90) :#: Go (-10) :#: Turn(-90) :#: Go (-10) :#: Turn(-90) :#: Turn 45 :#: Go (-10))) `equivalent` (Go 10 :#: Turn 45 :#: Go (-10)) ~?= True, optimise (Go 10 :#: Go (-10) :#: Go 10 :#: Go (-10) :#: Go 10 :#: Go (-10) :#: Go 10 :#: Go (-10) :#: Go 10 :#: Go (-10)) ~?= Sit ] --8. arrowhead arrowhead x = f x where f 0 = Go 5 f (x+1) = f x :#: n :#: g x :#: p :#: f x g 0 = Go 10 g (x+1) = g x :#: p :#: f x :#: p :#: g x n = Turn 60 p = Turn (-60) --9. snowflake snowflake x = f x :#: n :#: n :#: f x :#: n :#: n :#:f x :#: n :#: n where f 0 = Go 10 f (x+1) = f x :#: p :#: f x :#: n :#: n :#:f x :#: p :#: f x p = Turn 60 n = Turn (-90) --10. hilbert hilbert = undefined main = runTestTT (test [joinTests, splitTests, copyTests, pentagonTests, polygonTests, spiralTests, optimiseTests]) -- Hexagon hexagon a = polygon a 6 hexagonLine :: Float -> Int -> Command hexagonLine size reps = copy reps ( (hexagon size) :#: Turn (90) :#:(GrabPen Inkless) :#: Go (2*size*(cos (pi/6))) :#: Turn (-90) :#: (GrabPen black) ) --------------------------------------------------------------------------------------------------------------------------------------------------------------------- --COMP ENTRY points :: [[(Float,Float)]] points =[[(0.5,0.75),(0.25,1),(0.25,1.25),(1.25,1.5),(1.5,1.25)], -- left arm [(0.5,0.5)], -- head [(0.5,1.5),(1.75,2),(1,1)], -- tail [(-(1/6),0.5),((1/6),0.5),(0.5,(1/3)),((5/6),(1/3))], -- left leg [((1/3),(1/6))], -- right arm [((1/3),1.25),((1),(1))] -- right leg ] gaps = [0.66,0.66,0.45] -- each hexagon side has a partner who each share a gap scale :: Float -> [(Float,Float)] -> [(Float,Float)] scale a xs = map (\(x,y) -> (x*a,y*a)) xs -- showing off my lambda addEndPoints :: [(Float,Float)] -> [(Float,Float)] addEndPoints xs = (0.0,0.0): xs ++ (1.0,0.0):[] drawSide :: Int -> Command drawSide s = (drawShape (getShape s)) :#: (drawGap (getGap s)) where getShape :: Int -> [(Float,Float)] getShape s = scale (1.0-(getGap s)) (addEndPoints (points!!s)) drawShape :: [(Float,Float)] -> Command drawShape xs = join $ map plot $ differences xs drawGap :: Float -> Command drawGap x = GrabPen Inkless :#: move x :#: GrabPen black getGap :: Int -> Float -- a silly function to get round an error, should be in drawSide getGap s | even s = gaps!!(i s) | otherwise = 1.0 - getGap (s-1) where i s| s==0 = 0 | s==2 = 1 | s==4 = 2 getAngle :: (Float,Float) -> Float getAngle (x,y) | x > 0 = (180*(atan (y / x))/pi) | x == 0 = if y>0 then 90 else 270 | x < 0 = 180+(180*(atan (y / x))/pi) getLength :: (Float,Float) -> Float getLength (x,y) = sqrt ( x ^2 + y ^2) plot :: (Float,Float) -> Command plot p = Turn (getAngle p) :#: move (getLength p) :#: Turn (-(getAngle p)) differences :: [(Float,Float)] -> [(Float,Float)] differences xs = zipWith next (xs) (drop 1 xs) where next (x,y) (x',y') = (x'-x , y'-y) drawShape :: [(Float,Float)] -> Command drawShape xs = join $ map plot $ differences xs move x = Go (x*100) drawHex = Branch (dropTurtle (1.25,(80)) :#: lambda) :#: join [ ((drawSide a) :#: (Turn 60)) | a <- [0..5]] lambda = Turn (-60) :#: move 0.2 :#: Branch (Turn 120 :#: move 0.2) :#: move 0.2 dropTurtle :: (Float , Float) -> Command dropTurtle (dist,ang) = GrabPen Inkless :#: Turn (ang) :#: move dist :#: Turn (-ang) :#: GrabPen black hexGroup = copy 3 ( drawHex :#:( GrabPen Inkless :#: ( copy 2 (move 1 :#: Turn (-60))) :#: Turn 240 :#: GrabPen black)) groupLine = hexGroup :#: (copy 6( dropTurtle (3,(-60)) :#: hexGroup)) manyLine = copy 10 (Branch (groupLine) :#: dropTurtle (3,0)) disp = ( dropTurtle (17,160) :#: manyLine) lambdaLizards = display 0.5 disp -- type lambdaLizards in main to see the finished pic -- the screen should be filled with tesalating lizards, if it is not and there is white space then your viewer is different to mine.... -- cheers!