ClojureScript+React/Om clone of 2048
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

98 lines
3.6KB

  1. (ns ttfe.core
  2. (:require [om.core :as om :include-macros true]
  3. [om.dom :as dom :include-macros true]
  4. [ttfe.board :refer [move-left move-right move-up move-down
  5. new-board add-tile movements-left?]]))
  6. (enable-console-print!)
  7. (defn new-game []
  8. {:board (new-board)
  9. :already-won false
  10. :paused false})
  11. (def app-state
  12. (atom
  13. (new-game)))
  14. (def move-fns [move-up move-right move-down move-left])
  15. (defn divs-for-board [board]
  16. (reduce (fn [row-acc row-n]
  17. (let [row (nth board row-n)]
  18. (concat row-acc
  19. (reduce (fn [acc col-n]
  20. (let [cell (nth row col-n)]
  21. (if (nil? cell)
  22. acc
  23. (conj acc
  24. (dom/div #js {:className (str "tile tile-" cell " tile-position-" (inc col-n) "-" (inc row-n))}
  25. (dom/div #js {:className "tile-inner"} cell))))))
  26. []
  27. (range (count row))))))
  28. '()
  29. (range (count board))))
  30. (defn tiles-view [app owner]
  31. (reify
  32. om/IRender
  33. (render [_]
  34. (let [cell-divs (divs-for-board (:board app))]
  35. (apply dom/div
  36. #js {:id "tiles" :className "tile-container"}
  37. cell-divs)))))
  38. (defn end-game [class message]
  39. (let [msg-cont (. js/document (querySelector ".game-message"))]
  40. ; The game could have been won and continued, so let's make sure
  41. ; the "game won" mark is not there when the game is lost after
  42. ; winning
  43. (.remove (.-classList msg-cont) "game-won")
  44. (.add (.-classList msg-cont) class)
  45. (-> msg-cont
  46. (.getElementsByTagName "p")
  47. (.item 0)
  48. .-textContent
  49. (set! message))))
  50. (defn init []
  51. (let [input-manager (js/KeyboardInputManager.)]
  52. (.on input-manager
  53. "move"
  54. (fn [direction]
  55. (if-not (:paused @app-state)
  56. (let [move-fn (nth move-fns direction)]
  57. (swap! app-state
  58. (fn [state]
  59. (let [board (:board state)
  60. moved-board (move-fn board)]
  61. (if (not= board moved-board)
  62. (assoc-in state [:board] (add-tile moved-board))
  63. state))))
  64. (when (and (contains? (set (flatten (:board @app-state))) 2048)
  65. (not (:already-won @app-state)))
  66. (swap! app-state (fn [state] (merge state {:already-won true
  67. :paused true})))
  68. (end-game "game-won" "You win!"))
  69. (when-not (movements-left? (:board @app-state))
  70. (end-game "game-over" "Game over!"))))))
  71. (.on input-manager
  72. "restart"
  73. (fn []
  74. (let [msg-cont (. js/document (querySelector ".game-message"))]
  75. (.remove (.-classList msg-cont) "game-won")
  76. (.remove (.-classList msg-cont) "game-over")
  77. (swap! app-state
  78. (fn [state]
  79. (new-game))))))
  80. (.on input-manager
  81. "keepPlaying"
  82. (fn []
  83. (swap! app-state (fn [state] (assoc-in state [:paused] false)))
  84. (let [msg-cont (. js/document (querySelector ".game-message"))]
  85. (.remove (.-classList msg-cont) "game-won")
  86. (.remove (.-classList msg-cont) "game-over"))))
  87. (om/root tiles-view
  88. app-state
  89. {:target (. js/document (getElementById "tiles"))})))