Skip to content

Commit eb2a659

Browse files
cleanup: move game state from globals into game function
1 parent cae7a31 commit eb2a659

File tree

2 files changed

+82
-57
lines changed

2 files changed

+82
-57
lines changed

examples/snake.clj

Lines changed: 72 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,87 +2,104 @@
22
; Abhishek Reddy's snake: http://www.plt1.com/1070/even-smaller-snake/
33
; Mark Volkmann's snake: http://www.ociweb.com/mark/programming/ClojureSnake.html
44

5+
; The START:/END: pairs are production artifacts for the book and not
6+
; part of normal Clojure style
7+
58
(ns examples.snake
69
(:import (java.awt Color) (javax.swing JPanel JFrame Timer JOptionPane)
710
(java.awt.event ActionListener KeyListener))
811
(:use clojure.contrib.import-static
912
[clojure.contrib.seq-utils :only (includes?)]))
1013
(import-static java.awt.event.KeyEvent VK_LEFT VK_RIGHT VK_UP VK_DOWN)
1114

12-
; Game board and coordinates. points are [x,y] vectors
15+
; ----------------------------------------------------------
16+
; functional model
17+
; ----------------------------------------------------------
18+
; START: constants
1319
(def width 75)
1420
(def height 50)
1521
(def point-size 10)
1622
(def turn-millis 75)
1723
(def win-length 5)
24+
(def dirs { VK_LEFT [-1 0]
25+
VK_RIGHT [ 1 0]
26+
VK_UP [ 0 -1]
27+
VK_DOWN [ 0 1]})
28+
; END: constants
1829

30+
; START: board math
1931
(defn add-points [& pts]
2032
(vec (apply map + pts)))
2133

2234
(defn point-to-screen-rect [pt]
2335
(map #(* point-size %)
2436
[(pt 0) (pt 1) 1 1]))
37+
; END: board math
2538

26-
(def dirs { VK_LEFT [-1 0]
27-
VK_RIGHT [ 1 0]
28-
VK_UP [ 0 -1]
29-
VK_DOWN [ 0 1]})
30-
31-
; apple
32-
(def *apple* (ref nil))
33-
39+
; START: apple
3440
(defn create-apple []
3541
{:location [(rand-int width) (rand-int height)]
3642
:color (Color. 210 50 90)
3743
:type :apple})
44+
; END: apple
3845

39-
; snake
40-
(def *snake* (ref nil))
41-
46+
; START: snake
4247
(defn create-snake []
4348
{:body (list [1 1])
4449
:dir [1 0]
4550
:type :snake
4651
:color (Color. 15 160 70)})
52+
; START: snake
4753

48-
54+
; START: move
4955
(defn move [{:keys [body dir] :as snake} & grow]
5056
(assoc snake :body (cons (add-points (first body) dir)
5157
(if grow body (butlast body)))))
58+
; END: move
5259

60+
; START: turn
5361
(defn turn [snake newdir]
5462
(if newdir (assoc snake :dir newdir) snake))
63+
; END: turn
5564

65+
; START: win
5666
(defn win? [{body :body}]
5767
(>= (count body) win-length))
68+
; END: win
5869

70+
; START: lose
5971
(defn head-overlaps-body? [{[head & body] :body}]
6072
; have proposed to SS that argument order be reversed:
6173
(includes? head body))
6274

6375
(def lose? head-overlaps-body?)
76+
; END: lose
6477

65-
(defn collision? [{[snake-head] :body} {apple :location}]
78+
; START: eats
79+
(defn eats? [{[snake-head] :body} {apple :location}]
6680
(= snake-head apple))
81+
; END: eats
6782

68-
; state updates
83+
; ----------------------------------------------------------
84+
; mutable model
85+
; ----------------------------------------------------------
6986
(defn update-positions [snake apple]
7087
(dosync
71-
(if (collision? @snake @apple)
88+
(if (eats? @snake @apple)
7289
(do (ref-set apple (create-apple))
7390
(alter snake move :grow))
7491
(alter snake move))))
7592

7693
(defn update-direction [snake newdir]
7794
(dosync (alter snake turn newdir)))
7895

79-
(defn reset-game []
80-
(dosync (ref-set *apple* (create-apple))
81-
(ref-set *snake* (create-snake))))
82-
83-
(reset-game)
96+
(defn reset-game [snake apple]
97+
(dosync (ref-set apple (create-apple))
98+
(ref-set snake (create-snake))))
8499

85-
; drawing
100+
; ----------------------------------------------------------
101+
; gui
102+
; ----------------------------------------------------------
86103
(defn fill-point [g pt color]
87104
(let [[x y width height] (point-to-screen-rect pt)]
88105
(.setColor g color)
@@ -96,40 +113,38 @@
96113

97114
(defmethod paint :apple [g {:keys [location color]}]
98115
(fill-point g location color))
99-
100-
; gui elements
101-
(def frame (JFrame. "Snake"))
102-
103-
(def panel
104-
(proxy [JPanel ActionListener KeyListener] []
105-
(paintComponent [g]
106-
(proxy-super paintComponent g)
107-
(paint g @*snake*)
108-
(paint g @*apple*))
109-
(actionPerformed [e]
110-
(update-positions *snake* *apple*)
111-
(when (lose? @*snake*)
112-
(reset-game)
113-
(JOptionPane/showMessageDialog frame "You lose!"))
114-
(when (win? @*snake*)
115-
(reset-game)
116-
(JOptionPane/showMessageDialog frame "You win!"))
117-
(.repaint this))
118-
(keyPressed [e]
119-
(update-direction *snake* (dirs (.getKeyCode e))))
120-
(keyReleased [e])
121-
(keyTyped [e])))
122-
123-
(def timer (Timer. turn-millis panel))
124-
125-
(doto panel
126-
(.setFocusable true)
127-
(.addKeyListener panel))
128-
129-
(doto frame
130-
(.add panel)
131-
(.setSize (* width point-size) (* height point-size))
132-
(.setVisible true))
133-
(.start timer)
116+
117+
(defn game []
118+
(let [snake (ref (create-snake))
119+
apple (ref (create-apple))
120+
frame (JFrame. "Snake")
121+
panel (proxy [JPanel ActionListener KeyListener] []
122+
(paintComponent [g]
123+
(proxy-super paintComponent g)
124+
(paint g @snake)
125+
(paint g @apple))
126+
(actionPerformed [e]
127+
(update-positions snake apple)
128+
(when (lose? @snake)
129+
(reset-game snake apple)
130+
(JOptionPane/showMessageDialog frame "You lose!"))
131+
(when (win? @snake)
132+
(reset-game snake apple)
133+
(JOptionPane/showMessageDialog frame "You win!"))
134+
(.repaint this))
135+
(keyPressed [e]
136+
(update-direction snake (dirs (.getKeyCode e))))
137+
(keyReleased [e])
138+
(keyTyped [e]))
139+
timer (Timer. turn-millis panel)]
140+
(doto panel
141+
(.setFocusable true)
142+
(.addKeyListener panel))
143+
(doto frame
144+
(.add panel)
145+
(.setSize (* width point-size) (* height point-size))
146+
(.setVisible true))
147+
(.start timer)
148+
[snake, apple, timer]))
134149

135150

reader/snake.clj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
; START: namespace
2+
(ns reader.snake
3+
(:import (java.awt Color) (javax.swing JPanel JFrame Timer JOptionPane)
4+
(java.awt.event ActionListener KeyListener))
5+
(:use clojure.contrib.import-static
6+
[clojure.contrib.seq-utils :only (includes?)]))
7+
(import-static java.awt.event.KeyEvent VK_LEFT VK_RIGHT VK_UP VK_DOWN)
8+
; END: namespace
9+
10+
; TODO: implement the Snake!

0 commit comments

Comments
 (0)