intricacy 0.5.5 → 0.5.7
raw patch · 14 files changed
+206/−86 lines, 14 filesdep ~network-fancy
Dependency ranges changed: network-fancy
Files
- CursesUIMInstance.hs +7/−1
- InputMode.hs +1/−1
- Interact.hs +3/−2
- InteractUtil.hs +1/−2
- KeyBindings.hs +5/−0
- MainBoth.hs +11/−0
- MainSDL.hs +11/−0
- MainState.hs +35/−32
- NEWS +6/−0
- SDLUI.hs +44/−16
- SDLUIMInstance.hs +26/−24
- c_main.c +46/−0
- intricacy.cabal +9/−7
- tutorial/5-springs.lock +1/−1
CursesUIMInstance.hs view
@@ -14,6 +14,7 @@ import qualified UI.HSCurses.Curses as Curses import qualified UI.HSCurses.CursesHelper as CursesH import Control.Concurrent.STM+import Control.Concurrent import Control.Applicative import Data.Char (ord) import Data.Monoid@@ -366,7 +367,12 @@ toggleColourMode = modify $ \s -> s {monochrome = not $ monochrome s} - getDrawImpatience = return $ \_ -> return ()+ impatience ticks = do+ when (ticks>20) $ say "Waiting for server (^C to abort)..."+ unblock <- unblockInput+ liftIO $ forkIO $ threadDelay 50000 >> unblock+ cmds <- getInput IMImpatience+ return $ CmdQuit `elem` cmds getInput mode = do let userResizeCode = 1337 -- XXX: chosen not to conflict with HSCurses codes
InputMode.hs view
@@ -10,5 +10,5 @@ module InputMode where -data InputMode = IMEdit | IMPlay | IMReplay | IMMeta | IMTextInput+data InputMode = IMEdit | IMPlay | IMReplay | IMMeta | IMTextInput | IMImpatience deriving (Eq, Ord, Show, Read)
Interact.hs view
@@ -511,9 +511,10 @@ Just $ WrenchPush dir | otherwise = Just $ HookPush dir torque whs dir- | whs == WHSHook || (whs == WHSSelected && not wsel)=+ {- | whs == WHSHook || (whs == WHSSelected && not wsel)= Just $ HookTorque dir- | otherwise = Nothing+ | otherwise = Nothing -}+ = Just $ HookTorque dir (wsel', pm) = case cmd of CmdTile (WrenchTile _) -> (True, Nothing)
InteractUtil.hs view
@@ -173,7 +173,7 @@ Nothing -> waitConfirm ansOfCmd (CmdInputChar 'y') = Just True ansOfCmd (CmdInputChar 'Y') = Just True- ansOfCmd CmdRedraw = Nothing+ ansOfCmd CmdRedraw = Just False ansOfCmd CmdRefresh = Nothing ansOfCmd CmdUnselect = Nothing ansOfCmd _ = Just False@@ -229,7 +229,6 @@ Right $ (name++[':',lockIndexChar idx], Nothing) applyCmd x (CmdInputCodename name) = Right $ (name, Nothing)- applyCmd x CmdRedraw = Right x applyCmd x CmdRefresh = Right x applyCmd x CmdUnselect = Right x applyCmd _ _ = Left False
KeyBindings.hs view
@@ -186,11 +186,16 @@ , ('<', CmdPrevPage) ] ++ miscGlobal +impatienceBindings = lowerToo $+ [ ('Q', CmdQuit)+ , (ctrl 'C', CmdQuit) ]+ bindings :: InputMode -> KeyBindings bindings IMEdit = editBindings bindings IMPlay = playBindings bindings IMMeta = metaBindings bindings IMReplay = replayBindings+bindings IMImpatience = impatienceBindings bindings _ = [] findBindings :: KeyBindings -> Command -> [Char]
MainBoth.hs view
@@ -8,6 +8,10 @@ -- You should have received a copy of the GNU General Public License -- along with this program. If not, see http://www.gnu.org/licenses/. +{-# OPTIONS_GHC -cpp #-}+#ifdef APPLE+{-# LANGUAGE ForeignFunctionInterface #-}+#endif module Main where import Init@@ -16,6 +20,13 @@ import qualified CursesUI (UIM) import CursesUIMInstance () import MainState++#ifdef APPLE+foreign export ccall hs_MAIN :: IO ()++hs_MAIN :: IO ()+hs_MAIN = main+#endif main = main' (Just (doUI::SDLUI.UIM MainState -> IO (Maybe MainState)))
MainSDL.hs view
@@ -8,12 +8,23 @@ -- You should have received a copy of the GNU General Public License -- along with this program. If not, see http://www.gnu.org/licenses/. +{-# OPTIONS_GHC -cpp #-}+#ifdef APPLE+{-# LANGUAGE ForeignFunctionInterface #-}+#endif module Main where import Init import qualified SDLUI import SDLUIMInstance () import MainState++#ifdef APPLE+foreign export ccall hs_MAIN :: IO ()++hs_MAIN :: IO ()+hs_MAIN = main+#endif main = main' (Just (doUI::SDLUI.UIM MainState -> IO (Maybe MainState)))
MainState.hs view
@@ -62,7 +62,7 @@ unblockInput :: m (IO ()) setUIBinding :: InputMode -> Command -> Char -> m () getUIBinding :: InputMode -> Command -> m String- getDrawImpatience :: m ( Int -> IO () )+ impatience :: Int -> m Bool toggleColourMode :: m () warpPointer :: HexPos -> m () getUIMousePos :: m (Maybe HexPos)@@ -296,7 +296,7 @@ modify $ \ms -> ms { listOffset = 0 } mourNameSelected >>? getRandomNames lift $ modify $ \ms -> ms {retiredLocks = Nothing}- lift.lift $ drawMessage ""+ --lift.lift $ drawMessage "" where getRandomNames = do rnamestvar <- gets randomCodenames@@ -337,7 +337,8 @@ auth <- gets curAuth cOnly <- gets cacheOnly if cOnly then return $ ServerError "Can't contact server in cache-only mode"- else lift $ withImpatience $ liftIO $ makeRequest saddr $ ClientRequest protocolVersion auth act+ else (fromMaybe (ServerError "Request aborted") <$>) $+ lift $ withImpatience $ makeRequest saddr $ ClientRequest protocolVersion auth act curServerActionAsyncThenInvalidate :: UIMonad uiM => Protocol.Action -> Maybe Codenames -> MainStateT uiM () curServerActionAsyncThenInvalidate act names = do@@ -384,30 +385,32 @@ getFreshRecBlocking rec = do tvar <- getRecordCachedFromCur False rec cOnly <- gets cacheOnly- fetched <- lift $ withImpatience $ liftIO $ atomically $ do+ mfetched <- lift $ withImpatience $ atomically $ do fetched@(FetchedRecord fresh _ _) <- readTVar tvar check $ fresh || cOnly return fetched- case fetchError fetched of- Nothing -> return $ fetchedRC fetched- Just err -> lift (drawError err) >> return Nothing+ case mfetched of+ Nothing -> lift (drawError "Request aborted") >> return Nothing+ Just fetched ->+ case fetchError fetched of+ Nothing -> return $ fetchedRC fetched+ Just err -> lift (drawError err) >> return Nothing --- |draw indication that we're waiting for the server while we do so-withImpatience :: UIMonad uiM => uiM a -> uiM a+-- |indicate waiting for server, and allow cancellation+withImpatience :: UIMonad uiM => IO a -> uiM (Maybe a) withImpatience m = do- drawImpatience <- getDrawImpatience- finishedTV <- liftIO $ atomically $ newTVar False+ finishedTV <- liftIO $ atomically $ newTVar Nothing+ id <- liftIO $ forkIO $ m >>= atomically . writeTVar finishedTV . Just let waitImpatiently ticks = do- wakeTV <- atomically $ newTVar False- forkIO $ threadDelay (10^6) >> atomically (writeTVar wakeTV True)- finished <- atomically $ do- wake <- readTVar wakeTV- finished <- readTVar finishedTV- check $ wake || finished- return finished- when (not finished) $ drawImpatience (ticks+1) >> waitImpatiently (ticks+1)- liftIO $ forkIO $ waitImpatiently 0- m <* (liftIO $ atomically $ writeTVar finishedTV True)+ finished <- liftIO $ atomically $ readTVar finishedTV+ if isJust finished+ then return finished+ else do+ abort <- impatience ticks+ if abort+ then liftIO $ killThread id >> return Nothing+ else waitImpatiently $ ticks+1+ waitImpatiently 0 getRelScore :: (UIMonad uiM) => Codename -> MainStateT uiM (Maybe Int)@@ -451,22 +454,22 @@ metagameHelpText = [ "By ruthlessly guarded secret arrangement, the Council's agents can pick any lock in the city." , "The Guild produces the necessary locks - apparently secure, but with fatal hidden flaws."- , "The ritual game known as \"Intricacy\" is played to determine the best designs."- , "Players attempt to design locks which can be picked only by one who knows the secret,"- , "and try to discover the secret flaws in the designs of their colleagues."+ , "A ritual game is played to determine the best designs."+ , "To play the game well, you must build locks which can be picked only by one who knows the secret,"+ , "and you must discover the secret flaws in the locks designed by your colleagues." , "" , "You may put forward up to three prototype locks. They will guard the secrets you discover."- , "If you pick a colleague's lock, the rules require that a note be written describing your solution."- , "The composition and deciphering of notes is an art in itself, whose details do not concern us here,"- , "but a note proves that the author found a solution, while revealing as little detail as possible."+ , "If you pick a colleague's lock, the rules require that you have a note written on your solution."+ , "A note proves that a solution was found, while revealing no more of its details than necessary."+ , "Composing notes is a tricky and ritual-bound art of its own, performed by independent experts." , "To declare your success, you must secure your note behind a lock of your own." , "If you are able to unlock a lock, you automatically read all the notes it secures."- , "If you read three notes on a lock, you will piece together the clues and work out how to solve it."+ , "If you read three notes on a lock, you will piece together the secrets of unlocking that lock." , ""- , "Players are judged relative to each of their peers. There are no absolute rankings."- , "You win a point of esteem against another player for one of their locks if either:"- , "you have declared a note on the lock and the lock's owner has not read that note,"- , "or if you have read three notes on the lock."+ , "The game judges players relative to each of their peers. There are no absolute rankings."+ , "You win a point of esteem against another player for each of their locks for which either:"+ , "you have declared a note on the lock which the lock's owner has not read,"+ , "or you have read three notes on the lock." , "Relative esteem ranges from +3 (best) to -3 (worst), and is calculated as" , "the number of their locks which win you a point minus the number of your locks which win them one." , "If the secrets to one of your locks become widely disseminated, you may wish to replace it."
NEWS view
@@ -1,5 +1,11 @@ This is an abbreviated summary; see the git log for gory details. +0.5.7:+ Allow cancellation of server requests if the server doesn't respond.+ Minor UI tweaks.+0.5.6:+ Support OS X (thanks Kevin Eaves).+ Rework setting dimensions. 0.5.5: Save solutions-in-progress of locks and tutorial. Indicate when there's a pending request.
SDLUI.hs view
@@ -23,6 +23,7 @@ import Control.Monad.State import Control.Monad.Trans.Maybe import Control.Monad.Trans.Reader+import Control.Arrow import Data.Word import Data.Array import Data.List@@ -173,7 +174,7 @@ markGroup = ([Button (tl+^hw) CmdMark [("set",hu+^neg hw),("mark",hu+^neg hv)] , Button (tl+^hw+^hv) CmdJumpMark [("jump",hu+^neg hw),("mark",hu+^neg hv)] , Button (tl+^hw+^2*^hv) CmdReset [("jump",hu+^neg hw),("start",hu+^neg hv)]],(0,1))- global = if mode == IMTextInput then [] else+ global = if mode `elem` [IMTextInput,IMImpatience] then [] else [ singleButton br CmdQuit 0 [("quit",hu+^neg hw)] , singleButton (tr +^ 3*^hv +^ 3*^hu) CmdHelp 3 [("help",hu+^neg hw)] ] whsBGs :: Maybe WrHoSel -> InputMode -> [ ButtonGroup ]@@ -291,33 +292,33 @@ helpOfSelectable (SelLockUnset False _) = Just "An empty lock slot." helpOfSelectable (SelUndeclared _) = Just- "Declare yourself privy to a lock's secrets by securing a note on it behind a lock of your own."+ "Declare yourself able to unlock a lock by securing a note on it behind a lock of your own." helpOfSelectable (SelRandom _) = Just "Random set of guild members. Colours show relative esteem, bright red (-3) to bright green (+3)." helpOfSelectable (SelScoreLock (Just name) Nothing _) = Just $- "Your lock, the secrets of which "++name++" is not privy to."+ "Your lock, which "++name++" can not unlock." helpOfSelectable (SelScoreLock (Just name) (Just AccessedPrivyRead) _) = Just $- "Your lock, the secrets of which "++name++" has read three notes on: -1 to relative esteem."+ "Your lock, on which "++name++" has read three notes: -1 to relative esteem." helpOfSelectable (SelScoreLock (Just name) (Just (AccessedPrivySolved False)) _) = Just $ "Your lock, on which "++name++" has declared a note which you have not read: -1 to relative esteem." helpOfSelectable (SelScoreLock (Just name) (Just (AccessedPrivySolved True)) _) = Just $- "Your lock, on which "++name++" has declared a note which you have read."+ "Your lock, on which "++name++" has declared a note which you have, however, read." helpOfSelectable (SelScoreLock (Just name) (Just AccessedPub) _) = Just $ "Your lock, the secrets of which have been publically revealed: -1 to relative esteem." helpOfSelectable (SelScoreLock (Just name) (Just AccessedEmpty) _) = Just $- "Your empty lock slot; "++name++" is privy to the secrets of all your locks: -1 to relative esteem."+ "Your empty lock slot; "++name++" can unlock all your locks: -1 to relative esteem." helpOfSelectable (SelScoreLock Nothing Nothing (ActiveLock name _)) = Just $- name++"'s lock, the secrets of which you are not privy to."+ name++"'s lock, which you can not unlock." helpOfSelectable (SelScoreLock Nothing (Just AccessedPrivyRead) (ActiveLock name _)) = Just $- name++"'s lock, the secrets of which you have read three notes on: +1 to relative esteem."+ name++"'s lock, on which you have read three notes: +1 to relative esteem." helpOfSelectable (SelScoreLock Nothing (Just (AccessedPrivySolved False)) (ActiveLock name _)) = Just $ name++"'s lock, on which you have declared a note which "++name++" has not read: +1 to relative esteem." helpOfSelectable (SelScoreLock Nothing (Just (AccessedPrivySolved True)) (ActiveLock name _)) = Just $- name++"'s lock, on which you have declared a note which "++name++" has read."+ name++"'s lock, on which you have declared a note which "++name++" has, however, read." helpOfSelectable (SelScoreLock Nothing (Just AccessedPub) (ActiveLock name _)) = Just $ name++"'s lock, the secrets of which have been publically revealed: +1 to relative esteem." helpOfSelectable (SelScoreLock Nothing (Just AccessedEmpty) (ActiveLock name _)) = Just $- name++"'s empty lock slot; you are privy to the secrets of all "++name++"'s locks: +1 to relative esteem."+ name++"'s empty lock slot; you can unlock all "++name++"'s locks: +1 to relative esteem." helpOfSelectable (SelReadNote note) = Just $ "You have read "++noteAuthor note++"'s note on this lock." helpOfSelectable SelReadNoteSlot = Just $@@ -329,7 +330,7 @@ " has secured their note on this lock behind their lock " ++ [lockIndexChar idx] ++ "." Nothing -> noteAuthor note ++ "'s note on this lock is public knowledge." helpOfSelectable (SelAccessed name) = Just $- name ++ " did not pick this lock, but by reading three notes became privy its secrets."+ name ++ " did not pick this lock, but learnt how to unlock it by reading three notes on it." helpOfSelectable (SelPublicLock) = Just "Notes behind retired or public locks are public; locks with three public notes are public." helpOfSelectable (SelAccessedInfo meth) = Just $ case meth of@@ -343,7 +344,7 @@ helpOfSelectable SelLockPath = Just $ "Select a lock by its name. The names you give your locks are not revealed to others." helpOfSelectable SelPrivyHeader = Just $- "Fellow guild members privy to this lock's secrets, hence able to read its secured notes."+ "Fellow guild members able to unlock this lock, hence able to read its secured notes." helpOfSelectable SelNotesHeader = Just $ "Secured notes. Notes are obfuscated sketches of method, proving success but revealing little." helpOfSelectable SelToolWrench = Just $ "The wrench, one of your lockpicking tools. Click and drag to move."@@ -714,14 +715,29 @@ ] where enumerate = zip [0..] +initMisc :: IO ()+initMisc = void $ enableUnicode True >> enableKeyRepeat 250 30 >> setCaption "intricacy" "intricacy"+ initVideo :: Int -> Int -> UIM () initVideo w h = do+ liftIO $ (((w,h)==(0,0) &&) . (InitVideo `elem`) <$> wasInit [InitVideo]) >>?+ -- reset video so that passing (0,0) to setVideoMode sets to+ -- current screen res rather than current window size+ (quitSubSystem [InitVideo] >> initSubSystem [InitVideo] >> initMisc)+ fs <- fullscreen <$> gets uiOptions- liftIO $ setVideoMode w h 0 [if fs then Fullscreen else Resizable]+ liftIO $ do+ (w',h') <- if (fs || (w,h)/=(0,0)) then return (w,h) else do+ -- use smaller dimensions than the screen's, to work around a bug+ -- seen on mac, whereby a resizable window created with+ -- (w,h)=(0,0), or even with the (w,h) given by getDimensions+ -- after creating such a window, is reported to be larger than it+ -- is.+ (w',h') <- getDimensions+ return $ (4*w'`div`5,4*h'`div`5)+ setVideoMode w' h' 0 $ if fs then [Fullscreen] else [Resizable] - -- see what size we actually got:- vinfo <- liftIO $ getVideoInfo- let [w',h'] = map ($vinfo) [videoInfoWidth,videoInfoHeight]+ (w',h') <- liftIO getDimensions modify $ \ds -> ds { scrWidth = w' } modify $ \ds -> ds { scrHeight = h' }@@ -750,6 +766,10 @@ putStr text writeFile "error.log" text + where+ getDimensions = (videoInfoWidth &&& videoInfoHeight) <$> getVideoInfo++ initAudio :: UIM () #ifdef SOUND initAudio = do@@ -783,6 +803,14 @@ #else initAudio = return () #endif++pollEvents = do+ e <- pollEvent+ case e of+ NoEvent -> return []+ _ -> do+ es <- pollEvents+ return $ e:es drawMsgLine = void.runMaybeT $ do (col,str) <- msum
SDLUIMInstance.hs view
@@ -270,18 +270,21 @@ getUIBinding mode cmd = ($cmd) <$> getBindingStr mode initUI = liftM isJust (runMaybeT $ do- catchIOErrorMT $ SDL.init [InitVideo]+ catchIOErrorMT $ SDL.init+#ifdef SOUND+ [InitVideo,InitAudio]+#else+ [InitVideo]+#endif catchIOErrorMT TTF.init lift $ do readUIConfigFile initVideo 0 0- liftIO $ setCaption "intricacy" "intricacy"+ liftIO $ initMisc w <- gets scrWidth h <- gets scrHeight liftIO $ warpMouse (fi $ w`div`2) (fi $ h`div`2) renderToMain $ erase- liftIO $ enableUnicode True- liftIO $ enableKeyRepeat 250 30 initAudio readBindings )@@ -297,15 +300,22 @@ suspend = return () redraw = return () - getDrawImpatience = do- curState <- get- let pos = serverWaitPos- return $ \ticks -> void $ flip runStateT curState $ do- when (ticks>2) $ renderToMain $ do- mapM (drawAtRel (FilledHexGlyph $ bright black)) [ pos +^ i*^hu | i <- [0..3] ]- withFont (dispFontSmall curState) $- renderStrColAtLeft errorCol ("waiting..."++replicate (ticks`mod`3) '.') $ pos- refresh+ impatience ticks = do+ liftIO $ threadDelay 50000+ if (ticks>20) then do+ let pos = serverWaitPos+ smallFont <- gets dispFontSmall+ renderToMain $ do+ mapM (drawAtRel (FilledHexGlyph $ bright black)) [ pos +^ i*^hu | i <- [0..3] ]+ withFont smallFont $+ renderStrColAtLeft errorCol ("waiting..."++replicate ((ticks`div`5)`mod`3) '.') $ pos+ clearButtons+ registerButton (pos +^ neg hv) CmdQuit 0 [("abort",hu+^neg hw)]+ drawButtons IMImpatience+ refresh+ cmds <- getInput IMImpatience+ return $ CmdQuit `elem` cmds+ else return False warpPointer pos = do (scrCentre, size) <- getGeom@@ -481,7 +491,7 @@ processEvent (VideoResize w h) = do initVideo w h return [ CmdRedraw ]- processEvent VideoExpose = return [ CmdRedraw ]+ processEvent VideoExpose = return [ CmdRefresh ] processEvent Quit = return [ CmdForceQuit ] processEvent _ = return []@@ -640,14 +650,6 @@ es' <- pollEvents return $ es++es' -pollEvents = do- e <- pollEvent- case e of- NoEvent -> return []- _ -> do- es <- pollEvents- return $ e:es- updateHoverStr :: InputMode -> UIM () updateHoverStr mode = do p@(mPos,isCentral) <- gets mousePos@@ -790,7 +792,7 @@ size <- snd <$> lift getGeom lift $ do- renderToMain $ displaceRender (SVec size 0) $ renderStrColAt (brightish white) "PRIVY" $ accessedByPos +^ hv+ renderToMain $ displaceRender (SVec size 0) $ renderStrColAt (brightish white) "UNLOCKED BY" $ accessedByPos +^ hv registerSelectable (accessedByPos +^ hv) 0 SelPrivyHeader registerSelectable (accessedByPos +^ hv +^ hu) 0 SelPrivyHeader if public lockinfo@@ -833,7 +835,7 @@ renderToMain (drawAtRel (HollowGlyph $ dim green) pos))) lift $ do- renderToMain $ displaceRender (SVec size 0) $ renderStrColAt (brightish white) "NOTES" $ notesPos +^ hv+ renderToMain $ displaceRender (SVec size 0) $ renderStrColAt (brightish white) "SECURING" $ notesPos +^ hv registerSelectable (notesPos +^ hv) 0 SelNotesHeader registerSelectable (notesPos +^ hv +^ hu) 0 SelNotesHeader if null $ notesSecured lockinfo
+ c_main.c view
@@ -0,0 +1,46 @@+/* Workaround for SDL on OS X, based on the corresponding file in TimePiece-0.0.5+ * http://hackage.haskell.org/package/TimePiece ,+ * placed in the public domin by by Audrey Tang <audreyt@audreyt.org>. */+#include <stdio.h>+#include "HsFFI.h"++#ifdef __APPLE__+#include <objc/objc.h>+#include <objc/objc-runtime.h>+#endif++#include <SDL.h>++#ifdef __GLASGOW_HASKELL__+#include "Main_stub.h"+extern void __stginit_Main ( void );+#endif++#ifdef main+int SDL_main(int argc, char *argv[])+#else+int main(int argc, char *argv[])+#endif+{+ int i;++#ifdef __APPLE__+ void * pool =+ objc_msgSend(objc_lookUpClass("NSAutoreleasePool"), sel_getUid("alloc"));+ objc_msgSend(pool, sel_getUid("init"));+#endif++ hs_init(&argc, &argv);+#ifdef __GLASGOW_HASKELL__+ hs_add_root(__stginit_Main);+#endif++ hs_MAIN();++ hs_exit();++#ifdef __APPLE__+ objc_msgSend(pool, sel_getUid("release"));+#endif+ return 0;+}
intricacy.cabal view
@@ -1,5 +1,5 @@ name: intricacy-version: 0.5.5+version: 0.5.7 synopsis: A game of competitive puzzle-design homepage: http://mbays.freeshell.org/intricacy license: GPL-3@@ -69,9 +69,14 @@ if flag(Sound) Extra-Libraries: SDL_mixer ghc-options: -optl-mwindows- if os(darwin)- -- I'm told OSX doesn't like static builds- ghc-options: -dynamic+ if os(darwin)+ -- network-fancy-0.2.1+ causes a ___xpg_strerror_r link error+ build-depends: network-fancy < 0.2.1+ -- workaround for SDL on OS X, borrowed from TimePiece-0.0.5+ cpp-options: -DAPPLE+ ghc-options: -no-hs-main+ include-dirs: /usr/include/SDL /usr/local/include/SDL+ c-sources: c_main.c else Buildable: False if flag(Curses)@@ -110,9 +115,6 @@ , cryptohash >= 0.8 , random >= 1.0, pipes >= 4 , feed >= 0.3.1, xml >= 1.2.6- if os(darwin)- -- I'm told OSX doesn't like static builds- ghc-options: -dynamic else Buildable: False main-is: Server.hs
tutorial/5-springs.lock view
@@ -6,7 +6,7 @@ " # " " " & & % % % # # " " " ] " S S % # " -" ] " # # # # # # " +" ] " # # # # # " " ] " ] # # # # 7 " " ] # " ] # # # 7 " " " # # # " " # # S S % "