load it in the repl and start with
(game)
This code is loosely based on the Snake example in the excellent book Programming Clojure by Stuart Halloway.
(ns titres (:import (java.awt Color Dimension) (javax.swing JPanel JFrame Timer JOptionPane) (java.awt.event ActionListener KeyListener WindowAdapter)) ) (def frame-millis 60) (def cell-size 40) (def width 10) (def height 15) (def dirs { java.awt.event.KeyEvent/VK_LEFT [-1 0] java.awt.event.KeyEvent/VK_RIGHT [1 0] }) (def frames-to-drop 6) (def blocks [ '([0 0] [1 0] [0 1] [1 1]) '([0 0] [1 0] [2 0] [0 1]) '([0 0] [1 0] [2 0] [2 1]) '([0 0] [1 0] [1 1] [2 1]) '([0 0] [1 0] [2 0] [1 1]) '([1 0] [2 0] [0 1] [1 1]) '([0 0] [1 0] [2 0] [3 0]) ] ) (def colors [(Color. 15 160 70) (Color. 160 15 70) (Color. 15 70 160) (Color. 70 160 15) (Color. 70 15 160) (Color. 160 70 15) (Color. 160 70 160)] ) (defn make-block ([n] { :location [0 0] :body ( blocks n ) :color ( colors n )}) ([] (make-block (rand-int (count blocks))))) (defn point-to-screen-rect [pt] (map #(* cell-size %) [(pt 0) (pt 1) 1 1])) (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))) (defn paint-block [g {:keys [body location color]}] (doseq [point body] (fill-point g (vec (map + point location)) color))) (defn paint-stash [g stash] (doseq [m stash] (fill-point g (key m) (val m)))) (defn insert-to-stash [stash {:keys [body location color] }] (let [body-shifted (map #(vec (map + location %)) body) ] (merge stash (zipmap body-shifted (repeat color)) ))) (defn hits-bottom? [{:keys [location body] }] (let [[x y] (map + location (apply map max body))] (= y height) )) (defn collision? [stash {:keys [location body]} direction] (let [body-shifted (map #(vec (map + location direction %)) body) ] (some #(contains? stash %) body-shifted) )) (defn hits-stash? [stash block] (collision? stash block [0 1])) (defn delete-row [stash row] (let [new-stash (filter #(not= row ((key %) 1)) stash) new-stash-keys ( map #( cond (< (% 1) row) (vec (map + % [0 1])) :else % ) (keys new-stash) ) ] (zipmap new-stash-keys (vals new-stash)) )) (defn count-row [stash row] (count (filter #(= row (% 1)) (keys stash))) ) (defn delete-rows [stash row] (if (> row height) stash (if (> (count-row stash row) width) (recur (delete-row stash row) (inc row) ) (recur stash (inc row) ) ) )) (defn transpose-pt [pt height] [(- height (pt 1)) (pt 0) ]) (defn rotate-right [{:keys [body] :as block}] (let [[width height] (apply map max body)] (assoc block :body (map #(transpose-pt % height) body)))) (defn rotate-left [block] (rotate-right (rotate-right (rotate-right block)))) (defn move [{:keys [location] :as block} direction] (assoc block :location (vec (map + location direction))) ) (defn block-hits-border? [{:keys [body location] } newdir] (let [min_x (first (map + (vec (apply map min body)) newdir location)) max_x (first (map + (vec (apply map max body)) newdir location)) ] (or (< min_x 0) (> max_x width)) )) (defn adjust-border [{:keys [body location] :as block}] (let [offset (- width (first (map + (apply map max body) location)))] (cond (< offset 0) (move block [offset 0]) :else block ) )) (defn update-position [stash block newdir] (when-not (or (collision? stash @block newdir)
(block-hits-border? @block newdir) ) (dosync (alter block move newdir)) )) (defn update-drop [stash block] (when-not (collision? stash @block [0 1]) (dosync (alter block move [0 1])) )) (defn update-rotation [f stash block] (let [shifted-block (adjust-border (f @block))] (when-not (collision? stash shifted-block [0 0]) (dosync (ref-set block shifted-block)) ))) (defn update-stash [stash block] (dosync (alter stash insert-to-stash block)) (dosync (alter stash delete-rows 0)) ) (defn game-panel [frame block] (let [framecounter (ref 0) stash (ref {})] (proxy [JPanel ActionListener KeyListener] [] (paintComponent [g] (proxy-super paintComponent g) (paint-block g @block) (paint-stash g @stash) ) (actionPerformed [e] (dosync (alter framecounter inc)) (when (= 0 (mod @framecounter frames-to-drop)) (update-drop @stash block) (when (or (hits-stash? @stash @block) (hits-bottom? @block)) (update-stash stash @block) (dosync (ref-set block (make-block))))) (.repaint this) ) (keyPressed [e] (let [key (.getKeyCode e)] (cond (= key java.awt.event.KeyEvent/VK_UP) (update-rotation rotate-right @stash block) (= key java.awt.event.KeyEvent/VK_DOWN) (update-rotation rotate-left @stash block) :else (update-position @stash block (dirs key)) )) ) (keyReleased [e]) (keyTyped [e]) (getPreferredSize [] (Dimension. (* (inc width) cell-size) (* (inc height) cell-size))) ))) (defn frame-closing [timer] (proxy [WindowAdapter] [] (windowClosing [e] (do (println "stopped") (.stop timer))))) (defn game[] (let [frame (JFrame. "Tetris") block (ref (make-block)) panel (game-panel frame block) timer (Timer. frame-millis panel)] (doto panel (.setFocusable true) (.addKeyListener panel)) (doto frame (.addWindowListener (frame-closing timer)) (.add panel) (.pack) (.setVisible true)) (.start timer) [timer]))
No comments:
Post a Comment