From 60c13d2688faf4ad6ab8d078e675c615fe7cefba Mon Sep 17 00:00:00 2001 From: Chris Oakman Date: Thu, 18 Mar 2021 20:08:36 -0500 Subject: [PATCH] class refactoring --- .../com/github/dbasner/this_or_that/core.cljs | 6 +- .../dbasner/this_or_that/game_logic.cljs | 210 ++++++++++++------ .../game_logic/situation_generator.cljs | 37 +++ .../com/github/dbasner/this_or_that/html.cljs | 10 +- .../dbasner/this_or_that/interaction.cljs | 138 ++---------- .../github/dbasner/this_or_that/state.cljs | 18 +- 6 files changed, 206 insertions(+), 213 deletions(-) create mode 100644 src/main/com/github/dbasner/this_or_that/game_logic/situation_generator.cljs diff --git a/src/main/com/github/dbasner/this_or_that/core.cljs b/src/main/com/github/dbasner/this_or_that/core.cljs index 6b9b06b..6f5c42a 100644 --- a/src/main/com/github/dbasner/this_or_that/core.cljs +++ b/src/main/com/github/dbasner/this_or_that/core.cljs @@ -3,13 +3,13 @@ [goog.functions :as gfunctions] [oops.core :refer [ocall]] [com.github.dbasner.this-or-that.game-logic] - [com.github.dbasner.this-or-that.interaction :refer [trigger-render! init-watchers! init-dom-events!]])) + [com.github.dbasner.this-or-that.interaction :refer [trigger-render! init-watchers! attach-dom-events!]])) (def init! (gfunctions/once (fn [] (js/console.log "Initializing This-Or-That!!!!") - (init-dom-events!) + (attach-dom-events!) (init-watchers!) (trigger-render!)))) @@ -18,4 +18,4 @@ (trigger-render!)) -(ocall js/window "addEventListener" "load" init!) \ No newline at end of file +(ocall js/window "addEventListener" "load" init!) diff --git a/src/main/com/github/dbasner/this_or_that/game_logic.cljs b/src/main/com/github/dbasner/this_or_that/game_logic.cljs index b6b63c9..2cc5906 100644 --- a/src/main/com/github/dbasner/this_or_that/game_logic.cljs +++ b/src/main/com/github/dbasner/this_or_that/game_logic.cljs @@ -1,71 +1,141 @@ (ns com.github.dbasner.this-or-that.game-logic - (:require [ajax.core :refer [GET POST]])) - -;;OK so how does this game work? -;; what is it's core? -;; generate two possibilities -;; a or b -;; would you rather -;; [verb] [count] [nouns] or [verb] [count] [nouns] ? - -;; start by doing just generating a basic string - -(def verbs ["eat" "cook" "punch" "get punched by" - "get kicked by" "kick" "date" "kiss" - "marry" "love" "get laughed at by" - "live with" "barbecue" "get yelled at by" - "upset" "bother" "take a roadtrip with" - "fly sitting next to" "fall for" - "get trapped on a desert island with" - "become conjoined twins with" - "be trapped with" "be looked up to by" - "settle down with" "burn" "get wasted with"]) - -(def count-modifiers ["roughly" "about" "precisely" "exactly" "almost" "just about"]) - -(def nouns ["sharks" "people" "dogs" "turtles" - "chairs" "tables" "dinosaurs" "computers" - "men" "women" "cats" "rats" "couches" - "airplanes" "cars" "haters" "phones" - "jugglers" "clowns" "doctors" "therapists" - "lawyers" "artists" "hipsters"]) - -(defn rand-verb [] (rand-nth verbs)) -(defn rand-count-modifier [] (rand-nth count-modifiers)) -(defn rand-situation-count [n] (+ (rand-int n) 2)) -(defn rand-noun [] (rand-nth nouns)) - -(defn generate-situation [] - {:verb (rand-verb) - :count-modifier (rand-count-modifier) - :count (rand-situation-count 20) - :noun (rand-noun)}) - -(defn handler [response] - (.log js/console (str response))) - -(defn error-handler [{:keys [status status-text]}] - (.log js/console (str "something bad happened: " status " " status-text))) - - -(comment - (GET "https://wordsapiv1.p.rapidapi.com/words/?partOfSpeech=noun?random=true" - {:headers {:x-rapidapi-key "YOUR_API_KEY_HERE" - :x-rapidapi-host "wordsapiv1.p.rapidapi.com"}} - :handler handler - :error-handler error-handler) - - (GET "https://webknox-words.p.rapidapi.com/words/{WORD}/plural" - {:headers {:x-rapidapi-key "YOUR_API_KEY_HERE" - :x-rapidapi-host "wordsapiv1.p.rapidapi.com"}} - :handler handler - :error-handler error-handler)) - - - - - - - - - + "core game logic" + (:require + [ajax.core :refer [GET POST]] + [com.github.dbasner.this-or-that.game-logic.situation-generator :refer [random-situation]] + [com.github.dbasner.this-or-that.state :refer [state initial-state]])) + +(defn add-vote + [state player-id situation-id] + (update-in state [:current-round-votes situation-id] conj player-id)) + +(defn rotate-to-next-player + "increment the current player index or reset to 0 if we are ready for the next round" + [state] + (let [num-players (-> state :player-ids count) + max-player-idx (dec num-players) + voter-index (:current-voter-index state)] + (if (< voter-index max-player-idx) + (update-in state [:current-voter-index] inc) + (assoc state :current-voter-index 0)))) + +(defn increment-score [state player-id] + (update-in state [:scores player-id] inc)) + +(defn increment-player-scores + "for a collection of player ids, increment the score" + [state player-ids] + (reduce #(increment-score %1 %2) state player-ids)) + +(defn add-scores + [old-state] + (let [situationA-votes (get-in old-state [:current-round-votes :situationA]) + ;; FYI - these are all the same + ; situationB-votes (:situationB (:current-round-votes old-state)) + ; situationB-votes (-> old-state :current-round-votes :situationB) + situationB-votes (get-in old-state [:current-round-votes :situationB]) + + situationA-count (count situationA-votes) + situationB-count (count situationB-votes)] + (cond + (= situationA-count situationB-count) (increment-player-scores old-state (:player-ids state)) + (> situationA-count situationB-count) (increment-player-scores old-state situationA-votes) + (< situationA-count situationB-count) (increment-player-scores old-state situationB-votes) + :else old-state))) + +(defn generate-new-situations + [state] + (-> state + (assoc :situationA (random-situation)) + (assoc :situationB (random-situation)))) + +(defn reset-votes + [state] + (-> state + (assoc-in [:current-round-votes :situationA] []) + (assoc-in [:current-round-votes :situationB] []))) + + ;; FYI - another way to write this + ; (update-in state [:current-round-votes] merge {:situationA [] + ; :situationB []})) + +(defn dec-rounds-left + [state] + (update-in state [:rounds-left] dec)) + +(defn decide-winners + [state] + (let [scores (:scores state) + max-value (apply max (vals scores)) + winners (filter #(= (second %) max-value) scores)] + (assoc-in state [:winners] (keys winners)))) + +(defn prepare-next-round [state] + (-> state + add-scores + dec-rounds-left + reset-votes + generate-new-situations)) + +(defn apply-votes-and-rotate-player [state player-id situation-id] + (-> state + (add-vote player-id situation-id) + rotate-to-next-player)) + +(defn process-vote + "processes voting" + [state player-id situation-id] + (let [default-next-state (apply-votes-and-rotate-player state player-id situation-id) + is-new-round? (zero? (:current-voter-index default-next-state)) + new-round-state (prepare-next-round default-next-state) + last-round? (zero? (:rounds-left new-round-state)) + game-over? (and is-new-round? last-round?) + start-next-round? (and is-new-round? (not last-round?))] + (cond + game-over? (decide-winners new-round-state) + start-next-round? new-round-state + ;; else switch to the next player + :else default-next-state))) + +(defn generate-player-ids + "returns a Vector of player id Strings: ['Player-1', 'Player-2', 'Player-3']" + [player-count] + (mapv #(str "Player-" (inc %)) (range player-count))) + ;; Original: + ; (vec (for [n (range player-count)] + ; (str "Player-" (+ n 1))))) + +(defn generate-new-game-scores [player-ids] + (apply assoc {} (interleave player-ids + (repeat (count player-ids) 0)))) + +(defn reset-game-state + "reset the game to a fresh state" + [state player-count] + (let [player-ids (generate-player-ids player-count)] + (-> state + (assoc-in [:player-ids] player-ids) + (assoc-in [:scores] (generate-new-game-scores player-ids)) + (assoc-in [:current-voter-index] 0) + (generate-new-situations)))) + + +; (defn handler [response] +; (.log js/console (str response))) +; +; (defn error-handler [{:keys [status status-text]}] +; (.log js/console (str "something bad happened: " status " " status-text))) +; +; +; (comment +; (GET "https://wordsapiv1.p.rapidapi.com/words/?partOfSpeech=noun?random=true" +; {:headers {:x-rapidapi-key "YOUR_API_KEY_HERE" +; :x-rapidapi-host "wordsapiv1.p.rapidapi.com"}} +; :handler handler +; :error-handler error-handler) +; +; (GET "https://webknox-words.p.rapidapi.com/words/{WORD}/plural" +; {:headers {:x-rapidapi-key "YOUR_API_KEY_HERE" +; :x-rapidapi-host "wordsapiv1.p.rapidapi.com"}} +; :handler handler +; :error-handler error-handler)) diff --git a/src/main/com/github/dbasner/this_or_that/game_logic/situation_generator.cljs b/src/main/com/github/dbasner/this_or_that/game_logic/situation_generator.cljs new file mode 100644 index 0000000..be53d79 --- /dev/null +++ b/src/main/com/github/dbasner/this_or_that/game_logic/situation_generator.cljs @@ -0,0 +1,37 @@ +(ns com.github.dbasner.this-or-that.game-logic.situation-generator + "generate the random Situation sentences") + +(def verbs ["eat" "cook" "punch" "get punched by" + "get kicked by" "kick" "date" "kiss" + "marry" "love" "get laughed at by" + "live with" "barbecue" "get yelled at by" + "upset" "bother" "take a roadtrip with" + "fly sitting next to" "fall for" + "get trapped on a desert island with" + "become conjoined twins with" + "be trapped with" "be looked up to by" + "settle down with" "burn" "get wasted with"]) + +(def count-modifiers ["roughly" "about" "precisely" "exactly" "almost" "just about"]) + +(def nouns ["sharks" "people" "dogs" "turtles" + "chairs" "tables" "dinosaurs" "computers" + "men" "women" "cats" "rats" "couches" + "airplanes" "cars" "haters" "phones" + "jugglers" "clowns" "doctors" "therapists" + "lawyers" "artists" "hipsters"]) + +(defn rand-verb [] (rand-nth verbs)) +(defn rand-count-modifier [] (rand-nth count-modifiers)) +(defn rand-situation-count [n] (+ (rand-int n) 2)) +(defn rand-noun [] (rand-nth nouns)) + +;; ----------------------------------------------------------------------------- +;; Public API + +(defn random-situation + [] + {:verb (rand-verb) + :count-modifier (rand-count-modifier) + :count (rand-situation-count 20) + :noun (rand-noun)}) diff --git a/src/main/com/github/dbasner/this_or_that/html.cljs b/src/main/com/github/dbasner/this_or_that/html.cljs index ce6d95d..009bf1c 100644 --- a/src/main/com/github/dbasner/this_or_that/html.cljs +++ b/src/main/com/github/dbasner/this_or_that/html.cljs @@ -11,7 +11,8 @@ :name "new-game-player-count" :value 3 :min 2}] - [:input#startGameBtn {:class "button **is-large is-success is-rounded**" :type "submit" :value "Start Game!"}]]) + [:input#startGameBtn.button.is-success.is-rounded + {:type "submit" :value "Start Game!"}]]) (defn DescribeSituation @@ -97,8 +98,8 @@ [app-state] (let [result (:winners app-state) - game-started? (not= 0 (count (:player-ids app-state))) - game-over? (not= 0 (count (:winners app-state))) + game-started? (not (zero? (count (:player-ids app-state)))) + game-over? (not (zero? (count (:winners app-state)))) player-ids (:player-ids app-state) player-index (:current-voter-index app-state)] [:main.text-center @@ -112,6 +113,3 @@ [:div (PlayerTurnBanner (nth player-ids player-index)) (SituationOptions (:situationA app-state) (:situationB app-state))])])])) - - - diff --git a/src/main/com/github/dbasner/this_or_that/interaction.cljs b/src/main/com/github/dbasner/this_or_that/interaction.cljs index ccb4e3b..ef2fd87 100644 --- a/src/main/com/github/dbasner/this_or_that/interaction.cljs +++ b/src/main/com/github/dbasner/this_or_that/interaction.cljs @@ -2,140 +2,40 @@ (:require [oops.core :refer [oset! ocall oget]] [goog.dom :as gdom] [goog.functions :as gfunctions] - [com.github.dbasner.this-or-that.game-logic :refer [generate-situation]] + [com.github.dbasner.this-or-that.game-logic :as game-logic] [com.github.dbasner.this-or-that.state :refer [state initial-state]] [com.github.dbasner.this-or-that.html :refer [ThisOrThatGame]]) (:require-macros [hiccups.core :as hiccups])) -(defn add-vote - [state player-id situation-id] - (update-in state [:current-round-votes situation-id] conj player-id)) - -;; todo this could just be a continuous vote count that we mod player-count -(defn rotate-current-player - [state] - (let [max-player-index (dec (count (:player-ids state))) - voter-index (:current-voter-index state) - update-current-player #(assoc state :current-voter-index %)] - (if (< voter-index max-player-index) - (update-current-player (inc voter-index)) - (update-current-player 0)))) - -(defn increment-score [state player-id] - (update-in state [:scores player-id] inc)) - -(defn increment-mult-scores - [state player-ids] - (reduce #(increment-score %1 %2) state player-ids)) - -(defn add-scores - [old-state] - (let [ - situationA-votes (:situationA (:current-round-votes old-state)) - situationB-votes (:situationB (:current-round-votes old-state)) - situationA-count (count situationA-votes) - situationB-count (count situationB-votes)] - (cond - (= situationA-count situationB-count) (increment-mult-scores old-state (:player-ids state)) - (> situationA-count situationB-count) (increment-mult-scores old-state situationA-votes) - (< situationA-count situationB-count) (increment-mult-scores old-state situationB-votes) - :else old-state))) - -(defn generate-new-situations - [state] - (-> state - (assoc :situationA (generate-situation)) - (assoc :situationB (generate-situation)))) - -(defn reset-votes - [state] - (-> state - (assoc-in [:current-round-votes :situationA] []) - (assoc-in [:current-round-votes :situationB] []))) - - -(defn dec-rounds-left - [state] - (update-in state [:rounds-left] dec)) - -(defn decide-winners +(defn get-current-player [state] - (let [scores (:scores state) - max-value (apply max (vals scores)) - winners (filter #(= (second %) max-value) scores)] - (assoc-in state [:winners] (keys winners)))) - -(defn prepare-next-round [state] - (-> state - (add-scores) - (dec-rounds-left) - (reset-votes) - (generate-new-situations))) - -(defn vote-and-rotate [state player-id situation-id] - (-> state - (add-vote player-id situation-id) - (rotate-current-player))) - -(defn handle-vote - [state player-id situation-id] - "handles everything related to voting" - (let [state-voted-rotated (vote-and-rotate state player-id situation-id) - is-new-round? (= 0 (:current-voter-index state-voted-rotated)) - new-round-state (prepare-next-round state-voted-rotated)] - (if is-new-round? - (if (< (:rounds-left new-round-state) 1) - (decide-winners new-round-state) - new-round-state) - state-voted-rotated))) + (nth (:player-ids state) (:current-voter-index state))) (defn handle-vote! - [player-id situation-id] - (swap! state (fn [new-state] (handle-vote new-state player-id situation-id)))) - -(defn generate-player-ids [player-count] - (vec (for [n (range player-count)] ;; <1> - (str "Player-" (+ n 1))))) - - -(defn generate-new-game-scores [player-ids] - (apply assoc {} (interleave player-ids - (repeat (count player-ids) 0)))) + [situation-id] + (swap! state + (fn [current-state] + (let [player-id (get-current-player current-state)] + (game-logic/process-vote current-state player-id situation-id))))) (defn start-new-game! [count] - (let [player-ids (generate-player-ids count) - scores (generate-new-game-scores player-ids)] - (swap! state (fn - [old-state] - (-> old-state - (assoc-in [:player-ids] player-ids) - (assoc-in [:scores] scores) - (assoc-in [:current-voter-index] 0) - (generate-new-situations)))))) - - -(defn get-current-player - [state] - (nth (:player-ids state) (:current-voter-index state))) - -(defn get-element-by-id [id] - (.call (aget js/document "getElementById") js/document id)) + (swap! state #(game-logic/reset-game-state % count))) (defn click-app-container [event] (let [target-el (oget event "target") - isSituationAButton? (= (oget target-el "id") "voteSituationAButton") - isSituationBButton? (= (oget target-el "id") "voteSituationBButton") - isNewGameButton? (= (oget target-el "id") "newGameBtn") - isStartGameButton? (= (oget target-el "id") "startGameBtn")] + situation-A-btn? (= (oget target-el "id") "voteSituationAButton") + situation-B-btn? (= (oget target-el "id") "voteSituationBButton") + new-game-btn? (= (oget target-el "id") "newGameBtn") + start-game-btn? (= (oget target-el "id") "startGameBtn")] (cond - isStartGameButton? (start-new-game! (oget (get-element-by-id "newGamePlayerCount") "value")) - isSituationAButton? (handle-vote! (get-current-player @state) :situationA) - isSituationBButton? (handle-vote! (get-current-player @state) :situationB) - isNewGameButton? (reset! state initial-state) + start-game-btn? (start-new-game! (oget (gdom/getElement "newGamePlayerCount") "value")) + situation-A-btn? (handle-vote! :situationA) + situation-B-btn? (handle-vote! :situationB) + new-game-btn? (reset! state initial-state) :else nil))) -(def init-dom-events! +(def attach-dom-events! (gfunctions/once (fn [] (ocall (gdom/getElement "appContainer") "addEventListener" "click" click-app-container)))) @@ -158,4 +58,4 @@ (gfunctions/once (fn [] (add-watch state :render-ui render-ui!)))) - + ; (add-watch state :logger logger)))) diff --git a/src/main/com/github/dbasner/this_or_that/state.cljs b/src/main/com/github/dbasner/this_or_that/state.cljs index f56c35a..465e0ec 100644 --- a/src/main/com/github/dbasner/this_or_that/state.cljs +++ b/src/main/com/github/dbasner/this_or_that/state.cljs @@ -1,15 +1,4 @@ -(ns com.github.dbasner.this-or-that.state - (:require [com.github.dbasner.this-or-that.game-logic :as game-logic])) - - -(def example-situation1 {:verb "eat" - :count-modifier "exactly" - :count 2 - :noun "burgers"}) -(def example-situation2 {:verb "eat" - :count-modifier "exactly" - :count 5 - :noun "hotdogs"}) +(ns com.github.dbasner.this-or-that.state) (def initial-state {:player-ids [] @@ -17,10 +6,9 @@ :scores {} :current-round-votes {:situationA [] :situationB []} - :situationA example-situation1 - :situationB example-situation2 + :situationA nil + :situationB nil :rounds-left 5 :winners []}) (def state (atom initial-state)) -