-
Notifications
You must be signed in to change notification settings - Fork 0
End a Game
To follow along with this section, start with tag v2.1.8.
The game starts but it never ends. In order to have a winner, it must end. In this section you will use concepts that you are already familiar with the solve the problem of ending a game.
In order to make the game end, there must be some criteria for ending it.
The game will end when the total number of points earned by all players is equal to the number of players times 100. The player with the most points at this time is the winner.
In addition to ending the game and declaring a winner, it would be great to maintain a list of the high scores.
In the tutorial-client.behavior namespace, add a transform function
which will be used to maintain the list of high scores.
(defn high-score [scores {:keys [player score]}]
(let [s ((fnil conj []) scores {:player player :score score})]
(reverse (sort-by :score s))))This function will add the newest high scoring player to the list and then sort the list from highest to lowest score.
As with starting a game, it feels like a continue function is the best way to end a game. When "end of game" conditions are met, this continue function will send messages which
- stop the game
- declare a winner
- set the new high score
- reset all the scores to 0
- set-focus to the wait screen
The implementation of end-game is shown below.
(defn clear-end-game [players]
(vec (reduce (fn [a b]
(conj a ^:input {msg/type :swap msg/topic [:other-counters b] :value 0}))
[^:input {msg/type :swap msg/topic [:my-counter] :value 0}
^:input {msg/type :swap msg/topic [:max-count] :value 0}]
(keys players))))
(defn end-game [{:keys [players total active]}]
(when (and active (>= total (* (count players) 100)))
(let [[player score] (last (sort-by val (seq players)))]
(into (clear-end-game players)
[{msg/type :swap msg/topic [:active-game] :value false}
{msg/type :swap msg/topic [:winner] :value {:player player :score score}}
{msg/type :high-score msg/topic [:high-scores] :player player :score score}
^:input {msg/topic msg/app-model msg/type :set-focus :name :wait}]))))The tricky part of this implementation is to figure out which messages should happen within the transaction and which messages should be put on the input queue.
The :set-focus messages must go on the input queue. The messages
which reset the counters should also go on the input queue. The reason
for this is that in order for other players to know that the game is
over, they must see the latest score from each player. If the local
game notices that the game is over and then resets the scores before
the effect can go out which notifies the other players of the final
scores then the other players will never know that the game is over.
To make all of this work, add the high-score transform to the
dataflow definition.
[:high-score [:high-scores] high-score]Add the end-game continue to the dataflow definition.
[{[:counters] :players [:total-count] :total [:active-game] :active} end-game :map]Finally, emit changes to :winner and :high-scores to the :wait screen.
[#{[:winner]} (app/default-emitter [:wait])]
[#{[:high-scores]} (app/default-emitter [:wait])]You should now be able to try this in the Data UI. The simulation creates a game with three players. To end the game, enter a score higher than 300. Try this a couple of times to make sure that the high score list is working properly.
In the next section, you will update the Wait template to show the winner and the list of high scores. In the section after that you will render the changes to the wait screen.
The tag for this section is v2.1.9.