packages feed

midimory-0.0.2.3: src/Reactive.hs

module Main where

import qualified GUI
import qualified Game
import qualified Configuration as Config
import qualified Option
import qualified MIDI
import Game (Choice(First,Second), Player(PlayerA,PlayerB), switchPlayer)

import qualified Reactive.Banana.WX as RBWX
import qualified Reactive.Banana as RB
import Reactive.Banana.WX (Prop'((:==)))
import Reactive.Banana ((<@>), (<@))

import qualified Graphics.UI.WX as WX
import Graphics.UI.WX (Prop((:=)), command, selection, text)

import qualified System.Random as Rnd

import qualified Control.Monad.Trans.State as MS
import Control.Monad ((<=<))
import Control.Applicative ((<$>), (<$), (<*>))

import qualified Data.Foldable as Fold
import Data.Array ((!))
import Data.Tuple.HT (mapFst, mapSnd)


runGUI :: Config.T -> MIDI.Sequencer -> IO ()
runGUI cfg sequ = do
   gui <- GUI.create cfg

   seed <- Rnd.randomIO
   RBWX.actuate <=< RB.compile $ do
      restartEv <- RBWX.event0 (GUI.restart gui) command
      let accumMS m s ev =
            fmap fst <$> RB.accumB (MS.runState m s) (MS.runState m . snd <$ ev)
      pitchesBe <-
         accumMS (Game.shufflePitches cfg) (Rnd.mkStdGen seed) restartEv
      let clashMsg = "clicks cannot occur at the same time"
      buttonPitchEv <-
         fmap (foldl (RB.unionWith (error clashMsg)) RB.never) $
         mapM
            (\(pos,button) -> do
               ev <- RBWX.event0 button command
               return ((,) button . (! pos) <$> pitchesBe <@ ev)) $
         concat $ GUI.matrix gui
      RBWX.reactimate $ MIDI.sendNote sequ . snd <$> buttonPitchEv
      let accumRestart s0 ev =
            RB.accumB s0 $ RB.unionWith (.) (const s0 <$ restartEv) ev
      selectedBe <-
         accumRestart Nothing
            ((\buttonPitch -> maybe (Just buttonPitch) (const Nothing))
               <$> buttonPitchEv)
      let matchingPitchEv =
            (\mfirst -> mapSnd (\pitch -> mapSnd (pitch==) <$> mfirst))
               <$> selectedBe
               <@> buttonPitchEv
      let enable but state = WX.set but [ WX.enabled := state ]
      let restartAct = mapM_ (flip enable True . snd) $ concat $ GUI.matrix gui
      RBWX.reactimate (restartAct <$ restartEv)
      RBWX.reactimate $
         (\(button,match) ->
            case match of
               Just (firstButton, False) -> enable firstButton True
               _ -> enable button False)
         <$>
         matchingPitchEv
      choiceBe <-
         RB.stepper First $
         RB.unionWith const (First <$ restartEv)
            (maybe Second (const First) . snd <$> matchingPitchEv)
      playerBe <-
         accumRestart PlayerA
            ((\match -> if Fold.all snd match then id else switchPlayer)
               <$> fmap snd matchingPitchEv)
      scoreBe <-
         accumRestart (0,0)
            ((\pl match ->
               if Fold.any snd match
                  then case pl of PlayerA -> mapFst succ; PlayerB -> mapSnd succ
                  else id)
             <$> playerBe
             <@> fmap snd matchingPitchEv)
      let chooseMessage choice pl score =
            case choice of
               Second -> Game.makeMessage pl Second
               First -> Game.completeMessage (GUI.maxScore gui) pl score
      RBWX.sink (GUI.scoreA gui) [ selection :== fst <$> scoreBe ]
      RBWX.sink (GUI.scoreB gui) [ selection :== snd <$> scoreBe ]
      RBWX.sink (GUI.message gui)
         [ text :== chooseMessage <$> choiceBe <*> playerBe <*> scoreBe ]


main :: IO ()
main = do
   (config, (dests,chan)) <- Option.multiArgs "Concentration game for tones"
   MIDI.withSequencer "Midimory" chan $ \sequ -> do
      mapM_ (MIDI.parseAndConnect sequ) dests
      WX.start $ runGUI config sequ