hubFS: THE place for F#

. . . are you on The Hub?
Welcome to hubFS: THE place for F# Sign in | Join | Help
in Search

Dandy - A 2D game written in F#

Last post 06-16-2007, 11:09 by jonh. 11 replies.
Sort Posts: Previous Next
  •  05-21-2006, 18:41 269

    Dandy - A 2D game written in F#

    Hello! My name is Jack Palevich, and I am a developer at Microsoft. In my day job I work on graphics libraries and tools for the Xbox 360, but as a hobby I like to write video games.

    Most games are written in C or C++, with some migration to C#. While those are very good languages, I'm always on the lookout for something new. I see a lot of potential in F#. It's based on OCaml, which seems to have been used successfully in several small hobby game engines. To help myself learn F# I wrote a simple 2D video game in F#.

    (I'm having trouble attaching the ZIP file to this message. Look for a follow-up message that includes the zip file.)

    (Below are some excerpts from the manual that's included in the ZIP file.)

    Welcome to Dandy Dungeon

    Dandy Dungeon is a 1-to-4 player cooperative action adventure game. I originally wrote it in 1983 in 6502 assembly language for the Atari 800. Since then I've ported it to different environments and languages. Its graphics are quite primitive by modern standards, but the gameplay is still enjoyable, especially when two or more people are playing.

    Comparing Implementation Languages

    For fun I have recently implemented Dandy in several different computer languages. You might think it’s a little bit obsessive-compulsive to keep writing the same game over again. But I found it helpful for understanding the differences in performance and development time. Here are some notes:

    6502 Assembly Language on the Atari 800

    This was the original version of Dandy. It took several minutes to compile the game. The game was roughly 2000 lines of 6502 assembly source code. (Unfortunately I’ve long since lost the source code.) Since the game data variables all fit in one byte, it was actually pretty easy to write this in assembly. Also the Atari 800 hardware directly supported smooth scrolling of the map, easy generation of sound effects, and 4 joystick ports.


    It took me three months of part-time work to write the game. Originally there was a second computer (a Hewlett-Packard Pascal Workstation) that acted as a “dungeon master console”. The idea was that there would be a fifth player that controlled the pace of the game. “Send in the next wave of monsters”, and stuff like that. But this ended up being just a file server, and was eventually replaced with files on the local disk.


    Another feature of the original game was that when you left a level its state was stored, so that when you returned to the level everything would be as you left it. But in my play-testing I noticed that the only time people went up was by mistake, so I cut that feature.

    C++ and Direct3D 9.0 on Windows XP

    This version took about 15 hours to write, and was around 1500 lines of code. It was easy to write and debug. (But after all, I am a full time C++ programmer.)

    C# and Managed Direct3D on Windows XP

    This version was a port of the C++ version. It took about 5 hours to port, and ended up being around 1100 lines. (It was smaller than C++ due to less boilerplate D3D initialization code.)

    F# and Managed Direct3D on Windows XP

    This version was a re-write of the C# version, with Direct3D code lifted from the F# interactive 3D sample. It took quite a while (25 hours) to write, partly because I’m new to F# and partly because I was using a borrowed computer (I’m on vacation) that didn’t have Visual Studio installed. So I was stuck using fsi and notepad. Fsi has some benefits for rapid development, but it was tedious to track down errors by copying-and-pasting code between notepad and fsi.

    Currently the F# version has more features than the other versions, so it’s not fair to compare the line number count of the current version to the other versions. However, At the point where the F# version had the same functionality as the C# version, the line count was around 900 lines. It was shorter due to a coding style that used shorter variable names, and less white space. This coding style seems to be standard for F#, and is made possible by the type inferencing.

    Thoughts on F# for game development

    If you do a web search for games written in non-C-derived languages, you’ll see that very few people write two games in a row in a non-C-derived language. I think the reason is that it’s currently still most convenient to write games in a C-derived language. However, to help people who are considering using F# for game development, here’s my thoughts on the benefits and problems:


     Benefits of F#


    l          Functional code makes it easy to factor out common code, making programs shorter

    l          Being able to omit type names in many places makes programs shorter

    l          Strong type checking and null-less programming means fewer runtime errors

    l          Fsi is helpful for use as a “console” during game development. It’s easy to inspect data structures and call code in your game to change game state and test out functions.

    l          The language support for tagged unions and the match / with language construct are helpful. For example in Dandy there’s a “piece” type that has all the types of data that are present in the game. It’s very handy to use “match” to process piece data in the game.

    l          Runs roughly as fast as C#.


    Problems with F#:


    l          For loops that execute their terminal value are confusing to C programmers. And the code ends up with lots of  for x = 0 to size – 1 statements. It’s too bad that there isn’t a for x = 0 until size syntax.

    l          Lack of a “break” statement makes loops harder to write. Yeah, I know you’re supposed to factor the loops into helper functions, or use recursive functions but it’s still a pain.

    l          No language support for enums with assigned integer values, and converting between ints and enums. This feature is very useful in games. You can kludge around it with helper functions, but it’s tedious to write them.

    l          Ugly array indexing syntax. (I prefer foo[4] to foo.(4) . Fortunately I hear this is being fixed.)

    l          It’s tedious to have to manually convert between floating point and integers.

    l          You have to know way too much in order to get code to work. For example, in order to call into the CLR code I needed to know F#, C#, the CLR, how F# marshals code into C#. Compare this to writing the same code in C# where I can just blindly copy-and-paste some example code from the docs.

    l          No free IDE. The Visual Studio IDE for FSharp is quite good, but it requires a non-free version of Visual Studio. Compare this to C or C# where you can get an excellent free version of Visual Studio.

    l          Hard to fix syntax errors. Because F# syntax is so different from C-like languages, and because F# lets you omit semicolons, if you accidentally make a syntax error in you source code the resulting error message from the compiler is typically not very helpful. Once you’ve written 20 hours or so of F# code you finally get the hang of the F# syntax, and this becomes less of a problem.

    l          F# is not currently available on any video game consoles. Whereas C++ will work on all game consoles.


    So, having ported a game to F#, I think I see lots of promise in the language. In the future I’d like to try writing a new, 3D game of some sort in F#. I’ll let you know how it goes.

  •  05-21-2006, 21:19 271 in reply to 269

    Re: Dandy - A 2D game written in F#

    For the code, please see:

    Dandy - A 2D game written in F# (by Jack Palevich)

    My works made many a ring. I had an ideal setting at Berlin with Karl and Leopold.
  •  05-21-2006, 22:03 273 in reply to 271

    Re: Dandy - A 2D game written in F#

    OK, I just compiled and ran (and lost playing level A).

    Remember to modify the DirectX reference on your machine if you are using a different release.  This is line 10 in the code and I modified mine to read:

    #I @"C:\WINNT\Microsoft.NET\DirectX for Managed Code\1.0.2902.0" ;;

    I also modified line 39, where the level data is loaded:

    let gameMediaPath = @"C:\Projects\dandy\Media"

    I ran this in fsi, and had no other issues.  I'll be creating a project and trying to build this as a standalone F# exe.  The build.bat file is also useful, but I'm a VS2005 guy, so I prefer to have a project.

    Ahhhhhhhh, the good old days of 2D games with lots of blue, boxy shapes.  Only this one scrolls, which was a very, difficult feature in those days.  The ability to move an entire field of graphics was based on the cpu and in those days, 6502 and 6809 class machines were 8 bit, ran 1MHz, had 64K addressable memory of which 8K or so was graphics memory.

    Thanks for the memories, Jack and thanks for the implementation that is a great sample of F# in action in some very easy to read and understand code.


    My works made many a ring. I had an ideal setting at Berlin with Karl and Leopold.
  •  05-22-2006, 23:38 279 in reply to 273

    Re: Dandy - A 2D game written in F#

    Glad you like it!

    Hah, yes, well while scrolling was hard on some platforms (like the very popular Apple II), on other platforms (like the Atari 400/800) it was just a matter of setting the correct base address and setting some bits in an offset register. (The DMA hardware did the actual scrolling, the game data stayed at a constant address in memory.)



  •  06-01-2006, 9:29 309 in reply to 273

    Re: Dandy - A 2D game written in F#

    I made the suggested changes and finally got the "You can now run dandy.exe" message, but when I try to execute it, it dies. Running in fsi, I get the following.

    Error in the application.
    -2005529767 (D3DXERR_INVALIDDATA)
       at Microsoft.DirectX.Direct3D.TextureLoader.FromFile(Device device, String sr
       at FSI_0003.Dandy.GameForm.Reset()
       at <StartupCode>.FSI_0003_Dandy._main()
    stopped due to error

    Any idea what I need to do to fix this?


    Jim Huddleston
  •  06-02-2006, 9:36 317 in reply to 309

    Re: Dandy - A 2D game written in F#


    It appears that the loading of the textures from the files is failing.  Double check that you have modified line 39 of the code to point to the "Media" directory and have read rights on that directory.  Also check that the "Media" folder contains "dandy.bmp" for that line of code to execute correctly.

    I know these are obvious from my previous post, but I just want to rule out that these changes are not the problem.




    My works made many a ring. I had an ideal setting at Berlin with Karl and Leopold.
  •  06-02-2006, 9:54 318 in reply to 279

    Re: Dandy - A 2D game written in F#

    RE: Scrolling on Atari 400/800

    Yes, my Apple-ness from those days is showing through.  As I recall, there was religion on platform, i.e. "Apple rules, Atari sucks", "Atari rules, C64 drools", etc.  I was in that Apple-only type group (I was young and foolish; now I'm older and more foolish).

    Now that I look back, the guys that had no religion and just wrote games on all of the platforms were the ones that had the most titles (and probably made the most dough).


    My works made many a ring. I had an ideal setting at Berlin with Karl and Leopold.
  •  06-14-2007, 6:39 3258 in reply to 318

    Re: Dandy - A 2D game written in F#

    Hi, I am new to the community. I have been programming for a while but just started with F#. I am trying to convert this project to compile on the build of F#. I am having trouble with the pickDisplayMode () function. This used an old function Idioms.foldeach. I am not able to find much information on fold or foldeach. I am using IEnumerable.fold in place of Idioms.foldeach.


    let pickDisplayMode () =

    let acceptable format = match format with

    | Format.X8R8G8B8 -> true

    | Format.A8R8G8B8 -> true

    | _ -> false in

    IEnumerable.fold Manager.Adapters.Default.CurrentDisplayMode


    (fun (mode : DisplayMode) ->

    if (mode.Width = 640 && mode.Height = 480 && (acceptable mode.Format)) then

    mode else Manager.Adapters.Default.CurrentDisplayMode)


    I am getting these errors and I am not sure how to go about fixing them.

    Error 2 Type constraint mismatch. The type   DisplayMode is not compatibile with type  'a -> 'b -> 'a. The type DisplayMode is not compatible with the type 'a -> 'b -> 'a dandy.fs 706 45 

    Error 3 This function takes too many arguments, or is used in a context where a function is not expected. dandy.fs 709 7 

     Could you help me find some information regarding this?


    Thank you.

  •  06-14-2007, 7:49 3259 in reply to 3258

    Re: Dandy - A 2D game written in F#

    I think foldeach took it parameters in reverse to the way fold does, so you need to inverse the function (fun (mode ..) and the accumulator (Manager.Adapters.Default.CurrentDisplayMode and the list (Manager.Adapters.Default.SupportedDisplayModes).

    Looking at the function siganture (val fold : ('b -> 'a -> 'b) -> 'b -> #seq<'a> -> 'b) the order should be:

         (fun (mode : DisplayMode) ->
              if (mode.Width = 640 && mode.Height = 480 && (acceptable mode.Format)) then
              mode else Manager.Adapters.Default.CurrentDisplayMode)

    I haven't got direct X on this machine at work, so I can't check that this works, but I'm hopeful.

    Functions sigatures can look a little intimdating at first but they are useful once you understand them.

    Robert Pickering
  •  06-15-2007, 10:56 3267 in reply to 3259

    Re: Dandy - A 2D game written in F#

    Thank you for the help and quick reply.

    Before I continue trying to update with this example, I think will need to read some more information on F# and functional programming.
  •  06-16-2007, 6:10 3269 in reply to 3267

    Re: Dandy - A 2D game written in F#

    I updated the Dandy game so it compiles and runs correctly. Got a few strange error messages, related to directX assembly loading, when I ran it in the debugger (but it did eventually work) and works fine when run from explorer:

    The correction I told you was slightly wrong. It should have been:

        (fun acc (mode : DisplayMode) ->
          if mode.Width = 640 && mode.Height = 480 && (acceptable mode.Format) then
            mode else acc)

    Note the untyped_ prefix to the function, this is an unfornutate consequence of the fact that SupportedDisplayModes implments the IEnumerable interface and not the more modern generic IEnumerable<T> interface.


    Robert Pickering
  •  06-16-2007, 11:09 3271 in reply to 3269

    Re: Dandy - A 2D game written in F#

    Thank you very much. The reason why it needs to be untyped_fold make sense. This now works for me too.
View as RSS news feed in XML
Powered by Community Server, by Telligent Systems