|
49 | 49 | :dir [1 0]
|
50 | 50 | :type :snake
|
51 | 51 | :color (Color. 15 160 70)})
|
52 |
| -; START: snake |
| 52 | +; END: snake |
53 | 53 |
|
54 | 54 | ; START: move
|
55 | 55 | (defn move [{:keys [body dir] :as snake} & grow]
|
|
62 | 62 | (if newdir (assoc snake :dir newdir) snake))
|
63 | 63 | ; END: turn
|
64 | 64 |
|
65 |
| -; START: win |
| 65 | +; START: win? |
66 | 66 | (defn win? [{body :body}]
|
67 | 67 | (>= (count body) win-length))
|
68 |
| -; END: win |
| 68 | +; END: win? |
69 | 69 |
|
70 |
| -; START: lose |
| 70 | +; START: lose? |
71 | 71 | (defn head-overlaps-body? [{[head & body] :body}]
|
72 | 72 | ; have proposed to SS that argument order be reversed:
|
73 | 73 | (includes? head body))
|
74 | 74 |
|
75 | 75 | (def lose? head-overlaps-body?)
|
76 |
| -; END: lose |
| 76 | +; END: lose? |
77 | 77 |
|
78 |
| -; START: eats |
| 78 | +; START: eats? |
79 | 79 | (defn eats? [{[snake-head] :body} {apple :location}]
|
80 | 80 | (= snake-head apple))
|
81 |
| -; END: eats |
| 81 | +; END: eats? |
82 | 82 |
|
83 | 83 | ; ----------------------------------------------------------
|
84 | 84 | ; mutable model
|
85 | 85 | ; ----------------------------------------------------------
|
| 86 | +; START: update-positions |
86 | 87 | (defn update-positions [snake apple]
|
87 | 88 | (dosync
|
88 | 89 | (if (eats? @snake @apple)
|
89 | 90 | (do (ref-set apple (create-apple))
|
90 | 91 | (alter snake move :grow))
|
91 |
| - (alter snake move)))) |
| 92 | + (alter snake move))) |
| 93 | + nil) |
| 94 | +; END: update-positions |
92 | 95 |
|
| 96 | +; START: update-direction |
93 | 97 | (defn update-direction [snake newdir]
|
94 | 98 | (dosync (alter snake turn newdir)))
|
| 99 | +; END: update-direction |
95 | 100 |
|
| 101 | +; START: reset-game |
96 | 102 | (defn reset-game [snake apple]
|
97 | 103 | (dosync (ref-set apple (create-apple))
|
98 |
| - (ref-set snake (create-snake)))) |
| 104 | + (ref-set snake (create-snake))) |
| 105 | + nil) |
| 106 | +; END: reset-game |
99 | 107 |
|
100 | 108 | ; ----------------------------------------------------------
|
101 | 109 | ; gui
|
102 | 110 | ; ----------------------------------------------------------
|
| 111 | +; START: fill-point |
103 | 112 | (defn fill-point [g pt color]
|
104 | 113 | (let [[x y width height] (point-to-screen-rect pt)]
|
105 | 114 | (.setColor g color)
|
106 | 115 | (.fillRect g x y width height)))
|
| 116 | +; END: fill-point |
107 | 117 |
|
| 118 | +; START: paint |
108 | 119 | (defmulti paint (fn [g object & _] (:type object)))
|
109 | 120 |
|
110 |
| -(defmethod paint :snake [g {:keys [body color]}] |
111 |
| - (doseq [point body] |
112 |
| - (fill-point g point color))) |
113 |
| - |
114 |
| -(defmethod paint :apple [g {:keys [location color]}] |
| 121 | +(defmethod paint :apple [g {:keys [location color]}] ; <label id="code.paint.apple"/> |
115 | 122 | (fill-point g location color))
|
116 | 123 |
|
| 124 | +(defmethod paint :snake [g {:keys [body color]}] ; <label id="code.paint.snake"/> |
| 125 | + (doseq [point body] |
| 126 | + (fill-point g point color))) |
| 127 | +; END: paint |
| 128 | + |
| 129 | +; START: game-panel |
| 130 | +(defn game-panel [frame snake apple] |
| 131 | + (proxy [JPanel ActionListener KeyListener] [] |
| 132 | + (paintComponent [g] ; <label id="code.game-panel.paintComponent"/> |
| 133 | + (proxy-super paintComponent g) |
| 134 | + (paint g @snake) |
| 135 | + (paint g @apple)) |
| 136 | + (actionPerformed [e] ; <label id="code.game-panel.actionPerformed"/> |
| 137 | + (update-positions snake apple) |
| 138 | + (when (lose? @snake) |
| 139 | + (reset-game snake apple) |
| 140 | + (JOptionPane/showMessageDialog frame "You lose!")) |
| 141 | + (when (win? @snake) |
| 142 | + (reset-game snake apple) |
| 143 | + (JOptionPane/showMessageDialog frame "You win!")) |
| 144 | + (.repaint this)) |
| 145 | + (keyPressed [e] ; <label id="code.game-panel.keyPressed"/> |
| 146 | + (update-direction snake (dirs (.getKeyCode e)))) |
| 147 | + (keyReleased [e]) |
| 148 | + (keyTyped [e]))) |
| 149 | +; END: game-panel |
| 150 | + |
| 151 | +; START: game |
117 | 152 | (defn game []
|
118 |
| - (let [snake (ref (create-snake)) |
| 153 | + (let [snake (ref (create-snake)) ; <label id="code.game.let"/> |
119 | 154 | apple (ref (create-apple))
|
120 | 155 | 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])) |
| 156 | + panel (game-panel frame snake apple) |
139 | 157 | timer (Timer. turn-millis panel)]
|
140 |
| - (doto panel |
| 158 | + (doto panel ; <label id="code.game.panel"/> |
141 | 159 | (.setFocusable true)
|
142 | 160 | (.addKeyListener panel))
|
143 |
| - (doto frame |
| 161 | + (doto frame ; <label id="code.game.frame"/> |
144 | 162 | (.add panel)
|
145 | 163 | (.setSize (* width point-size) (* height point-size))
|
146 | 164 | (.setVisible true))
|
147 |
| - (.start timer) |
148 |
| - [snake, apple, timer])) |
149 |
| - |
| 165 | + (.start timer) ; <label id="code.game.timer"/> |
| 166 | + [snake, apple, timer])) ; <label id="code.game.return"/> |
| 167 | +; END: game |
150 | 168 |
|
0 commit comments