Skip to content

Instantly share code, notes, and snippets.

@luisgerhorst
Created March 30, 2014 18:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save luisgerhorst/9877138 to your computer and use it in GitHub Desktop.
Save luisgerhorst/9877138 to your computer and use it in GitHub Desktop.
2048 in Clojure
(def grid (insert [[nil nil nil nil]
[nil nil nil nil]
[nil nil nil nil]
[nil nil nil nil]]))
;; Grid functions.
(defn move
"Moves all field to the choosen direction, merges fields and inserts field value for random free location. Returns nil if game is over. Returns same grid if move doesn't change the grid."
[grid direction]
(if (can-move grid)
(try-move grid direction)
nil))
(defn try-move
[grid direction]
(case direction
:left (try-move-with-function grid move-left)
:rigth (try-move-with-function grid move-rigth)
:top (try-move-with-function grid move-top)
:bottom (try-move-with-function grid move-bottom)))
(defn try-move-with-function
[grid function]
(let [moved (function grid)]
(if (= moved grid)
grid
(insert moved))))
(defn can-move
[grid]
(not (= grid
(try-move grid :left)
(try-move grid :rigth)
(try-move grid :top)
(try-move grid :bottom))))
(defn insert
"Inserts 2 or 4 at random free location."
[grid]
(assoc-in grid (rand-nth (get-empty-coordinates grid)) (rand-nth [2 4])))
(defn get-empty-coordinates
[grid]
(loop [empty-fields []
y 0]
(if (= y 4)
empty-fields
(recur (into empty-fields (loop [empty-fields []
x 0]
(if (= x 4)
empty-fields
(recur (if (get-in grid [y x])
empty-fields
(conj empty-fields [y x])) (inc x))))) (inc y)))))
(defn move-left
[grid]
(vec (map merge-row grid)))
(defn move-rigth
[grid]
(vec (map (comp vec rseq) (move-left (vec (map (comp vec rseq) grid))))))
(defn move-top
[grid]
(vec (apply map vector (move-left (vec (apply map vector grid))))))
(defn move-bottom
[grid]
(vec (apply map vector (move-rigth (vec (apply map vector grid))))))
(defn merge-row
"Move all field in the row to the left and merge fields with the same value"
[row]
(loop [merged-row (merge-compressed-row (filter identity row))]
(if (< (count merged-row) 4)
(recur (conj merged-row nil))
merged-row)))
(defn merge-compressed-row
[row]
"Merge field in compressed row (row without empty fields)"
(reduce (fn [merged field]
(if (number? field)
(conj merged field)
(conj merged (reduce + field))))
[]
(pair-compressed-row row)))
(defn pair-compressed-row
"Creates vector with equal fields paired into subvectors"
[row]
(reduce (fn [paired field]
(if (and (= field (last paired)) (number? (last paired)))
(set-last paired [field field])
(conj paired field)))
[]
row))
(defn set-last
"Sets the last item of the vector to value"
[vector value]
(if (= (count vector) 0)
[value]
(assoc vector (- (count vector) 1) value)))
;; Grid test funcions.
(defn random-game
"Makes as many random moves as possible and returns final grid and number of moves."
[grid]
(loop [g grid
i 0]
(let [moved (move g (rand-nth [:left :rigth :top :bottom]))]
(if (or (not moved) (= i 500))
{:moves i :final-grid g}
(recur moved (inc i))))))
@luisgerhorst
Copy link
Author

I'm pretty new to Clojure so I hope my code isn't that bad. Try move, insert and random-game on grid.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment