FunSpIns - No attributes, No vectors, A tiny Workflow and more squares

20 Sep 2013 in software-development | haskell | fun-spin |

Functional space-invaders series

  1. A recap of Rob Ashton’s lessons - Das Intro
  2. Drawing a Rectangle
  3. Moving a Rectangle
  4. No attributes, No vectors, A tiny Workflow and more squares
  5. State, the World, the Loop
  6. The hero must move, the enemies must move smarter
  7. The hero shoots
  8. Collisions, the dead, and a (not so) grateful ending

Inspired by Rob Ashton’s series “Learn functional programming with me

This post kind of covers 3 of Rob’s posts, as, frankly, I can’t say much about attributes - in Rob’s case it refers to the DOM, which we don’t have here. Why he would mention Vectors? I don’t know, maybe to sound fancy ;)

When it comes to the working workflow in Haskell, my tool of choice right now is the interactive haskell (ghci) - saying :load game (provided I have a game.hs lying around) will either tell me that all is cool or throw relevant compiler errors at me, which usually is some form of elaborate type mismatch (90%) or a syntax error (9%) with an error margin of ±1%.

As to drawing lots of enemies, functional approaches should be somewhat similar. The permutation thing that Rob does with the (for [x […]] [y […]]) is also achieved with list comprehensions, just that you have to specify two ranges to feed the output. Here is a function to give us a grid of squares:

enemyGrid (originX,originY) (rows, cols) = 
  map toRect [ (x, y) | x <- take cols [originX,originX+60..], y <- take rows [originY,originY+30..]]
    toRect (x,y) = Rect x y 20 10

Providing two comma-separated values to the Haskell range (the [n..m] thing, or here, the [n, n+s, ..]) defines the step of the range. Due to the lazy nature of Haskell, I can leave the range open ended. The breaking condition is that I just take as many values as I want (columns and rows, respectively).

These little buggers need to be fed into the drawing API, which we can do as such:

canvas <- FX.getVideoSurface
mapM (\r -> FX.fillRect canvas (Just r) white) $ enemyGrid (10,10) (5, 10)
FX.flip canvas

mapM is again the monadic version of map*), required to create something wrapped in IO context. The (\r -> denotes the start of a lambda, while $ influences associativity such that I spare myself surrounding the enemyGrid call with brackets.

*) or Select, for the LINQ folks crazy enough to have followed this so far)


comments powered by Disqus