May 26, 2015

Tetris in clojurescript and re-frame Part 9 - Game Ticker

Now we need to define what happens every "round" of the game. In Tetris this just means that the current active tetriminio will drop by 1 on the y axis until it reaches the bottom or another tetriminion on the grid.
For this we have three functions.

move-tick will check if the tetriminion is allowed to move one step down and if so, moves it one step down

(defn- move-tick [app-state]
  (let [cur-active (:cur-active app-state)
        cur-grid (:grid-state app-state)
        remove-cur-grid (minios/draw-tet cur-active minios/tet-recipe 0 cur-grid)]    
    (if (minios/is-move-allowed? (update-in cur-active [:y] + 1) cur-active remove-cur-grid minios/tet-recipe)
      (let [moved-active (update-in cur-active [:y] + 1)
            moved-grid (minios/draw-tet moved-active minios/tet-recipe 1 remove-cur-grid)]
        (assoc app-state :grid-state moved-grid :cur-active moved-active))
      (let [new-act (minios/get-rand-tetriminio)]
        (if (minios/is-move-allowed? (update-in new-act [:y] + 1) cur-active cur-grid minios/tet-recipe)
          (assoc app-state :cur-active new-act)
          (do (dispatch [:game-over]) app-state))))))

check-lvl just checks if a level up is reached and if so, increases the level count and decreases the timer.


(defn check-lvl [app-state new-points]
  (when (< (* 10 (:lvl app-state)) (+ new-points (:score app-state))) (dispatch [:next-lvl])))

Whenever the :game-sec-tick event is dispatched we move the tetriminio one step down, check if we have to update the score and update the level if needed.
As you can see, we do that by updating the app state, which is then returned from the handler.

(register-handler
  :game-sec-tick
  (rf/after grid-changed-mw)
  (fn [app-state]
    (let [moved-app-state (move-tick app-state)
          new-points (minios/count-points (:grid-state moved-app-state))]
      (check-lvl moved-app-state new-points)
      (update-in
        (assoc moved-app-state :grid-state (minios/remove-full-lines (:grid-state moved-app-state)))
        [:score] + new-points))))

Here we have a few more functions needed for our game logic, to count the points and to remove the full lines.

Transpose the 2d vec:

(defn transpose [xs] (apply map vector xs))

Does the vec contain zero?

(defn wanted? [xs] (some zero? xs))

Fill the vec with zeros after removing them.

(defn lpad [n xs] (vec (concat (repeat (- n (count xs)) 0) xs)))

And, tie the functions together:

(defn remove-full-lines [xs]
  (let [n (count (first xs))]
    (vec (map (partial lpad n) (transpose (filter wanted? (transpose xs)))))))

(defn count-points [xs]
  (count (filter #(not (some zero? %)) (transpose xs))))

Tags: tetris clojuresrcipt