読者です 読者をやめる 読者になる 読者になる

by shigemk2

当面は技術的なことしか書かない

Land of Lisp 5章 メモ

5章 テキストゲームのエンジンを作る

5.1 魔法使いのアドベンチャー

  • 周囲を見渡す
  • 別の場所へ移動する
  • オブジェクトを拾う
  • 拾ったオブジェクトで何かする

以上4つの機能のうち3つを搭載したゲームエンジンをこの章では作る。この節では、*node* をつくる。

連想リスト(alist)を使った機能

(defparameter *nodes* '((living-room (you are in the living-room.
 a wizard is snoring loudly on the couch.))
 (garden (you are in a beautiful garden.
 there is a well in front of you.))
 (attic (you are in the attic.
 there is a giant welding torch in the corner.))))

ここで文字列ではないのは、元となるデータ構造を、出力形式に縛られない形で最初から持っていて、自分のプログラミング言語の得意な点を活かしたコーディングができるようにするため。実際のプログラミングでもデータ構造が文字列オンリーといったシンプルな構造はありえない。

以下、定義したalistから情報を引っ張ってくるサンプル

[8]> (defun describe-location (location nodes)
 (cadr (assoc location nodes)))
DESCRIBE-LOCATION
[9]>  (describe-location 'living-room *nodes*)
(YOU ARE IN THE LIVING-ROOM. A WIZARD IS SNORING LOUDLY ON THE COUCH.)
[10]>  (describe-location 'test *nodes*)
NIL
[11]>  (describe-location 'garden *nodes*)
(YOU ARE IN A BEAUTIFUL GARDEN. THERE IS A WELL IN FRONT OF YOU.)
[12]>  (describe-location 'attic *nodes*)
(YOU ARE IN THE ATTIC. THERE IS A GIANT WELDING TORCH IN THE CORNER.)
[13]>  (describe-location 'living-room *nodes*)
(YOU ARE IN THE LIVING-ROOM. A WIZARD IS SNORING LOUDLY ON THE COUCH.)

5.2 通り道を描写する

  • 通り道であるedgeの表現
  • 準クオートで計算結果を埋め込んで返す
    • データモードとコードモードを行ったり来たりしている

edgeの片割れを取り出す

[14]> (defparameter *edges* '((living-room (garden west door)
                                     (attic upstairs ladder))
                        (garden (living-room east door))
                        (attic (living-room downstairs ladder))))
*EDGES*
[15]> (defun describe-path (edge)
 `(there is a ,(caddr edge) going ,(cadr edge) from here.))
DESCRIBE-PATH
[16]>  (describe-path '(garden west door))
(THERE IS A DOOR GOING WEST FROM HERE.)
[17]>  (describe-path '(attic upstairs ladder))
(THERE IS A LADDER GOING UPSTAIRS FROM HERE.)
[18]>  (describe-path '(living-room east door))
(THERE IS A DOOR GOING EAST FROM HERE.)

edgeを全部取り出す

[21]> (defun describe-paths (location edges)
 (apply #'append (mapcar #'describe-path (cdr (assoc location edges)))))
DESCRIBE-PATHS
[22]>  (describe-paths 'living-room *edges*)
(THERE IS A DOOR GOING WEST FROM HERE. THERE IS A LADDER GOING UPSTAIRS FROM HERE.)

mapcar

[23]>  (mapcar #'describe-path '((GARDEN WEST DOOR) (ATTIC UPSTAIRS LADDER)))
((THERE IS A DOOR GOING WEST FROM HERE.) (THERE IS A LADDER GOING UPSTAIRS FROM HERE.))

apply

[24]>  (apply #'append '((THERE IS A DOOR GOING WEST FROM HERE.)
 (THERE IS A LADDER GOING UPSTAIRS FROM HERE.)))
(THERE IS A DOOR GOING WEST FROM HERE. THERE IS A LADDER GOING UPSTAIRS FROM HERE.)

5.3 特定の場所にあるオブジェクトを描写する

object(鎖とか蛇とか)をつくる

[26]> (defparameter *objects* '(whiskey bucket frog chain))
*OBJECTS*

オブジェクトの場所を定義する

[27]> (defparameter *object-locations* '((whiskey living-room)
 (bucket living-room)
 (chain garden)
 (frog garden)))
*OBJECT-LOCATIONS*

特定の場所に何のオブジェクトがあるかを判定

[34]> (defun objects-at (loc objs obj-locs)
  (labels ((at-loc-p (obj)
                     (eq (cadr (assoc obj obj-locs)) loc)))
    (remove-if-not #'at-loc-p objs)))
OBJECTS-AT
[35]> (objects-at 'living-room *objects* *object-locations*)
(WHISKEY BUCKET)
[36]> (objects-at 'garden *objects* *object-locations*)
(FROG CHAIN)
[37]> (objects-at 'attic *objects* *object-locations*)
NIL

初期位置をliving-roomとして、全てを見渡す

[49]> (defparameter *location* 'living-room)
*LOCATION*
[50]> (defun look ()
  (append (describe-location *location* *nodes*)
          (describe-paths *location* *edges*)
          (describe-objects *location* *objects* *object-locations*)))
LOOK
[51]> (look)
(YOU ARE IN THE LIVING-ROOM. A WIZARD IS SNORING LOUDLY ON THE COUCH. THERE IS A DOOR GOING WEST FROM HERE. THERE IS A LADDER GOING UPSTAIRS FROM HERE. YOU SEE A WHISKEY ON THE FLOOR. YOU SEE A BUCKET
 ON THE FLOOR.)

5.5 ゲーム世界を動き回る

walkを実装して、色んな所を行き来しよう

[52]> (defun walk (direction)
  (let ((next (find direction
                    (cdr (assoc *location* *edges*))
                    :key #'cadr)))
    (if next
        (progn (setf *location* (car next))
               (look))
      '(you cannot go that way.))))
WALK
[53]> (walk 'west)
(YOU ARE IN A BEAUTIFUL GARDEN. THERE IS A WELL IN FRONT OF YOU. THERE IS A DOOR GOING EAST FROM HERE. YOU SEE A FROG ON THE FLOOR. YOU SEE A CHAIN ON THE FLOOR.)
[54]> (walk 'west)
(YOU CANNOT GO THAT WAY.)
[55]> (walk 'west)
(YOU CANNOT GO THAT WAY.)
[56]> (walk 'east)
(YOU ARE IN THE LIVING-ROOM. A WIZARD IS SNORING LOUDLY ON THE COUCH. THERE IS A DOOR GOING WEST FROM HERE. THERE IS A LADDER GOING UPSTAIRS FROM HERE. YOU SEE A WHISKEY ON THE FLOOR. YOU SEE A BUCKET
 ON THE FLOOR.)
[57]> (walk 'east)
(YOU CANNOT GO THAT WAY.)
[58]> (walk 'upstair)
(YOU CANNOT GO THAT WAY.)
[59]> (walk 'upstairs)
(YOU ARE IN THE ATTIC. THERE IS A GIANT WELDING TORCH IN THE CORNER. THERE IS A LADDER GOING DOWNSTAIRS FROM HERE.)
[60]> (walk 'upstairs)
(YOU CANNOT GO THAT WAY.)
[61]> (walk 'downstairs)
(YOU ARE IN THE LIVING-ROOM. A WIZARD IS SNORING LOUDLY ON THE COUCH. THERE IS A DOOR GOING WEST FROM HERE. THERE IS A LADDER GOING UPSTAIRS FROM HERE. YOU SEE A WHISKEY ON THE FLOOR. YOU SEE A BUCKET
 ON THE FLOOR.)
[62]> (walk 'east)
(YOU CANNOT GO THAT WAY.)
[63]> (walk 'west)
(YOU ARE IN A BEAUTIFUL GARDEN. THERE IS A WELL IN FRONT OF YOU. THERE IS A DOOR GOING EAST FROM HERE. YOU SEE A FROG ON THE FLOOR. YOU SEE A CHAIN ON THE FLOOR.)
[64]> (walk 'upstairs)
(YOU CANNOT GO THAT WAY.)

5.6 オブジェクトを手に取る

pickup objectへpushする

[65]> (defun pickup (object)
  (cond ((member object
                 (objects-at *location* *objects* *object-locations*))
         (push (list object 'body) *object-locations*)
         `(you are now carrying the ,object))
        (t '(you cannot get that.))))
PICKUP
[66]>  (walk 'east)
(YOU ARE IN THE LIVING-ROOM. A WIZARD IS SNORING LOUDLY ON THE COUCH. THERE IS A DOOR GOING WEST FROM HERE. THERE IS A LADDER GOING UPSTAIRS FROM HERE. YOU SEE A WHISKEY ON THE FLOOR. YOU SEE A BUCKET
 ON THE FLOOR.)
[67]>  (pickup 'whiskey)
(YOU ARE NOW CARRYING THE WHISKEY)

5.7 持っているものを調べる

bodyから何のobjectがあるかを調べる

[68]> (defun inventory ()
  (cons 'items- (objects-at 'body *objects* *object-locations*)))
INVENTORY
[69]>  (inventory)
(ITEMS- WHISKEY)
[70]> (pickup 'whiskey)
(YOU CANNOT GET THAT.)
[71]> (pickup 'bucket)
(YOU ARE NOW CARRYING THE BUCKET)
[72]>  (inventory)
(ITEMS- WHISKEY BUCKET)

今のゲームではモノを拾うだけ。何もしない。

5章まとめ

  • ゲームの世界で表現する数学的なグラフ
    • プレーヤーが行くことができる場所をノード
    • 場所間を行き来する経路をエッジ
  • このグラフは、変数 nodes に 連想リスト(alist)の形で持っておくことができる
    • これによって、 ノード(場所)の名前からその場所の属性を引ける
    • このゲームでは、属性として各ノード(場 所)の描写を格納しておいた
  • assoc関数 により、キー(ここでは場所の名前)を使って alist からデータを引き出すことができる
  • 準クオート を使えば、大きなデータの中に、その一部分を計算するためのコードを埋め込むことができる
  • Lisp の関数には、他の関数を引数として受け取るものがある。これらは高階関数と呼ばれる
    • mapcarは Common Lisp で最もよく使われる高階関数。
  • alist 中の値を置き換えたければ、新しい要素をリストに pushするだけでいい。
    • assocは最も新しい値だけを返すから。