;; Setup ;;This is the test bed for me to test new things (ns ai.snake (:import (java.awt Color Dimension) (javax.swing JPanel JFrame Timer JOptionPane) (java.awt.event ActionListener KeyListener)) (:use clojure.contrib.import-static [clojure.contrib.seq-utils :only (includes?)] clojure.contrib.math)) (import-static java.awt.event.KeyEvent VK_LEFT VK_RIGHT VK_UP VK_DOWN) (def width 75) (def height 50) (def point-size 10) (def turn-millis 75) (def win-length 50) (def dirs { VK_LEFT [-1 0] VK_RIGHT [1 0] VK_DOWN [0 1] VK_UP [0 -1]}) ;; The Functional Part (defn add-points [& pts] "Adds points together" (vec (apply map + pts))) (defn point-to-screen-rect [pt] "Converts a point to a rectangle" (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 [colour] {:body (list [(rand-int width) (rand-int height)]) :dir [1 0] :type :snake :color colour}) (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}] "Pretty neat, does the head an body include the same point?" (includes? body head)) (def lose? head-overlaps-body?) (defn eats? [{[snake-head] :body} {apple :location}] "Very neat, All the work is done in the bindings in the function args" (= snake-head apple)) (defn turn [snake newdir] (assoc snake :dir newdir)) ;; This is the mutable part (defn reset-game [snake ai-snake apple] (dosync (ref-set apple (create-apple)) (ref-set snake (create-snake (Color. 15 160 70)))) (ref-set ai-snake (create-snake (Color. 15 255 70))) nil) (defn update-direction [snake newdir] (when newdir (dosync (alter snake turn newdir)))) (defn update-positions [snake ai-snake apple] (dosync (if (eats? @snake @apple) (do (ref-set apple (create-apple)) (alter snake move :grow)) ( (alter snake move) (alter ai-snake move)))) nil) ;;My AI part;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;(defn eats? [{[snake-head] :body} {apple :location}] ;; "Very neat, All the work is done in the bindings in the function args" ;; (= snake-head apple)) ;;The functional part (defn point-dif [& pts] "The difference between two points" (vec (apply map - pts))) (defn int-to-dir [pt] ;;Exception handling from Programming Clojure P83 (try (/ pt (abs pt)) (catch ArithmeticException _ 0))) (defn point-to-dir [& pts] (vec (apply map int-to-dir pts))) ;;New direction Note that params needs to be derefed using @ (defn intercept-direction [{[target-head] :body} {[ai-head] :body}] (point-to-dir (point-dif target-head ai-head))) ;;End of my AI part;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; The GUI part (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))) ;;Multimethod ;;Paint takes 2 args, g is the Java.awt graphics object, object is the object to be painted (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))) ;;The meaty part (defn game-panel [frame snake apple ai-snake] (proxy [JPanel ActionListener KeyListener] [] (paintComponent [g] (proxy-super paintComponent g) ;;Draw the panel (paint g @snake) (paint g @ai-snake) (paint g @apple)) (actionPerformed [e] (update-positions snake ai-snake apple) ;;Update positions ;;(update-direction ai-snake (intercept-direction @snake @ai-snake)) ;;Putting it here will make the pred track you all the time (when (lose? @snake) (reset-game snake ai-snake apple) (JOptionPane/showMessageDialog frame "You Lose!")) (when (win? @snake) (reset-game snake ai-snake apple) (JOptionPane/showMessageDialog frame "You Win!")) (.repaint this)) (keyPressed [e] (update-direction snake (dirs (.getKeyCode e))) (update-direction ai-snake (intercept-direction @snake @ai-snake))) ;;Putting it here will make the pred track you on change ;;Update the direction of the ai-snake (getPreferredSize [] (Dimension. (* (inc width) point-size) (* (inc height) point-size))) (keyReleased [e]) ;;Ignore (keyTyped [e]))) ;;Ignore (defn game [] (let [snake (ref (create-snake (Color. 15 160 70))) apple (ref (create-apple)) ai-snake (ref (create-snake (Color. 15 255 70))) frame (JFrame. "Snake") panel (game-panel frame snake apple ai-snake) timer (Timer. turn-millis panel)] (doto panel (.setFocusable true) (.addKeyListener panel)) (doto frame (.add panel) (.pack) (.setVisible true)) (.start timer) [snake, apple, timer])) (def test-snake (ref nil)) (def test-ai (ref nil)) (def test-apple (ref nil))
Monday, November 30, 2009
Clojure Snake with Predator
Today I spent the morning trying out the Clojure snake from Stuart Halloway's Programming Clojure. I spent the afternoon making up / doing from memory a predator snake that follows the player's snake around. Here is the full Code.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment