Friday, September 17, 2010

Digit recognition with a neural network. First attempt!

I bought myself "On Intelligence", and I thought as the first step on the long road to building an intelligent machine I would go back to neural networks. I have my own little library that seems to work (kind of), so I decided I was going to try to write a little number recognition program. I was inspired of course by ai-junkie, but it was a bit short on details...
So here is the problem: I have a 5x5 grid of LEDs that can be on or off, and I want to recognize the digit that they show. 
The input will be a list of 25 numbers, either 0 (off) or 1 (on)
The output will be a list of 10 numbers, either 0 or 1. The first position of a 1 indicates the result (so if the first element is a 1, it's number 0, etc).
I arbitrarily decide to set the number of hidden neurons to 50, twice the input neurons. So my topology is 25-50-10. 
I train the network with only one version of each digit. For example 3 would be:
*****
    *
 ****
    *
*****
Wrote a few lines of Haskell code to read the training sets (moving from the representation with *, spaces and new lines to the list of 1 and 0, so that 3 becomes: [1.0,1.0,1.0,1.0,1.0,0.0,0.0,0.0,0.0,1.0,0.0,1.0,1.0,1.0,1.0,0.0,0.0,0.0,0.0,1.0,1.0,1.0,1.0,1.0,1.0]).
Then I train the network. It learns after 200 iterations, and successfully recognizes the training sets (ok, so things are working as they should be).
Then the real test: can my network recognize small variants of the training set?
Lets's try with:
****
    *
 ***
    *
****
(a slightly rounded 3): the network answers 3!!
Works also for a slightly rounded 6. But then:
 ***
*   *
 ***
*   *
 ***
(rounded 8) gives me 6. Oh dear... I could probably add more cases in the training sets and hopefully resolve the ambiguities, but there are bigger issues (I know I know, I'm very candid, but it's one thing being told something in a book, and another to build something to actually show it to you). For example:
***
* *
***
Is a small zero that doesn't take the full 5 LED width. My network recognizes 0 when it fills the full square, not when it's smaller (tells me it's a 5).
And of course, 1 is a disaster. The training "1" is a vertical row of LEDs on the left. A vertical row of LEDs on the right gives me 9, usually (and I suppose, 9 has all the LEDs on the right on).
So not only is basic training not sufficient to detect simple variations, but also the fact that we deal with absolute positioning of leds mean than scaling and simple side translations are not supported. So I suppose the next step is to not work with absolute positions, but relative positions of "on" LEDs. Another day's work...

Monday, September 06, 2010

EclipseFP development: using the GHC Lexer

The new version of EclipseFP is slowly coming along. Slowly, because of some holidays, because of a lot of bug fixes, and mainly because of some big changes.
There were a few bugs revolving around syntax highlighting. The original code was using some lexing rules from the JFace packages in Eclipse, but it didn't cover some common enough cases of Haskell syntax, and of course was fairly hopeless at more complex stuff (things like Template Haskell). So I could either trod on and fix the lexing rules for everything, or I could use a different approach. So I decided to use Scion again, and to add support for lexing. Now Scion exposes a method that calls the GHC Lexer on an arbitrary Haskell source. We use the result to color highlight properly the Haskell source code. Of course I have to handle things like CPP preprocessing directives and literal source files myself, but that was ok. The two main issues I had to tackle were performance, and advanced lexing options.
Performance was poor at start, and there are two reasons: the underlying Scion code deals with JSON Strings while the socket code uses ByteStrings, and, I think, the Eclipse console. For the first issue, I'm using now AttoJson, so that Json marshalling is done with ByteStrings and try to use ByteString as much as possible in the code. The Eclipse console is a different matter. I think when too much data is written to it it struggles to update the console control, blocks the UI thread and the whole UI becomes unresponsive. For the moment, I have disabled server output in the console for commands (since now getting the tokens from the source code is quite a big response). Since lexing is also done in the UI thread, we've probably lost a bit in that area, but I feel the advantages (less Haskell knowledge in the Java code, more reliance on the GHC code, which I suspect knows about Haskell pretty well) outweigh the costs.
One funny thing with the Lexer I found: for example when you have TemplateHaskell code in your source file, and you have the TemplateHaskell pragma, the Lexer doesn't automatically set the TemplateHaskell flag. So I decided that I was going to enable all the lexer flags, since performance didn't seem to suffer. Be liberal in what you parse...


Anyway, I'm not going to release the next version right now, a bit of testing is still needed, and there are a few little things that I want to fix. The testing is easy, in a way: I use the development version of EclipseFP to hack the Scion code, so by eating my own dog food I hope that I can release something of value to Haskell programmers.
So don't worry, EclipseFP is coming soon!

Wednesday, June 02, 2010

EclipseFP 1.111 released

I have just released version 1.111 of EclipseFP. You can get it from sourceforge or via the Eclipse update site (http://eclipsefp.sourceforge.net/updates/).
This is mostly a bug-fixing release to ensure compatibility with GHC 6.12.1 (Haskell Platform 2010) and IPv6. It also bundles a version of the Scion source code that it builds under the covers to save people from downloading the source code from github and building it themselves.


There aren't too many feature enhancements because I didn't get any requests for it. I'm not too sure a lot of people are using EclipseFP and are interested in seeing it improved. I suppose now with a Cabalized gtk2hs Leksah becomes more attractive. Anyway, if you have requests for EclipseFP let me know!

Friday, May 28, 2010

Gtk2hs and Leksah on Windows, check!

Things have moved since I posted about the difficulty of being a Haskell programmer on Windows. Now, Gtk2hs has been cabalized and put on Hackage. I went into my MSYS prompt, and it installed like a charm! I've downloaded the latest version of Leksah, and it installed and started properly! As I write this I've seen the initial Leksah screen, and it's now busy referencing my packages (I wonder if giving it the location where Cabal stores the packages it downloads was a good idea, it seem to be taking a while...). 
So I think now all the things I was struggling to get working are now installed and doing fine on my Windows 7 machine! There is hope!

Wednesday, May 05, 2010

Haskell SDL on Windows, check! (hacking required)

Things looked grim. Even though Timothy could get it to work, the SDL Haskell library wouldn't build for me. libSDL itself installed ok and the samples compiled and ran. But I couldn't compile the hsc files because of a linker error: gcc couldn't find SDLMain or whatever. In desperation, I posted to Haskell-cafe. I didn't get a single answer or hint. I was truly alone.
OK, it was time to either give up or take drastic action. And I never give up.

By then I had narrowed the problem. The order of arguments to gcc were wrong for linking, according to the gcc manual: the .o file should be referenced before the SDL libraries on the command line. It would work like that for other people, but for me, on my system, in my corner of the universe, the order was wrong, and that was it. If I tried the same order of arguments on the SDL samples, they would fail to link. If I reverted the arguments, they would succeed.
So I was left with the straight and narrow: I managed to get the source code for hsc2hs from darcs. I fiddled for a while with the include-dirs and extra-lib options so I could get it to compile and produce the same error than I had with the Haskell Platform version, and not some stupid errors about hsFFI.h not found or so.

Then I opened up, hands trembling and sweat pouring from my forehead, Main.hs in an editor. Found the offending lines:
    rawSystemL ("linking " ++ oProgName) beVerbose linker
( [f | LinkFlag f <- flags]
++ [oProgName]
++ ["-o", progName]
)

Changed them to:

      rawSystemL ("linking " ++ oProgName) beVerbose linker
( [oProgName]
++ [f | LinkFlag f <- flags]
++ ["-o", progName]
)
So that the name of the o file would be before the libraries.
Recompiled, copied the exe in the Haskell Platform bin folder...
Relaunched the SDL build...
And watched SDL build and install properly!
Gaped at the first sample of Timothy opening up a blank window that would close when I press a key! (OK, the last print fails, but by now little errors are not going to disturb my feeling of invulnerability).
Danced around my desk!
Had a beer!

Friday, April 30, 2010

Happstack on Window, check! (in a long, round-about, fear-inducing way)

The other day I noted that I couldn't install the darcs version of Happstack on my windows machine. There was a build failure that was reported on the mailing list. So I waited a few days, and tried again. But then, before I started building Happstack, something happened. I don't exactly remember what, I think I was a bit to eager with cabal upgrade, and I upgraded some base packages that I shouldn't have. I remember directory-1.0.1.1 failing to build and having to go through MSYS to get it to build. Then more and more things needed to be upgraded and ghc-pkg check was reporting more and more weird things. When I thought I had upgraded everything, I tried happstack. happstack-util built, but then happstack-data reported that happstack-util couldn't be built because it required both time-1.1.4 and time-2-something. Oh oh. I screwed around for a while. Then I saw some posts on the web talking about removing package.conf.d. In my random experiments with cabal upgrade I had managed to install everything in my user profile and not inside my Haskell Platform directory, so there was hope!
I deleted package.conf.d. ghc-pkg list reported a saner list of packages. I tried building happstack again. Then something "funny" happened: every cabal build I was trying to do first tried to reinstall process-1.0.1.2, and failed every time!! And of course process-1.0.1.2 appeared in the ouput of ghc-pkg list... WTF? Ah, ok, directory-1.0.1.1 is still installed, let's kill the beast! And then everything worked! Happstack built, happstack "hello world" server even runs, life is happy again!
I didn't try to investigate why Cabal or directory-1.0.1.1 were insisting on reinstalling something that I have already. I've seen enough weird things and spent enough time this week away from the nice world of pure lazy functional programming and in dirty low-level configuration and build issues to not care too much anymore...

Wednesday, April 28, 2010

WXWidgets and WxHaskell on Windows, check!

Despite my earlier problems, I finally managed to install WXWidgets and WxHaskell on my Windows machine. Pfiu! I started again from scratch, and things went much better when I installed, as recommended, MinGW and MSYS in different folders. When I need to compile stuff, I only put MinGW in my path, and when I need Unixy tools like make I add MSYS. Of course MinGW has its own version of make, mingw32-make, that works when MSYS make doesn't...

So I installed MinGW in c:\dev\MinGW and:
set PATH=c:\dev\MinGW\bin;c:\dev\MinGW\libexec\gcc\mingw32\3.4.5;%PATH%

Then I modified the WxWidgets config.gcc file to set SHARED=1 UNICODE=1 MONOLITHIC=1.
And then:
mingw32-make -f makefile.gcc BUILD=debug
worked!

Setting new environment variables (wxWidgets is installed in c:\ui):
set WXWIN=c:\ui\wxWidgets-2.8.10
set WXCFG=gcc_dll\mswud

Then I could install then wxdirect, wxcore and wx packages using a few more flags than what a default cabal install would do, for example:
runhaskell Setup configure --extra-lib-dirs=c:\dev\MinGW\lib --extra-include-dirs=c:\dev\MinGW\include --extra-include-dirs=c:\dev\MinGW\include\c++\3.4.5\mingw32 --extra-include-dirs=c:\ui\wxWidgets-2.8.10\include --extra-include-dirs=c:\dev\MinGW\include\c++\3.4.5

And it install, and works!

Thursday, April 22, 2010

The sorry plight of a Windows programmer (aka me)

Having secured fame and fortune with MazesOfMonad, my console based RPG, and having some ideas for some games that would look even better with a graphical front end, I decided it was time to take the bulls by the horn and go seriously into Haskell GUI programming. Well, the bull won and I feel truly gored...
So what's wrong? Well, after fooling around on my Windows 7 computer for a few nights I've achieved nothing. I now have three different copies of MinGW installed in different places. I have tried to installed SDL, GTK2HS, WXWidgets and Happstack and I have failed at all of them. I know WXWidgets is manageable because I've done it before, I only seem to have some include paths issues. libSDL itself installed but not the Haskell bindings. I found somebody with the same problem on the web, that then told me he had given up. I even managed to get Darcs errors with Gtk2Hs. In despair I thought I could develop a web based game but even the darcs version of happstack failed to compile, I remembered then some bug mentioned on the mailing list, so I should probably just download the latest stable version instead of the development branch.
Some problems are down to some silly things I did. For example installing the Haskell Platform in a directory with spaces is not a great idea (I don't remember if it was the default), because then a lot of paths referencing its MinGW install are wrong in the MSYS shell...
But most are due to the Unixy nature of a lot of the GUI components. Now I see a configure script and my blood freezes. I run "cabal install foo" and I get the dreaded message "this package has a configure script, you need to dive into Unix hell". Can you tell me why I have three versions of GCC, and two make a configure script crash with "c compiler cannot create executables" and the third one works? Where can I download one single file that will allow me to have a full MSYS/MinGW setup with most of what I need? I'm sure there are solutions to a lot of the problems I have, and maybe I need to spend more time reading the INSTALL files and such, and download everything from the MinGW files page. But why can't it Just Work? I mean, I work with Java at work, we do GUI work in SWT now and before that in Swing, we use a lot of libraries, and we never run into any installation problem. Maybe once or twice over the past couple of years we had a cross-platform compatibility issue or a library conflict. I know, I know, apples and oranges, but I'm now thinking that writing my own lazy, purely functional language with monads on top of the JVM with Java integration is going to be easier than getting a GUI library with Haskell bindings to work.
I like to write applications, not spend my free time trying to get libraries to install.

Friday, April 09, 2010

Found my neural network bug (three years later)

After the university of Waterloo AI Challenge I went back to a bit of AI programming in Haskell. Three years ago I had written some code to implement a neural network, and it didn't seem to learn as fast as the book said it should. I had not worked on that any more, but I decided to look at it again. And I found a bug!! I mixed up the deltas to pass on from one run to the next. Changing two lines of code makes my network learning much faster.
So now onto something a bit more meaty than exclusive or: very simple character recognition. I want to be able to recognize numbers written as a digital alarm clock would display them (at least mine): you have 7 lights, four vertical and three horizontal, and various patterns make the numbers. If all lights are on, it's 8, if only the middle horizontal light is off we have zero, etc...
I just test things with a network that has 7 input neurons (one for each light), 10 output neurons (one for each number) and 8 hidden neurons (the average between input and output)

g<-getStdGen
n<-network 7 8 10
let trainingSet=[
([1,1,1,-1,1,1,1],[1,0,0,0,0,0,0,0,0,0]), -- 0
([-1,-1,1,-1,-1,1,-1],[0,1,0,0,0,0,0,0,0,0]), -- 1
([1,-1,1,1,1,-1,1],[0,0,1,0,0,0,0,0,0,0]), -- 2
([1,-1,1,1,-1,1,1],[0,0,0,1,0,0,0,0,0,0]), -- 3
([-1,1,1,1,-1,1,-1],[0,0,0,0,1,0,0,0,0,0]), -- 4
([1,1,-1,1,-1,1,1],[0,0,0,0,0,1,0,0,0,0]), -- 5
([1,1,-1,1,1,1,1],[0,0,0,0,0,0,1,0,0,0]), -- 6
([1,1,1,-1,-1,1,-1],[0,0,0,0,0,0,0,1,0,0]), -- 7
([1,1,1,1,1,1,1],[0,0,0,0,0,0,0,0,1,0]), -- 8
([1,1,1,1,-1,1,1],[0,0,0,0,0,0,0,0,0,1]) -- 9
]
let (LNS(n',err,ds),e) = run (initialState n) defaultLearningRate trainingSet (1,1000)

And after something like 150 iterations I get a network that has a low error rate. A little helper method to massage the results:

runNumber n inputs=do
let
(_, output)=exec n inputs
normalized=map (\v->if (1-v)>0.5 then 0 else 1) output
return $ elemIndex 1 normalized

Et voila!

Main> runNumber nn [1,-1,1,1,-1,1,1]
Just 3

Now, things are starting to get interesting! Hopefully I won't wait another three years before trying maybe more complex character recognition.

Thursday, February 11, 2010

Waterloo Contest and Human Intelligence

I've entered the Waterloo university Google AI Challenge, hiding under a cunning nickname, since I saw that you could develop in Haskell, and a few others Haskellers are on it too! At the moment, I'm cruising at around the 110th position, which is not too bad given that I only worked a few hours at night on it (I have a day job, you know...) and that I implemented everything myself since the starter pack wasn't ready when I started working. I've played a bit with the standard strategies, implemented some flood fill, reuse some A* implementation I have.
All of this is interesting, of course, but there is no AI as such. The only intelligence in my code is the one I put in, and the strategies I devised. So I'm thinking of playing around with genetic programming and evolution techniques to try to get my code to find the right strategies at the right time. I have the feeling the trick will be to design something that is not too restricted and is truly able to evolve into some more advanced and complex than what I've designed myself. But hey, I'll learn, even if my program doesn't (-:

Monday, February 01, 2010

EclipseFP 1.110 released, with debugging support

Hello,
I have released a new version of EclipseFP. OK, I haven't kept to the one-release-a-month schedule, but with the Christmas season and everything, I deserved a break.
I have fixed a few issues here and there (thanks for everybody who commented on the blog or used the Sourceforge tracker or forum), and tried to externalize strings into resources, but there's still a lot to be done on that subject.
I have mainly worked at integrating the GHCi debugger with the Eclipse debugging framework. This tutorial was really useful! Now what works is that you can set breakpoints in the source, and GHCi will stop at them if launched in debug mode in Eclipse. Once stopped (either via a breakpoint or if you use :step) you can see the bindings and force their evaluation (use the set value to variable function in Eclipse). You can also create some expressions and get them evaluated. Underneath with use GHCi commands like :show bindings, etc.
I have removed some obsolete preferences and added debugging preferences (stop on error/exception, use show instances...).
I'll be happy with any feedback on all these features. One thing missing is support for :hist when invoking :trace, tell me if you'd like that.
Enjoy!

Monday, November 23, 2009

EclipseFP 1.109.0 is out!

A month exactly after the previous release, I got another release of EclipseFP out! I tried to fix some of the issues that people reported, to improve the overall stability of the plugins and incorporated some new features that I hope could be useful (generation of extensions and ghc-options field in Cabal from the preferences, some quick fixes on GHC warnings...). I'll need feedback from users to know what features are really needed and could make a different to Haskell hackers.

On a related note, I read that in "Coders at Work" Simon Peyton-Jones talks about the need for Visual Studio and Eclipse based IDEs to get more people interested in functional languages. I hope that my modest contribution helps...

Friday, October 23, 2009

Releasing my code on the unsuspecting public (EclipseFP)

Today I've released a new version of EclipseFP (Haskell support in Eclipse). There are quite a few changes in it, and there are even some changes in the Scion library that I've made and that Nominolo integrated. So people can give it a try if they want: get the current Scion source code, build/install it, and get the 1.108 version of EclipseFP.
Hopefully that's another step for me in getting more proficient in Haskell: interacting with other Haskell hackers, learning from them and their code, and releasing code that may actually been of use to somebody (or annoy them no end if it doesn't work) instead of only a little game as before. And of course working on an Haskell IDE, even in Java, gets me to learn new things about the language, the options for Cabal or GHC, etc...
Anyway, I hope I'll get some feedback on the new EclipseFP, and that we can move that thing forward!

Friday, October 09, 2009

What client for an Haskell Multi Player Game?

I've been thinking again, in between working on eclipsefp, about writing a multi player role-playing game in Haskell. OK, I'm not going to write something the size of WoW by myself in my free time (unfortunately my day job doesn't involve any game programming nor any Haskell hacking :-(), but since I enjoyed writing Mazes of Monad, I figured that a game is a good way to learn things. A MMORPG should get me into concurrency, persistence, graphics, performance etc...

What I'm trying to figure out at the moment is what architecture to use. Mainly, if I have no doubt there is going to be a server somewhere running Haskell code, I wonder about the client. Should I try to write a browser based game coupled with a Haskell web server? That way, users could easily play the game when (if) it gets to a usable state, and I won't have to worry about cross platform issues for the client. Getting proper server side events or push should be fun, and of course if I end up using something like web sockets I will probably require a fairly cutting edge browser, which negates the advantage. I'm not too sure meddling with AJAX long polling and dynamic HTML is such a attractive option.
The other path is to write a Haskell client. Granted, users will have to download it, which means either me dealing with cross platform issues (provide several exes) or restricting my users to people happy to use cabal install. But I'll get maybe into stuff like OpenGL in Haskell, and do everything in Haskell instead of mixing Haskell with HTML and JavaScript (I've just stumbled upon jmacro, which could be cool I guess).

I'm sure everybody has faced that question at some stage. Ah well, for the moment I'm still wondering what my game will do, so I guess I have time to play with a few things before I make my mind up.

Monday, September 28, 2009

Haskell development in Eclipse

I have started contributing to the eclipsefp project. For those who don't know, it's a Eclipse-based environment for developing in Haskell.
EclipseFP has had a troubled history so far. Leif started the project and developed an impressive set of features. He then pursued the Cohatoe project to be able to write Eclipse plugins inm Haskell, in a effort to avoid rewriting in Java all kind of things that already existed in Haskell. And then this summer Thomas started integrating with Scion, which is a Haskell library that communicates with JSON data over simple sockets with non Haskell clients. Scion offers method to load Cabal projects, type check files, etc... That work is not finished, but Leif and Thomas have done such a great job that it's easy enough now to finish the missing bits here and there.
This is perfect for me: part of the work is in Java and Eclipse, which is a lot of what I do in my day job, so I can bring a lot of experience there. Scion is a small enough library that need to be extended to provide me with all I want, so I can do little enhancements in Haskell and slowly build on features. And, last but not least, I need to learn the GHC API (which is what Scion uses to parse, check and compile files), to find what I need for.
This will only enhance my knowledge of Haskell, and I hope that I'll be able to make something useful to provide Haskell hackers with another IDE to develop in.
For those interested, the github repositories where my current work can be found are:

git://github.com/JPMoresmau/scion.git
git://github.com/JPMoresmau/eclipsefp.git

Friday, August 07, 2009

Yoohoo: mazes of monad is kind of popular!

Don Stewart has published some statistics on packages popularity here . MazesOfMonad is the fastest rising application over the past 4 months. Thanks to all people who downloaded it, hope you enjoyed it, feel free to send me bug reports or enhancement requests. Hopefully I'll find the free time (in between day work, family, sports, etc.) to develop another more ambitious game in the coming months!

Friday, July 10, 2009

Mazes of Monad: 1.0.5 and (maybe) final version uploaded

I've uploaded yesterday on Hackage the 1.0.5 version of my Role Playing Game called Mazes of Monad. I think this will be the last upload unless some big bugs are found. But I think I'll stop here in terms of functionality. It was fun, I learned a lot about Haskell, both on programming with the language, and on how to design Haskell code. Working on a full project over a long period of time forced me to actually understand code I had written previously and make it evolve instead of just having a clean slate.

I also realized than while developing a game is a lot of fun, the actual programming is the, ahem, easy part. I find that the actual game design (how to get usable and useful features) and the tweaking of the difficulty, for example, are the most challenging. For a while my test character was quite a strong warrior and I thought the game was easy, so I changed some settings to make it harder, then I realized that I had made it quite hard to start with a poor naked wizard that knew only one spell... I can see that balancing all the different game settings and getting the right formulas is critical, and that only comes from testing and collecting feedback...

Anyway, I have ideas for other games, probably the next one will be web based to have some kind of GUI and be multi player, maybe using happstack as the server (or should I just use a little home grown server in Haskell, for fun?). I'll keep on posting about that!

Friday, May 15, 2009

Adding a Writer Monad transformer

I'm still working on MazesOfMonad, my Haskell RPG, now on code.haskell.org. It's actually fun to now have a working program and think about extensions in functionality, and then how to implement them in Haskell, instead of just playing around with the language.
I wanted to give the user more feedback that they currently got about what was going on in the game, and at the same time I wasn't happy with what I had written previously: I had the output messages as part of the method signature:

myFunction :: Type1 -> Type2 -> (Type3,[String])

So I decided a Monad was called for. The Writer Monad, in this case, which lets us append stuff together to get it all back at the end. I already had most of my game running in a monad transformer, so it was **just** a matter of figuring out how to wrap everything in a WriterT. I had a bit of fun at first, then got it working. So now my game runs in a

WriterT a (RandT (StateT b IO)) c

Where a b a are various types for the things I need: a is the type of messages I write, b the state I keep, and c the return value. RandT gives me random numbers, StateT gives me a state, IO allows me to do IO operations when I need to save the game or something like that.

The amazing thing, well, for me, is that a lot of my functions do not need to be aware of the whole transformer stack, but of only the type classes that they care about. My final transformer is an instance of both MonadWriter and MonadRandom. So my method that needs to write messages becomes:

myFunction :: (MonadWriter [String] m)=>Type1 -> Type2 -> m Type3

And I can write my code calling the MonadWriter methods to add messages. It's in a monad, but it's still purely functional: I return a Type3 object and write Strings. If my function needs random numbers:

myFunction :: (MonadRandom m, MonadWriter [String] m)=>Type1 -> Type2 -> m Type3

and I get random numbers capabilities!

The final code is now much cleaner than before (I actually don't use [String] directly for the MonadWriter type but an alias for it, so I can replace it with Seq String or something faster the day I fancy it). I think I now understand monads and monad type classes better, and I understand better how using a monad doesn't automatically mean using the IO Monad and lose purity. Haskell Nirvana is not far off, says he naively...

Monday, April 27, 2009

Haskell RPG Game uploaded on Hackage!

I've worked on it on and off for months, I've rewritten parts of it entirely, it gave me the opportunity to knock together a real, complete application in Haskell, and it's finally done! I've uploaded my console based RPG game (read: nethack clone) to Hackage (get it here). You create a character and walk through mazes, trading, stealing, casting spells and fighting monsters.
You can look at the source code (no documentation, sorry) and see what I've used. I've used a monad to abstract away the state and the random generator (so I can plug a non random generator for testing, one that for example will guarantee that the next throw of the D20 die will give 8). I have developped my own little framework for console handling (executing actions from what the user typed in and displaying results) maybe I should look at the existing libraries and try to plug one in instead.
What I found was the most awkward was the record syntax for nested structures, I'll have to investigate to find an easier way to read and update my structures.
There are a few HUnit tests to verify that the most basic functionality works, so hopefully there is no huge bug.
It was fun to play around with Haskell and with game concepts. I hope I'll have time to work on even cooler stuff like adding AI for the monsters (so they can decide when to run away, etc...).
Any feedback on the game mechanism or on the code itself is welcome!

Friday, February 27, 2009

A high-level GUI library for Haskell

There seems to still be a void in the Haskell world regarding GUI libraries. On one hand we have low level libraries based on well know solutions but doing everything in the IO monad, to cut short, on another functional reactive libraries that just don't seem to be accessible for haskell programmers of my level, somehow... And I'm not the only one: just the other day I saw a new attempt being made at a functional easy to use GUI library: Barrie. It looks promising, but I had already started my own, so I think I'm going to continue a little bit, I find I'm learning a lot in the process...

The ideas being my own library, tentatively called UITree are the following:

- you build your GUI using combinators that start from simple widgets and use composite widgets and layouts to augment a tree
- event handler can be attached to widgets in the tree. The handlers are of type (ui, state)->IO(ui,state): you get the current ui tree and the current state, and you transform them. The IO bit is to allow you to do IO actions if you want but you can do without.

- inside the event handlers you manipulate the ui tree a bit like JQuery does with HTML DOM: you can select particular widgets, operate on them, etc... Most operations are just transformers from the UITree type to the same type, so you can chain them.

- since you can't reference directly the widgets in your fonctional tree, you can give them explicit ids, so there is a disconnect between the actual tree and the handlers, which is similar to JQuery (you cannot rely on the compiler to make sure you manipulate an existing widget).


For the moment I got some small samples to work with WXWidgets (Barrie uses GTK, by the way).
The API is bound to change, but this is how a frame containing a close button and a button that increases a internal integer state and displays the current state:



simpleState :: IO (Int)
simpleState=do
start simpleUI 0 -- start the WXWidgets UI with the given UI tree and initial state
where
simpleUI=(frame [text := "Simple"]) -- the enclosing frame
(
(panel []) -- a panel to enclose our children
(row [space := 5] -- children in a row, a bit of space
[
(button [text := "0"] [command :> (return . doInc)]), -- a button showing 0 and increasing the state when clicked
(button [wid:="close", text := "Close!"] [command :> (pureUI doClose)]) -- a button to close the frame, pureUI is just a helper function that shows we're not modifying the state
]
)
)
doInc (tree,st)=(set [text := (show (st+1))] tree,(st+1)) -- set the button text and state to state + 1
doClose tree=close $ top tree -- close the top of the tree (the frame)

Note how the ui building and the handlers are pure functions, so you just manipulate your tree and your state.