- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
- Contact
* [Top](/)
* [Softwares](/#Softwares)
* [Auto Complete Mode](/software/auto-complete/)
* [RSense](/software/rsense/)
* [Undo History](/repo/undohist.git/)
* [Publications](/#Publications)
* * * *
* Contact
* [tomo@cx4a.org](mailto: tomo@cx4a.org)
* [Twitter](http://twitter.com/m2ym/)
* [GitHub](http://github.com/m2ym/)
* [Top](/)
* [Softwares](/#Softwares)
* [Auto Complete Mode](/software/auto-complete/)
* [RSense](/software/rsense/)
* [Undo History](/repo/undohist.git/)
* [Publications](/#Publications)
* * * *
* Contact
* [tomo@cx4a.org](mailto: tomo@cx4a.org)
* [Twitter](http://twitter.com/m2ym/)
* [GitHub](http://github.com/m2ym/)
- Contact
Table of Contents
1 必要な物
- Emacs 22 or higher
2 Emacs の基本操作
- C-x はコントロールキーを押しながら x を押す
-
M-x はメタキー(Altキー)を押しながら x を押す
C-x C-f ファイルを開く C-x C-s ファイルを保存する C-x b バッファを切り替える C-f, C-b, C-n, C-p: カーソルを移動する C-w カット M-w コピー C-y ペースト C-d 一文字削除 C-x C-e カーソルの前の S 式を実行する M-: S 式を評価する M-x COMMAND コマンドを実行 C-x k バッファを削除する
3 進め方
- puyo.el というファイルを作って指示に従って作っていってください
- M-: あるいは scratch バッファによって評価して結果を確かめることができます
4 Emacs Lisp の概要
- Emacs を拡張するための Lisp の方言
4.1 データ
4.1.1 整数
- 固定幅整数
- リテラル
123 ; => 123 -123 ; => 123
- 基本演算
(+ 1 2) ; => 3 (- 2 1) ; => 1 (* 2 3) ; => 6 (/ 6 2) ; => 3
4.1.2 シンボル
- 値を入れる箱
- 一つのシンボルに値セルと関数セルが存在する
- リテラル
hello ; => hello の値 foo-bar ; => foo-bar の値
- setq
-
シンボルに値を設定する関数
; x というシンボルに 100 をいれる (setq x 100) ; => 100 x ; => 100
-
シンボルに値を設定する関数
- set
-
setq と同じだが第一引数はクオートされない
(set 'x 200) ; => 200 (setq xref 'y) ; => y (set xref 123) ; => 123 y ; => 123
-
setq と同じだが第一引数はクオートされない
- symbol-value
-
シンボルの値を取得する関数
(symbol-value 'x) ; => 200
-
シンボルの値を取得する関数
- fset
-
シンボルに関数を設定する関数
(fset 'x 300) ; => 300
-
シンボルに関数を設定する関数
- symbol-function
-
シンボルに設定された関数を取得する関数
(symbol-function 'x) ; => 300
-
シンボルに設定された関数を取得する関数
- クオート
a
はシンボル a の値を返すが'a
はシンボル a 自体を返す。 クオートによって値を評価するかどうかを制御できる。
4.1.3 コンスセル
- car と cdr の二つのペアを持つオブジェクト
- コンスセルを連結させてリストを表現する
- リテラル
; コンスセル '(1 . 2) ; リスト '(1 2 3) ; リストはコンスセルで表現できる '(1 . (2 . (3 . ())))
- car
-
コンスセルの car 部を返す
(car '(1 . 2)) ; => 1
-
コンスセルの car 部を返す
- cdr
-
コンスセルの cdr 部を返す
(cdr '(1 . 2))
-
コンスセルの cdr 部を返す
- setcar
-
コンスセルの car 部に値を設定する
(setq c '(1 . 2)) ; => (1 . 2) (setcar c 3) ; => 3 c ; => (3 . 2)
-
コンスセルの cdr 部に値を設定する
(setq c '(1 . 2)) ; => (1 . 2) (setcar c 3) ; => 3 c ; => (1 . 3)
-
コンスセルの car 部に値を設定する
4.1.4 配列(ベクター)
- ランダムアクセスが高速なベクター
- aref
-
配列から指定したインデックスの値を取得する
(setq a [1 2 3]) ; =>b [1 2 3] (aref a 0) ; => 1
-
配列から指定したインデックスの値を取得する
- aset
-
配列から指定したインデックスに値を設定する
(setq a [1 2 3]) ; =>b [1 2 3] (aset a 0 2) ; => 2 a ; => [2 2 3]
-
配列から指定したインデックスに値を設定する
4.1.5 文字列
- リテラル
"foo" ; => "foo"
- substring
(substring "hello" 0 4) ; => "hell"
- concat
(concat "Hello" " world") ; => "Hello world"
4.1.6 シーケンス
- リストや配列、文字列はシーケンスとして扱うことができる
- length
-
シーケンスの長さを取得する
(length '(1 2 3)) ; => 3 (length [1 2 3]) ; => 3 (length "foo") ; => 3
-
シーケンスの長さを取得する
- mapcar
-
シーケンスの要素をマッピングして返す
(mapcar 'identity '(1 2 3)) ; => (1 2 3) (mapcar 'identity '[1 2 3]) ; => (1 2 3) (mapcar 'identity "foo") ; => (102 111 111) (mapcar (lambda (x) (* x x)) '(1 2 3)) ; => (1 4 9)
-
シーケンスの要素をマッピングして返す
- elt
-
要素を取得する
(elt '(1 2 3) 1) ; => 2 (elt '[1 2 3] 1) ; => 2 (elt "foo" 1) ; => 111
-
要素を取得する
4.1.7 関数
- (lambda (x y z) …) という lambda 式が関数の本体になる
- defun
-
関数を定義する
; 二乗を求める関数 (defun square (x) (* x x)) (square 2) ; => 4
-
関数を定義する
- funcall/apply
-
関数を呼び出す
(funcall 'square 3) ; => 9 (apply 'square '(3)) ; => 9
-
関数を呼び出す
- lambda
-
関数を生成する
(setq square2 (lambda (x) (* x x))) (square2 4) ; => (void-functoin square2) (funcall square2 4) ; => 16 (fset 'square2 square2) ;うまく呼べる (square2 4) ; => 16
-
関数を生成する
4.1.8 特殊
- t
-
真を意味する
t ; => t
-
真を意味する
- nil
-
偽を意味する
nil ; => nil () ; => nil
-
偽を意味する
4.1.9 比較
- eq
-
オブジェクトの比較(ポインタ的)
(eq 1 1) ; => t (eq 1 2) ; => nil (eq 'foo 'foo) ; => t (eq '(1) '(1)) ; => nil (eq 1.2 1.2) ; => nil (eq "foo" "foo") ; => nil
-
オブジェクトの比較(ポインタ的)
- eql
-
eq に加えて浮動小数点数の比較に対応
(eql '(1) '(1)) ; => nil (eql 1.2 1.2) ; => t (eql "foo" "foo") ; => nil
-
eq に加えて浮動小数点数の比較に対応
- equal
-
リストの構造や文字列の比較が可能
(equal '(1) '(1)) ; => t (equal "foo" "foo") ; => t
-
リストの構造や文字列の比較が可能
- =
-
数値の比較
(= 1 1) ; => t (= 1 2) ; => nil **** /=
-
数値の否定比較
(/= 1 1) ; => nil (/= 1 2) ; => t **** >/>= (> 1 2) ; => nil (>= 1 2) ; => nil **** </<= (< 1 2) ; => t (<= 1 2) ; => t
-
数値の比較
- 否定
(not t) ; => nil (not nil) ; => t
4.2 構文
4.2.1 if
(if COND THEN ELSE...)
(if (eq 1 1)
(message "OK")
(error "error")
(error "do something"))
4.2.2 when
(when COND BODY...)
(if (not COND)
(progn
BODY...)) と同等
(when (eq 1 1)
(message "do")
(message "something"))
4.2.3 unless
(unless COND BODY...)
(if COND
nil
BODY...) と同等
(unless (eq 1 1)
(message "oh")
(message "my")
(message "god"))
4.2.4 or
(or CONDITIONS...) (or nil) ; => nil (or nil t) ; => t (or nil 1) ; => 1
4.2.5 and
(and CONDITIONS...) (and nil) ; => nil (and nil t) ; => nil (and 1 2) ; => 2
4.2.6 while
(while TEST BODY...) (setq i 0) (while (< i 5) (message "%s" i) (setq i (1+ i))) ; => 0 1 2 3 4
4.2.7 dotimes
(dotimes (VAR COUNT) BODY...) (dotimes (i 5) (message "%s" i)) ; => 0 1 2 3 4
4.2.8 dolist
(dolist (VAR LIST) BODY...) (dolist (i '(1 2 3)) (message "%s" i)) ; => 1 2 3
4.2.9 let
- 束縛を生成するために使う
-
ブロックのローカル変数みたいなもの
(let VARLIST BODY...) (let ((x 100) (y 200)) (message "%s %s" x y)) ; => 100 200
4.3 スコープ
-
Emacs Lisp はダイナミックスコープ
(setq x 1) (defun scope-test () (+ x 2)) (scope-test 2) ; => 3 (let ((x 3)) (scope-test 2) ; => 5
-
クロージャーなし
(defun make-adder (m) (lambda (n) (+ m n))) (funcall (make-adder 1) 2) ; => (void-variable m)
4.4 末尾再帰最適化
- Emacs Lisp は末尾再帰最適化しない
- ループを書くには再帰を使わず mapcar, while, dotimes, dolist を使う
4.5 スレッド
- Emacs Lisp はシングルスレッド
- 非同期 IO やタイマーコールバックはアイドル時に実行される
5 基盤
- ゲームボードを作成するところまで
- gamegrid というライブラリを使っている
- M-: (progn (switch-to-buffer "Puyo") (puyo-init) (puyo-init-buffer)) してみよう
6 ゲームスタートからゲームオーバーまで
-
ぷよぷよを開始する puyo という関数を定義する
(defun puyo () "ぷよぷよをはじめる" (interactive) (switch-to-buffer "*Puyo*") ...)
-
メジャーモードを定義する
(defun puyo-mode () "ぷよぷよモード" (interactive) (setq major-mode 'puyo-mode) (setq mode-name "Puyo") (puyo-init))
- puyo 関数から puyo-mode を呼ぶ
-
ぷよぷよを開始する puyo-start-game という関数を定義する
(defun puyo-start-game () "ぷよぷよをはじめる" (interactive) (puyo-init-buffer) (gamegrid-start-timer puyo-default-tick-period 'puyo-update-game))
-
puyo-update-game 関数を定義する
(defun puyo-update-game (puyo-buffer) "ゲームをすすめる" (let (hit) (setq puyo-drop (+ puyo-drop (/ puyo-default-tick-period puyo-default-drop-speed))) (when (>= puyo-drop 1) ...))) -
ぷよを表示する
-
puyo-get-cell と puyo-set-cell という関数を定義する
(defun puyo-get-cell (x y) (gamegrid-get-cell (+ puyo-top-left-x x) (+ puyo-top-left-y y))) (defun puyo-set-cell (x y color) (gamegrid-set-cell (+ puyo-top-left-x x) (+ puyo-top-left-y y) color)) -
(0, 0) に赤のぷよを表示する
(puyo-set-cell 0 0 1)
-
(1, 1) に黄のぷよを表示する
(puyo-set-cell 1 1 3)
- (puyo-init-buffer) で初期化できる
-
ぷよを描画する関数を定義する
(defvar puyo-shape nil) (defun puyo-get-shape-cell (x y) (aref puyo-shape (+ (* y 3) x))) (defun puyo-draw-shape () "ぷよを描画する" (dotimes (y 3) (dotimes (x 3) (let ((c (puyo-get-shape-cell x y))) (if (/= c puyo-blank) (puyo-set-cell (+ puyo-pos-x x) (+ puyo-pos-y y) c)))))) -
ぷよを描画してみよう
-
ぷよは3*3のベクター
(setq puyo-shape [0 1 0 0 1 0 0 0 0]) (puyo-draw-shape)
-
ぷよは3*3のベクター
-
puyo-get-cell と puyo-set-cell という関数を定義する
-
ぷよを消去する関数を定義する
(defun puyo-erase-shape () "ぷよを消去する" (dotimes (y 3) (dotimes (x 3) (let ((c (puyo-get-shape-cell x y))) (if (/= c puyo-blank) (puyo-set-cell (+ puyo-pos-x x) (+ puyo-pos-y y) puyo-blank)))))) -
ぷよを作る関数を定義する
(defun puyo-make-shape () "ぷよを作る" (vector 0 (1+ (random 4)) 0 0 (1+ (random 4)) 0 0 0 0))-
使ってみる
(progn (setq puyo-shape (puyo-make-shape)) (puyo-draw-shape))
-
使ってみる
-
次のぷよを出す関数を定義する
(defun puyo-new-shape () "次のぷよを出す" (setq puyo-pos-x (- (floor (/ puyo-width 2)) 2)) (if (/= (puyo-get-cell (+ puyo-pos-x 1) puyo-top-left-y) puyo-blank) (puyo-end-game) (setq puyo-pos-y 0) (setq puyo-shape (puyo-make-shape)) (puyo-draw-shape))) -
puyo-end-game を定義する
(defun puyo-end-game () "ゲームオーバー" (interactive) (puyo-cleanup) (message "ゲームオーバー"))
-
ゲームをすすめよう
-
puyo-update-game にロジックを追加する
... (puyo-erase-shape) (setq puyo-pos-y (1+ puyo-pos-y)) (setq puyo-drop 0) (puyo-draw-shape)))) -
puyo-start-game に以下を追加する
(puyo-new-shape)
-
puyo-update-game にロジックを追加する
-
衝突判定
-
衝突判定関数を定義する
(defun puyo-test-shape() "ぷよが衝突したか" (let ((hit nil)) (dotimes (y 3) (dotimes (x 3) (unless hit (setq hit (let ((c (puyo-get-shape-cell x y)) (xx (+ puyo-pos-x x)) (yy (+ puyo-pos-y y))) (and (/= c puyo-blank) (if (< yy 0) (or (< xx 0) (>= xx puyo-width)) (or (>= xx puyo-width) (>= yy puyo-height) (/= (puyo-get-cell x y) puyo-blank))))))))) hit)) -
puyo-update-game 関数にロジックに変更する
(puyo-erase-shape) (setq puyo-pos-y (1+ puyo-pos-y)) (let ((hit (puyo-test-shape))) (if hit (setq puyo-pos-y (1- puyo-pos-y)) (setq puyo-drop 0)) (puyo-draw-shape) (if (and hit (>= puyo-drop 2)) (puyo-new-shape))))))
-
衝突判定関数を定義する
7 動かせるようにする
-
メジャーモードに keymap を定義する
(defvar puyo-mode-map (let ((keymap (make-sparse-keymap))) (define-key keymap "x" 'puyo-move-down) (define-key keymap "s" 'puyo-move-bottom) (define-key keymap "z" 'puyo-move-left) (define-key keymap "c" 'puyo-move-right) (define-key keymap "v" 'puyo-rotate-left) (define-key keymap "b" 'puyo-rotate-right) keymap)) -
メジャーモードになったときに puyo-mode-map を使うようにする
puyo-mode 関数に以下を追加する
(use-local-map puyo-mode-map)
-
puyo-rotate-left 関数を定義する
(defun puyo-rotate-left () "ぷよを反時計回りに回す" (interactive) (let ((old puyo-shape)) (puyo-erase-shape) (setq puyo-shape (vector (aref puyo-shape 2) (aref puyo-shape 5) (aref puyo-shape 8) (aref puyo-shape 1) (aref puyo-shape 4) (aref puyo-shape 7) (aref puyo-shape 0) (aref puyo-shape 3) (aref puyo-shape 6))) (unless (puyo-try-rotate-move) (setq puyo-shape old)) (puyo-draw-shape))) -
puyo-rotate-right 関数を定義する
(defun puyo-rotate-right () "ぷよを時計回りに回す" (interactive) (let ((old puyo-shape)) (puyo-erase-shape) (setq puyo-shape (vector (aref puyo-shape 6) (aref puyo-shape 3) (aref puyo-shape 0) (aref puyo-shape 7) (aref puyo-shape 4) (aref puyo-shape 1) (aref puyo-shape 8) (aref puyo-shape 5) (aref puyo-shape 2))) (if (puyo-test-shape) (setq puyo-shape old)) (puyo-draw-shape))) -
puyo-move-left 関数を定義する
(defun puyo-move-left () "ぷよを左に動かす" (interactive) (puyo-erase-shape) (setq puyo-pos-x (1- puyo-pos-x)) (if (puyo-test-shape) (setq puyo-pos-x (1+ puyo-pos-x))) (puyo-draw-shape)) -
puyo-move-right 関数を定義する
(defun puyo-move-left () "ぷよを右に動かす" (interactive) (puyo-erase-shape) (setq puyo-pos-x (1+ puyo-pos-x)) (if (puyo-test-shape) (setq puyo-pos-x (1- puyo-pos-x))) (puyo-draw-shape)) -
puyo-move-down 関数を定義する
(defun puyo-move-down () "ぷよを下に動かす" (interactive) (puyo-erase-shape) (setq puyo-pos-y (1+ puyo-pos-y)) (if (puyo-test-shape) (progn (setq puyo-pos-y (1- puyo-pos-y)) (puyo-draw-shape) (puyo-new-shape)) (puyo-draw-shape)))
8 ちぎれるようにする
(defvar puyo-task nil "実行中のタスク")
- (puyo-new-shape) となっているところを (puyo-shape-done) にする
- puyo-shape-done 関数を定義する
-
puyo-update-game 関数にタスク処理を追加する
(defun puyo-update-game (puyo-buffer) "ゲームをすすめる" (let (hit) (setq puyo-drop (+ puyo-drop (/ puyo-default-tick-period puyo-default-drop-speed))) (when (>= puyo-drop 1) (cond ((eq puyo-task 'drop) (puyo-drop) (setq puyo-drop 0)) (t (puyo-erase-shape) (setq puyo-pos-y (1+ puyo-pos-y)) (let ((hit (puyo-test-shape))) (if hit (setq puyo-pos-y (1- puyo-pos-y)) (setq puyo-drop 0)) (puyo-draw-shape) (if (and hit (>= puyo-drop 2)) (puyo-new-shape)))))))) -
puyo-drop 関数を定義する
(defun puyo-drop () "浮いてるぷよを落とす" (dotimes (y (1- puyo-height)) (dotimes (x puyo-width) (let* ((yy (- puyo-height y)) (c (puyo-get-cell x yy))) (when (/= c puyo-blank) (while (= (puyo-get-cell x (1+ yy)) puyo-blank) (setq yy (1+ yy))) (puyo-set-cell x (- puyo-height y) puyo-blank) (puyo-set-cell x yy c))))))
9 連鎖できるようにする
-
連鎖できるかをテストする puyo-chain 関数を定義する
(defun puyo-chain-1 (x y c color footprint) (if (or (< x 0) (< y 0) (>= x puyo-width) (>= y puyo-height) (= (aref footprint (+ x (* y puyo-width))) 1)) 0 (let ((d (puyo-get-cell x y))) (if (= c d) (progn (aset footprint (+ x (* y puyo-width)) 1) (if color (puyo-set-cell x y color)) (+ (puyo-chain-1 (1- x) y c color visit) (puyo-chain-1 x (1- y) c color visit) (puyo-chain-1 (1+ x) y c color visit) (puyo-chain-1 x (1+ y) c color visit) 1)) 0)))) (defun puyo-chain (x y color) "連鎖する" (let ((c (puyo-get-cell x y))) (if (/= c puyo-blank) (let ((footprint (make-vector (* puyo-width puyo-height) 0))) (+ (puyo-chain-1 (1- x) y c color visit) (puyo-chain-1 x (1- y) c color visit) (puyo-chain-1 (1+ x) y c color visit) (puyo-chain-1 x (1+ y) c color visit) )) 0))) -
puyo-update-game 関数に連鎖ロジックを追加する
(defun puyo-update-game (puyo-buffer) "ゲームをすすめる" (let (hit) (setq puyo-drop (+ puyo-drop (/ puyo-default-tick-period puyo-default-drop-speed))) (when (>= puyo-drop 1) (cond ((eq puyo-task 'drop) (puyo-drop) (setq puyo-task 'chain) (setq puyo-drop 0)) ((eq puyo-task 'chain) (let ((count 0)) (dotimes (y puyo-height) (dotimes (x puyo-width) (let ((c (puyo-chain x y nil))) (message "%s" c) (when (>= c 4) (puyo-chain x y puyo-blank) (setq count (1+ count)))))) (if (> count 0) (setq puyo-task 'drop) (setq puyo-task nil) (puyo-new-shape))) (setq puyo-drop 0)) (t (puyo-erase-shape) (setq puyo-pos-y (1+ puyo-pos-y)) (let ((hit (puyo-test-shape))) (if hit (setq puyo-pos-y (1- puyo-pos-y)) (setq puyo-drop 0)) (puyo-draw-shape) (if (and hit (>= puyo-drop 2)) (puyo-shape-done))))))))
10 仕上げ
- 連鎖中はキー入力できないようにする
11 遊ぶ
- M-x puyo でゲーム開始です
Date: 2008/11/15 19時28分00秒
HTML generated by org-mode 6.05b in emacs 23
Tomohiro Matsuyama tomo@cx4a.org
http://cx4a.org/
![]()