首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >避免进入流星

避免进入流星
EN

Code Review用户
提问于 2018-07-01 13:39:30
回答 1查看 168关注 0票数 7

我正在参观一所大学的函数式编程课程,这门课程有一个小项目要考试。我们使用的语言是clojure,讲座的内容主要是关于语法的,而不是函数式编程的好处和范例。在评分方面,我们的讲师似乎并不太苛刻,但我们仍然不确定我们的程序是否完全符合函数式编程的要求。以下是我们的一些问题:

  1. 当涉及到函数式编程时,您能把事情搞得那么糟糕吗?或者,在使用clojure时,有些事情已经得到了保证?
  2. 我们有一些随机数的函数来随机化输出(例如“创建星号”)。这使得它们“不纯”,因为每次输入相同的输出是不同的。如果我们通过参数传递随机数,那么代码的可读性和清晰性就会大大降低。坚持函数式编程的参数比可见性更重要吗?(如果我们遵循这一思路,我们甚至必须在“更新状态”函数参数中创建和传递所有随机值,因为它是要调用的最外层函数,如果它包含随机性,也是不纯的。)
  3. 框架内部使用一个原子作为状态变量,我们在某些方法中修改了该变量。但是我们总是把它作为参数传递并返回它,所以我们不使用全局变量。这是否违反了不可变对象的原则?
  4. 下面的代码中还有什么违反函数式编程原则的吗?

下面是我们代码的主要部分。完整的项目可以在https://github.com/Niggls/rocket_游戏上找到。这是一个小游戏,你必须控制火箭,以避免进入流星。我们正在使用"quil“库来绘制游戏屏幕的内容。它使用一个"state“变量来处理屏幕上所有对象的数据。主要部分是创建输出的“绘制”函数和评估每个帧的所有功能的“更新状态”函数。这两个都在底部。

代码语言:javascript
复制
(ns rocket-game.core
  (:require [quil.core :as q]
            [quil.middleware :as m]))


(defn star-color-index [x]
  (if (< x 12)
      0
      (if (< x 15)
          2
          3)))

(defn create-star [y]
 {:x (rand-int (q/width))
   :y (rand-int y)
   :size (+ (rand-int 5) 1)
   :speed (+ (rand-int 3) 1)
   :color (star-color-index (rand-int 20))})

;; function for reset state
(defn reset-state-variable [state]
 {:rocket {:image (q/load-image "images/rocket.png")
           :x 260
           :y 340
           :dir 0}
  :background (q/load-image "images/stars.jpg")
  :fires []
  :smoke []
  :score 0
  :stars (:stars state)
  :highscore (if ( > (:score state) (:highscore state))
                 (:score state)
                 (:highscore state))
  :gameOver true
  :meteors []
  :bonus []})

;; setup: here we define our global state variable
;; # --> anonymous function
(defn setup []
  ;; these two lines, a map (data structure) is added in step 1-6
  {:rocket {:image (q/load-image "images/rocket.png")
            :x 260
            :y 340
            :dir -1}
   :background (q/load-image "images/stars.jpg")
   :fires []
   :score 0
   :smoke []
   :highscore 0
   :stars (take 150 (repeatedly #(create-star (q/height))))
   :gameOver false
   :meteors []
   :bonus []})

;;;; helper methods;;;;;;;;;;;;;;;;;;;;;;;;
(defn inside? [x y]
  (or
   (< x -12)
   (> (+ x 33) (q/width))
   (< y 0)
   (> (+ y 40) (q/height))))

(defn item-inside? [item]
 (let [x (:x item)
       y (:y item)]
  (> y (q/height))))

(defn remove-stars [stars]
 (remove item-inside? stars))

(defn meteor-out [state]
  (let [old (-> state :meteors (count))
        new-meteor (remove item-inside? (:meteors state))
        new (count new-meteor)]
   {:rocket (:rocket state)
    :background (q/load-image "images/stars.jpg")
    :fires (:fires state)
    :score (+ (:score state) (- old new))
    :highscore (:highscore state)
    :gameOver false
    :smoke (:smoke state)
    :stars (:stars state)
    :meteors new-meteor
    :bonus (:bonus state)}))

(defn meteor-hit [state]
 (let [rocket-x (-> state :rocket :x)
       rocket-y (-> state :rocket :y)
       meteors (:meteors state)]
   (if (empty? meteors)
       state
       (if (loop [[m1 & rest] meteors]
             (if (or (and
                       (<= (:x m1) rocket-x (+ (:x m1) 45))
                       (<= (:y m1) rocket-y (+ (:y m1) 45)))
                     (and
                       (<= (:x m1) (+ rocket-x 45) (+ (:x m1) 45))
                       (<= (:y m1) (+ rocket-y 45) (+ (:y m1) 45))))
               true
               (if (empty? rest)
                   false
                   (recur rest))))
           (reset-state-variable state)
           state))))

(defn bonus-out [state]
  (if (item-inside? (:bonus state))
    state
    {:rocket (:rocket state)
     :background (q/load-image "images/stars.jpg")
     :fires (:fires state)
     :score (:score state)
     :highscore (:highscore state)
     :gameOver false
     :smoke (:smoke state)
     :stars (:stars state)
     :meteors (:meteors state)
     :bonus []}))

(defn bonus-hit [state]
 (let [rocket-x (-> state :rocket :x)
       rocket-y (-> state :rocket :y)
       bonus (get (:bonus state) 0)]
   (if (empty? bonus)
       state
       (if (or (and
                 (<= (:x bonus) rocket-x (+ (:x bonus) 40))
                 (<= (:y bonus) rocket-y (+ (:y bonus) 40)))
               (and
                 (<= (:x bonus) rocket-x (+ (:x bonus) 40))
                 (<= (:y bonus) (+ rocket-y 45) (+ (:y bonus) 40)))
               (and
                 (<= (:x bonus) (+ rocket-x 45) (+ (:x bonus) 40))
                 (<= (:y bonus) rocket-y (+ (:y bonus) 40)))
               (and
                 (<= (:x bonus) (+ rocket-x 45) (+ (:x bonus) 40))
                 (<= (:y bonus) (+ rocket-y 45) (+ (:y bonus) 40))))
         {:rocket (:rocket state)
          :background (q/load-image "images/stars.jpg")
          :fires (:fires state)
          :score (+ (:score state) (:points bonus))
          :highscore (:highscore state)
          :gameOver false
          :smoke (:smoke state)
          :stars (:stars state)
          :meteors (:meteors state)
          :bonus []}
         state))))

;; # defines a function --> (fn [oldAge] (+ oldAge 0.3))
(defn age-smoke [smoke]
  (update-in smoke [:age] #(+ % 0.3)))

(defn old? [smoke]
  (< 2.0 (:age smoke)))

(defn remove-old-smokes [smokes]
  (remove old? smokes))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;; creation methods ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn create-meteor [state]
 (if (= (rand-int 10) 1)
     (if-not (or
               (-> state :rocket :dir (= 0))
               (-> state :rocket :dir (= -1)))
             (update-in state [:meteors] conj {:x (rand-int (+ (q/width) -40)) :y -40 :speed (+ (rand-int 30) 5)})
             ;(update-in state [:meteors] conj {:x (rand-int (+ (q/width) -40)) :y -40 :speed 1})
             state)
     state))

(defn create-smoke [x y]
  {:pos [(+ x 25 (- (rand-int 10) 5))
         (+ y 50 (- (rand-int 10) 5))]
   :dir 0.0
   :age 0.0
   :col [(+ (rand-int 105) 150)
         (+ (rand-int 100) 100)
         (rand-int 100)]})

(defn emit-smoke [state]
  (let [x (-> state :rocket :x)
        y (-> state :rocket :y)]
    (update-in state [:smoke] conj (create-smoke x y))))

(defn create-new-star [state]
 (if(= (rand-int 7) 1)
   (if-not (or
             (-> state :rocket :dir (= 0))
             (-> state :rocket :dir (= -1)))
           {:rocket (:rocket state)
            :background (q/load-image "images/stars.jpg")
            :fires (:fires state)
            :score (:score state)
            :highscore (:highscore state)
            :gameOver true
            :smoke (:smoke state)
            :stars (conj (:stars state) (create-star 1))
            :meteors (:meteors state)
            :bonus (:bonus state)}
           state)
   state))

(defn create-bonus [state]
 (if (and (empty? (:bonus state)) (= (rand-int 100) 1))
   (if-not (or
             (-> state :rocket :dir (= 0))
             (-> state :rocket :dir (= -1)))
           (if (= (rand-int 5) 1)
             (update-in state [:bonus] conj {:x (rand-int (+ (q/width) -40)) :y (rand-int (+ (q/height) -40)) :points 25 :speed 3 :image "images/bonus2.png"})
             (update-in state [:bonus] conj {:x (rand-int (+ (q/width) -40)) :y (rand-int (+ (q/height) -40)) :points 10 :speed 2 :image "images/bonus.png"}))
     state)
  state))

(defn fly-backwards [smoke state]
 (if (-> state :rocket :dir (= 2))
     []
     smoke))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;; reset methods;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defn reset-game [state]
  (if
    (inside? (:x (:rocket state )) (:y (:rocket state)))
    (reset-state-variable state)
    state))

(defn reset-game-over [gameOver state]
  (if (-> state :rocket :dir (not= 0))
    false
    true))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;,

;;;;;;;; move methods;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn move [state event]
  (case (:key event)
    (:w :up) (assoc-in state [:rocket :dir] 1)
    (:s :down) (assoc-in state [:rocket :dir] 2)
    (:a :left) (assoc-in state [:rocket :dir] 3)
    (:d :right) (assoc-in state [:rocket :dir] 4)
    state))

(defn move-meteors [meteor]
 (let [speed (:speed meteor)]
  (update-in meteor [:y] #(+ % speed))))

(defn move-star [star]
 (update-in star [:y] #(+ % (:speed star))))

(defn move-stars [state]
 (if-not (or
           (= (:dir (:rocket state)) 0)
           (= (:dir (:rocket state)) -1))
         {:rocket (:rocket state)
          :background (q/load-image "images/stars.jpg")
          :fires (:fires state)
          :score (:score state)
          :highscore (:highscore state)
          :gameOver true
          :smoke (:smoke state)
          :stars (doall (map move-star (:stars state)))
          :meteors (:meteors state)
          :bonus (:bonus state)}
         state))

(defn move-bonus [state]
  (if (empty? (:bonus state))
    state
    (update-in (:bonus state) [:y] + 4)))

(defn move-rocket [rocket]
 (case (:dir rocket)
  (1) (update-in  rocket [:y] - 10)
  (2) (update-in  rocket [:y] + 10)
  (3) (update-in  rocket [:x] - 10)
  (4) (update-in  rocket [:x] + 10)
  (0) (update-in  rocket [:x] + 0)
  (-1) (update-in  rocket [:x] + 0)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



; draw method
(defn draw [state]
  ;; q/background-image and q/image functions are added in step 1-6
  ;(q/background-image (:background state))
  (q/background 0)
  (q/fill 250 250 250)
  (q/stroke 250 250 250)
  (doseq [star (:stars state)]
   (if (= (:color star) 0)
       (do
         (q/fill 250 250 250)
         (q/stroke 250 250 250)
         (q/ellipse (:x star) (:y star) (:size star) (:size star)))
       (if (= (:color star) 1)
           (do
             (q/fill 255 255 26)
             (q/stroke 255 255 26)
             (q/ellipse (:x star) (:y star) (:size star) (:size star)))
           (do
             (q/fill 255 77 77)
             (q/stroke 255 77 77)
             (q/ellipse (:x star) (:y star) (:size star) (:size star))))))
  (doseq [bonus (:bonus state)]
    (q/image (q/load-image (:image bonus))
             (:x bonus)
             (:y bonus)
             45 45))
  (q/image (:image (:rocket state))
           (:x (:rocket state))
           (:y (:rocket state)))
  (q/fill 0 0 255)
  (q/text-align :left)
  (q/stroke 0 0 255)
  (doseq [meteor (:meteors state)]
    (q/image (q/load-image "images/meteor.png")
             (:x meteor)
             (:y meteor)))
  (doseq [smoke (:smoke state)]
    (let [age (:age smoke)
          size (max 0.0 (- 10.0 (* 5.0 age)))
          [r g b] (:col smoke)
          [x y] (:pos smoke)]
      (q/fill 0 0 250 150)
      (q/stroke 0 0 250 150)
      (q/ellipse x y size size)))
  (q/fill 255 255 255)
  (q/text-size 20)
  (q/text (str "Score: " (:score state)) 10 30)
  (q/text (str "Highscore: " (:highscore state)) (- (q/width) 140) 30)
  (q/fill 200 0 0)
  (q/text-font (q/create-font "DejaVu Sans" 40 true))
  (q/text-align :center)
  (when (:gameOver state)
    (q/text (str "Game Over...\nMove to try again") (/ (q/width) 2) 500)))

; update method
(defn update-state [state]
 (-> state
  (update-in [:meteors] (fn [meteors] (doall (map move-meteors meteors))))
  (update-in [:rocket] move-rocket)
  ; (update-in [:bonus] (fn [bonus] (doall (map move-bonus bonus))))
  move-stars
  create-new-star
  (update-in [:stars] remove-stars)
  emit-smoke
  (update-in [:smoke] (fn [smokes] (map age-smoke smokes)))
  (update-in [:smoke] remove-old-smokes)
  meteor-out
  create-meteor
  ; bonus-out
  create-bonus
  bonus-hit
  meteor-hit
  reset-game
  (update-in [:smoke] fly-backwards state)
  (update-in [:gameOver] reset-game-over state)))

;; defsketch
(q/defsketch rocket_game
  :host "host"
  :title "rocket game"
  :size [600 700]
  :setup setup
  :draw draw
  :key-pressed move
  :update update-state
  :middleware [m/fun-mode])
EN

回答 1

Code Review用户

回答已采纳

发布于 2018-07-01 22:52:36

票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/197600

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档