Skip to content

Instantly share code, notes, and snippets.

@wpcarro
Last active April 23, 2019 20:06
Show Gist options
  • Select an option

  • Save wpcarro/791df4e656416295bfaa853330297bd5 to your computer and use it in GitHub Desktop.

Select an option

Save wpcarro/791df4e656416295bfaa853330297bd5 to your computer and use it in GitHub Desktop.
;; Rewriting the example of a Finite State Machine using Clojure and
;; Haskell-inspired type annotations. Why mix the two languages? Firstly, I like
;; LISPs. Secondly, Haskell type-annotations help me organize my thoughts.
;;
;; This example was sourced from page 91 of Game Programming Patterns by Robert
;; Nystrom.
;; type State = Ducking
;; | Standing
;; | Jumping
;; | Diving
;; type Key = ReleaseDown
;; | PressDown
;; | PressB
;; type Graph a e b = Map a (Map e b)
;; release-down :: Key
(def release-down "R-D")
;; press-down :: Key
(def press-down "P-D")
;; press-b :: Key
(def press-b "P-B")
;; fsm :: Graph State Key State
(def fsm
{:ducking {release-down :standing}
:standing {press-down :ducking
press-b :jumping}
:jumping {press-down :diving}
})
;; handle-input :: State -> Key -> State
(defn handle-input [state key]
(get-in fsm [state key]))
;; key-pressed? :: Event -> Boolean
(defn key-pressed? [event]
(not (nil? event)))
;; render-state :: State -> IO ()
(defn render-state [state]
(case state
:ducking (println "___")
:standing (println "-|-")
:jumping (println "\\|/")
:diving (println "-->")))
;; handle-frame :: State -> Event -> State
(defn handle-frame [state event]
(let [new-state (if (key-pressed? event)
(handle-input state event)
state)]
(render-state new-state)
new-state))
;; type Event = Maybe Key
;; events :: List Event
(def events
;;
[nil ;; standing
press-down ;; ducking
release-down ;; standing
press-down ;; ducking
release-down ;; standing
press-b ;; jumping
press-down ;; diving
])
;; Intentionally throttling the loop so that it approximately goes 1FPS instead
;; of 60FPS. This is intended for educational purposes.
;; loop-game :: State -> List Events -> IO ()
(defn loop-game [state events]
(if (empty? events)
nil
(let [new-state (handle-frame state (first events))]
(Thread/sleep 1000)
(loop-game new-state (next events)))))
(loop-game :standing events)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment