import qualified Game as G
import qualified MountainCar as MC
import qualified Point as P
import qualified Graphics.Shine as S
import qualified Graphics.Shine.Input as S
import qualified Graphics.Shine.Picture as S
import qualified Web.KeyCode as W
import System.Random (newStdGen, randoms)
import GHCJS.DOM (currentDocumentUnchecked)
gameWidth :: Double
gameWidth = 800
gameHeight :: Double
gameHeight = 600
fps :: Double
fps = 30
minSpaceX :: Double
minSpaceX = MC.minPosition - 0.1
maxSpaceX :: Double
maxSpaceX = MC.maxPosition + 0.1
minSpaceY :: Double
minSpaceY = 0
maxSpaceY :: Double
maxSpaceY = 1.1
goalSpaceX :: Double
goalSpaceX = 0.5
goalSpaceY :: Double
goalSpaceY = MC.heightObs goalSpaceX
displayH :: G.Game -> S.Picture
displayH g = goalP <> carP <> episodeP <> nwinsP <> stepP <> actionP <> groundP
where carP = mkCarP $ G._observation g
episodeP = S.Translate 10 30 $ S.Text "20px Sans" S.LeftAlign Nothing $ "episode " ++ show (G._episode g)
nwinsP = S.Translate 10 60 $ S.Text "20px Sans" S.LeftAlign Nothing $ "nwins " ++ show (G._nwins g)
stepP = S.Translate 10 90 $ S.Text "20px Sans" S.LeftAlign Nothing $ "step " ++ show (G._step g) ++ " / " ++ show G.maxStep
actionP = S.Translate 10 120 $ S.Text "20px Sans" S.LeftAlign Nothing $ "action " ++ show (G._lastAction g)
mkCarP :: MC.Observation -> S.Picture
mkCarP obs = S.Translate x y $ S.Colored (S.Color 0 0 255 1) $ S.CircleF 10
where pos = MC._position obs
(x,y) = spaceToScreen (pos, MC.heightObs pos)
goalP :: S.Picture
goalP = S.Translate x y $ S.Colored (S.Color 255 0 0 1) $ S.RectF 10 20
where (x,y) = spaceToScreen (goalSpaceX, goalSpaceY)
groundP :: S.Picture
groundP = foldl (<>) S.Empty [S.Line x0 y0 x1 y1 | ((x0,y0), (x1,y1)) <- zip xys (tail xys)]
where xys = [spaceToScreen (x, MC.heightObs x)
| x<-[MC.minPosition, MC.minPosition+0.05 .. MC.maxPosition]]
spaceToScreen :: P.Point -> P.Point
spaceToScreen (x, y) = (xScreen, yScreen)
where xScreen = gameWidth * (x - minSpaceX) / (maxSpaceX - minSpaceX)
yScreen = gameHeight * (1 - (y - minSpaceY) / (maxSpaceY - minSpaceY))
eventH :: S.Input -> G.Game -> G.Game
eventH (S.Keyboard W.ArrowLeft S.Down _) g = g { G._inputLeft = True }
eventH (S.Keyboard W.ArrowLeft S.Up _) g = g { G._inputLeft = False }
eventH (S.Keyboard W.ArrowRight S.Down _) g = g { G._inputRight = True }
eventH (S.Keyboard W.ArrowRight S.Up _) g = g { G._inputRight = False }
eventH _ g = g
idleH :: Double -> G.Game -> G.Game
idleH _ = G.step
-- The mountain car problem is generally defined using a fixed time step,
-- so we ignore the time delta and just compute the next step.
main :: IO ()
main = do
myRands <- randoms <$> newStdGen
doc <- currentDocumentUnchecked
ctx <- S.fixedSizeCanvas doc (floor gameWidth) (floor gameHeight)
let myModel = G.createGame myRands
S.play ctx doc fps myModel displayH eventH idleH