6 people like it.

# Flappy bird with sequence expressions

This is a somewhat sensible solution to the flappy bird dojo by Phil Trelford. It uses sequence expressions eliminate some mutation of the flappy bird. To play this sample on Windows simply copy the code from the raw view and execute in F# interactive, for cross platform implementations check out: http://trelford.com/prognet15.zip

 ``` 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: ``` ``````(Standard flappy bird setup) // In this version, we use the F# sequence to represent the flappy // bird game logic. Each time the screen updates, we call MoveNext // on the workflow and so we can nicely keep state. We still need // one mutable variable for the flappy bird, because he's also // mutated by the keyboard & click events. let flappy = ref { X = 30.0; Y = 150.0; VY = 0.0; IsAlive=true } let rec starting () = seq { for i in 0 .. 100 do yield () yield! running (0) } and running (scroll) = seq { flappy := update !flappy let bird = !flappy move bird_sing (bird.X, bird.Y) for ((x,y),tube1,tube2) in tubes do let scrollX = float (x + scroll) if scrollX > bird.X - 10.0 && scrollX < bird.X + 10.0 then if (bird.Y + bird_sing.Height > float y + 100.0) || (bird.Y < float y - 320.0 + tube1.Height) then yield! dead() move tube1 (float (x + scroll),float (y-320)) move tube2 (float (x + scroll),float (y+100)) yield () yield! running (scroll - 1) } and dead () = seq { // Just loop forever in the dead state yield () yield! dead () } // The user interaction - this is where we still have a bit of mutation // because we are changing the flappy bird let flapme () = if flappy.Value.IsAlive then flappy := flap !flappy let window = Window(Title="Flap me",Width=288.0,Height=440.0) window.Content <- canvas window.MouseDown.Add(fun _ -> flapme()) window.KeyDown.Add(fun args -> if args.Key = Key.Space then flapme()) window.Show() let game = starting().GetEnumerator() CompositionTarget.Rendering.Add(fun _ -> game.MoveNext() |> ignore ) ``````
#if INTERACTIVE
#r "PresentationCore.dll"
#r "PresentationFramework.dll"
#r "System.Xaml.dll"
#r "UIAutomationTypes.dll"
#r "WindowsBase.dll"
#endif

open System
open System.IO
open System.Windows
open System.Windows.Controls
open System.Windows.Input
open System.Windows.Media
open System.Windows.Media.Imaging

/// Bird type
type Bird = { X:float; Y:float; VY:float; IsAlive:bool }
/// Respond to flap command
let flap (bird:Bird) = { bird with VY = - System.Math.PI }
/// Applies gravity to bird
let gravity (bird:Bird) = { bird with VY = bird.VY + 0.1 }
/// Applies physics to bird
let physics (bird:Bird) = { bird with Y = bird.Y + bird.VY }
/// Updates bird with gravity & physics
let update = gravity >> physics

/// Generates the level's tube positions
let generateLevel n =
let rand = System.Random()
[for i in 1..n -> 50+(i*150), 32+rand.Next(160)]

let level = generateLevel 10

/// Converts specified bitmap to an image
let toImage (bitmap:#BitmapSource) =
let w, h = float bitmap.PixelWidth, float bitmap.PixelHeight
Image(Source=bitmap,Stretch=Stretch.Fill,Width=w,Height=h)
/// Loads image from file if it exists or the url otherwise
let path = Path.Combine(__SOURCE_DIRECTORY__, file)
let uri =
if File.Exists(path)
then Uri(path, UriKind.Relative)
else Uri(url, UriKind.Absolute)
BitmapImage(uri)

let bg =
|> toImage
let ground =
|> toImage
let tube1 = load "tube1.png" "http://flappycreator.com/default/tube1.png"
let tube2 = load "tube2.png" "http://flappycreator.com/default/tube2.png"
let bird_sing =
|> toImage

let canvas = Canvas()
let move image (x,y) =
Canvas.SetLeft(image, x)
Canvas.SetTop(image, y)
move image (float x, float y)

// Level's tubes
let tubes =
[for (x,y) in level ->
let tube1 = toImage tube1
let tube2 = toImage tube2
(x,y), tube1, tube2]
val flappy : Bird ref

Full name: Script.flappy
Multiple items
val ref : value:'T -> 'T ref

Full name: Microsoft.FSharp.Core.Operators.ref

--------------------
type 'T ref = Ref<'T>

Full name: Microsoft.FSharp.Core.ref<_>
Bird.X: float
Bird.Y: float
val starting : unit -> seq<unit>

Full name: Script.starting
Multiple items
val seq : sequence:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Core.Operators.seq

--------------------
type seq<'T> = Collections.Generic.IEnumerable<'T>

Full name: Microsoft.FSharp.Collections.seq<_>
val i : int
val running : scroll:int -> seq<unit>

Full name: Script.running
val scroll : int
val update : (Bird -> Bird)

Full name: Script.update

Updates bird with gravity & physics
val bird : Bird
val move : image:UIElement -> x:float * y:float -> unit

Full name: Script.move
val bird_sing : Image

Full name: Script.bird_sing
val x : int
val y : int
val tube1 : Image
val tube2 : Image
val tubes : ((int * int) * Image * Image) list

Full name: Script.tubes
val scrollX : float
Multiple items
val float : value:'T -> float (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.float

--------------------
type float = Double

Full name: Microsoft.FSharp.Core.float

--------------------
type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>
property FrameworkElement.Height: float
val dead : unit -> seq<unit>

val flapme : unit -> unit

Full name: Script.flapme
property Ref.Value: Bird
Bird.IsAlive: bool
val flap : bird:Bird -> Bird

Full name: Script.flap

Respond to flap command
val window : Window

Full name: Script.window
Multiple items
type Window =
inherit ContentControl
new : unit -> Window
member Activate : unit -> bool
member AllowsTransparency : bool with get, set
member Close : unit -> unit
member DialogResult : Nullable<bool> with get, set
member DragMove : unit -> unit
member Hide : unit -> unit
member Icon : ImageSource with get, set
member IsActive : bool
member Left : float with get, set
...

Full name: System.Windows.Window

--------------------
Window() : unit
property ContentControl.Content: obj
val canvas : Canvas

Full name: Script.canvas
event UIElement.MouseDown: IEvent<MouseButtonEventHandler,MouseButtonEventArgs>
member IObservable.Add : callback:('T -> unit) -> unit
event UIElement.KeyDown: IEvent<KeyEventHandler,KeyEventArgs>
val args : KeyEventArgs
property KeyEventArgs.Key: Key
type Key =
| None = 0
| Cancel = 1
| Back = 2
| Tab = 3
| LineFeed = 4
| Clear = 5
| Return = 6
| Enter = 6
| Pause = 7
| Capital = 8
...

Full name: System.Windows.Input.Key
field Key.Space = 18
Window.Show() : unit
val game : Collections.Generic.IEnumerator<unit>

Full name: Script.game
type CompositionTarget =
inherit DispatcherObject
member Dispose : unit -> unit
member RootVisual : Visual with get, set
member TransformFromDevice : Matrix
member TransformToDevice : Matrix
static event Rendering : EventHandler

Full name: System.Windows.Media.CompositionTarget
event CompositionTarget.Rendering: IEvent<EventHandler,EventArgs>
Collections.IEnumerator.MoveNext() : bool
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore