Skip to content

Commit 7758eac

Browse files
snake with all state in single game object
1 parent 87bd317 commit 7758eac

File tree

2 files changed

+128
-1
lines changed

2 files changed

+128
-1
lines changed

examples/fsnake.clj

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
; Inspired by the snakes the have gone before:
2+
; Abhishek Reddy's snake: http://www.plt1.com/1070/even-smaller-snake/
3+
; Mark Volkmann's snake: http://www.ociweb.com/mark/programming/ClojureSnake.html
4+
5+
(ns examples.fsnake
6+
(:import (java.awt Color Dimension)
7+
(javax.swing JPanel JFrame Timer JOptionPane)
8+
(java.awt.event ActionListener KeyListener))
9+
(:use clojure.contrib.import-static
10+
[clojure.contrib.seq-utils :only (includes?)]))
11+
(import-static java.awt.event.KeyEvent VK_LEFT VK_RIGHT VK_UP VK_DOWN)
12+
13+
; ----------------------------------------------------------
14+
; functional model
15+
; ----------------------------------------------------------
16+
(def width 75)
17+
(def height 50)
18+
(def point-size 10)
19+
(def turn-millis 75)
20+
(def win-length 5)
21+
(def dirs { VK_LEFT [-1 0]
22+
VK_RIGHT [ 1 0]
23+
VK_UP [ 0 -1]
24+
VK_DOWN [ 0 1]})
25+
26+
(defn add-points [& pts]
27+
(vec (apply map + pts)))
28+
29+
(defn point-to-screen-rect [pt]
30+
(map #(* point-size %)
31+
[(pt 0) (pt 1) 1 1]))
32+
33+
(defn create-apple []
34+
{:location [(rand-int width) (rand-int height)]
35+
:color (Color. 210 50 90)
36+
:type :apple})
37+
38+
(defn create-snake []
39+
{:body (list [1 1])
40+
:dir [1 0]
41+
:type :snake
42+
:color (Color. 15 160 70)})
43+
44+
(defn move [{:keys [body dir] :as snake} & grow]
45+
(assoc snake :body (cons (add-points (first body) dir)
46+
(if grow body (butlast body)))))
47+
48+
(defn turn [snake newdir]
49+
(if newdir (assoc snake :dir newdir) snake))
50+
51+
(defn win? [{body :body}]
52+
(>= (count body) win-length))
53+
54+
(defn head-overlaps-body? [{[head & body] :body}]
55+
(includes? body head))
56+
57+
(def lose? head-overlaps-body?)
58+
59+
(defn eats? [{[snake-head] :body} {apple :location}]
60+
(= snake-head apple))
61+
62+
(defn update-positions [{snake :snake, apple :apple, :as game}]
63+
(if (eats? snake apple)
64+
(merge game {:apple (create-apple) :snake (move snake :grow)})
65+
(merge game {:snake (move snake)})))
66+
67+
(defn update-direction [{snake :snake :as game} newdir]
68+
(merge game {:snake (turn snake newdir)}))
69+
70+
(defn reset-game [game]
71+
(merge game {:apple (create-apple) :snake (create-snake)}))
72+
73+
; ----------------------------------------------------------
74+
; gui
75+
; ----------------------------------------------------------
76+
(defn fill-point [g pt color]
77+
(let [[x y width height] (point-to-screen-rect pt)]
78+
(.setColor g color)
79+
(.fillRect g x y width height)))
80+
81+
(defmulti paint (fn [g object & _] (:type object)))
82+
83+
(defmethod paint :apple [g {:keys [location color]}] ; <label id="code.paint.apple"/>
84+
(fill-point g location color))
85+
86+
(defmethod paint :snake [g {:keys [body color]}] ; <label id="code.paint.snake"/>
87+
(doseq [point body]
88+
(fill-point g point color)))
89+
90+
(defn game-panel [frame game]
91+
(proxy [JPanel ActionListener KeyListener] []
92+
(paintComponent [g] ; <label id="code.game-panel.paintComponent"/>
93+
(proxy-super paintComponent g)
94+
(paint g (@game :snake))
95+
(paint g (@game :apple)))
96+
(actionPerformed [e] ; <label id="code.game-panel.actionPerformed"/>
97+
(dosync (alter game update-positions))
98+
(when (lose? (@game :snake))
99+
(dosync (alter game reset-game))
100+
(JOptionPane/showMessageDialog frame "You lose!"))
101+
(when (win? (@game :snake))
102+
(dosync (alter game reset-game))
103+
(JOptionPane/showMessageDialog frame "You win!"))
104+
(.repaint this))
105+
(keyPressed [e] ; <label id="code.game-panel.keyPressed"/>
106+
(dosync (alter game update-direction (dirs (.getKeyCode e)))))
107+
(getPreferredSize []
108+
(Dimension. (* (inc width) point-size)
109+
(* (inc height) point-size)))
110+
(keyReleased [e])
111+
(keyTyped [e])))
112+
113+
(defn game []
114+
(let [game (ref (reset-game {}))
115+
frame (JFrame. "Snake")
116+
panel (game-panel frame game)
117+
timer (Timer. turn-millis panel)]
118+
(doto panel ; <label id="code.game.panel"/>
119+
(.setFocusable true)
120+
(.addKeyListener panel))
121+
(doto frame ; <label id="code.game.frame"/>
122+
(.add panel)
123+
(.pack)
124+
(.setVisible true))
125+
(.start timer) ; <label id="code.game.timer"/>
126+
[game, timer])) ; <label id="code.game.return"/>
127+

examples/snake.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
; Inspired by the snakes the have gone before:
1+
; Inspired by the snakes that have gone before:
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

0 commit comments

Comments
 (0)