1

I am tying to create a simple animation using haskell gloss. I want that at the first 4 seconds, every rectangle will change its color to a darker one. The problem is that after a relatively long time of linking, nothing really happens -

enter image description here

All rectangles appear and they don't change color

This is the following code i used -

window :: Display
window = InWindow "Simon" (width, height) (offset, offset)

background :: Color
background = black

data SimonGame = Game {
    rectangleGreen::Picture, 
    rectangleRed::Picture,
    rectangleBlue::Picture,
    rectanglYellow::Picture
} deriving Show

initialState :: SimonGame
initialState = Game
  { rectangleGreen = translate (-100) (0) $ color green $ rectangleSolid 60 60,
    rectangleRed = translate (100) (0) $ color red $ rectangleSolid 60 60,
    rectangleBlue = translate (0) (100)  $  color blue $ rectangleSolid 60 60,
    rectanglYellow = translate (0) (-100)  $  color yellow $ rectangleSolid 60 60
  }

render :: SimonGame -> Picture 
render game = pictures
              [ rectangleGreen game,
                rectangleRed game,
                rectangleBlue game,
                rectanglYellow game
              ]

updateBoard :: Float-> SimonGame -> SimonGame 
updateBoard 1.0 game = game { 
                              rectangleGreen = translate (-100) (0) $ color (dark green)  $ rectangleSolid 60 60,
                              rectangleRed = translate (100) (0) $ color red $ rectangleSolid 60 60,
                              rectangleBlue = translate (0) (100)  $  color blue $ rectangleSolid 60 60,
                              rectanglYellow = translate (0) (-100)  $  color yellow $ rectangleSolid 60 60
                            }
updateBoard 2.0 game = game { 
                              rectangleGreen = translate (-100) (0) $ color green $ rectangleSolid 60 60,
                              rectangleRed = translate (100) (0) $ color (dark red) $ rectangleSolid 60 60,
                              rectangleBlue = translate (0) (100)  $  color blue $rectangleSolid 60 60,
                              rectanglYellow = translate (0) (-100)  $  color yellow $rectangleSolid 60 60
                            }                         
updateBoard 3.0 game = game {
                              rectangleGreen = translate (-100) (0) $ color green $ rectangleSolid 60 60,
                              rectangleRed = translate (100) (0) $ color red $ rectangleSolid 60 60,
                              rectangleBlue = translate (0) (100)  $  color (dark blue) $rectangleSolid 60 60,
                              rectanglYellow = translate (0) (-100)  $  color yellow $rectangleSolid 60 60
                            }
updateBoard 4.0 game = game { 
                              rectangleGreen = translate (-100) (0) $ color green $ rectangleSolid 60 60,
                              rectangleRed = translate (100) (0) $ color red $ rectangleSolid 60 60,
                              rectangleBlue = translate (0) (100)  $  color blue $rectangleSolid 60 60,
                              rectanglYellow = translate (0) (-100)  $  color (dark yellow) $rectangleSolid 60 60
                            }
updateBoard _ game = game

main :: IO ()
main = animate window background frame
  where
    frame :: Float -> Picture
    frame seconds = render $ updateBoard seconds initialState
Alex Goft
  • 1,114
  • 1
  • 11
  • 23
  • 1
    My guess is that the pattern matches on `1.0`, `2.0`, etc. are failing and you're matching on the `_` case. Maybe temporarily make the `_` case the same as the `4.0` case and see if that behaves differently. Do you know about the pitfalls of comparing floating point numbers? – ja. Oct 28 '17 at 14:09
  • [What every computer scientist should know about floating-point arithmetic](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). (But the problem with your code is actually more fundamental than the typical float problems.) – leftaroundabout Oct 28 '17 at 14:35
  • Another thing: please don't write such repetitive code as in `updateBoard`. – leftaroundabout Oct 28 '17 at 14:38

1 Answers1

4

Never equality-check on floating point numbers. (Or, if you do, always assume the result may be False even if the numbers are conceptually equal.)

In your case, that's not even conceptually the case, because animate is called on some finite points in time; why would these include any exact integral number of seconds?

One simple way to get around this is to look up the rounded-to-whole-seconds time.

updateBoard :: Int -> SimonGame -> SimonGame 
updateBoard 1 game = game { ... }
updateBoard 2 game = game { ... }
...

main = animate window background frame
  where
    frame :: Float -> Picture
    frame seconds = render $ updateBoard (floor seconds) initialState
leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
  • Guards can also result in an acceptable looking solution. `updateBoard t game | t < 1 = ... ; | t < 2 = ... ; | t < 3 = ... ; | t < = 4 ... ; | otherwise = ...`. – Thomas M. DuBuisson Oct 28 '17 at 16:49