In 35 lines. (It had to be done.)

Here’s a take on Jeremy’s Snake, implemented in Clojure (a Lisp for the JVM). As you can see, I’m still using the same library classes. Only the language is different.

A major difference is that this is the whole application code, including incantations and setup. Excerpting the Snake-specific bits would make it shorter — maybe under 30 lines even.

Still, it is a little messy for the sake of brevity. With a more liberal style, it could be a neat 50-100 line Snake.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
(import '(java.awt.event KeyEvent ActionListener KeyListener)
        '(javax.swing JPanel JFrame Timer))
 
(defn gen-apple [] {:x (int (* 790 (. Math (random)))) :y (int (* 590 (. Math (random))))})
 
(def snake (ref {:body (list {:x 10 :y 10}) :dir {:x 10 :y 0}}))
(def apple (ref (gen-apple)))
(def dirs {(. KeyEvent VK_LEFT) {:x -10 :y 0} (. KeyEvent VK_RIGHT) {:x 10 :y 0}
           (. KeyEvent VK_UP)   {:x 0 :y -10} (. KeyEvent VK_DOWN)  {:x 0 :y 10}})
 
(defn move [{body :body dir :dir :as snake} & opts]
  (merge snake {:body (cons {:x (+ (:x dir) (:x (first body)))
                             :y (+ (:y dir) (:y (first body)))}
                            (if (:grow (apply hash-map opts)) body (butlast body)))}))
 
(defn turn [snake newdir] (if newdir {:body (:body snake) :dir newdir} snake))
 
(defn collision? [{[b & bs] :body} a]
  (and (>= (+ 10 (:x b)) (:x a)) (<= (:x b) (+ 10 (:x a)))
       (>= (+ 10 (:y b)) (:y a)) (<= (:y b) (+ 10 (:y a)))))
 
(defn run-snake []
  (let [panel (proxy [JPanel ActionListener KeyListener] []
                (paintComponent [g] (proxy-super paintComponent g)
                                    (. g (fillRect (:x @apple) (:y @apple) 10 10))
                                    (doseq p (:body @snake) (. g (fillRect (:x p) (:y p) 10 10)))
                                    (if (collision? @snake @apple)
                                      (dosync (ref-set apple (gen-apple))
                                              (alter snake move :grow true))))
                (actionPerformed [e] (dosync (alter snake move))
                                     (. this (repaint)))
                (keyPressed [e] (dosync (alter snake turn (get dirs (. e (getKeyCode)))))))]
    (doto panel (setFocusable true) (addKeyListener panel))
    (doto (new JFrame "Snake") (add panel) (setSize 800 600) (setVisible true))
    (. (new Timer 75 panel) (start))))

Edit: fixed per cgrand’s comment.