(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__)
Last active
May 8, 2018 01:50
-
-
Save philoskim/5d806aca0f53c57a682d2573db3d690f to your computer and use it in GitHub Desktop.
Game state management by using nested refs in Clojure
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment