`
flyfoxs
  • 浏览: 295262 次
  • 性别: Icon_minigender_1
  • 来自: 合肥
社区版块
存档分类
最新评论

使用Clojure实现贪吃蛇

 
阅读更多

最近学习Clojure,买了<<Clojure程序设计>>,对里面大部分章节进行了精读. 比如贪吃蛇程序,并且这个程序也比较精简,200行不到.

在读的过程中,对不能一目了然的地方,添加了注释,现发出来,希望能对有的人有用.

 

 

;;
;;Excerpted from "Programming Clojure, Second Edition",
;;published by The Pragmatic Bookshelf.
;;Copyrights apply to this code. It may not be used to create training material, 
;;courses, books, articles, and the like. Contact us if you are in doubt.
;;We make no guarantees that this code is fit for any purpose. 
;;Visit http://www.pragmaticprogrammer.com/titles/shcloj2 for more book information.
;;
; Inspired by the snakes that have gone before:
; Abhishek Reddy's snake: http://www.plt1.com/1070/even-smaller-snake/
; Mark Volkmann's snake: http://www.ociweb.com/mark/programming/ClojureSnake.html 

; The START:/END: pairs are production artifacts for the book and not 
; part of normal Clojure style

(ns examples.snake
  (:import (java.awt Color Dimension) 
   (javax.swing JPanel JFrame Timer JOptionPane)
           (java.awt.event ActionListener KeyListener))
  (:use examples.import-static))
(import-static java.awt.event.KeyEvent VK_LEFT VK_RIGHT VK_UP VK_DOWN)

; ----------------------------------------------------------
; functional model
; ----------------------------------------------------------
(def width 75)
(def height 50)
(def point-size 10)
(def turn-millis 75)
(def win-length 5)
;通过Map定义了4个运动方向
(def dirs { VK_LEFT  [-1  0] 
            VK_RIGHT [ 1  0]
            VK_UP    [ 0 -1] 
    VK_DOWN  [ 0  1]})

;当snake吃到苹果后, snak的长度需要添加1	
(defn add-points [& pts] 
  (vec (apply map + pts)))

  
;pt是 snake的节点坐标(body)信息,或者apple的位置(location)坐标信息, 数据结构是一个2维vector [(rand-int width) (rand-int height)]   ;
;(pt 0):取vector#pt的第一个元素返回,X坐标
;(pt 1):取vector#pt的第二个元素返回,Y坐标
(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)})

;这种函数定义方式很少见,可参考 http://stuartsierra.com/2010/01/15/keyword-arguments-in-clojure
;参数1是一个Map, 使用了结构的办法直接传递给函数体,
;参数2不是必须的, 是变长参数(在此Demo程序中,传入了一个类型为key的实参,类似于传入了True )  
;业务逻辑就是在snak移动的过程中,判断是否需要增长一个节点
;:keys [body dir]} 从输入Map中,寻找:body所对应的Value并赋值给body
;
(defn move [{:keys [body dir] :as snake} & grow]
  (assoc snake :body (cons (add-points (first body) dir) 
   (if grow body (butlast body)))))

(defn turn [snake newdir] 
  (assoc snake :dir newdir))

(defn win? [{body :body}]
  (>= (count body) win-length))

;检测snake是否出现交叉,当snake长度扩张后容易出现  
(defn head-overlaps-body? [{[head & body] :body}]
  (contains? (set body) head))

(def lose? head-overlaps-body?)

;snake头和苹果重合时, snake吃掉apple
(defn eats? [{[snake-head] :body} {apple :location}]
   (= snake-head apple))

; ----------------------------------------------------------
; mutable model
; ----------------------------------------------------------
;苹果被吃和新苹果显示,需要在一个事务中;
;如果被吃,需要重新生成苹果,然后Move需要考虑蛇的增长
;如果没有被吃,则只需简单Move
(defn update-positions [snake apple]
  (dosync
   (if (eats? @snake @apple)
     (do (ref-set apple (create-apple))
 (alter snake move :grow))
     (alter snake move)))
  nil)

;Snake的:dir因为同时被move函数读取,所以读取:dir与update :dir需要通过事务来隔离.
(defn update-direction [snake newdir]
  (when newdir (dosync (alter snake turn newdir))))

(defn reset-game [snake apple]
  (dosync (ref-set apple (create-apple))
  (ref-set snake (create-snake)))
  nil)

; ----------------------------------------------------------
; gui
; ----------------------------------------------------------
(defn fill-point [g pt color] 
  (let [[x y width height] (point-to-screen-rect pt)]
    (.setColor g color) 
;x,y指定了横纵坐标, 	width,height指定了矩形的宽和高
    (.fillRect g x y width height)))
	
;声明一个多态的方法,具体的实现由Object中的:type决定, 在此Demo中:type有2类, :apple, :snake
(defmulti paint (fn [g object & _] (:type object)))

;多态的实现之一
(defmethod paint :apple [g {:keys [location color]}] ; <label id="code.paint.apple"/>
  (fill-point g location color))

;多态的实现之一
(defmethod paint :snake [g {:keys [body color]}] ; <label id="code.paint.snake"/>
;doseq 使用point 迭代 body里面的每一个元素,然后传递给(fill-point g point color)处理
;确保snake body里面的每一个元素都被打印
  (doseq [point body]
    (fill-point g point color)))

	
;返回的是一个Class,实现了2个接口,覆写父类的部分方法	
(defn game-panel [frame snake apple]
;JPanel是Class, ActionListener KeyListener 是Interface
  (proxy [JPanel ActionListener KeyListener] []
    (paintComponent [g] ; <label id="code.game-panel.paintComponent"/> 重写 JComponent.paintComponent方法
      (proxy-super paintComponent g)
      (paint g @snake)
      (paint g @apple))
   
   (actionPerformed [e] ; <label id="code.game-panel.actionPerformed"/>实现接口ActionListener总的方法
      (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] ; <label id="code.game-panel.keyPressed"/> ;实现接口KeyListener中的方法
      (update-direction snake (dirs (.getKeyCode e)))) ;捕捉键盘方向键,然后转换成之前定义的Map中寻找匹配的Value
   
   (getPreferredSize []  ;重写 JComponent.getPreferredSize 方法
      (Dimension. (* (inc width) point-size) 
  (* (inc height) point-size)))
    
(keyReleased [e]) ;实现接口KeyListener中的方法
    
(keyTyped [e]))) ;实现接口KeyListener中的方法

(defn game [] 
  (let [snake (ref (create-snake)) ; <label id="code.game.let"/>
apple (ref (create-apple))
frame (JFrame. "Snake")
panel (game-panel frame snake apple)
timer (Timer. turn-millis panel)]
;设置Panel对应的监听
    (doto panel ; <label id="code.game.panel"/>
      (.setFocusable true)
      (.addKeyListener panel))
;将游戏Panel关联到frame上面	  
    (doto frame ; <label id="code.game.frame"/>
      (.add panel)
      (.pack)
      (.setVisible true))
    (.start timer) ; <label id="code.game.timer"/>
    [snake, apple, timer])) ; <label id="code.game.return"/>

  

0
0
分享到:
评论

相关推荐

    Lacinia纯Clojure实现的GraphQL

    Lacinia 纯Clojure实现的GraphQL

    [Clojure] 网络应用开发 (Clojure 实现) (英文版)

    [Pragmatic Bookshelf] 网络应用开发 (Clojure 实现) (英文版) [Pragmatic Bookshelf] Web Development with Clojure Build Bulletproof Web Apps with Less Code (E-Book) ☆ 图书概要:☆ If the usual ...

    Python-利用Clojure实现的一个可拖放的看板示例

    利用Clojure实现的一个可拖放的“看板”示例

    clojurec, 在C 之上,一个Clojure实现.zip

    clojurec, 在C 之上,一个Clojure实现 ClojureC这是面向的面向对象编程语言的编译器。 它基于 ClojureScript,并开始于ClojureScript提交 0e0aa7fdd379649bf87f8fff5c6a64e37fe616a4 社区和组织我们使用

    有关JAVA的译文 Clojure:“挑战”Java编程语言

    在今后的软件设计中无论使用Clojure语言,还是坚持使用Java语言,Clojure语言都将与java做比较,哪种是设计软件的最佳方式。 Clojure语言是一个JVM(包括Groovy,Jython和JRuby等语言)的新语言,它提供了活力,...

    Programming Clojure 英文电子版

    这是Programming Clojure 电子版的 纸质版本在美国亚马逊要到2009年3月才能上架 Paperback: 200 pages Publisher: Pragmatic Bookshelf (March 15, 2009) Language: English ISBN-10: 1934356336 ISBN-13: 978-...

    Clojure可选类型系统TypedClojure.zip

    主要特性:从 Java 中保护你的 Clojure 程序,进行安全的互操作,正确的使用外部 Java 库。Typed Clojure 支持关键字映射的使用,类型检测器中包含有用的映射操作,包括关键字查找,添加或者移除关键字,合并映射...

    trammel, 使用Clojure进行编程.zip

    trammel, 使用Clojure进行编程 用于Clojure的合同程序。 官方文档和使用场景原始公告 ( 自那时起,语法已经发展)示例函数约定 (require '[trammel.provide :as provide]) (defn sqr [n

    Clojure编程乐趣

    Clojure is an opinionated language—it doesn’t try to cover all paradigms or provide every checklist bullet-point feature. Instead it provides the features needed to solve all kinds of real-world ...

    nginx-clojure-0.5.1.tar.gz

    Nginx-Clojure 是一个 Nginx 的模块,用于嵌入 Clojure 或者 Java 或者 Groovy 程序。 可以通过nginx-clojure实现JAVA扩展nginx的功能,如权限验证。

    Professional.Clojure.1119267277

    Clear, practical Clojure for the professional programmer Professional Clojure is the experienced developer's guide to functional programming using the Clojure language. Designed specifically to meet ...

    Clojure学习——使用clojure jdbc操作mysql

    NULL 博文链接:https://clojure.iteye.com/blog/1741375

    Python-cljcbloom一个用Clojure脚本实现的跨平台布隆过滤器

    cljc-bloom 一个用Clojure(脚本)实现的跨平台布隆过滤器

    Practical Clojure.pdf

    Practical Clojure Clojure语言书籍

    blurhash:Blushash算法的Clojure实现

    uNGM{nTNHMzIVnn → Clojure实现是用CLJC文件编写的,因此可以像Clojure和ClojureScript代码一样使用它们。编码方式您可以使用功能blurhash.encode/encode将图像编码为blurhash。 它将图像作为RGB矩阵,当前表示为...

    clojure eclipse

    clojure clojure clojureclojure clojure

    clojure相关书籍1

    【1】[Clojure编程乐趣](The Joy of Clojure).pdf 【2】Clojure – Functional Programming for the JVM中文版.pdf 【3】Clojure Cookbook.pdf 【4】Clojure Data Analysis Cookbook.pdf 【5】clojure Hand book...

    Clojure电子书合集2(13本)

    [2013] Functional Programming Patterns in Scala and Clojure - Write Lean Programs for the JVM.(Michael Bevilacqua-Linn).[1937785475].pdf+epub.rar [2014] Clojure Cookbook - Recipes for Functional ...

    clojure 解释程序

    带语法高亮的clojure 1.4 解释器, 对学习clojure很有帮助

Global site tag (gtag.js) - Google Analytics