<html><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; ">This is really good stuff, Luke. &nbsp;I am interested in learning more, especially in seeing examples or actual game code that implement the more common parts of a game. &nbsp;I build a game ("silkworm") in Haskell that was one of my first Haskell programs. &nbsp;The code was not pretty, and I always felt there was a better way. &nbsp;It seems you are on to a better way.<div><br></div><div>When you're ready, I'll be watching for the announcement ;)<div><br></div><div>Regards,</div><div>Duane Johnson</div><div><br><div><div>On Apr 21, 2010, at 6:39 PM, Luke Palmer wrote:</div><br class="Apple-interchange-newline"><blockquote type="cite"><div>On Wed, Apr 21, 2010 at 4:47 PM, Ben Christy &lt;<a href="mailto:ben.christy@gmail.com">ben.christy@gmail.com</a>&gt; wrote:<br><blockquote type="cite">I have an interest in both game programming and artificial life. I have<br></blockquote><blockquote type="cite">recently stumbled on Haskell and would like to take a stab at programming a<br></blockquote><blockquote type="cite">simple game using FRP such as YAMPA or Reactive but I am stuck. I am not<br></blockquote><blockquote type="cite">certain which one I should choose. It seems that Reactive is more active but<br></blockquote><blockquote type="cite">is it suitable for game programming. Also has anyone attempted to implement<br></blockquote><blockquote type="cite">neural networks using FRP if so again which of these two approaches to FRP<br></blockquote><blockquote type="cite">would you suggest?<br></blockquote><br>I am in the process of writing a game using FRP. &nbsp;I haven't followed<br>reactive in a while, but last I checked it had some rather annoying<br>issues, such as joinE (monad join on events) not working and an open<br>space leak. &nbsp;So we are using a Yampa-like approach, but not<br>specifically Yampa. &nbsp;However most of the game logic is *not* in AFRP<br>("arrowized" FRP) style, it is just there to give a nice foundation<br>and top level game loop, playing much the same role as IO does in many<br>Haskell programs (but it is implemented purely!).<br><br>The workhorse of our game has so far been "generalized differentials".<br> While not entirely rigorous, they have provided a very nice framework<br>in which to express our thoughts and designs, and are very good at<br>"highly dynamic" situations which appear in games. &nbsp;For example, with<br>arrows it is painful to maintain a list of moving actors such that can<br>be added and removed. &nbsp;With differentials this is quite natural.<br><br>I haven't published the differential library yet, I am waiting until<br>we have used them enough to discover essential techniques and find a<br>nice bases for primitives. &nbsp;But I will give a sketch here. &nbsp;Let the<br>types be your guide, as I am implementing from memory without a<br>compiler :-P<br><br><blockquote type="cite">import qualified Data.Accessor.Basic as Acc<br></blockquote><blockquote type="cite">import Data.VectorSpace<br></blockquote><blockquote type="cite">import Control.Comonad<br></blockquote><br>A differential is implemented as a function that takes a timestep and<br>returns an update function. &nbsp;Don't expose the D constructor; step is<br>okay to expose, it's kind of a generalized "linear approximation".<br><br><blockquote type="cite">newtype D a = D { step :: Double -&gt; a -&gt; a }<br></blockquote><br><blockquote type="cite">instance Monoid (D a) where<br></blockquote><blockquote type="cite"> &nbsp;&nbsp;&nbsp;mempty = D (const id)<br></blockquote><blockquote type="cite"> &nbsp;&nbsp;&nbsp;mappend da db = D (\dt -&gt; step da dt . step db dt)<br></blockquote><br>Given a differential for a component of a value, we can construct a<br>differential for that value.<br><br><blockquote type="cite">accessor :: Acc.T s a -&gt; D a -&gt; D s<br></blockquote><blockquote type="cite">accessor acc da = D (Acc.modify acc . step da)<br></blockquote><br>Given a differential for each component of a tuple, we can find the<br>differential for the tuple.<br><br><blockquote type="cite">product :: D a -&gt; D b -&gt; D (a, b)<br></blockquote><blockquote type="cite">product da db = D (\dt (x,y) -&gt; (step da dt x, step db dt y))<br></blockquote><br>A differential can depend on the current value.<br><br><blockquote type="cite">dependent :: (a -&gt; D a) -&gt; D a<br></blockquote><blockquote type="cite">dependent f = D (\dt x -&gt; step (f x) dt x)<br></blockquote><br>Vectors can be treated directly as differentials over themselves.<br><br><blockquote type="cite">vector :: (VectorSpace v, Scalar v ~ Double) =&gt; v -&gt; D v<br></blockquote><blockquote type="cite">vector v = D (\dt x -&gt; x ^+^ dt *^ v)<br></blockquote><br>Impulses allow non-continuous "burst" changes, such as adding/removing<br>an element from a list of actors. This is the only function that bugs<br>me. &nbsp;Incorrectly using it you can determine the framerate, which is<br>supposed be hidden. &nbsp;But if used correctly; i.e. only trigger them on<br>passing conditions, they can be quite handy. &nbsp;But my eyes and ears are<br>open for alternatives.<br><br><blockquote type="cite">impulse :: (a -&gt; a) -&gt; D a<br></blockquote><blockquote type="cite">impulse f = D (const f)<br></blockquote><br>If we can can find the differential for an element of some comonad<br>given its context, we can find the differential for the whole<br>structure. &nbsp;(Our "game world" is a comonad, that's why this is in<br>here)<br><br><blockquote type="cite">comonad :: (Comonad w) =&gt; (w a -&gt; D a) -&gt; D (w a)<br></blockquote><blockquote type="cite">comonad f = D (\dt -&gt; let h w = step (f w) dt (extract w) in extend h)<br></blockquote><br>I add new primitives at the drop of a hat. I would like to find a nice<br>combinator basis, but as yet, one hasn't jumped out at me. It might<br>require some tweaking of the concept.<br><br>The arrow we are using is implemented in terms of differentials:<br><br><blockquote type="cite">data Continuous a b = forall s. Continuous s (s -&gt; a -&gt; (b, D s))<br></blockquote><br><blockquote type="cite">instance Category Continuous where<br></blockquote><blockquote type="cite"> &nbsp;&nbsp;&nbsp;id = Continuous () (\() x -&gt; (x, mempty))<br></blockquote><blockquote type="cite"> &nbsp;&nbsp;&nbsp;Continuous sg0 g . Continuous sf0 f = MkC (sg0,sf0) $ \(sg,sf) x -&gt;<br></blockquote><blockquote type="cite"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let !(y, df) = f sf x &nbsp;&nbsp;&nbsp;&nbsp;-- mind the strict patterns<br></blockquote><blockquote type="cite"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;!(z, dg) = g sg y in<br></blockquote><blockquote type="cite"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(z, product dg df)<br></blockquote><br>Exercise: implement the Arrow and ArrowLoop instances.<br><br>And here is where it comes together. &nbsp;Integration over generalized<br>differentials is a continuous arrow:<br><br><blockquote type="cite">integral :: Continuous (D a) a<br></blockquote><blockquote type="cite">integral a0 = Continuous a0 (,)<br></blockquote><br>So our game loop looks something like:<br><br><blockquote type="cite">dGameState :: Input -&gt; D GameState<br></blockquote><blockquote type="cite">dGameState = ... -- built out of simpler Ds of its components<br></blockquote><br><blockquote type="cite">mainGame = proc input -&gt; do<br></blockquote><blockquote type="cite"> &nbsp;&nbsp;&nbsp;gameState &lt;- integral initialGameState -&lt; dGameState input<br></blockquote><blockquote type="cite"> &nbsp;&nbsp;&nbsp;returnA -&lt; drawGameState gameState<br></blockquote><br>This is my first experience with functional game programming, and so<br>far I love it! &nbsp;It makes so much more sense than the imperative<br>alternative. &nbsp;But the techniques are quite new and different as well,<br>and sometimes it takes a lot of thinking to figure out how to do<br>something that would be obvious for an experienced imperative game<br>programmer. &nbsp;But I consider it a virtue: it's a chance to re-evaluate<br>all the nonsense we've built up over the years and pioneer the field<br>all over again.<br><br>I hope this helps. &nbsp;If you go with a different approach, please write<br>about it!<br><br>Luke<br>_______________________________________________<br>Haskell-Cafe mailing list<br><a href="mailto:Haskell-Cafe@haskell.org">Haskell-Cafe@haskell.org</a><br>http://www.haskell.org/mailman/listinfo/haskell-cafe<br></div></blockquote></div><br></div></div></body></html>