...
 
Commits (3)
......@@ -12,6 +12,7 @@
[hiccup "1.0.5"]
[environ "1.1.0"]
[franz "0.3.1"]
[re-frame "0.10.5"]
[ring-transit "0.1.6"]]
:plugins [[lein-cljsbuild "1.1.6"]]
......@@ -21,9 +22,10 @@
:resource-paths ["resources"]
:profiles {:dev {:dependencies [[com.cemerick/piggieback "0.2.1"]
[binaryage/devtools "0.9.10"]
[figwheel-sidecar "0.5.10"]
[cljs-ajax "0.6.0"]
[reagent "0.6.0"]]
[reagent "0.7.0"]]
:source-paths ["dev/clj" "dev/cljs"]
:resource-paths ["dev/resources"]}
:uberjar {:main harmony.core
......@@ -40,7 +42,8 @@
:source-map true
:asset-path "/js/out"
:main harmony.core
:optimizations :none}}
:optimizations :none
:preloads [devtools.preload]}}
:prod {:source-paths ["src/cljs"]
:jar true
:compiler {:output-to "resources/public/js/app.js"
......
......@@ -28,16 +28,16 @@
[:td (str (:id level))]
[:td (str (:size level))]
[:td
[:div {:class "field has-addons"}
[:div {:class "buttons"}
[:a {:class "button is-primary is-outlined"
:href (str "/play/" (:id level))}
:href (str "/play/" (:id level))}
"Play"]
[:a {:class "button is-info is-outlined"
:href (str "/edit/" (:id level))}
:href (str "/edit/" (:id level))}
"Edit"]
(form/form-to [:post (str "/delete/" (:id level))]
[:input {:class "button is-danger is-outlined"
:type :submit
:type :submit
:value "Delete"}])]]])]])))
(defroutes routes
......@@ -58,12 +58,14 @@
(prn "No level: " level-id)))
(POST "/create-level" {:keys [body]}
(when (db/create! (assoc body :id (UUID/randomUUID)))
{:status 200
:body {:saved? true}}))
(let [id (UUID/randomUUID)]
(when (db/create! (assoc body :id id))
{:status 200
:body {:saved? true
:id id}})))
(GET "/load-stuff" [] {:status 200
:body {:something "here"}})
:body {:something "here"}})
(route/resources "/")
(route/not-found "Not found"))
......
(ns harmony.core
(:require [reagent.core :as r]
[re-frame.core :as re-frame]
[harmony.events :as events]
[harmony.utils :as utils]
[harmony.create :as create]
[harmony.play :as play]))
(enable-console-print!)
(defn ^:export render-play []
(re-frame/dispatch-sync [::events/initialize! (utils/read-embedded-data)])
(re-frame/clear-subscription-cache!)
(r/render
[play/play-component]
(js/document.getElementById "app")))
(defn ^:export render-new []
(re-frame/dispatch-sync [::events/init-board! (utils/read-embedded-data)])
(re-frame/clear-subscription-cache!)
(r/render
[create/create-component]
(js/document.getElementById "app")))
......
(ns harmony.create
(:require [ajax.core :as ajax]
[clojure.string :as str]
[franz.core :as f]
[harmony.board :as b]
[harmony.utils :as utils]
[reagent.core :as r]))
[re-frame.core :as re-frame]
[harmony.events :as events]
[harmony.board :as b]))
(defonce app-state (r/atom {}))
(defonce main-topic (f/topic (f/log)))
(defmulti handle-command
(fn [_state msg] (first msg)))
(re-frame/reg-sub :app-state
identity)
(defn save! []
(ajax/POST "/create-level"
{:params @app-state
:error-handler (fn [e] (js/alert e))
:handler (fn [resp] (when (:saved? resp)
(js/alert "Level saved")
(.replace (.-location js/document) "/")))}))
(ajax/ajax-request {:uri "/create-level"
:method :post
:params @(re-frame/subscribe [:app-state])
:handler (fn [[ok resp]]
(if ok
(do (.log js/console resp)
(when (:saved? resp)
(js/alert (str "Level saved: " (:id resp)))
(.replace (.-location js/document) "/")))
(.error js/console resp)))
:format (ajax/transit-request-format)
:response-format (ajax/transit-response-format {:keywords? true})}))
(defn size-chooser []
[:div {:class "field is-horizontal"}
......@@ -27,25 +28,23 @@
[:div {:class "field-body"}
[:div {:class "field"}
[:p {:class "control"}
[:input {:class "input"
:type "Number"
:min 1
:value (:size @app-state)
:on-change #(f/send! main-topic [:change-size (-> % .-target .-value js/parseInt)])}]]]
[:input {:class "input"
:type "Number"
:min 1
:value (:size @(re-frame/subscribe [:app-state]))
:on-change #(re-frame/dispatch [::events/change-size! (-> % .-target .-value js/parseInt)])}]]]
[:div {:class "field"}
[:a {:class "button is-primary"
[:a {:class "button is-primary"
:on-click save!}
"Save"]]]])
(defmethod handle-command :change-size
[state [_cmd value]]
{:size value
:column-links (into [] (repeat (* value (dec value)) 1))
:row-links (into [] (repeat (* value (dec value)) 1))})
(defn level-creator []
(let [board (->> @app-state b/create-board b/solve)
(let [
board (->> @(re-frame/subscribe [:app-state])
b/create-board b/solve)
{:keys [size cells]} board]
[:div
[:div {:class "board preview-board"}
......@@ -54,114 +53,61 @@
(fn [i row]
[:div {:class "row"
:style {:height (str (/ 100 size) "%")}
:key i}
:key i}
(doall
(map-indexed
(fn [j c]
(let [index (+ (* i size) j)
tile-name (name (:tile c))]
[:img {:key (str i j)
:class (str "cell rotate-" (get-in cells [index :status]))
:src (str "/tiles/" tile-name ".svg")
:width (str (/ 100 size) "%")
:on-click #(f/send! main-topic [:toggle-cell index])}]))
[:img {:key (str i j)
:class (str "cell rotate-" (get-in cells [index :status]))
:src (str "/tiles/" tile-name ".svg")
:width (str (/ 100 size) "%")
:on-click #(re-frame/dispatch [::events/toggle-cell! index])}]))
row))])
(partition size cells)))]]))
(defmethod handle-command :init-board
[_state [_cmd data]]
(if (:size data)
data
{:size 2
:column-links [0 1]
:row-links [0 0]
:pattern [1 1 1 1]}))
(defn column-link-board []
(let [{:keys [column-links size]} @app-state
(let [{:keys [column-links size]} @(re-frame/subscribe [:app-state])
row-count (dec size)
col-count size]
[:div {:class "create-board"}
(for [i (range row-count)]
[:div {:key i
[:div {:key i
:class "row"
:style {:height (str (/ 100 row-count) "%")}}
(for [j (range col-count)]
(let [index (+ (* i col-count) j)]
[:div {:key (str "col-" i j)
:on-click #(f/send! main-topic [:toggle-column-link index])
:style {:width (str (/ 100 col-count) "%")}
:class (str "link-block"
(when (= 1 (get column-links index))
" fill"))}]))])]))
[:div {:key (str "col-" i j)
:on-click #(re-frame/dispatch [::events/toggle-column-link! index])
:style {:width (str (/ 100 col-count) "%")}
:class (str "link-block"
(when (= 1 (get column-links index))
" fill"))}]))])]))
(defn row-link-board []
(let [{:keys [row-links size]} @app-state
(let [{:keys [row-links size]} @(re-frame/subscribe [:app-state])
row-count size
col-count (dec size)]
[:div {:class "create-board"}
(for [i (range row-count)]
[:div {:key i
[:div {:key i
:class "row"
:style {:height (str (/ 100 row-count) "%")}}
(for [j (range col-count)]
(let [index (+ (* i col-count) j)]
[:div {:key (str "row-" i j)
:on-click #(f/send! main-topic [:toggle-row-link index])
:style {:width (str (/ 100 col-count) "%")}
:class (str "link-block"
(when (= 1 (get row-links index))
" fill"))}]))])]))
(defmethod handle-command :toggle-column-link
[state [_cmd index]]
(update-in state [:column-links index] (comp #(mod % 2) inc)))
(defmethod handle-command :toggle-row-link
[state [_cmd index]]
(update-in state [:row-links index] (comp #(mod % 2) inc)))
(defmethod handle-command :toggle-cell
[state [_cmd index]]
(update-in state [:pattern index]
(comp #(mod % 2) inc)))
(defmethod handle-command :clear-board
[state [_cmd]]
(-> state
(update-in [:row-links] (partial mapv (constantly 0)))
(update-in [:column-links] (partial mapv (constantly 0))))
)
(defmethod handle-command :fill-board
[state [_cmd]]
(-> state
(update-in [:row-links] (partial mapv (constantly 1)))
(update-in [:column-links] (partial mapv (constantly 1)))))
[:div {:key (str "row-" i j)
:on-click #(re-frame/dispatch [::events/toggle-row-link! index])
:style {:width (str (/ 100 col-count) "%")}
:class (str "link-block"
(when (= 1 (get row-links index))
" fill"))}]))])]))
(defmethod handle-command :randomize-board
[state [_cmd]]
(-> state
(update-in [:row-links] (partial mapv #(rand-int 2)))
(update-in [:column-links] (partial mapv #(rand-int 2))))
#_(-> state
(assoc-in [:row-links] (repeatedly (:size state) #(rand-int 2)))
(assoc-in [:column-links] (repeatedly (:size state) #(rand-int 2))))
)
(defn init-handler []
(f/subscribe! main-topic
:main
(f/sparse-handler
(fn [_o [_key msg]]
(swap! app-state handle-command msg)))))
(defn create-component []
(init-handler)
(f/send! main-topic [:init-board (utils/read-embedded-data)])
(fn []
[:div
[:div {:class "columns"}
......@@ -169,15 +115,15 @@
[size-chooser]]]
[:div {:class "columns"}
[:div {:class "column is-half is-offset-one-quarter has-centered-stuff"}
[:button {:class "button btn-create is-info"
:on-click #(f/send! main-topic [:clear-board])} "Clear"]
[:button {:class "button btn-create is-info"
:on-click #(f/send! main-topic [:fill-board])} "Fill"]
[:button {:class "button btn-create is-info"
:on-click #(f/send! main-topic [:randomize-board])} "Random"]]]
[:button {:class "button btn-create is-info"
:on-click #(re-frame/dispatch [::events/clear-board!])} "Clear"]
[:button {:class "button btn-create is-info"
:on-click #(re-frame/dispatch [::events/fill-board!])} "Fill"]
[:button {:class "button btn-create is-info"
:on-click #(re-frame/dispatch [::events/randomize-board!])} "Random"]]]
[:div {:class "columns"}
[:div {:class "column is-half is-offset-one-quarter has-centered-stuff"}
[:p "Board fitness: " (->> @app-state b/create-board b/board-fitness)]]]
[:p "Board fitness: " (->> @(re-frame/subscribe [:app-state]) b/create-board b/board-fitness)]]]
[:div {:class "columns"}
[:div {:class "column is-one-quarter"}
......
(ns harmony.events
(:require [re-frame.core :as re-frame]
[harmony.board :as board]))
(re-frame/reg-event-db
::initialize!
(fn [_ [_cmd data]]
(board/create-board data)))
(re-frame/reg-event-db
::rotate-tile!
(fn [db [_cmd index]]
(update-in db [:cells index :status] #(rem (inc %) 4))))
(re-frame/reg-event-db
::reset-game!
(fn [db _]
(board/scramble db)))
(re-frame/reg-event-db
::change-size!
(fn [_state [_cmd value]]
{:size value
:column-links (into [] (repeat (* value (dec value)) 1))
:row-links (into [] (repeat (* value (dec value)) 1))}))
(re-frame/reg-event-db
::init-board!
(fn [_state [_cmd data]]
(if (:size data)
data
{:size 2
:column-links [0 1]
:row-links [0 0]
:pattern [1 1 1 1]})))
(re-frame/reg-event-db
::toggle-column-link!
(fn [state [_cmd index]]
(update-in state [:column-links index] (comp #(mod % 2) inc))))
(re-frame/reg-event-db
::toggle-row-link!
(fn [state [_cmd index]]
(update-in state [:row-links index] (comp #(mod % 2) inc))))
(re-frame/reg-event-db
::toggle-cell!
(fn [state [_cmd index]]
(update-in state [:pattern index]
(comp #(mod % 2) inc))))
(re-frame/reg-event-db
::clear-board!
(fn [state [_cmd]]
(-> state
(update-in [:row-links] (partial mapv (constantly 0)))
(update-in [:column-links] (partial mapv (constantly 0)))))
)
(re-frame/reg-event-db
::fill-board!
(fn [state [_cmd]]
(-> state
(update-in [:row-links] (partial mapv (constantly 1)))
(update-in [:column-links] (partial mapv (constantly 1))))))
(re-frame/reg-event-db
::randomize-board!
(fn [state [_cmd]]
(-> state
(update-in [:row-links] (partial mapv #(rand-int 2)))
(update-in [:column-links] (partial mapv #(rand-int 2))))))
\ No newline at end of file
(ns harmony.play
(:require [reagent.core :as r]
[harmony.board :as b]
[harmony.utils :as utils]
[franz.core :as f]))
[harmony.events :as events]
[re-frame.core :as re-frame]))
(defonce game-state (r/atom nil))
(defonce main-topic (f/topic (f/log)))
(defmulti handle-command
(fn [_state msg] (first msg)))
(re-frame/reg-sub
:game-state
(fn [app-state]
app-state))
(defn board []
(let [{:keys [size cells]} @game-state]
(let [{:keys [size cells]} @(re-frame/subscribe [:game-state])]
[:div
[:div {:class "board-controls"}
[:div {:class "play-board"}
(doall
(map-indexed
......@@ -31,54 +29,32 @@
:class (str "cell rotate-" (get-in cells [index :status]))
:src (str "/tiles/" (name (:tile c)) ".svg")
:width (str (/ 100 size) "%")
:on-click #(f/send! main-topic [:rotate-tile index])}]))
:on-click #(re-frame/dispatch [::events/rotate-tile! index])}]))
row))])
(partition size cells)))]]
[:div {:class "columns"}
[:div {:class "column is-half is-offset-one-quarter"}
[:div {:class "board-controls"}
[:button {:class "button is-primary"
:on-click #(f/send! main-topic [:reset-game])} "Reset"]]]]]))
(defmethod handle-command :rotate-tile
[state [_cmd index]]
(update-in state [:cells index :status] #(rem (inc %) 4)))
:on-click #(re-frame/dispatch [::events/reset-game!])} "Reset"]]]]]))
(defn init-handler []
(f/subscribe! main-topic
:main
(f/sparse-handler
(fn [_o [_key msg]]
(swap! game-state handle-command msg)))))
(defmethod handle-command :reset-game
[state [_cmd]]
(b/scramble state))
(defmethod handle-command :init-game
[_state [_cmd data]]
(->> data
(b/create-board)))
(defn notification []
(if (and @game-state
(b/correct? @game-state))
[:div {:class "notification is-success"}
"Congratulations! Harmony achieved."]
[:div {:class "notification is-blank"}]))
(let [game-state @(re-frame/subscribe [:game-state])]
(if (and game-state
(b/correct? game-state))
[:div {:class "notification is-success"}
"Congratulations! Harmony achieved."]
[:div {:class "notification is-blank"}])))
(defn play-component []
(init-handler)
(f/send! main-topic [:init-game (utils/read-embedded-data)])
(fn []
[:div
[notification]
[board]
#_[:iframe {:width "1px"
:height "1px"
:scrolling "no"
:frameBorder "no"
:src "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/58173187&auto_play=true&hide_related=false&show_comments=true&show_user=true&show_reposts=false&visual=true"}]]))
[:div
[notification]
[board]])
(comment #_[:iframe {:width "1px"
:height "1px"
:scrolling "no"
:frameBorder "no"
:src "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/58173187&auto_play=true&hide_related=false&show_comments=true&show_user=true&show_reposts=false&visual=true"}])
\ No newline at end of file