Skip to content

Instantly share code, notes, and snippets.

@philoskim
Last active May 8, 2018 01:50
Show Gist options
  • Save philoskim/5d806aca0f53c57a682d2573db3d690f to your computer and use it in GitHub Desktop.
Save philoskim/5d806aca0f53c57a682d2573db3d690f to your computer and use it in GitHub Desktop.
Game state management by using nested refs in Clojure

Game state management by using nested refs in Clojure

(defn- ->vec [k]
  (if (vector? k) k [k]))

(defmacro get-in* [r ks]
  `(-> (deref ~r)
       ~@(->> (map (fn [k] `(get-in ~(->vec k))) ks)
              (interpose 'deref))))

(defmacro update-in! [r ks f & args]
  `(let [r# ~r]
     (-> (get-in* r# ~(vec (butlast ks)))
         (alter update-in ~(->vec (last ks)) ~f ~@args))
     r#))


(def game*
  (ref {:room-1 (ref {:user-1 {:name "aaa" :score 100 ,,,}
                      :user-2 {:name "bbb" :score 200 ,,,}
                      :room-state {:user-number 2 ,,,}})
        :room-2 (ref {:user-1 {:name "ccc" :score 300 ,,,}
                      :user-2 {:name "ddd" :score 400 ,,,}
                      :room-state {:user-number 2 ,,,}})
        ,,,}))


(get-in* game* [:room-1 [:user-1 :score]])
; => 100

(macroexpand-1 '(get-in* game* [:room-1 [:user-1 :score]]))
; => (-> (deref game*)
;        (get-in [:room-1])
;        deref
;        (get-in [:user-1 :score]))

(dosync
  (-> (update-in! game* [:room-1 [:user-1 :score]] - 10)
      (update-in! [:room-1 [:user-2 :score]] + 10)))
; => #<Ref@62be32d6:
;      {:room-1
;       #<Ref@27d0c0a5:
;         {:user-1 {:name "aaa", :score 90},
;          :user-2 {:name "bbb", :score 210},
;          :room-state {:user-number 2}}>,
;       :room-2
;       #<Ref@1a5ec74d:
;         {:user-1 {:name "ccc", :score 300},
;          :user-2 {:name "ddd", :score 400},
;          :room-state {:user-number 2}}>}>

@game*
; => {:room-1
;     #<Ref@27d0c0a5:
;       {:user-1 {:name "aaa", :score 90},
;        :user-2 {:name "bbb", :score 210},
;        :room-state {:user-number 2}}>,
;     :room-2
;     #<Ref@1a5ec74d:
;       {:user-1 {:name "ccc", :score 300},
;        :user-2 {:name "ddd", :score 400},
;        :room-state {:user-number 2}}>}

(macroexpand-1 '(update-in! game* [:room-1 [:user-1 :score]] + 1000))
; => (let [r__14301__auto__ game*]
;      (-> (get-in* r__14301__auto__ [:room-1])
;          (alter update-in [:user-1 :score] + 1000))
;      r__14301__auto__)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment