Friday, August 21, 2015

A Clojure Snake Game

I recently decided to write a Snake game in Clojure as a small project. In the game you have a snake that grows as it consumes food. The goal of the game is to make the snake as long as possible without self-intersecting. I got the idea to do it on a Saturday morning and by that night I was done. Most of the coding was done in the evening since I was doing family activities all day. All told, this was probably a 3 hour project. The entire program is 75 lines of code. That's awesome! This includes both the JVM and JavaScript targets via Clojure and ClojureScript.
Here's the game:

Here's how you play:
  • Use your arrow keys or w, a, s, d to change the snake's direction. Note that you probably will need to click on the canvas first to gain focus.
  • When the snake hits a green "food" pill it grows one unit longer.
  • When the snake intersects itself it resets to its original length.
  • Your score is the length of your snake.
The Program
Here's the program. Read past it for my observations and comments.

(ns snake.core
  (:require [quil.core :as q #?@(:cljs [:include-macros true])]
            [quil.middleware :as m]))

(def world { :width 100 :height 100 :food-amount 1000 })

(defn gen-food [] [(rand-int (world :width)) (rand-int (world :width))])

(defn replenish-food [initial amount]
  (loop [food initial] (if (>= (count food) amount) food (recur (conj food (gen-food))))))

(defn wrap [i m]
  (loop [x i] (cond (< x 0) (recur (+ x m)) (>= x m) (recur (- x m)) :else x)))

(defn grow-snake [{:keys [snake velocity] :as state}]
  (let [[px py] (map + (peek snake) velocity)]
    (assoc state :snake (conj snake [(wrap px (world :width)) (wrap py (world :height))]))))

(defn eat [{:keys [snake food] :as state}]
  (if-let [pellet (food (peek snake))]
    (-> state (update :food disj pellet))
    (-> state (update :snake subvec 1))))

(defn reset? [{:keys [snake] :as state}]
  (if (apply distinct? snake)
    (assoc state :snake [(peek snake)])))

(defn setup []
  (do (q/smooth)
      (q/frame-rate 30)
      {:snake [[50 50]] :velocity [1 0] :food (replenish-food #{} (world :food-amount))}))

(defn draw [{ :keys [snake food] }]
  (let [w (/ (q/width) (world :width))
        h (/ (q/height) (world :height))]
      (q/stroke-cap :round)
      (q/stroke-join :round)
      (q/background 0 0 0)

      (q/fill 0 255 0)
      (q/stroke 0 255 0)
      (doseq [[x y] food](q/rect (* w x) (* h y) w h))

      (q/fill 255 0 0)
      (q/stroke 255 0 0)
      (doseq [[x y] snake](q/rect (* w x) (* h y) w h))

      (q/fill 0 255 255)
      (q/text (str "Score: " (count snake)) 10 15))))

(defn launch-sketch [{:keys[width height host]}]
    :title "Snake"
    :setup setup
    :update #(-> % grow-snake eat (update :food replenish-food (world :food-amount)) reset?)
    :draw draw
    (fn [{ :keys [velocity] :as state} { :keys [key key-code] }]
      (case key
        (:a :left) (if (not= [1 0] velocity) (assoc state :velocity [-1 0]) state)
        (:d :right) (if (not= [-1 0] velocity) (assoc state :velocity [1 0]) state)
        (:w :up) (if (not= [0 1] velocity) (assoc state :velocity [0 -1]) state)
        (:s :down) (if (not= [0 -1] velocity) (assoc state :velocity [0 1]) state)
    :middleware [m/fun-mode]
    :size [width height]
    #?@(:cljs [:host host])))

;#?(:clj (launch-sketch { :width 400 :height 400 }))

#?(:cljs (defn ^:export launch-app[host width height]
           (launch-sketch { :width width :height height :host host})))

Observations and Comments
The state in this program is represented by a simple data structure containing a vector describing the snake, a vector representing the snake's velocity, and a set of coordinates representing the locations of food. You can see where I create this at the end of the setup function.There are also some constants in the world def.

To update the state, this program makes use of the common Clojure pattern of "threading state." Basically, you write your functions so that program state is passed in as an argument and a modified, updated version of the program state is returned. Functions with a single action or concern are written in this manner and then chained to form more complicated behaviors. It makes your program very easy to reason about. In this program you can see where this is done in the update method:

#(-> % grow-snake eat (update :food replenish-food (world :food-amount)) reset?)

For those unfamiliar with Clojure, I am using the "thread first" macro (The arrow). The # creates an anonymous function with the % as the passed in argument. The arrow takes the argument and feeds it through each function in succession (grow-snake, then eat, then updating food, and so on.).

At the program level, the Quil library handles passing state to each relevant function for processing. In "fun-mode" (functional mode), Quil functions hand you the initial state for modification in methods for program setup, update, and input. For drawing, state is passed in and there is no function output since you will draw your state to the screen. In other applications you can follow this same pattern of state management using Clojure's amazing concurrency primitives (atoms, agents, and refs). In fact Quil is just using an atom under the hood.

Other minor details:
  • There's a commented out form (;#?(:clj (launch-sketch { :width 400 :height 400 }))) towards the end. Uncomment this if you want to launch the file from a REPL. I leave it commented so that it doesn't launch when I do a cljsbuild.
  • For some reason, the (:gen-class) directive doesn't seem to have any effect in the cljc file, so I have a separate launcher.clj that defines a main method for uberjar builds. Clone the project if you want to see what I mean.
You can clone the project here.

Clojure continues to amaze me by repeatedly enabling me to do so much in such a small amount to time and code. Simple ideas, such as representing your domain as data structures vs. classes and the ability to thread your state throughout your program via functions make development in Clojure rapid and productive. Nowadays, whenever I get an itch to try out a new program or concept, I just sit down and model the domain as data and then start writing functions to manipulate the data. Before I know it, I end up with a complete program. It's a very powerful and fun way to write applications.

Thursday, August 6, 2015

Clojure is Homoiconic, Java is Not

Recently I was reading this article regarding what is (or was) new in Java 8 and took an interest in the following section on Lambdas:
Lambda Expressions, a new language feature, has been introduced in this release. They enable you to treat functionality as a method argument, or code as data. Lambda expressions let you express instances of single-method interfaces (referred to as functional interfaces) more compactly.
I did a quick Google search on "java lambdas" and this tutorial was the first hit. Once again, the same type of statement is made:
One issue with anonymous classes is that if the implementation of your anonymous class is very simple, such as an interface that contains only one method, then the syntax of anonymous classes may seem unwieldy and unclear. In these cases, you're usually trying to pass functionality as an argument to another method, such as what action should be taken when someone clicks a button. Lambda expressions enable you to do this, to treat functionality as method argument, or code as data.
The thing that struck me about these articles is the consistent statement that that Java Lambdas (a.k.a. anonymous instances of single method interfaces) are "code as data" because the function can be passed as an argument. I guess if you define data as "instances of classes or functions" then this description is fine, but when I read these articles this is what comes to mind:

Technically, the ability to assign a function to a variable or pass a function as an argument to another function means that your functions are "first class" which is a good thing. However, I would not call this "code as data." Another related term is "higher order functions." These are functions that take functions as their arguments or return functions as their results. Again, this is a very powerful language feature that Java now sort of does, but this is not "code as data."

What do I think of when I think of "code as data?" I think of "Homoiconicity." Google seems to agree, since when I type "code as data" into a search box the first thing that comes up is this Wikipedia article on Homoiconicity. Let's explore the concept in more detail.

What is Homoiconicity?
Often homoiconicity is defined one the following ways:
  • The code is written in the data structures of the language.
  • Code as data.
Those more familiar with the concept seem to use "Code as data" more, but I think the first definition is a bit more clear if you are getting started with the idea. Either way, I am going to illustrate the concept by showing how Clojure code is actually written as Clojure data structures. To contrast, I'll also show what "homoiconic Java" would look like. Finally, I'll show you a simple Clojure macro in which we rearrange our code/data.

Homoiconic Clojure
To start, consider the following core Clojure data structures:
  • () is an empty List.
  • {} is an empty Map.
  • [] is an empty Vector.
  • #{} is an empty Set.
When we write code in Clojure, it is expressed in terms of the above data structures. Here is a very simple example that applies the str function (it's kind of like .toString) to the number 42:
user=> (str 42)
You might look at this and think this is the same as the following Java code:
public static String str(Integer i){
    return i.toString();

The only difference is the location of the parenthesis, right? Wrong! (str 42) not actually the function str with the argument of 42, but is a list containing two elements, the str symbol and the value 42. When the Clojure evaluator sees a list it attempts to resolve the first symbol (str in this case) and calls that function with the remaining elements of the list as arguments. While this may seem like splitting hairs at the moment, this is very important when you get to macros. It is also crucial to the point that Clojure code is data.

Here's another one:
(defn add [a b](+ a b))
Again, you might think this is the same thing as this Java function:
public static int add(int a, int b){
    return a + b;
As before, they are not the same. The above Clojure add function is actually built up with two lists (one nested) and a vector of arguments. The inner list contains three symbols (+, a, and b) and the outer list contains the defn symbol, a symbol naming your function ("add"), a vector of arguments, and our inner list.

Here's another example:
(merge-with + { :x 1 :y 2 :z 3 } { :x 9 :y 2 :z 4 })
By this point you should see that we have a list, two maps, and a bunch of symbols and values as opposed to a function that adds two maps. Yes, the evaluator will merge the maps, but the code itself is data (as in the native data structures of the language).

You can do this type of structural breakdown with any Clojure code. Clojure programs are literal data structures consisting of nested literal data structures, values, and symbols. For more details, read this. The key takeaway is that we are indeed writing our code as a bunch of data (literal data structures containing nested data structures, values, and symbols).

Homoiconic Java
What if you wanted to write Java in its own data structures?

Here are our Java collection interfaces that correspond to the Clojure ones above:
  • java.util.List
  • java.util.Map
  • java.util.Vector
  • java.util.Set
Java, for some inexplicable reason, does not yet have collection literals, so this will be a very verbose exercise. My apologies up front.

Ok, now let's write some Java code in the data structures of the language:
    List add = new LinkedList();
Sadly, I have no way to evaluate this code, but, hey, it's data. I chose to use a String to represent the + operation because Java doesn't have symbols, either.

Here's another attempt at how I might construct some code as data in Java. I realize that this will be an utter failure, but you're just going to have to follow along as I write a ton of code to make my point.
    Map a = new HashMap();
    a.put("x", 1);
    a.put("y", 2);
    a.put("z", 3);

    Map b = new HashMap();
    b.put("x", 9);
    b.put("y", 2);
    b.put("z", 4);
    List mergemaps = new LinkedList();
Again, this can't be evaluated, but it's about as close as you can get to homoiconicity in Java.

Why Homoiconicity? - Macros
Consider this question: "What can you do with data?" Think about an Excel spreadsheet, a simple comma separated value file, or some JSON data. These are things you can easily sort, manipulate, and transform.

In the same way, Clojure code can be manipulated and transformed as data. In Clojure there is a facility for this called a macro. When Clojure evaluates macros, it does not evaluate the arguments as with a regular function. Rather, it passes the unevaluated arguments into the macro to be manipulated. Once this is done, the result is returned and evaluated.

Here's an example:
(defmacro bizzaro-math
  "Do everything the opposite of normal"
  [[op & rest]]
  (conj rest (case op
               + -
               - +
               * /
               / *
This macro takes its arguments and inverts the first argument if it is a basic arithmetic operator. Note that this would not be possible if the arguments were evaluated rather than treated as data. Here are some examples of it in use:
(bizzaro-math (+ 2 3))
=> -1
(bizzaro-math (- 2 3))
=> 5
(bizzaro-math (* 2 3))
=> 2/3
(bizzaro-math (/ 2 3))
=> 6
(bizzaro-math (rem 2 3))
=> 2
The most important thing to note here is that the inner forms (e.g. (+ 2 3)) are not evaluated, but are manipulated by the macro, then evaluated. This is the ultimate demonstration that we do, indeed, have code as data.

A more in-depth discussion of macros is beyond the scope of this post, but the main takeaway is that macros allow you to manipulate code. This is a feature unique to homoiconic languages.

Parting Thoughts
Code as data, a.k.a. homoiconicity, is the ability to write your code in the data structures of your language. This goes way beyond the simple ability to pass a function as an argument. If that's all you are looking for, Java might be all you need. If you want full on FP on the JVM, you've got better options. Scala is a great bridge language that has first class functions, higher order functions, partial evaluation, composition, and all the other things you'd expect from a functional language. Clojure, however, is King if want it all. Not only is it functional, it is truly homoiconic.

Further Reading:
Beating the Averages
Clojure For the Brave and True
Clojure Macros