Hi Everyone. Could someone help me understanding why this code from Alex Clojure book doesn't work? I can't move the snake with arrow keys.
@seancorfield
Yes. This is what I'm trying to figure out. 😞
@smith.adriane
How can I print the arrow keycode in keyPressed? I can't have access to it on REPL. 😞
• The game is running;
• It's updating, the green dot (who should be the snake) is always going to right (ignoring my arrow key press);
• How should I make sure this?
◦ Is keyPressed receiving key events?
◦ Is update-direction receiving a valid direction?
◦ Does calling update-direction manually work?
> How can I print the arrow keycode in keyPressed? I can't have access to it on REPL.
why can't you access it in the repl?
> • How should I make sure this?
> ◦ Is keyPressed receiving key events?
> ◦ Is update-direction receiving a valid direction?
> ◦ Does calling update-direction manually work?
I would start with just adding prn statements
I wrote this code:
(ns reader.dummy
(:import (javax.swing JFrame JPanel)
(java.awt.event KeyListener KeyEvent)))
(defn make-panel []
(doto (JPanel.)
(.setFocusable true) ; panels don't get focus by default
(.addKeyListener
(proxy [KeyListener] []
(keyPressed [^KeyEvent e]
(println "Pressed:" (.getKeyCode e)))
(keyReleased [^KeyEvent e]
(println "Released:" (.getKeyCode e)))
(keyTyped [^KeyEvent e]
(println "Typed:" (.getKeyChar e)))))))
(defn show []
(let [frame (JFrame. "KeyListener test")
panel (make-panel)]
(.add (.getContentPane frame) panel)
(doto frame
(.setSize 300 200)
(.setVisible true)
(.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE))
(.requestFocusInWindow panel)
panel))
When open the the frame, and I press the arrow keys, works.what key codes do you get when you press the arrow keys? do they match what the code is checking for:
(def VK-LEFT KeyEvent/VK_LEFT)
(def VK-RIGHT KeyEvent/VK_RIGHT)
(def VK-UP KeyEvent/VK_UP)
(def VK-DOWN KeyEvent/VK_DOWN)Yes, they match.
(def VK-LEFT KeyEvent/VK_LEFT) ; 37
(def VK-RIGHT KeyEvent/VK_RIGHT) ; 39
(def VK-UP KeyEvent/VK_UP) ; 38
(def VK-DOWN KeyEvent/VK_DOWN) ; 40I did this, trying to debug on the game-panel, but doesn´t work.
(defn game-panel [frame snake apple]
(proxy [JPanel ActionListener KeyListener] []
(paintComponent [g]
(proxy-super paintComponent g)
(paint g @snake)
(paint g @apple))
(actionPerformed [e]
(update-positions snake apple)
(when (lose? @snake)
(reset-game snake apple)
(JOptionPane/showMessageDialog frame "You lose!"))
(when (win? @snake)
(reset-game snake apple)
(JOptionPane/showMessageDialog frame "You win!"))
(.repaint this))
(keyPressed [e]
(println "DEBUG keyPressed:" (.getKeyCode e))
(when-let [dir (dirs (.getKeyCode e))]
(update-direction snake dir)))
;(keyPressed [e]
; (update-direction snake (dirs (.getKeyCode e))))
(getPreferredSize []
(Dimension. (* (inc width) point-size)
(* (inc height) point-size)))
(keyReleased [e])
(keyTyped [e])))How does it fail? What do you expect to see? What happens?
I expecting to see in the REPL: DEBUG keyPressed 38, but nothing was printed.
PS: I'm not pressing right arrow key.
your debug keyboard example adds a key listener while the snake game panel is a key listener. Not sure if there’s something missing from the book code that makes it work, but you can try adapting the code to add a key listener instead of implementing a key listener
anyway, it seems like you’ve isolated the problem. the key events aren’t getting passed to the game code
Dude, I miss this part of the code 😞
It works.
you can also try searching slack or the internet to see if someone else ran into the same problem to see how they fixed it
no worries, seems like someone else did exactly the same thing https://clojurians.slack.com/archives/C053AK3F9/p1687279875114859?thread_ts=1687277320.977869&channel=C053AK3F9&message_ts=1687279875.114859
(ns reader.snake
(:import (java.awt Color Dimension)
(javax.swing JPanel JFrame Timer JOptionPane)
(java.awt.event ActionListener KeyListener KeyEvent)))
;(:import-static java.awt.event.KeyEvent VK_LEFT VK_RIGHT VK_UP VK_DOWN)
(def VK-LEFT KeyEvent/VK_LEFT)
(def VK-RIGHT KeyEvent/VK_RIGHT)
(def VK-UP KeyEvent/VK_UP)
(def VK-DOWN KeyEvent/VK_DOWN)
(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 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))
(defn turn [snake newdir]
(assoc snake :dir newdir))
(defn reset-game [snake apple]
(dosync (ref-set apple (create-apple))
(ref-set snake (create-snake)))
nil)
(defn update-direction [snake newdir]
(when newdir (dosync (alter snake turn newdir))))
(defn update-positions [snake apple]
(dosync
(if (eats? @snake @apple)
(do (ref-set apple (create-apple))
(alter snake move :grow))
(alter snake move)))
nil)
(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 snake apple]
(proxy [JPanel ActionListener KeyListener] []
(paintComponent [g]
(proxy-super paintComponent g)
(paint g @snake)
(paint g @apple))
(actionPerformed [e]
(update-positions snake apple)
(when (lose? @snake)
(reset-game snake apple)
(JOptionPane/showMessageDialog frame "Youo lose!"))
(when (win? @snake)
(reset-game snake apple)
(JOptionPane/showMessageDialog frame "You win!"))
(.repaint this))
(keyPressed [e]
(update-direction snake (dirs (.getKeyCode e))))
(getPreferredSize []
(Dimension. (* (inc width) point-size)
(* (inc height) point-size)))
(keyReleased [e])
(keyTyped [e])))
;; TODO: the game doesn't work as expected, some keys are not working
;; maybe it's because the import-static thing.
(defn game []
(let [snake (ref (create-snake))
apple (ref (create-apple))
frame (JFrame. "Snake")
panel (game-panel frame snake apple)
timer (Timer. turn-millis panel)]
(doto frame
(.add panel)
(.pack)
(.setVisible true))
(.start timer)
[snake, apple, timer]))
Can you print the arrow keycode in keyPressed and compare it to the VK-LEFT, etc keys?
I would try to isolate the issue:
• Is the game running?
• Is the game updating per the timer?
• Is keyPressed receiving key events?
• Is update-direction receiving a valid direction?
• Does calling update-direction manually work?
The code you've posted does not match the book. Here's what's in the book:
(ns reader.snake
(:import (java.awt Color Dimension)
(javax.swing JPanel JFrame Timer JOptionPane)
(java.awt.event ActionListener KeyListener))
(:refer examples.import-static :refer :all))
(import-static java.awt.event.KeyEvent VK_LEFT VK_RIGHT VK_UP VK_DOWN)
There's a bug in the book: it should be (:require [examples.import-static :refer :all])
And then it's (import-static ...) not (:import-static ...)examples/import_static.clj must be part of the source code for the book (but it isn't shown in the book)...
Asking for a coworker: Has anyone figured out a good editing workflow in Zed by any chance?
Not yet. They are still in the process of defining an extension mechanism. Without that, we cannot start with a Clojure plugin.