|
;;; 2048-game.el --- play 2048 in Emacs
|
|
|
|
;; Copyright 2014 Zachary Kanfer
|
|
|
|
;; Author: Zachary Kanfer <zkanfer@gmail.com>
|
|
;; Version: 2014.03.27
|
|
;; Package-Version: 20151026.1933
|
|
;; URL: https://bitbucket.org/zck/2048.el
|
|
|
|
;; This file is not part of GNU Emacs
|
|
|
|
;; This program is free software: you can redistribute it and/or modify
|
|
;; it under the terms of the GNU General Public License as published by
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
;; (at your option) any later version.
|
|
|
|
;; This program is distributed in the hope that it will be useful,
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
;; GNU General Public License for more details.
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
|
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
;; This program is an implementation of 2048 for Emacs.
|
|
;; To begin playing, call `M-x 2048-game`, then use the arrow keys,
|
|
;; p/n/b/f, or C-p/C-n/C-b/C-f to move the tiles around.
|
|
|
|
;; Whenever you move the board, all tiles slide as far to that direction
|
|
;; as they can go. If a tile collides with another tile of the same value,
|
|
;; the tiles combine into a tile with double the initial value, and you
|
|
;; gain the new tile's value as your score.
|
|
|
|
;; The goal is to create a tile with value 2048.
|
|
|
|
;; The size of the board and goal value can be customized -- see the variables
|
|
;; *2048-columns*, *2048-rows*, and *2048-victory-value*.
|
|
|
|
|
|
;;; Code:
|
|
|
|
(define-derived-mode 2048-mode special-mode "2048-mode"
|
|
(define-key 2048-mode-map (kbd "p") '2048-up)
|
|
(define-key 2048-mode-map (kbd "C-p") '2048-up)
|
|
(define-key 2048-mode-map (kbd "<up>") '2048-up)
|
|
(define-key 2048-mode-map (kbd "n") '2048-down)
|
|
(define-key 2048-mode-map (kbd "C-n") '2048-down)
|
|
(define-key 2048-mode-map (kbd "<down>") '2048-down)
|
|
(define-key 2048-mode-map (kbd "b") '2048-left)
|
|
(define-key 2048-mode-map (kbd "C-b") '2048-left)
|
|
(define-key 2048-mode-map (kbd "<left>") '2048-left)
|
|
(define-key 2048-mode-map (kbd "f") '2048-right)
|
|
(define-key 2048-mode-map (kbd "C-f") '2048-right)
|
|
(define-key 2048-mode-map (kbd "<right>") '2048-right)
|
|
(define-key 2048-mode-map (kbd "r") '2048-random-move))
|
|
|
|
;;;###autoload
|
|
(defun 2048-game () "Start playing 2048."
|
|
(interactive)
|
|
(switch-to-buffer "2048")
|
|
(buffer-disable-undo "2048")
|
|
(2048-mode)
|
|
(2048-init))
|
|
|
|
(require 'cl-lib)
|
|
|
|
(defvar *2048-board* nil
|
|
"The board itself.
|
|
|
|
If a number is in the square, the number is stored. Otherwise, 0 is stored.
|
|
|
|
Instead of accessing this directly, use 2048-get-cell.")
|
|
|
|
(defvar *2048-combines-this-move* nil
|
|
"This stores, for each cell in the board, whether the number in it was generated this turn by two numbers combining.")
|
|
|
|
(defvar *2048-columns* 4
|
|
"The width of the board. It could be customized, if you wanted to make the game very very hard, or very very easy.")
|
|
|
|
(defvar *2048-rows* 4
|
|
"The height of the board. It could be customized, if you wanted to make the game very very tall, or very very short.")
|
|
|
|
(defvar *2048-possible-values-to-insert* (cons 4 (make-list 9 2))
|
|
"When a new element is inserted into the board, randomly choose a number from this sequence.")
|
|
|
|
(defvar *2048-victory-value* nil
|
|
"When this number is reached, the user wins! Yay!")
|
|
|
|
(defvar *2048-default-victory-value* 2048
|
|
"When the game starts, reset *2048-victory-value* to this value.")
|
|
|
|
(defvar *2048-debug* nil
|
|
"When 't, print debugging information.")
|
|
|
|
(defconst *2048-numbers* '(0 2 4 8 16 32 64 128 256 512 1024 2048))
|
|
|
|
(defvar *2048-score* nil
|
|
"Current score in the game.")
|
|
|
|
(defvar *2048-hi-tile* nil
|
|
"Current highest-number tile.")
|
|
|
|
(defvar *2048-history* nil
|
|
"Score history in this Emacs session. Each element is (SCORE HI-TILE TIME).")
|
|
|
|
(defvar *2048-history-size* 10
|
|
"Keep this many items in the history.")
|
|
|
|
(defvar *2048-game-has-been-added-to-history* nil
|
|
"Whether the current game has been added to the history yet.
|
|
|
|
Right now, it's only for use when the game has been lost. Since the user can choose to not start a new game, we want to add the score to the history the first time the game is lost, but not any other time.")
|
|
|
|
(defvar *2048-game-epoch* nil
|
|
"The time the current game started.")
|
|
|
|
;; These are prefixed with "twentyfortyeight-face-", not "2048-face"
|
|
;; because face names starting with numbers break htmlfontify-buffer,
|
|
;; as CSS classes beginning with numbers are ignored.
|
|
(defface twentyfortyeight-face-2 '((t . (:background "khaki" :foreground "black"))) "Face for the tile 2" :group '2048-faces)
|
|
(defface twentyfortyeight-face-4 '((t . (:background "burlywood" :foreground "black"))) "Face for the tile 4" :group '2048-faces)
|
|
(defface twentyfortyeight-face-8 '((t . (:background "orange3" :foreground "black"))) "Face for the tile 8" :group '2048-faces)
|
|
(defface twentyfortyeight-face-16 '((t . (:background "orange" :foreground "black"))) "Face for the tile 16" :group '2048-faces)
|
|
(defface twentyfortyeight-face-32 '((t . (:background "orange red" :foreground "black"))) "Face for the tile 32" :group '2048-faces)
|
|
(defface twentyfortyeight-face-64 '((t . (:background "firebrick" :foreground "white"))) "Face for the tile 64" :group '2048-faces)
|
|
(defface twentyfortyeight-face-128 '((t . (:background "dark red" :foreground "white"))) "Face for the tile 128" :group '2048-faces)
|
|
(defface twentyfortyeight-face-256 '((t . (:background "dark magenta" :foreground "white"))) "Face for the tile 256" :group '2048-faces)
|
|
(defface twentyfortyeight-face-512 '((t . (:background "magenta" :foreground "black"))) "Face for the tile 512" :group '2048-faces)
|
|
(defface twentyfortyeight-face-1024 '((t . (:background "gold" :foreground "black"))) "Face for the tile 1024" :group '2048-faces)
|
|
(defface twentyfortyeight-face-2048 '((t . (:background "yellow" :foreground "black"))) "Face for the tile 2048" :group '2048-faces)
|
|
|
|
(defun 2048-get-face (number)
|
|
"Return the face for squares holding NUMBER."
|
|
(let ((face-symbol (2048-get-face-symbol number)))
|
|
(if (facep face-symbol)
|
|
face-symbol
|
|
'twentyfortyeight-face-2048)))
|
|
|
|
(defun 2048-get-face-symbol (number)
|
|
"Return the face symbol for squares holding NUMBER."
|
|
(intern (concat "twentyfortyeight-face-"
|
|
(int-to-string number))))
|
|
|
|
(defun 2048-empty-tile (num)
|
|
"Return the tile to be inserted for the blank part of a square holding NUM.
|
|
|
|
That is, an empty string with font stuff on it."
|
|
(symbol-value (2048-empty-symbol num)))
|
|
|
|
(defun 2048-empty-symbol (num)
|
|
"Return symbol of the variable holding empty space for number NUM."
|
|
(intern (concat "2048-empty-" (int-to-string num))))
|
|
|
|
(defun 2048-tile-symbol (num)
|
|
"Return symbol of the variable for the tile for squares holding NUM."
|
|
(intern (concat "2048-tile-" (int-to-string num))))
|
|
|
|
(defun 2048-tile (num)
|
|
"Return the tile to be inserted for a square holding NUM.
|
|
|
|
The tile is the string, but with extra font stuff on it."
|
|
(symbol-value (2048-tile-symbol num)))
|
|
|
|
(defmacro 2048-game-move (&rest body)
|
|
"Perform the game move indicated by BODY.
|
|
|
|
This macro is used to do some housekeeping around the move."
|
|
`(progn (setq *2048-combines-this-move* (make-vector (* *2048-columns* *2048-rows*)
|
|
nil))
|
|
|
|
,@body
|
|
(2048-print-board)
|
|
(2048-check-game-end)))
|
|
|
|
(defmacro 2048-debug (&rest body)
|
|
"If *2048-debug* is 't, log ,@BODY as a string to the buffer named '2048-debug'."
|
|
`(when *2048-debug*
|
|
(print (concat ,@body)
|
|
(get-buffer-create "2048-debug"))))
|
|
|
|
(defun 2048-init-tiles ()
|
|
"Initialize each variable 2048-empty-N and 2048-tile-N with appropriate string and face."
|
|
(mapc #'2048-init-tile
|
|
*2048-numbers*))
|
|
|
|
(defun 2048-init-tile (number)
|
|
"Initialize the tile holding NUMBER.
|
|
|
|
This sets up both the tile to hold it, and the empty space around it."
|
|
(set (2048-empty-symbol number) (format "%7s" " "))
|
|
;; if constant then all faces are applied to this one constant. (Symptom: all background is yellow)
|
|
;; The bytecompiler is smart enough to see that (concat...) is a constant, but not (format...) ;-)
|
|
(set (2048-tile-symbol number) (format "%5s " (2048-num-to-printable number)))
|
|
(when (> number 0)
|
|
(let ((face (2048-get-face number)))
|
|
(put-text-property 0 7 'font-lock-face face (2048-empty-tile number))
|
|
(put-text-property 0 7 'font-lock-face face (2048-tile number)))))
|
|
|
|
(defun 2048-test-tiles ()
|
|
"Test out the tile colors."
|
|
(interactive)
|
|
(let ((*2048-board*
|
|
(vconcat *2048-numbers*
|
|
(make-vector (- (* *2048-columns* *2048-rows*)
|
|
(length *2048-numbers*))
|
|
0)))
|
|
(*2048-score* 123456)
|
|
(*2048-history* '((123 512 "2014-06-18 12:34:56" (0 30 0 0))
|
|
(456 1024 "2014-06-18 12:45:00" (0 123 0 0)))))
|
|
(switch-to-buffer "2048-test")
|
|
(2048-init-tiles)
|
|
(2048-mode)
|
|
(2048-print-board)))
|
|
|
|
(defun 2048-init ()
|
|
"Begin a game of 2048."
|
|
(setq *2048-board* (make-vector (* *2048-columns* *2048-rows*)
|
|
0))
|
|
(setq *2048-combines-this-move* (make-vector (* *2048-columns* *2048-rows*)
|
|
nil))
|
|
(setq *2048-score* 0
|
|
*2048-hi-tile* 2)
|
|
(setq *2048-victory-value* *2048-default-victory-value*)
|
|
(setq *2048-game-has-been-added-to-history* nil)
|
|
(setq *2048-game-epoch* (current-time))
|
|
(2048-insert-random-cell)
|
|
(2048-insert-random-cell)
|
|
(2048-init-tiles)
|
|
(2048-print-board)
|
|
(message "Good luck!"))
|
|
|
|
(defun 2048-get-cell (row col)
|
|
"Get the value in (ROW, COL)."
|
|
(elt *2048-board*
|
|
(+ (* row *2048-columns*)
|
|
col)))
|
|
|
|
(defun 2048-set-cell (row column val)
|
|
"Set the value in (ROW, COLUMN) to VAL."
|
|
(when (< *2048-hi-tile* val)
|
|
(setq *2048-hi-tile* val))
|
|
(aset *2048-board*
|
|
(+ (* row *2048-columns*)
|
|
column)
|
|
val))
|
|
|
|
(defun 2048-num-to-printable (num)
|
|
"Return NUM as a string that can be put into the board.
|
|
|
|
That is, print zeros as empty strings, and all other numbers as themselves."
|
|
(if (eq num 0)
|
|
""
|
|
(format "%d" num)))
|
|
|
|
(defun 2048-was-combined-this-turn (row column)
|
|
"Return whether the number in (ROW, COLUMN) was generated this turn by two numbers combining."
|
|
(elt *2048-combines-this-move*
|
|
(+ (* row *2048-columns*)
|
|
column)))
|
|
|
|
(defun 2048-set-was-combined-this-turn (row column)
|
|
"Set that the number in (ROW, COLUMN) was generated this turn by two numbers combining."
|
|
(2048-debug (format "setting (%d, %d) as combined this turn." row column))
|
|
(aset *2048-combines-this-move*
|
|
(+ (* row *2048-columns*)
|
|
column)
|
|
t))
|
|
|
|
(defun 2048-insert-random-cell ()
|
|
"Pick a number randomly, and insert it into a random cell."
|
|
(let ((number-to-insert (elt *2048-possible-values-to-insert*
|
|
(random (length *2048-possible-values-to-insert*))))
|
|
(row (random *2048-rows*))
|
|
(column (random *2048-columns*)))
|
|
(while (not (eq (2048-get-cell row column)
|
|
0))
|
|
(setq row (random *2048-rows*))
|
|
(setq column (random *2048-columns*)))
|
|
(2048-set-cell row column number-to-insert)))
|
|
|
|
(defun 2048-check-game-end ()
|
|
"Check whether the game has either been won or lost. If so, notify the user and restarting."
|
|
(cond ((2048-game-was-won)
|
|
(2048-print-board)
|
|
(if (y-or-n-p "Yay! You beat the game! y to start again; n to continue. Start again? ")
|
|
(progn (2048-add-new-history-item *2048-score* *2048-hi-tile* (current-time) (time-subtract (current-time) *2048-game-epoch*))
|
|
(2048-init))
|
|
(setq *2048-victory-value*
|
|
(* *2048-victory-value* 2))))
|
|
((2048-game-was-lost)
|
|
(unless *2048-game-has-been-added-to-history*
|
|
(2048-add-new-history-item *2048-score* *2048-hi-tile* (current-time) (time-subtract (current-time) *2048-game-epoch*))
|
|
(setq *2048-game-has-been-added-to-history* t))
|
|
(2048-print-board)
|
|
(when (y-or-n-p "Aw, too bad. You lost. Want to play again? ")
|
|
(2048-init)))))
|
|
|
|
(defun 2048-add-new-history-item (score hi-tile game-end-time game-duration)
|
|
"Generate and add a new history item to the score list.
|
|
|
|
This item should have score SCORE, the highest tile reached as HI-TILE,
|
|
have ended at GAME-END-TIME, and have duration GAME-DURATION"
|
|
(setq *2048-history*
|
|
(let ((history-length (length *2048-history*)))
|
|
;; get the history length before calling cl-sort because cl-sort is destructive.
|
|
(butlast (cl-sort (cons (list *2048-score*
|
|
*2048-hi-tile*
|
|
(format-time-string "%Y-%m-%d" game-end-time)
|
|
game-duration)
|
|
*2048-history*)
|
|
'>
|
|
:key 'car)
|
|
(max 0
|
|
(- (1+ history-length)
|
|
*2048-history-size*))))))
|
|
|
|
(defun 2048-game-was-won ()
|
|
"Return t if the game was won, nil otherwise."
|
|
(let ((game-was-won nil))
|
|
(dotimes (row *2048-rows*)
|
|
(dotimes (column *2048-columns*)
|
|
(when (eq (2048-get-cell row column)
|
|
*2048-victory-value*)
|
|
(setq game-was-won t))))
|
|
game-was-won))
|
|
|
|
(defun 2048-game-was-lost ()
|
|
"Return t if the game was lost, nil otherwise."
|
|
(let ((game-was-lost t))
|
|
(dotimes (row *2048-rows*)
|
|
(dotimes (column *2048-columns*)
|
|
(when (eq (2048-get-cell row column)
|
|
0)
|
|
(setq game-was-lost nil))))
|
|
|
|
;; For each square, if that square has one below it that's the same,
|
|
;; the game's not over.
|
|
(dotimes (row (1- *2048-rows*))
|
|
(dotimes (column *2048-columns*)
|
|
(when (eq (2048-get-cell row column)
|
|
(2048-get-cell (1+ row) column))
|
|
(setq game-was-lost nil))))
|
|
|
|
;; For each square, if that square has one to its right that's the same,
|
|
;; the game's not over.
|
|
(dotimes (row *2048-rows*)
|
|
(dotimes (column (1- *2048-columns*))
|
|
(when (eq (2048-get-cell row column)
|
|
(2048-get-cell row (1+ column)))
|
|
(setq game-was-lost nil))))
|
|
game-was-lost))
|
|
|
|
(defun 2048-print-board ()
|
|
"Wipes the entire field, and prints the board."
|
|
(let ((inhibit-read-only t))
|
|
(erase-buffer)
|
|
(dotimes (row *2048-rows*)
|
|
|
|
;;print the separator line on top of, and between cells
|
|
(dotimes (col *2048-columns*)
|
|
(insert "+-------"))
|
|
(insert "+")
|
|
(insert "\n")
|
|
|
|
;;print the empty line above numbers
|
|
(dotimes (col *2048-columns*)
|
|
(insert "|")
|
|
(let ((current-value (2048-get-cell row col)))
|
|
(insert (2048-empty-tile current-value))))
|
|
(insert "|")
|
|
(insert "\n")
|
|
|
|
;; print the number tiles
|
|
(dotimes (col *2048-columns*)
|
|
(insert "|")
|
|
(let ((current-value (2048-get-cell row col)))
|
|
(insert (2048-tile current-value))))
|
|
(insert "|")
|
|
(insert "\n")
|
|
|
|
;;print the empty line below numbers
|
|
(dotimes (col *2048-columns*)
|
|
(insert "|")
|
|
(let ((current-value (2048-get-cell row col)))
|
|
(insert (2048-empty-tile current-value))))
|
|
(insert "|")
|
|
(insert "\n"))
|
|
|
|
;;print the separator line on the bottom of the last row.
|
|
(dotimes (col *2048-columns*)
|
|
(insert "+-------"))
|
|
(insert "+\n")
|
|
(insert "\n")
|
|
|
|
(let ((score-width (if (= 0 *2048-score*)
|
|
1
|
|
(ceiling (log *2048-score* 10)))))
|
|
(insert (format "%10s%s%s\n" "/" (make-string (+ 9
|
|
score-width)
|
|
?\=) "\\"))
|
|
(insert (format "%10s %s %d %s\n" "|" "Score:" *2048-score* "|"))
|
|
(insert (format "%10s%s%s\n" "\\" (make-string (+ 9
|
|
score-width)
|
|
?\=) "/")))
|
|
(insert "\n")
|
|
|
|
(2048-print-help)
|
|
|
|
(insert "\n")
|
|
|
|
;; print score and history
|
|
(insert (format "%10s%s%s\n" "/" (make-string 13 ?\=) "\\"))
|
|
(insert (format "%24s\n" "| HIGH SCORES |"))
|
|
(insert (format "%10s%s%s\n" "\\" (make-string 13 ?\=) "/"))
|
|
(insert "\n")
|
|
(insert (format "%8s %7s %7s %4s\n" "Score" "Hi-Tile" "Date" "Duration"))
|
|
(mapc #'(lambda (x)
|
|
(insert (format "%8d %7d %10s %s\n"
|
|
(elt x 0) (elt x 1) (elt x 2) (format-time-string "%H:%M:%S" (elt x 3) t))))
|
|
*2048-history*)
|
|
|
|
(goto-char (point-min))))
|
|
|
|
(defun 2048-print-help ()
|
|
"Print basic help text."
|
|
(insert "The goal is to create a tile with value 2048.
|
|
Use the arrow keys, p/n/b/f, or C-p/C-n/C-b/C-f
|
|
to move the tiles around. Press r to move randomly.
|
|
|
|
If two tiles of the same value collide, the tiles
|
|
combine into a tile with twice the value.\n"))
|
|
|
|
(defun 2048-move (from-row from-column delta-row delta-column)
|
|
"Try to move the number in (FROM-ROW, FROM-COLUMN)
|
|
|
|
Move it by (DELTA-ROW, DELTA-COLUMN).
|
|
This succeeds when the destination (to-row, to-column) either is 0,
|
|
or is the same value as (from-row, from-column).
|
|
If (to-row, to-column) is zero, cascade and try to move further.
|
|
Returns t if we were able to move; otherwise nil."
|
|
(let ((to-row (+ from-row delta-row))
|
|
(to-column (+ from-column delta-column)))
|
|
(when (in-bounds to-row to-column)
|
|
(2048-debug (format "moving the cell (%d, %d) by (%d, %d) to (%d, %d)" from-row from-column delta-row delta-column to-row to-column))
|
|
(let ((from-val (2048-get-cell from-row from-column))
|
|
(to-val (2048-get-cell to-row to-column)))
|
|
(cond ((eq from-val to-val)
|
|
(unless (or (eq from-val 0)
|
|
(2048-was-combined-this-turn to-row to-column))
|
|
(2048-debug (format "combining (%d, %d) into (%d, %d)" from-row from-column to-row to-column))
|
|
(let ((combined-value (* from-val 2)))
|
|
(unless (boundp (2048-tile-symbol combined-value))
|
|
(2048-init-tile combined-value))
|
|
(2048-set-cell to-row to-column combined-value)
|
|
(setq *2048-score* (+ *2048-score* combined-value))
|
|
(2048-set-cell from-row from-column 0)
|
|
(2048-set-was-combined-this-turn to-row to-column))))
|
|
((eq to-val 0)
|
|
(2048-set-cell to-row to-column from-val)
|
|
(2048-set-cell from-row from-column 0)
|
|
(2048-move to-row to-column delta-row delta-column)
|
|
t)
|
|
(t nil)))))) ;;ugh, need to pass out whether something was combined, and pass that to the _next_ call to 2048-move. We see bugs on rows like 4 0 4 0.
|
|
|
|
(defun in-bounds (row column)
|
|
"Return t if (ROW, COLUMN) is in the bounds of the field."
|
|
(and (>= row 0)
|
|
(>= column 0)
|
|
(< row *2048-rows*)
|
|
(< column *2048-columns*)))
|
|
|
|
|
|
(defun 2048-up ()
|
|
"Shift the board up."
|
|
(interactive)
|
|
(2048-game-move
|
|
(setq *2048-combines-this-move* (make-vector (* *2048-columns* *2048-rows*)
|
|
nil))
|
|
(let ((has-moved nil))
|
|
(dotimes (col *2048-columns*)
|
|
(dolist (row (number-sequence 1
|
|
(1- *2048-rows*)))
|
|
(setq has-moved (or (2048-move row col -1 0)
|
|
has-moved))))
|
|
(when has-moved
|
|
(2048-insert-random-cell)))))
|
|
|
|
(defun 2048-down ()
|
|
"Shift the board down."
|
|
(interactive)
|
|
(2048-game-move
|
|
(setq *2048-combines-this-move* (make-vector (* *2048-columns* *2048-rows*)
|
|
nil))
|
|
(let ((has-moved nil))
|
|
(dotimes (col *2048-columns*)
|
|
(dolist (row (number-sequence (- *2048-rows* 2) 0 -1))
|
|
(setq has-moved (or (2048-move row col 1 0)
|
|
has-moved))))
|
|
(when has-moved
|
|
(2048-insert-random-cell)))))
|
|
|
|
(defun 2048-left ()
|
|
"Shifts the board left."
|
|
(interactive)
|
|
(2048-game-move
|
|
(let ((has-moved nil))
|
|
(dotimes (row *2048-rows*)
|
|
(dolist (col (number-sequence 1 (1- *2048-columns*)))
|
|
(setq has-moved (or (2048-move row col 0 -1)
|
|
has-moved))))
|
|
(when has-moved
|
|
(2048-insert-random-cell)))))
|
|
|
|
(defun 2048-right ()
|
|
"Shifts the board right."
|
|
(interactive)
|
|
(2048-game-move
|
|
(let ((has-moved nil))
|
|
(dotimes (row *2048-rows*)
|
|
(dolist (col (number-sequence (- *2048-columns* 2) 0 -1))
|
|
(setq has-moved (or (2048-move row col 0 1)
|
|
has-moved))))
|
|
(when has-moved
|
|
(2048-insert-random-cell)))))
|
|
|
|
(defun 2048-random-move ()
|
|
"Move the board in a random direction.
|
|
|
|
This may result in no changes to the board,
|
|
if the move was the same as the last one."
|
|
(interactive)
|
|
(funcall (elt '(2048-left 2048-right 2048-up 2048-down) (random 4))))
|
|
|
|
(provide '2048-game)
|
|
;;; 2048-game.el ends here
|