Skip to content
This repository has been archived by the owner on Oct 2, 2018. It is now read-only.

From having "zones with cards" to "cards with zones" #122

Merged
merged 14 commits into from
Aug 31, 2018
48 changes: 48 additions & 0 deletions backend/old_tests/api/base_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
(ns api.base-test
(:require [expectations.clojure.test :refer :all]
[clojure.test :as ctest]
[mocking :as mocking]
[api.base :as api]
[configs.messages :as messages]))

(ctest/use-fixtures :each mocking/mock-persistence)

(defexpect create-game

(let [game (api/create-game)]
; Checking it has been created
(expect
#(contains? % :game-id)
game)

(expect
#(contains? % :player-id)
game)

(expect
#(= 5 (count (:rows %)))
game)))

(defexpect add-player
(let [game (api/create-game)
opponent (api/add-player (:game-id game))]
(expect
#(contains? % :player-id)
opponent)

(expect
#(not (contains? % :error))
opponent)

(expect
true
(= (:game-id game) (:game-id opponent)))

; A third player causes an error
(expect
{:error messages/too-many-players}
(api/add-player (:game-id game)))

(expect
false
(= (:player-id game) (:player-id opponent)))))
15 changes: 15 additions & 0 deletions backend/old_tests/mocking.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
(ns mocking
(:require [persistence.persistence :as persistence]))

(def mock-game (atom nil))

(defn mock-persistence
[tests]
(with-redefs
[persistence/next-id (constantly 0)
persistence/save-game (fn [a]
(reset! mock-game a))
persistence/fetch-game (fn [x]
@mock-game)]
(tests))
(reset! mock-game nil))
42 changes: 24 additions & 18 deletions backend/src/api/base.clj
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,44 @@
(player-view/get-game-as-player (persistence/fetch-game game-id) player-id))

(defn play-card-as-player
[game-id player index row-id & target]
[game-id player-id card-id row-id & target]
(let [game-state (persistence/fetch-game game-id)]
(if (= (:status (get-game game-id player)) messages/play)
(if (= (:status (get-game game-id player-id)) messages/play)
(do
(persistence/save-game (play-card/play-card game-state (conversions/player-num game-state player) index row-id (first target)))
(get-game game-id player))
(persistence/save-game (play-card/play-card game-state player-id card-id row-id (first target)))
(get-game game-id player-id))
{:error messages/out-of-turn})))

(defn ^:private create-empty-game
"Creates a new instance of a game"
"Creates a new instance of a game lobby"
[ini-config]
(let [game-id (persistence/next-id)]
(persistence/save-game
(assoc (create-game/new-game ini-config)
:game-id game-id))
game-id))
{:status messages/no-opp
:game-id game-id
:player-ids [(generators/player-uuid {})]})))

(defn add-player
"Adds a player to a game"
[game-id]
(let [saved-game (persistence/fetch-game game-id)
players-connected (count (:player-ids saved-game))]
(if (> players-connected 1)
{:error messages/too-many-players}
(let [game-state (persistence/save-game
(generators/player-uuid
saved-game))]
(get-game game-id (last (:player-ids game-state)))))))
(let [lobby (persistence/fetch-game game-id)
players-connected (count (:player-ids lobby))]
(cond
(> players-connected 1) {:error messages/too-many-players}
(< players-connected 1) {:error messages/lobby-not-created}
:else (let [second-uuid (generators/player-uuid lobby)]
(-> (create-game/new-game
{:player-ids [(first (:player-ids lobby))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost went crazy during the weekend til I realized that I needed to pass the player-ids to the new-game constructor (which is perfectly logical, I might add) instead of putting them by assoc post-construction as with the game-id. In fact, I think the game-idshould also be passed in and added to the game in new-game to ease the logic (everything is done at construction instead of modifying stuff afterwards)

second-uuid]})
(assoc :game-id (:game-id lobby))
persistence/save-game
(get-game second-uuid))))))

(defn create-game
"Creates a new instance of a game with a player"
([] (create-game {}))
([ini-config]
(-> (create-empty-game ini-config)
(add-player))))
(let [lobby (create-empty-game ini-config)]
(-> lobby
(assoc :player-id (first (:player-ids lobby)))
(dissoc :player-ids)))))
2 changes: 1 addition & 1 deletion backend/src/api/generators.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
(loop [id (uuid)]
(if (some #{id} (:player-ids game))
(recur (uuid))
(update game :player-ids #(vec (conj % id))))))
id)))
3 changes: 1 addition & 2 deletions backend/src/api/player_view.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
[game-state player-id]
{:game-id (:game-id game-state)
:player-id player-id
:hand (functions/get-hand game-state player-id)
:cards (functions/get-cards game-state player-id)
:rows (functions/get-rows game-state player-id)
:rows-power (functions/get-rows-power game-state player-id)
:scores (functions/get-scores game-state player-id)
:status (functions/get-status game-state player-id)
:winner (functions/get-winner game-state player-id)})
83 changes: 44 additions & 39 deletions backend/src/api/player_view_functions.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,59 +3,64 @@
[api.conversions :as conversions]
[configs.messages :as messages]))

(defn get-hand
"Return hand as seen by the player"
(defn get-cards
"Returns cards as seen by a player"
[game-state player-id]
(get-in game-state [:players (conversions/player-num game-state player-id) :hand]))

(defn get-row-cards
"Returns the cards as seen by the player"
[row-cards game-state player-id]
(mapv (fn [card]
(assoc card :owner
(conversions/translate-player game-state (:owner card) player-id)))
row-cards))
(vec (map
#(if (= (:owner %) player-id)
(assoc % :owner "me")
{:location (:location %)
:owner "opp"})
(:cards game-state))))

(defn get-rows
"Return the rows as seen by the player"
[game-state player-id]
(mapv (fn [row]
(assoc row
:cards
(get-row-cards (:cards row) game-state player-id)))
(:rows game-state)))

(defn get-rows-power
"Return the powers of each row as seen by the player"
"Return the rows info as seen by the player"
[game-state player-id]
(let [player (conversions/player-num game-state player-id)
opponent (mod (inc player) 2)]
(loop [rows-power []
rows (:rows game-state)]
(if (empty? rows)
rows-power
(let [player-ids (:player-ids game-state)
opp-id (if (= (first player-ids) player-id)
(second player-ids)
(first player-ids))]
(loop [rows (:rows game-state)
row 0]
(if (= row (count (:rows game-state)))
rows
(recur
(conj rows-power
[(victory/points-in-row (:cards (first rows)) player)
(victory/points-in-row (:cards (first rows)) opponent)])
(rest rows))))))
(assoc-in
rows
[row :scores]
[(victory/points-in-row game-state row player-id)
(victory/points-in-row game-state row opp-id)])
(inc row))))))

(defn get-scores
"Return the scores as seen by the player"
[game-state player-id]
(let [player (conversions/player-num game-state player-id)
opponent (mod (inc player) 2)]
[(victory/get-won-rows game-state player)
(victory/get-won-rows game-state opponent)]))
(let [player-ids (:player-ids game-state)
opp-id (if (= (first player-ids) player-id)
(second player-ids)
(first player-ids))]
[(victory/get-won-rows game-state player-id)
(victory/get-won-rows game-state opp-id)]))

(defn get-winner
"Return the winner as seen by the player"
[game-state player-id]
(conversions/translate-player game-state (victory/winner game-state) player-id))
(let [winner (victory/winner game-state)
player-ids (:player-ids game-state)
opp-id (if (= (first player-ids) player-id)
(second player-ids)
(first player-ids))]
(cond (= winner player-id)
"me"
(= winner opp-id)
"opp"
:else
winner)))

(defn get-status
"Returns the status of the game from a player's perspective"
[game-state player-id]
(cond (= (count (:player-ids game-state)) 1) messages/no-opp
(nil? (get-in game-state [:next-play (conversions/player-num game-state player-id)])) messages/play
:else messages/wait))
(:status game-state
(if (nil? (get-in game-state [:next-play (keyword player-id)]))
messages/play
messages/wait)))
8 changes: 8 additions & 0 deletions backend/src/configs/messages.clj
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,11 @@
(def row-limit (get-in config-file [:messages :row-limit]))

(def too-many-players (get-in config-file [:messages :too-many-players]))

(def need-target (get-in config-file [:messages :need-target]))

(def not-owned-card (get-in config-file [:messages :not-owned-card]))

(def no-row (get-in config-file [:messages :no-row]))

(def lobby-not-created (get-in config-file [:messages :no-lobby-not-created]))
4 changes: 4 additions & 0 deletions backend/src/configs/player_ids.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(ns configs.player-ids)

(def default-player-ids
["p0" "p1"])
10 changes: 5 additions & 5 deletions backend/src/rules/alter_card.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

(defn alter-card
"Alters a cards' values, merging the new values with existing ones"
[game-state path new-values]
(update-in game-state path #(merge % new-values)))
[game-state card-id new-values]
(update-in game-state [:cards card-id] #(merge % new-values)))

(defn add-power
"Alters a cards' power, by adding the passed value to it.
This power may be negative, resulting in a decrease"
[game-state path increase]
(let [power (get-in game-state (conj path :power))]
(alter-card game-state path {:power (+ power increase)})))
[game-state card-id increase]
(let [power (get-in game-state [:cards card-id :power])]
(alter-card game-state card-id {:power (+ power increase)})))
15 changes: 15 additions & 0 deletions backend/src/rules/count_cards.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
(ns rules.count-cards)

(defn count-cards
"Count all cards that has keys as in condition an sums their summand"
[game-state condition & summand]
(let [k (vec (keys condition))
summand (first summand)]
(reduce
#(if (= (select-keys %2 k) condition)
(if (some? summand)
(+ %1 (summand %2 0))
(inc %1))
%1)
0
(:cards game-state))))
38 changes: 19 additions & 19 deletions backend/src/rules/create_game.clj
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
(ns rules.create-game
(:require [configs.hands :as hands]
[configs.rows :as rows]))
[configs.rows :as rows]
[configs.player-ids :as player-ids]))

(defn new-player
"Creates a new player object"
[hand]
{
:hand hand
})
(defn locate-in-hand
"Creates location for a vec of cards on player's hand"
[hand player]
(vec (map #(assoc % :location [:hand] :owner player)
hand)))

(defn new-game
"Creates a new game object"
([] (new-game {}))
([ini-config]
{

:players (let [hands (:hands ini-config hands/default-hands)]
[(new-player (first hands))
(new-player (second hands))])

:rows (vec (reduce
#(concat %1 [{:limit %2 :cards []}])
[]
(:limits ini-config rows/default-limits)))
:next-play [nil nil]
}))
(let [player-ids (:player-ids ini-config player-ids/default-player-ids)]
{
:player-ids player-ids
:cards (let [hands (:hands ini-config hands/default-hands)]
(vec (concat (locate-in-hand (first hands) (first player-ids))
(locate-in-hand (second hands) (second player-ids)))))
:rows (vec (reduce
#(concat %1 [{:limit %2}])
[]
(:limits ini-config rows/default-limits)))
:next-play {}
})))
Loading