Re: the snake game with core.async and swing

2013-08-06 Thread Andreas Liljeqvist
+1 for Daniels suggestion.
Swing can be quite bothersome if you just want a canvas and key-events.

I would avoid multimethods:

(defn calc-new-pos [xy prev-pos dir]
  (condp = [xy dir]
[:x :right] (+ prev-pos step)
[:x :left]) (- prev-pos step)
[:y :down] (+ prev-pos step)
[:y :up] (- prev-pos step)
:default prev-pos)

But really I think you should see the current 'velocity' as 2d vector eg:
[x y] [step 0] [0 -step]
Both speed and direction integrated.
so:

(defn calc-new-pos [pos vel]
  (map + pos vel))

I would avoid multimethods:

(defn inside-window? [dir canvas [x y]]
  (condp = dir
:left (>= x (.getX canvas))
:right (<= x (+ (.getX canvas) (.getWidth canvas)))
:up (>= y (.getY canvas))
:down (<= y (+ (.getY canvas) (.getHeight canvas)

inside-window? depends on information from the actual window, I would
decouple the game-state from swing.
Remember: functional is good :)

alternative:
(defn inside-window? [[x y]]
  (and
   (< -1 x max-x)
   (< -1 y max-y)))

Also look up the 'doto' macro.

And I should go read up on what core.async does, I have ignored it for too
long.


On Sun, Aug 4, 2013 at 5:56 PM, Daniel  wrote:

> Or quil, a processing wrapper, would be well suited to this application.
>
> --
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clojure@googlegroups.com
> Note that posts from new members are moderated - please be patient with
> your first post.
> To unsubscribe from this group, send email to
> clojure+unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> ---
> You received this message because you are subscribed to the Google Groups
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to clojure+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>
>

-- 
-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.




Re: the snake game with core.async and swing

2013-08-04 Thread Daniel
Or quil, a processing wrapper, would be well suited to this application.

-- 
-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.




Re: the snake game with core.async and swing

2013-08-03 Thread Chris Ford
Cool!

If you want to make the Swing stuff more idiomatic, you could take a look
at Seesaw .


On 2 August 2013 17:11,  wrote:

> Below is a little (stupid) snake game I wrote using core.async and swing.
> It uses channels for timer, keyboard input and repaint to make everything
> nice and sequential.
> Thought it could be a nice example of the power of core.async.
>
> I'm not an experienced clojure/lisp developer so I'd be happy if someone
> could give me some feedback on the code.
> Is it clojure idiomatic?
> Am I using core.async properly?
> etc.
>
> Thanks
> --anders
>
>
> (ns my-test
>   (use [midje.sweet])
>   (require [clojure.core.async :as async :refer :all])
>   (import [javax.swing JFrame JButton JPanel SwingUtilities])
>   (import [java.awt Color Dimension])
>   (import [java.awt.event ActionListener WindowAdapter KeyListener]))
>
>
> (defn map-chan [f in]
>   (let [c (chan)]
> (go (loop []
>   (when-let [v (f ( (>! c v))
>   (recur)))
> c))
>
> (defn start-timer! []
>   (let [c (chan)]
> (go (while true (! c :go)))
> c))
>
> (defn closing-channel [frame]
>   (let [c (chan)]
> (.addWindowListener frame
> (proxy [WindowAdapter] []
>   (windowClosing [e] (put! c e
> c))
>
> (defn array-of [coordinates index]
>   (int-array (map #(nth % index) coordinates)))
>
> (defn points-of [coordinates]
>   [(array-of coordinates 0) (array-of coordinates 1)])
>
> (defn draw-poly-line [canvas coordinates]
>   (SwingUtilities/invokeLater
>(fn []
>  (let [[x-points y-points] (points-of coordinates)
>g (.getGraphics canvas)
>prev-color (.getColor g)]
>   (.setColor g Color/BLACK)
>   (.drawPolyline g x-points y-points (count coordinates))
>   (.setColor g prev-color)
>
> (def step 5)
> (defmulti calc-new-pos (fn[xy prev-pos dir] [xy dir]))
> (defmethod calc-new-pos [:x :right][xy prev-pos dir] (+ prev-pos step))
> (defmethod calc-new-pos [:x :left][xy prev-pos dir] (- prev-pos step))
> (defmethod calc-new-pos [:y :down][xy prev-pos dir] (+ prev-pos step))
> (defmethod calc-new-pos [:y :up][xy prev-pos dir] (- prev-pos step))
> (defmethod calc-new-pos :default [xy prev-pos dir] prev-pos)
>
> (defn calc-snake [dir snake-obj counter]
>   (let [[l-x l-y] (last snake-obj)
> old-snake (if (= (mod counter 2) 0) snake-obj (rest snake-obj))]
> (conj (vec old-snake) [(calc-new-pos :x l-x dir) (calc-new-pos :y l-y
> dir)])))
>
> (facts "snake positions"
>(fact "snake moves and grows"
>  (calc-snake :right [[1 2]] 2) => [[1 2] [6 2]]
>  (calc-snake :right [[2 2]] 4) => [[2 2] [7 2]])
>(facts "snake moves"
>   (calc-snake :right [[1 2]] 1) => [[6 2]]
>   (calc-snake :down [[1 2]] 1) => [[1 7]]
>   (calc-snake :left [[10 2]] 1) => [[5 2]]
>   (calc-snake :up [[10 7]] 1) => [[10 2]]
>   ))
>
> (def key-to-dir-map {37 :left, 38 :up, 39 :right, 40 :down})
>
> (defn key-channel [obj]
>   (let [c (chan)]
> (.addKeyListener obj
>  (reify KeyListener
>(keyTyped [_ e] )
>(keyPressed [_ e] )
>(keyReleased [_ e]
>  (put! c e
> c))
>
> (defn create-canvas [paint-channel]
>   (proxy [JButton] []
>  (getPreferredSize [] (Dimension. 300 300))
>  (paintComponent [g]
>(go
>  (proxy-super paintComponent g)
>  (>! paint-channel :repaint)
>
> (defmulti inside-window? (fn [dir canvas pos] dir))
> (defmethod inside-window? :left [dir canvas [x _]] (>= x (.getX canvas)))
> (defmethod inside-window? :right [dir canvas [x _]] (<= x (+ (.getX
> canvas) (.getWidth canvas
> (defmethod inside-window? :up [dir canvas [_ y]] (>= y (.getY canvas)))
> (defmethod inside-window? :down [dir canvas [_ y]] (<= y (+ (.getY canvas)
> (.getHeight canvas
>
>
>
> (def initial-snake (vec  (map (fn [x] [x 10])  (take 20 (iterate (partial
> + step) 0)
>
>
> (defn game-rules-ok? [snake dir canvas]
>   (and
>(apply distinct? snake)
>(inside-window? dir canvas (last snake
>
> (facts "game rules"
>(let [canvas (JButton.)]
>  (.setBounds canvas 0 0 10 10)
>  (facts "inside window"
> (game-rules-ok? [[0 0]] :right canvas) => truthy
> (game-rules-ok? [[11 0]] :right canvas) => falsey
> (game-rules-ok? [[11 0]] :left canvas) => truthy
> (game-rules-ok? [[11 0]] :up canvas) => truthy
> (game-rules-ok? [[11 0]] :down canvas) => truthy
> (game-rules-ok? [[11 11]] :down canvas) => falsey)
>  (facts "snake eating itself"
>   (game-rules-ok? [[0 0] [0 0]] :right canvas) => falsey
>   (game-rules-ok?

the snake game with core.async and swing

2013-08-02 Thread eliassonaand
Below is a little (stupid) snake game I wrote using core.async and swing.
It uses channels for timer, keyboard input and repaint to make everything 
nice and sequential.
Thought it could be a nice example of the power of core.async.

I'm not an experienced clojure/lisp developer so I'd be happy if someone 
could give me some feedback on the code.
Is it clojure idiomatic?
Am I using core.async properly?
etc.

Thanks
--anders


(ns my-test
  (use [midje.sweet])
  (require [clojure.core.async :as async :refer :all])
  (import [javax.swing JFrame JButton JPanel SwingUtilities])
  (import [java.awt Color Dimension])
  (import [java.awt.event ActionListener WindowAdapter KeyListener]))


(defn map-chan [f in]
  (let [c (chan)]
(go (loop []
  (when-let [v (f (! c v))
  (recur)))
c))

(defn start-timer! []
  (let [c (chan)]
(go (while true (! c :go)))
c))

(defn closing-channel [frame]
  (let [c (chan)]
(.addWindowListener frame
(proxy [WindowAdapter] []
  (windowClosing [e] (put! c e
c))

(defn array-of [coordinates index]
  (int-array (map #(nth % index) coordinates)))

(defn points-of [coordinates]
  [(array-of coordinates 0) (array-of coordinates 1)])

(defn draw-poly-line [canvas coordinates]
  (SwingUtilities/invokeLater
   (fn []
 (let [[x-points y-points] (points-of coordinates)
   g (.getGraphics canvas)
   prev-color (.getColor g)]
  (.setColor g Color/BLACK)
  (.drawPolyline g x-points y-points (count coordinates))
  (.setColor g prev-color)

(def step 5)
(defmulti calc-new-pos (fn[xy prev-pos dir] [xy dir]))
(defmethod calc-new-pos [:x :right][xy prev-pos dir] (+ prev-pos step))
(defmethod calc-new-pos [:x :left][xy prev-pos dir] (- prev-pos step))
(defmethod calc-new-pos [:y :down][xy prev-pos dir] (+ prev-pos step))
(defmethod calc-new-pos [:y :up][xy prev-pos dir] (- prev-pos step))
(defmethod calc-new-pos :default [xy prev-pos dir] prev-pos)

(defn calc-snake [dir snake-obj counter]
  (let [[l-x l-y] (last snake-obj)
old-snake (if (= (mod counter 2) 0) snake-obj (rest snake-obj))]
(conj (vec old-snake) [(calc-new-pos :x l-x dir) (calc-new-pos :y l-y 
dir)])))

(facts "snake positions"
   (fact "snake moves and grows"
 (calc-snake :right [[1 2]] 2) => [[1 2] [6 2]]
 (calc-snake :right [[2 2]] 4) => [[2 2] [7 2]])
   (facts "snake moves"
  (calc-snake :right [[1 2]] 1) => [[6 2]]
  (calc-snake :down [[1 2]] 1) => [[1 7]]
  (calc-snake :left [[10 2]] 1) => [[5 2]]
  (calc-snake :up [[10 7]] 1) => [[10 2]]
  ))

(def key-to-dir-map {37 :left, 38 :up, 39 :right, 40 :down})

(defn key-channel [obj]
  (let [c (chan)]
(.addKeyListener obj
 (reify KeyListener
   (keyTyped [_ e] )
   (keyPressed [_ e] )
   (keyReleased [_ e]
 (put! c e
c))

(defn create-canvas [paint-channel]
  (proxy [JButton] []
 (getPreferredSize [] (Dimension. 300 300))
 (paintComponent [g]
   (go
 (proxy-super paintComponent g)
 (>! paint-channel :repaint)

(defmulti inside-window? (fn [dir canvas pos] dir))
(defmethod inside-window? :left [dir canvas [x _]] (>= x (.getX canvas)))
(defmethod inside-window? :right [dir canvas [x _]] (<= x (+ (.getX canvas) 
(.getWidth canvas
(defmethod inside-window? :up [dir canvas [_ y]] (>= y (.getY canvas)))
(defmethod inside-window? :down [dir canvas [_ y]] (<= y (+ (.getY canvas) 
(.getHeight canvas



(def initial-snake (vec  (map (fn [x] [x 10])  (take 20 (iterate (partial + 
step) 0)


(defn game-rules-ok? [snake dir canvas]
  (and
   (apply distinct? snake)
   (inside-window? dir canvas (last snake

(facts "game rules"
   (let [canvas (JButton.)]
 (.setBounds canvas 0 0 10 10)
 (facts "inside window"
(game-rules-ok? [[0 0]] :right canvas) => truthy
(game-rules-ok? [[11 0]] :right canvas) => falsey
(game-rules-ok? [[11 0]] :left canvas) => truthy
(game-rules-ok? [[11 0]] :up canvas) => truthy
(game-rules-ok? [[11 0]] :down canvas) => truthy
(game-rules-ok? [[11 11]] :down canvas) => falsey)
 (facts "snake eating itself"
  (game-rules-ok? [[0 0] [0 0]] :right canvas) => falsey
  (game-rules-ok? [[0 0] [1 0]] :right canvas) => true
  )))
(defn you-loose! [cc]
  (println "you loose!")
  (put! cc :close))


(defn snake [cc]
  (let [paint-channel (chan)
timer-channel (start-timer!)
canvas (create-canvas paint-channel)
dir-channel (map-chan #(key-to-dir-map (.getKeyCode %)) 
(key-channel canvas))
]
(go
 (loop [last-dir :right
   snake-obj initial