Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/lujun9972/emoji-recall.el


https://github.com/lujun9972/emoji-recall.el

Last synced: about 2 months ago
JSON representation

Awesome Lists containing this project

README

        

#+TITLE: README
#+AUTHOR: lujun9972
#+DATE: [2016-07-01 五 21:43]
#+OPTIONS: ^:{}
* Emoji Recall
*献给脸盲症的你~~~~~~~~~~~~~*

Emoji Recall 考验你的记忆力,它会每轮依次显示几个 Emoji 表情,然后让你回忆刚刚出现的 Emoji 表情顺序,必须全对才可以。
[[file:./emoji-recall.gif]]
#+BEGIN_SRC emacs-lisp
;;
(defgroup emoji-recall nil
"Emoji recall game"
:prefix "emoji-recall-"
:group 'game)
#+END_SRC
* 设计
游戏界面应该包含两个buffer,一个buffer用于列出所有的emoji供玩家选择:
#+BEGIN_SRC emacs-lisp
(defcustom emoji-recall-option-buffer "*emoji-recall-option*"
"buffer name for the options of emoji-recall"
:group 'emoji-recall
:type 'string)
#+END_SRC

还有一个buffer用于显示游戏场次,依次显示Emoji表情,显示玩家选择的Emoji表情顺序
#+BEGIN_SRC emacs-lisp
(defcustom emoji-recall-answer-buffer "*emoji-recall-answer*"
"buffer name for the answers of emoji-recall"
:group 'emoji-recall
:type 'string)
#+END_SRC

Emoji的图片放在一个目录下,以png格式存储:
#+BEGIN_SRC emacs-lisp
(defcustom emoji-recall-pics-dir (concat (if load-file-name
(file-name-directory load-file-name)
default-directory)
"emoji-cheat-sheet/")
"Directory storing emoji pictures which should be png file"
:group 'emoji-recall
:type 'file)
#+END_SRC

** answer-buffer
在answer-buffer中,我们先显示Round N: emoji1 emoji2 ... emojiN. 然后在最后一行留空用于存放玩家的答案.类似下面这样:

[[./answer-buffer.png]]

1. 我们需要一个变量存放目前时第几轮的游戏:
#+BEGIN_SRC emacs-lisp
(defvar emoji-recall-round 1
"Round of the game")
#+END_SRC

2. 每个emoji在显示一段时间后要变成 =*= 号:

+ 我们先定义这个时间
#+BEGIN_SRC emacs-lisp
(defcustom emoji-recall-display-interval 3
"Emoji becomes an asterisk after the number of seconds"
:group 'emoji-recall
:type 'number)
#+END_SRC

+ 然后定义一个函数,该函数先在answer-buffer的合适位置处放置一个emoji,然后等待一段时间后将其变为 =*= 号
#+BEGIN_SRC emacs-lisp
(defun emoji-recall-create-emoji-image (emoji)
"Create an image displaying EMOJI"
(let ((emoji-file (concat emoji-recall-pics-dir emoji ".png")))
(create-image emoji-file nil nil)))
(defun emoji-recall-insert-emoji (emoji)
"Insert an emoji and turn it into an asterisk after certain seconds"
(with-current-buffer (get-buffer-create emoji-recall-answer-buffer)
(goto-char (point-max))
(search-backward-regexp "^Round [0-9]+: \\(.*\\)$")
(move-end-of-line nil)
(let* ((emoji-image (emoji-recall-create-emoji-image emoji))
(start (point))
end)
(insert (propertize emoji 'display emoji-image))
(setq end (point))
(insert " ")
(run-at-time emoji-recall-display-interval nil (lambda ()
(put-text-property start end
'display "*" (get-buffer-create emoji-recall-answer-buffer)))))))
#+END_SRC

3. 接下来再定义个函数画出第N轮的问题
#+BEGIN_SRC emacs-lisp
(defun emoji-recall-list-all-emojis (emoji-dir)
"List all emojis stored in EMOJI-DIR"
(mapcar #'file-name-base (directory-files emoji-dir nil "\\.png$")))

(defun emoji-recall-random-emoji ()
"Return random emoji stored in `emoji-recall-pics-dir"
(let* ((emojis (emoji-recall-list-all-emojis emoji-recall-pics-dir))
(len (length emojis))
(idx (random len)))
(nth idx emojis)))

(defun emoji-recall-insert-random-emojis (N)
"Insert N random emojis"
(when (> N 0)
(emoji-recall-insert-emoji (emoji-recall-random-emoji))
(run-at-time emoji-recall-display-interval nil (lambda ()
(emoji-recall-insert-random-emojis (- N 1))))))

(defun emoji-recall-draw-question (N)
"Draw round N question"
(with-current-buffer (get-buffer-create emoji-recall-answer-buffer)
(goto-char (point-max))
(move-beginning-of-line nil)
(delete-region (point) (point-max))
(insert (format "Round %d: " N))
(newline)
(insert "> ")
(emoji-recall-insert-random-emojis N)))
#+END_SRC
** option-buffer
在opton-buffer中需要列出所有可能的emoji供玩家选择,玩家点击一个emoji则在 =answer-buffer= 中存放答案的区域添加一个emoji,当然为了防止玩家输错,需要允许玩家点击答案区域的emoji撤回该emoji.

考虑到这些emoji都需要对点击事件做出响应,我们考虑把这些emoji做成button. 而且将 =answer-buffer= 中答案位置的emoji做成button还有个好处: 由于Elisp中的button其实就是带有一堆text/overlay属性的文本,这样在核对玩家给出的答案是否正确时,只需要将答案区域的文本与问题处的文本对比一下内容是否一致就行了.

最后,我们还需要一个提交按钮,用于提交答案.

*** answer-button

1. 我们先定义答案区域emoji button的类型(暂时命名为answer-button吧),该类button要在被点击的时候将自己从答案区域删除掉.
#+BEGIN_SRC emacs-lisp
(define-button-type 'emoji-recall-answer-button
'action (lambda (b)
(delete-region (button-start b)
(+ 1 (button-end b)))) ;这里+1是因为每个emoji后面都带个空格
'follow-link t)
#+END_SRC

*** option-button

1. 然后定义option区域中emoji button的类型(暂时命名为option-button吧),该类型的button要在点击的时候,在答案区域插入一个answer-button. 当然,其插入的answer-button的显示与label应该与option-button一致.
#+BEGIN_SRC emacs-lisp
(defun emoji-recall-insert-answer-button (b)
(let ((label (button-label b))
(display (button-get b 'display))
(help-echo (button-get b 'help-echo)))
(with-current-buffer (get-buffer-create emoji-recall-answer-buffer)
(goto-char (point-max))
(insert-text-button label
'display display
'help-echo help-echo
:type 'emoji-recall-answer-button)
(insert " ")))) ;这里必须带个空格时因为当相同的emoji靠在一起时,由于display属性相同,Emacs只显示一个emoji image

(define-button-type 'emoji-recall-option-button
'action #'emoji-recall-insert-answer-button
'follow-link t)
#+END_SRC

2. 定义函数用于在option-buffer中插入option button
#+BEGIN_SRC emacs-lisp
(defun emoji-recall-insert-option-button (emoji)
"Insert an option-button with label EMOJI"
(let ((emoji-image (emoji-recall-create-emoji-image emoji)))
(with-current-buffer (get-buffer-create emoji-recall-option-buffer)
(goto-char (point-max))
(insert-text-button emoji
'display emoji-image
'help-echo emoji
:type 'emoji-recall-option-button)
(insert " "))))

(defun emoji-recall-draw-options ()
"Draw all the options"
(with-current-buffer (get-buffer-create emoji-recall-option-buffer)
(erase-buffer)
(mapc #'emoji-recall-insert-option-button
(emoji-recall-list-all-emojis emoji-recall-pics-dir))))
#+END_SRC

*** submit-button
1. 定义一个函数来检查玩家的回答是否正确

获取回答的答案
#+BEGIN_SRC emacs-lisp
(defun emoji-recall-get-user-answer ()
"Geth the user answer"
(with-current-buffer emoji-recall-answer-buffer
(goto-char (point-min))
(search-forward "> ")
(buffer-substring-no-properties (point) (point-max))))
#+END_SRC

获取正确的答案
#+BEGIN_SRC emacs-lisp
(defun emoji-recall-get-correct-answer ()
"Geth the user answer"
(with-current-buffer emoji-recall-answer-buffer
(goto-char (point-max))
(search-backward-regexp "^Round [0-9]+: \\(.+\\)$")
(match-string-no-properties 1))) ;remove the last newline

#+END_SRC

检查玩家输入的答案是否正确
#+BEGIN_SRC emacs-lisp
(defun emoji-recall-verify-user-answer ()
(string= (emoji-recall-get-user-answer)
(emoji-recall-get-correct-answer)))
#+END_SRC

2. 定义submit-button的类型. 该类型的button要在点击的时候,要检查玩家输入的答案是否正确,若正确则进入下一关,否则游戏结束.
#+BEGIN_SRC emacs-lisp
(define-button-type 'emoji-recall-submit-button
'action (lambda (b)
(if (emoji-recall-verify-user-answer)
(emoji-recall-next-level)
(emoji-recall-game-over)))
'follow-link t)
#+END_SRC

3. 定义函数用于在option-buffer中插入option button
#+BEGIN_SRC emacs-lisp
(defun emoji-recall-draw-submit-button ()
"Draw the submit-button"
(with-current-buffer (get-buffer-create emoji-recall-option-buffer)
(goto-char (point-max))
(insert-text-button "Submit"
:type 'emoji-recall-submit-button)))
#+END_SRC

** 其他

*** 游戏开始时

1. 保存window configuration
2. 画出游戏界面

#+BEGIN_SRC emacs-lisp
(defvar emoji-recall-orign-window-configuration nil
"orign widndow configuration")

;;;###autoload
(defun emoji-recall-game-start ()
(interactive)
(setq emoji-recall-round 1)
(setq emoji-recall-orign-window-configuration (current-window-configuration))
(switch-to-buffer (get-buffer-create emoji-recall-answer-buffer))
(erase-buffer)
(delete-other-windows)
(split-window-below)
(windmove-down)
(switch-to-buffer (get-buffer-create emoji-recall-option-buffer))
(emoji-recall-draw-question emoji-recall-round)
(emoji-recall-draw-options)
(emoji-recall-draw-submit-button))
#+END_SRC

*** 下一关

1. round加一
2. 插入新的问题
3. 清空原答案

#+BEGIN_SRC emacs-lisp
(defun emoji-recall-next-level ()
(setq emoji-recall-round (+ 1 emoji-recall-round))
(emoji-recall-draw-question emoji-recall-round)
(with-current-buffer emoji-recall-answer-buffer
(goto-char (point-min))
(search-forward "> ")
(delete-region (point) (point-max))))
#+END_SRC

*** 游戏结束时

1. 清空answer-buffer的内容
2. 显示游戏结束画面

#+BEGIN_SRC emacs-lisp

(defun emoji-recall-game-over ()
"Game over and show achievements"
(interactive)
(switch-to-buffer emoji-recall-answer-buffer)
(erase-buffer)
(insert (propertize (format "Game Over! You Got Round %d\nPress any key to quit !" emoji-recall-round) 'display '(height 2)))
(while (not (input-pending-p))
(sit-for 0.01))
(emoji-recall-game-quit))
#+END_SRC

*** 游戏退出

1. 删除answer-buffer
2. 删除option-buffer
3. 还原window configuration

#+BEGIN_SRC emacs-lisp
(defun emoji-recall-game-quit ()
"Quit game"
(interactive)
(kill-buffer emoji-recall-answer-buffer)
(kill-buffer emoji-recall-option-buffer)
(when emoji-recall-orign-window-configuration
(set-window-configuration emoji-recall-orign-window-configuration)
(setq emoji-recall-orign-window-configuration nil)))
#+END_SRC