3
3
; Mark Volkmann's snake: http://www.ociweb.com/mark/programming/ClojureSnake.html
4
4
5
5
(ns examples.snake
6
- (:import (java.awt Color) (javax.swing JPanel JFrame Timer)
6
+ (:import (java.awt Color) (javax.swing JPanel JFrame Timer JOptionPane )
7
7
(java.awt.event ActionListener KeyListener))
8
- (:use clojure.contrib.import-static))
8
+ (:use clojure.contrib.import-static
9
+ [clojure.contrib.seq-utils :only (includes? )]))
9
10
(import-static java.awt.event.KeyEvent VK_LEFT VK_RIGHT VK_UP VK_DOWN)
10
11
11
12
; Game board and coordinates. points are [x,y] vectors
12
13
(def width 75 )
13
14
(def height 50 )
14
15
(def point-size 10 )
15
16
(def turn-millis 75 )
17
+ (def win-length 5 )
16
18
17
19
(defn add-points [& pts]
18
20
(vec (apply map + pts)))
27
29
VK_DOWN [ 0 1 ]})
28
30
29
31
; apple
30
- (defn random- apple [] [( rand-int width) ( rand-int height)] )
32
+ (def * apple* ( ref nil ) )
31
33
32
- (def *apple* (ref (random-apple )))
34
+ (defn create-apple []
35
+ {:location [(rand-int width) (rand-int height)]
36
+ :color (Color. 210 50 90 )
37
+ :type :apple })
33
38
34
39
; snake
35
- (def *snake* (ref {:body (list [1 1 ]) :dir [1 0 ]}))
40
+ (def *snake* (ref nil ))
41
+
42
+ (defn create-snake []
43
+ {:body (list [1 1 ])
44
+ :dir [1 0 ]
45
+ :type :snake
46
+ :color (Color. 15 160 70 )})
47
+
36
48
37
49
(defn move [{:keys [body dir] :as snake} & grow]
38
50
(assoc snake :body (cons (add-points (first body) dir)
41
53
(defn turn [snake newdir]
42
54
(if newdir (assoc snake :dir newdir) snake))
43
55
44
- ; per-game-turn update
45
- (defn collision? [{[snake-head] :body } apple]
56
+ (defn win? [{body :body }]
57
+ (>= (count body) win-length))
58
+
59
+ (defn head-overlaps-body? [{[head & body] :body }]
60
+ ; have proposed to SS that argument order be reversed:
61
+ (includes? head body))
62
+
63
+ (def lose? head-overlaps-body? )
64
+
65
+ (defn collision? [{[snake-head] :body } {apple :location }]
46
66
(= snake-head apple))
47
67
48
- (defn update-positions [snake-ref apple-ref]
68
+ ; state updates
69
+ (defn update-positions [snake apple]
49
70
(dosync
50
- (if (collision? @snake-ref @apple-ref )
51
- (do (ref-set apple-ref ( random -apple ))
52
- (alter snake-ref move :grow ))
53
- (alter snake-ref move))))
71
+ (if (collision? @snake @apple)
72
+ (do (ref-set apple ( create -apple ))
73
+ (alter snake move :grow ))
74
+ (alter snake move))))
54
75
55
- ; drawing
56
- (def colors {:apple (Color. 210 50 90 ) :snake (Color. 15 160 70 )})
76
+ (defn update-direction [snake newdir]
77
+ (dosync (alter snake turn newdir)))
78
+
79
+ (defn reset-game []
80
+ (dosync (ref-set *apple* (create-apple ))
81
+ (ref-set *snake* (create-snake ))))
57
82
58
- (defn paint [g pt color]
83
+ (reset-game )
84
+
85
+ ; drawing
86
+ (defn fill-point [g pt color]
59
87
(let [[x y width height] (point-to-screen-rect pt)]
60
88
(.setColor g color)
61
89
(.fillRect g x y width height)))
62
90
63
- (defn paint-snake [g {:keys [body]}]
91
+ (defmulti paint (fn [g object & _] (:type object)))
92
+
93
+ (defmethod paint :snake [g {:keys [body color]}]
64
94
(doseq [point body]
65
- (paint g point ( colors :snake ) )))
95
+ (fill-point g point color )))
66
96
67
- (defn paint- apple [g apple ]
68
- (paint g apple ( colors :apple ) ))
97
+ (defmethod paint : apple [g { :keys [location color]} ]
98
+ (fill-point g location color ))
69
99
70
100
; gui elements
101
+ (def frame (JFrame. " Snake" ))
102
+
71
103
(def panel
72
104
(proxy [JPanel ActionListener KeyListener] []
73
105
(paintComponent [g]
74
106
(proxy-super paintComponent g)
75
- (paint-snake g @*snake*)
76
- (paint-apple g @*apple*))
107
+ (paint g @*snake*)
108
+ (paint g @*apple*))
77
109
(actionPerformed [e]
78
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!" ))
79
117
(.repaint this))
80
118
(keyPressed [e]
81
- (dosync ( alter *snake* turn (dirs (.getKeyCode e) ))))
119
+ (update-direction *snake* (dirs (.getKeyCode e))))
82
120
(keyReleased [e])
83
121
(keyTyped [e])))
122
+
123
+ (def timer (Timer. turn-millis panel))
84
124
85
125
(doto panel
86
126
(.setFocusable true )
87
127
(.addKeyListener panel))
88
128
89
- (doto ( JFrame. " Snake " )
129
+ (doto frame
90
130
(.add panel)
91
131
(.setSize (* width point-size) (* height point-size))
92
132
(.setVisible true ))
93
- (.start (Timer. turn-millis panel))
133
+ (.start timer)
134
+
135
+
0 commit comments