![[flappy-bird-haskell.webp]] Functional Reactive Programming (FRP)[^1] simplifies real-time application development by _modeling_ systems with continuous time. I wanted to make another graphical app with [Haskell](https://www.haskell.org/), and decided to make a ==Flappy Bird== clone. Since Haskell is purely functional I wanted to go down the _rabbit hole_ and avoid as much as possible handling the game state / inputs with dirty ==IO Monads==. After benchmarking several libraries I decided to go with [Yampa](https://hackage.haskell.org/package/Yampa), which works well with [SDL2](https://www.libsdl.org/). # The Basics of FRP in Yampa FRP allows us to model the game world as a series of signals that change over time. In Yampa, we use signal functions to manage these signals, defining relationships between inputs (like user commands) and outputs (game state changes). This declarative approach contrasts with traditional imperative loops, providing clearer and more expressive code. ## Key Concepts 1. **Signal Functions**: Core components that transform input signals into output signals over time. 2. **Event Handling**: Managing discrete events (e.g., user inputs) within the continuous signal framework. 3. **Integration**: Handling the accumulation of changes, such as the bird's position based on velocity and gravity. # Creating Flappy Bird To create Flappy Bird, we need to define several components: 1. **Bird Motion**: Model the bird’s vertical position and velocity, affected by gravity and player inputs. 2. **Obstacle Generation**: Generate pipes at regular intervals, each with varying heights. 3. **Collision Detection**: Check if the bird collides with pipes or the ground. 4. **Score Calculation**: Track the player's score based on the number of pipes passed. And some other little things like playing a sound during score changes etc. ## Bird Motion For instance here the signal corresponding to the bird moving forward and being affected by the gravity. ```haskell fallingBird :: Bird -> SF a Bird fallingBird (Bird y0 v0 s0) = proc _ -> do v <- imIntegral v0 -< 250 -- ^ velocity y <- imIntegral y0 -< (v*2) -- ^ position s <- imIntegral s0 -< 15 -- ^ to handle bird sprite animation state p <- time >>^ ((6 *) . sin . ((2 * pi) *)) -< () -- ^ this will make the bird fly more "naturally" (like a wave) returnA -< Bird (y+p) v s ``` As you can see, only time is affecting this signal. ## Events from input ```haskell flapTrigger :: SF AppInput (Event ()) flapTrigger = proc input -> do mouseTap <- lbp -< input spacebarTap <- keyPressed ScancodeSpace -< input returnA -< mouseTap `lMerge` spacebarTap flapVelocity :: Double flapVelocity = -100.0 flappingBird :: Bird -> SF AppInput Bird flappingBird bird0 = switch sf cont where sf = proc input -> do b <- fallingBird bird0 -< () flap <- flapTrigger -< input returnA -< (b, flap `tag` b) cont (Bird y _ s) = flappingBird $ Bird y flapVelocity s ``` Here `flapTrigger` will return an `Event` when the mouse or the space bar is tapped. Then we can compose it with `fallingBird` to make the whole `FlappingBird` signal. When an event is detected with make add to the Y velocity of the bird a constant value, basically making it fly higher. ![[bird.gif]] Source: https://github.com/Rydgel/flappy-haskell [^1]: https://en.wikipedia.org/wiki/Functional_reactive_programming