You would start it in the repl as
(game "GUN44.LIF")
where "GUN44.LIF" is a LIF file -- you can find LIF files all over the
internet, e.g. Paul Calahan's great collection
http://www.radicaleye.com/lifepage/
The lif reader demonstrates how to read and process a file in Clojure.
(ns gol (:import (java.awt Color Dimension) (javax.swing JPanel JFrame Timer JOptionPane) (java.awt.event ActionListener KeyListener WindowAdapter)) (:use lif)) (def frame-millis 30) (def cell-size 5) (def width 250) (def height 160) (def glider ^ints #{ [1 0] [2 1] [0 2] [1 2] [2 2] }) (def gun ^ints (process-file "../../lifep/GUN44.LIF")) (defn get-neighbors[ ^ints [ox oy] ] (for [x (range (- ox 1) (+ ox 2)) y (range (- oy 1) (+ oy 2)) ] [x y] )) (defn collect-neighbors [field] (distinct (mapcat get-neighbors field))) (defn count-alive-neighbors [ ^ints field] (fn [ cell ] (count (filter field (get-neighbors cell) )))) (defn survives? [field] (let [count-alive-neighbors (count-alive-neighbors field)] (fn [ cell ] (let [n (count-alive-neighbors cell)] (or (= n 3) (= n 4)))))) (defn birth? [field] (let [count-alive-neighbors (count-alive-neighbors field)] (fn [ cell ] (= 3 (count-alive-neighbors cell))))) (defn apply-rules [filterfunc] (fn [ field ] (let [dead-cells (remove field (collect-neighbors field)) survives? (survives? field) birth? (birth? field)] (set (concat (filterfunc survives? field) (filterfunc birth? dead-cells)))))) (defn pfilter [pred list] (map second (filter first (pmap (fn [cell] [(pred cell) cell]) list)))) (def apply-rules-p (apply-rules pfilter)) (def apply-rules-n (apply-rules filter)) (defn update-field [field] (swap! field apply-rules-n)) (defn point-to-screen-rect [pt] (map #(* cell-size %) [(pt 0) (pt 1) 1 1])) (defn fill-point [^java.awt.Graphics g pt color] (let [[x y width height] (point-to-screen-rect pt)] (.fillRect g x y width height))) (defn paint-game [^java.awt.Graphics g field] (let [center [(bit-shift-right width 1) (bit-shift-right height 1)] color (Color. 15 160 70)] (.setColor g color) (doseq [point field] (fill-point g (vec (map + center point)) color) ))) (defn game-panel [frame field] (proxy [JPanel ActionListener KeyListener] [] (paintComponent [^java.awt.Graphics g] (proxy-super paintComponent g) (paint-game g @field)) (actionPerformed [e] (.repaint this) (println "active cells:" (count @field)) (time (update-field field))) (keyPressed [e] ) (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 ([filename] (let [frame (JFrame. "Game of Life") field (atom (^ints process-file filename) ) panel (game-panel frame field) 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])) ([] (game "../../lifep/GUN44.LIF")))
The lif reader:
(ns lif (:import (java.io BufferedReader FileReader)) (:use [clojure.string :only (split)] [clojure.java.io :only (reader)]) ) (defn collect-data [[x y] line] (remove nil? (map #(when (= %1 \*) [%2 y]) line (iterate inc x)))) (defn is-data? [line] (or (= \. (first line)) (= \* (first line)))) (defn char-to-long [c] (Long. (str c))) (defn is-coords? [line] (let [tokens (split line #"\s")] (= "#P" (tokens 0)))) (defn get-coords [line] (let [tokens (split line #"\s")] [(char-to-long (tokens 1)) (char-to-long (tokens 2)) ])) (defn process-lines [[x y] lines accu] (let [line (first lines)] (cond (empty? lines) accu (is-data? line) (recur [x (inc y)] (rest lines) (concat accu (collect-data [x y] line))) (is-coords? line) (recur (get-coords line )(rest lines) accu) :else (recur [x y] (rest lines) accu)))) (defn process-file [file-name] (with-open [rdr (reader file-name)] (set (process-lines [0 0] (line-seq rdr) nil))))
Have fun!
update:
I cleaned the mess up a bit. Still not as concise as cgrand's solution. Hope I find the time to check it out and see if it's faster than mine.
See also:
ReplyDeletehttp://clj-me.cgrand.net/2011/08/19/conways-game-of-life/
which is a very compact implementation of the game engine, though wihtout a GUI...
cgrand's solution also seems fast ... I'll try it out and let you know.
ReplyDeleteThanks!
thomas