Skip to content

Instantly share code, notes, and snippets.

@quephird
Last active August 29, 2015 14:10
Show Gist options
  • Save quephird/aefc8f95308b977055a5 to your computer and use it in GitHub Desktop.
Save quephird/aefc8f95308b977055a5 to your computer and use it in GitHub Desktop.

The struggle I'm having isn't that I can't do I/O; it's that I can! ;P

Seriously, though, it's that I can do so in places that I'm pretty sure that I shouldn't. That is, my understanding is that things that are dirty, like anything involving I/O, should be sectioned off from everything else that is pure, as much as possible. This makes more of your code understandable, testable, and reusable.

Indeed, I have striven to do that but there are places where I have taken the easy way out in the middle of routines that "update" state. Below is an example from my game (you can look at the entire source here, https://github.com/quephird/space-invaders/blob/master/src/space_invaders/core.clj):

(defn key-pressed [{{sound :sound} :player-bullets :as state}
                    {key           :key
                     key-code      :key-code       :as event}]
  "Primary hook to return new version of game state taking into account:
   * moving the player left or right
   * generating a new bullet"
  (cond
    (and (= :s key) (game-over? state))
      (reset-board state)
    (and (= 32 key-code) (no-player-bullets? state))
      (do
        (doto sound .rewind .play)
        (add-player-bullet state))
    (contains? #{:left :right} key)
      (move-player state event)
    :else
      state))

Everything here is pure except that one pesky line to play a sound clip. I do it here because 1) it's convenient, 2) I want it to play precisely when a bullet is created, and 3) I want it to play only once. As the rest of my code is written, there is really no way that is immediately apparent to me to accomplish these goals. (Well... 2 and 3 that is.)

There are other examples in my code where I either play a sound or draw something in response to a condition and I'm worried that as I add in features my code will become less and readable and reasonable.

The only idea I can think of is to add another part to my data structure that contains all of my state: an event queue of sorts. Instead of doing I/O directly in my "update" functions, I conj an event to the list of events passed into the function and return that new version of the state. Then in the main game loop, after all updating of state is completed, I can then start drawing and playing sounds. What worries me is 1) potentially overcomplicating my data structure, and 2) making it even harder to coordinate sounds and graphics with the conditions that should trigger them.

Does this sound logical?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment