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 load file url =
   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 =
   load "bg.png" "http://flappycreator.com/default/bg.png"
   |> toImage
let ground =
   load "ground.png" "http://flappycreator.com/default/ground.png"
   |> 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 =
   load "bird_sing.png" "http://flappycreator.com/default/bird_sing.png"
   |> toImage

let canvas = Canvas()
let move image (x,y) =
   Canvas.SetLeft(image, x)
   Canvas.SetTop(image, y)
let add image (x,y) =
   canvas.Children.Add(image) |> ignore
   move image (float x, float y)

add bg (0,0)
add bird_sing (30,150)
// Level's tubes
let tubes =
   [for (x,y) in level ->
      let tube1 = toImage tube1
      let tube2 = toImage tube2
      add tube1 (x,y-320)
      add tube2 (x,y+100)
      (x,y), tube1, tube2]
add ground (0,360)
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>

Full name: Script.dead
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

More information

Link:http://fssnip.net/s8
Posted:8 years ago
Author:Tomas Petricek
Tags: flappy bird , sequence expressions