-
Notifications
You must be signed in to change notification settings - Fork 136
Expand file tree
/
Copy pathatom_snake.clj
More file actions
130 lines (108 loc) · 3.65 KB
/
atom_snake.clj
File metadata and controls
130 lines (108 loc) · 3.65 KB
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
; Inspired by the snakes the have gone before:
; Abhishek Reddy's snake: http://www.plt1.com/1070/even-smaller-snake/
; Mark Volkmann's snake: http://www.ociweb.com/mark/programming/ClojureSnake.html
(ns examples.atom-snake
(:require [examples.import-static :refer :all])
(:import [java.awt Color Dimension]
[java.awt.event ActionListener KeyListener]
[javax.swing JFrame JOptionPane JPanel Timer]))
(import-static java.awt.event.KeyEvent VK_LEFT VK_RIGHT VK_UP VK_DOWN)
; ----------------------------------------------------------
; functional model
; ----------------------------------------------------------
(def width 75)
(def height 50)
(def point-size 10)
(def turn-millis 75)
(def win-length 5)
(def dirs { VK_LEFT [-1 0]
VK_RIGHT [ 1 0]
VK_UP [ 0 -1]
VK_DOWN [ 0 1]})
(defn add-points [& pts]
(vec (apply map + pts)))
(defn point-to-screen-rect [pt]
(map #(* point-size %)
[(pt 0) (pt 1) 1 1]))
(defn create-apple []
{:location [(rand-int width) (rand-int height)]
:color (Color. 210 50 90)
:type :apple})
(defn create-snake []
{:body (list [1 1])
:dir [1 0]
:type :snake
:color (Color. 15 160 70)})
(defn move [{:keys [body dir] :as snake} & grow]
(assoc snake :body (cons (add-points (first body) dir)
(if grow body (butlast body)))))
(defn turn [snake newdir]
(if newdir (assoc snake :dir newdir) snake))
(defn win? [{body :body}]
(>= (count body) win-length))
(defn head-overlaps-body? [{[head & body] :body}]
(contains? (set body) head))
(def lose? head-overlaps-body?)
(defn eats? [{[snake-head] :body} {apple :location}]
(= snake-head apple))
; START: update-positions
(defn update-positions [{snake :snake, apple :apple, :as game}]
(if (eats? snake apple)
(merge game {:apple (create-apple) :snake (move snake :grow)})
(merge game {:snake (move snake)})))
; END: update-positions
(defn update-direction [{snake :snake :as game} newdir]
(merge game {:snake (turn snake newdir)}))
(defn reset-game [game]
(merge game {:apple (create-apple) :snake (create-snake)}))
; ----------------------------------------------------------
; gui
; ----------------------------------------------------------
(defn fill-point [g pt color]
(let [[x y width height] (point-to-screen-rect pt)]
(.setColor g color)
(.fillRect g x y width height)))
(defmulti paint (fn [g object & _] (:type object)))
(defmethod paint :apple [g {:keys [location color]}]
(fill-point g location color))
(defmethod paint :snake [g {:keys [body color]}]
(doseq [point body]
(fill-point g point color)))
(defn game-panel [frame game]
(proxy [JPanel ActionListener KeyListener] []
(paintComponent [g]
(proxy-super paintComponent g)
(paint g (@game :snake))
(paint g (@game :apple)))
; START: swap!
(actionPerformed [e]
(swap! game update-positions)
(when (lose? (@game :snake))
(swap! game reset-game)
(JOptionPane/showMessageDialog frame "You lose!"))
; END: swap!
(when (win? (@game :snake))
(swap! game reset-game)
(JOptionPane/showMessageDialog frame "You win!"))
(.repaint this))
(keyPressed [e]
(swap! game update-direction (dirs (.getKeyCode e))))
(getPreferredSize []
(Dimension. (* (inc width) point-size)
(* (inc height) point-size)))
(keyReleased [e])
(keyTyped [e])))
(defn game []
(let [game (atom (reset-game {}))
frame (JFrame. "Snake")
panel (game-panel frame game)
timer (Timer. turn-millis panel)]
(doto panel
(.setFocusable true)
(.addKeyListener panel))
(doto frame
(.add panel)
(.pack)
(.setVisible true))
(.start timer)
[game, timer]))