I, a Java imperative programmer, would like to understand how to generate a simple version of Space Invaders based on Functional Programming design principles (in particular Referential Transparency). However, each time I try to think of a design, I get lost in the morass of extreme mutability, the same mutability which is shunned by the functional programming purists.
As an attempt to learn Functional Programming, I decided to attempt to create a very simple 2D interactive game, Space Invader (note the lack of plural), in Scala using the LWJGL. Here are the requirements for the basic game:
-
User ship at the bottom of the screen moved left and right by the "A" and "D" keys respectively
-
User ship bullet fired straight up activated by the space bar with a minimum pause between shots to be .5 seconds
-
Alien ship bullet fired straight down activated by a random time of .5 to 1.5 seconds between shots
Things intentionally left out from the original game are WxH aliens, degradable defense barriers x3, high speed saucer ship at at the top of the screen.
Okay, now to the actual problem domain. For me, all the deterministic parts are obvious. It's the non-deterministic parts that seem to be blocking my ability to consider how to approach. The deterministic parts are the bullet's trajectory once they exist, the continuous motion of the alien and the explosion due to a hit on either (or both) of the player's ship or the alien. The non-deterministic parts (to me) are handling the stream of user input, handling fetching a random value for determining alien bullet firings and handling the output (both graphics and sound).
I can do (and have done) lots of this type of game development over the years. However, all of it was from the imperative paradigm. And LWJGL even provides a very simple Java version of Space invaders (of which I began moving to Scala using Scala as Java-without-semicolons).
Here are some links that talk around this area of which none seem to have directly dealt with the ideas in a way a person coming from Java/Imperative programming would understand:
It appears that there are some ideas in the Clojure/Lisp and Haskell games (with source). Unfortunately, I am not able to read/interpret the code into mental models that make any sense to my simpleminded Java imperative brain.
I'm so excited by the possibilities offered by FP, I can just taste the multi-threaded scalability capabilities. I feel like if were able to grok how something as simple as the time + event + randomness model for Space Invader can be implemented, segregating the deterministic and non-deterministic parts in a properly designed system without it turning into what feels like advanced mathematical theory; i.e. Yampa, I'd be set. If learning the level of theory Yampa seems to require to successfully generate simple games is necessary, then the overhead of acquiring all the necessary training and conceptual framework will vastly outweigh my understanding of the benefits of FP (at least for this over-simplified learning experiment).
Any feedback, proposed models, suggested methods of approaching the problem domain (more specific than the generalities covered by James Hague) would be greatly appreciated.
Best Answer
An idiomatic Scala/LWJGL implementation of Space Invaders wouldn’t look that much like a Haskell/OpenGL implementation. Writing a Haskell implementation might be a better exercise in my opinion. But if you want to stick with Scala, here are some ideas for how to write it in functional style.
Try to use immutable objects only. You could have a
Game
object which holds aPlayer
, aSet[Invader]
(be sure to useimmutable.Set
), etc. GivePlayer
anupdate(state: Game): Player
(it could also takedepressedKeys: Set[Int]
, etc), and give the other classes similar methods.For randomness,
scala.util.Random
isn’t immutable like Haskell’sSystem.Random
, but you could make your own immmutable generator. This one is inefficient but it demonstrates the idea.For keyboard/mouse input and rendering, there’s no way around calling impure functions. They’re impure in Haskell too, they’re just encapsulated in
IO
etc. so that your actual function objects are technically pure (they don’t read or write state themselves, they describe routines which do, and the runtime system executes those routines).Just don’t put I/O code in your immutable objects like
Game
,Player
andInvader
. You can givePlayer
arender
method, but it should look likeUnfortunately this doesn’t fit well with LWJGL since it’s so state-based, but you could build your own abstractions on top of it. You could have an
ImmutableCanvas
class that holds an AWTCanvas
, and itsblit
(and other methods) could clone the underlyingCanvas
, pass it toDisplay.setParent
, then perform the rendering and return the newCanvas
(in your immutable wrapper).Update: Here is some Java code showing how I would go about this. (I would have written almost the same code in Scala, except that an immutable set is built-in and a few for-each loops could be replaced with maps or folds.) I made a player which moves around and fires bullets, but I didn’t add enemies since the code was getting long already. I made just about everything copy-on-write – I think this is the most important concept.