Functional Reactive F # - Przechowywanie stanów w grach

Jestem obecnie studentem uczącym się paradygmatu reaktywności funkcjonalnej za pomocą F#. To dla mnie radykalnie nowy punkt widzenia. Wczoraj dowiedziałem się o tworzeniu prostej gry ping-pong za pomocą tego paradygmatu. Idea, którą do tej pory pojmuję, brzmi: uważamy wartości za funkcje czasu. W czystej postaci jest bezpaństwowcem. Muszę jednak pamiętać pozycję piłki(lub stan). Więc zawsze przekazuję aktualną pozycję piłki jako parametr funkcji globalnej.

Jeśli mówimy o lekkim bardziej skomplikowane gry, Jak Space Invaders, mamy wiele państw (pozycja kosmitów, obecne HP kosmitów, liczba pozostałych bomb itp.)

Czy istnieje elegancki/najlepszy sposób na rozwiązanie tego problemu? Czy zawsze przechowujemy stany na najwyższym poziomie? Czy wszystkie bieżące stany powinny być podane jako dodatkowy argument wejściowy funkcji globalnej?

Czy ktoś może to wyjaśnić używając prostego przykładu na F#? Wielkie dzięki.

Author: user248836, 2010-07-28

5 answers

Jest więcej niż jeden sposób na zrobienie FRP i jest to aktywny obszar badań. To, co najlepsze, zależy w dużej mierze od szczegółów interakcji między sobą, a w przyszłości mogą pojawić się nowe i lepsze techniki.

Ogólnie rzecz biorąc, chodzi o zachowanie, które są funkcjami czasu w miejsce zwykłych wartości (jak pan powiedział). Zachowania mogą być definiowane w kategoriach innych zachowań i mogą być definiowane do wymiany między innymi zachowaniami, gdy występują określone zdarzenia.

W twój przykład, ogólnie nie trzeba pamiętać pozycję piłki poprzez argumenty (ale dla niektórych rodzajów FRP można zrobić). Zamiast tego możesz po prostu mieć zachowanie:
ballPos : time -> (float * float)
Może to mieć zasięg globalny, lub dla większego programu może być lepiej mieć zasięg lokalny ze wszystkimi jego zastosowaniami w tym zakresie.

Gdy sprawy stają się coraz bardziej skomplikowane, zachowania będą definiowane na coraz bardziej złożone sposoby, zależą od innych zachowań i zdarzeń - w tym od zależności rekurencyjnych które są obsługiwane w różny sposób w różnych frameworkach FRP. W F # Dla zależności rekurencyjnych spodziewałbym się, że będziesz potrzebował let rec, w tym wszystkich zaangażowanych zachowań. Można je jednak nadal organizować w struktury - na najwyższym poziomie możesz mieć: {]}

type alienInfo =  { pos : float*float; hp : float }
type playerInfo = { pos : float*float; bombs : int } 
let rec aliens : time -> alienInfo array =             // You might want laziness here.
    let behaviours = [| for n in 1..numAliens -> 
                        (alienPos player n, alienHP player n) |]
    fun t -> [| for (posBeh, hpBeh) in behaviours -> 
                {pos=posBeh t; hp=hpBeh t} |]          // You might want laziness here.
and player : time -> playerInfo  = fun t ->
    { pos=playerPos aliens t; bombs=playerBombs aliens t}

I wtedy zachowania dla alienPos, alienHP mogą być zdefiniowane z zależnościami od gracza, a playerPos, playerBombs mogą być zdefiniowane z zależnościami od obcych.

W każdym razie, jeśli możesz podać więcej szczegółów, jakiego rodzaju FRP używasz, łatwiej będzie dać bardziej konkretne porady. (A jeśli chcesz rady na jaki rodzaj-osobiście polecam lekturę: http://conal.net/papers/push-pull-frp/push-pull-frp.pdf )

 13
Author: RD1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-28 10:44:41

Nie mam doświadczenia z programowaniem reaktywnym pod F#, ale problem stanu globalnego w systemach czysto funkcjonalnych jest dość powszechny i ma dość eleganckie rozwiązanie: monady .

Podczas gdy same monady są używane głównie w Haskell, podstawowa koncepcja przekształciła je w F # jako wyrażenia obliczeniowe.

Chodzi o to, że nie zmienia się stanów, a jedynie opisuje przejścia stanu, czyli jak tworzyć nowe stany. Państwo sam moĹźe byÄ ‡ caĹ ' kowicie ukryty w programie. Używając specjalnej monadycznej składni, możesz pisać czyste, ale stanowe programy niemal imperatywnie.

Biorąc (zmodyfikowaną) implementację z tego źródła , monada State może wyglądać tak

let (>>=) x f =
   (fun s0 ->
      let a,s = x s0    
      f a s)       
let returnS a = (fun s -> a, s)

type StateBuilder() =
  member m.Delay(f) = f()
  member m.Bind(x, f) = x >>= f
  member m.Return a = returnS a
  member m.ReturnFrom(f) = f

let state = new StateBuilder()     

let getState = (fun s -> s, s)
let setState s = (fun _ -> (),s) 

let runState m s = m s |> fst

Więc weźmy przykład: chcemy napisać funkcję, która może zapisywać wartości do dziennika (tylko listy)podczas kontynuowania. Definiujemy zatem

let writeLog x = state {
  let! oldLog = getState // Notice the ! for monadic computations (i.e. where the state is involved)
  do! setState (oldLog @ [x]) // Set new state
  return () // Just return (), we only update the state
}

Wewnątrz state, możemy teraz użyć tego w imperatywnej składni bez konieczność obsługi listy dziennika ręcznie.

let test = state {
   let k = 42
   do! writeLog k // It's just that - no log list we had to handle explicitly
   let b = 2 * k
   do! writeLog b
   return "Blub"
}

let (result, finalState) = test [] // Run the stateful computation (starting with an empty list)
printfn "Result: %A\nState: %A" result finalState

Mimo to wszystko jest tu czysto funkcjonalne;)

 6
Author: Dario,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-28 07:49:40

Tomas dał miłą rozmowę o programowaniu reaktywnym W F#. Wiele pojęć powinno mieć zastosowanie w Twoim przypadku.

 3
Author: Mau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-28 09:55:30

Może będziesz chciał rzucić okiem na FsReactive .

 0
Author: jcmincke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-22 14:36:10

Elm jest nowoczesną implementacją FRP. Do modelowania dynamicznych zbiorów, które są wszechobecne w grach takich jak Space Invaders, zawiera bibliotekę automatów opartą na pojęciach arrowized FRP. Powinieneś to sprawdzić.

 0
Author: thSoft,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-09 18:32:13