ビープ音の代わりにslライクなアスキーアートを表示するchokichoki-beep.elを書いてみた

昨日からEmacs lispをちょこちょこ勉強してきて,少しはEmacs lisp的なプログラムの書き方がわかってきた,と思う.

ひょんなことでビープ音の代わりに呼び出す関数を(setq ring-bell-function)で設定できることを知って,今自分のスキルでアスキーアートの描画とか簡単にできるのではないかと思ったのが昨晩の夜中.色々ハマりながら書いて,気が付いたら朝になっていた.反省はしていない,だが後悔はしている.

Emacsではビープ音が鳴る (場合によってはビープの代わりに画面がフラッシュする) けれど,これはうるさいし,気安くビープを鳴らす自分を矯正したいという人のためにslコマンド*1のようなツールを作ってみた.

Emacs lispのスキル的にはslクローンを作ることは可能だけれど,あんだけ細かい動きを再現することは相当難しいだろうし,slを呼び出せば実現できそうなので,Emacs lispで書き直す意味もあまりないということで,手抜きで動きのあるAAを自作してみた.

これを利用するとEmacsのビープ音の代わりにバッファが切り替わり,チョキチョキとハサミが点線を切っていくAAが表示される.こんな感じ

            ただいまビープ中
        しばらくお待ちください...

------------------>8

ハサミが音を立てながら左に進んでいく.

            ただいまビープ中
        しばらくお待ちください...

-----------------=8 チョキ

チョキチョキし終わるのにけっこう時間がかかり,想像以上にイライラする.
もう二度とビープ音は鳴らさないぞ! という気分になる.

Download & ソースコード

久々にGitHubを使ってみた.わざわざあげるようなものでもないのですが..

;;; chokichoki-beep.el --- Joke beep soft
;;    v.0.0.1 (Last update 2011-03-20)
;;    by Yoshihiko Suhara <@sleepy_yoshi>
;;
;; How to use?
;; 
;; (1) Put chokichoki-beep.el in a load-path directory.
;; (2) Add below descriptions in .emacs:
;;       (load "chokichoki-beep")
;;       (setq visible-bell t)
;;       (setq ring-bell-function 'chokichoki-beep)
;;
;;     Or just play with it:
;;       (load "chokichoki-beep")
;;       M-x chokichoki-beep

(defvar chokichoki-beep-interval-time 0.2)
(defvar chokichoki-beep-buffer-name "*chokichoki-beep*")

(defun chokichoki-beep ()
  (interactive)
  (save-excursion
    (switch-to-buffer chokichoki-beep-buffer-name)

    (insert "\n\n\n")
    (insert "            ただいまビープ中\n")
    (insert "        しばらくお待ちください...\n\n")
    (insert "------------------------------>8")

    (move-beginning-of-line nil)
    (let ((count 30))
      (while (<= 0 count)
	(if (= (mod count 2) 0)
	    (progn
	      (save-excursion
		(search-forward ">")
		(replace-match "=")
		(move-end-of-line nil)
		(insert " チョキ")))
	  (progn
	    (save-excursion
	      (search-forward "=")
	      (replace-match ">")
	      (move-end-of-line nil)
	      (delete-backward-char 4))))
	(delete-char 1)
	(redisplay t)
	(sleep-for chokichoki-beep-interval-time)
	(setq count (- count 1))))
    (kill-buffer chokichoki-beep-buffer-name)))


簡単な処理の流れを解説.愚直に書いたので,もっと良い方法があるのかもしれない.

  1. (switch-to-buffer)でバッファを切り替える
  2. 表示する文字列を(insert)する
  3. 30回イテレーションをする
    • 偶数回では「=8 チョキ」を表示
    • 奇数回では「>8」を表示
    • 1文字(delete)する
    • (sleep-for)で少し待つ

使い方

load-pathが通っているディレクトリにchokichoki-beep.elを置いて,.emacsに以下の記述を追加する.

(load "chokichoki-beep")
(setq visible-bell t) ; ring-bell-funcitonを設定すれば必要ないけれど,なんとなく
(setq ring-bell-function 'chokichoki-beep) ; beepの代わりに呼び出す関数を設定

次にビープ音が鳴る動作を行うか,(ding)または(beep)を評価する.

ビープ音が鳴るたびにAAが表示されるのは精神的負担が大きい場合には,M-x chokichoki-beepで呼び出して動きを確認した後にrm chokichoki-beep.elをしてこのことを忘れよう.

関係する関数・変数一覧 (自分用メモ)

関数
  • (ding), (beep)
    • ビープ音を鳴らす
  • (move-beginning-of-line nil)
    • いつも引数を忘れて怒られる
  • (sleep-for n)
    • n秒間停止する
  • (redisplay t)
    • 画面の再描画
変数
  • visible-bell
    • tに設定すると,ビープの代わりに画面をフラッシュする
  • ring-bell-function
    • ビープの代わりに呼び出す関数を設定できる.

ハマったところ

  • (sleep-for)はバッファの表示が停止するため,(redisplay t)で画面を再描画する必要がある.

まとめ

正直ここまでイライラするとは思わなかった.ビープ音は溜めることができるらしく,何回も連続して出現すると思わず\C-gで止めてしまう.今のところは\C-gで動作を止めることができるようにしているけれど,\C-gを無効にすれば,より極悪度は高くなる.

どうやらジョークソフトとしては,悪い出来ではなさそうだ.

けして,研究室の共有マシンのデフォルト設定にしたり,後輩の.emacsに埋め込んだりして困らせないように! 人間関係にヒビが入る恐れがあるので注意.


さて,今回はチョキチョキ君にしたけれど,基本的には同じロジックで色々なバリエーションを試すことができる.2chのズザーAAにしてみたりしたけれど,文字化けでうまくできなかったので,興味ある方はご自分で改造していろいろ試してください.

というわけでだんだんとEmacs lispの使い方がわかってきた.Emacs lispは他のプログラミング言語に比べてウェブ上に情報が少ないため,細かいところでよくハマるのが難点だが,基本的には検索関数,移動関数などを中心に組み合わせてプログラムを書くので,普通のプログラミング言語が不得意とすることが簡単に書けるような気がしている.

巷ではCommon Lisp関数を (require 'cl) して利用するのが一般的だという流れもあるみたい↓だし.Common Lispも少しはかじったことあるので,clの関数も使ったりしようかなぁ.

たとえば思いつきだけれど,テキストベースのサウンドノベルのようなゲームはEmacs lispで簡単に書ける気がする.今度は,テキストベースの三択恋愛ゲームでも書いてみようか.

*1:ご存じない方は,Wikipediaをご参照ください.