![[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