1

I'm in the process of making a simple game using re-frame (and thus react and reagent), but I'm stuck at one point. In

https://github.com/Day8/re-frame#control-via-fsm

they claim

Not every app has lots of logical states, but many do, and if you are implementing one of them, then formally recognising it and using a technique like state charts will help greatly in getting a clean design and a nice datamodel.

Now, what I'm wondering is whether or not I should implement my game as a state machine explicitly, such as https://github.com/ztellman/automat, or if that is just to be implicit in the handlers and db?

Let me elaborate.

In my game, the following "macro states" are possible (not implemented this way yet):

  1. not connected
  2. connected to server, not to game (in the "choose game" area)
  3. connected to game
  4. created game

The transitions then would be

1->1 if pressing "connect", but getting a network error or using a bad username

1->2 if pressing "connect", and everything ok

2->1 if pressing "connect to game", and network error

2->2 if pressing "connect to game", and game not available

2->3 if pressing "connect to game", and everything ok

2->1 if pressing "create game", and network error

2->2 if pressing "create game", with some error in creating game

2->4 if pressing "create game", everything ok

4->1 if i get any network error

4->2 if i cancel my game

4->3 if someone joins my game

Now, much of this will be done over http, and thus asynchronously. To make sure that you won't press "connect" twice i added a boolean "connecting to server" (and then one for every http request), and then i get something like:

(register-handler :connect-to-server
  (fn [db _])
   (if (not (:connecting-to-server db))
     (do 
       (POST ... #(dispatch [:connecting-to-server-succeeded ...] #(dispatch [:connecting-to-server-failed] )
       (assoc db :connecting-to-server true))
     db)))


(register-handler :connecting-to-server-succeeded 
  ...
     (-> db (assoc :connecting-to-server false) (assoc :connected-to-server true) ... ))

(register-handler :connecting-to-server-failed
  ...
     (assoc db :connecting-to-server false) ... )

(register-sub :can-press-connect
  (fn [db _]
    (reaction (not :connecting-to-server)))

So basically, when I'm connecting to the server, nothing else can be done.

This will turn into a huge amount of states, and it seems like a lot of boilerplate code which I'm going to mess up somewhere. Naturally, I can abstract some of it away, but I'm guessing there's already a good idea of how to deal with this. So my questions:

  • Am I reinventing some bad, old school approach on top of reagent, and should do it in some other way?

  • Is re-frame perhaps even the wrong approach with so much network io?

  • If the idea is ok, would it be fruitful to do this explicitly via a state machine to automatically tell whether or not a button can be pressed?

(A better way, long term, is likely websockets, but this is a learning experience.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.