9章の最終節、8節「グラフ」。
今までプレイヤーのHPやら持ち物を操作する手続きを作ってきた。
その中に、現在位置を更新する手続きset-position!も定義してみたが、positionが保持する値の意味は規定していなかった。
ということで、positionを使って、動けるようにしよう、という流れ。
さて、次のような5個のノードからなる簡単な迷宮を想定する。
このグラフを、
(define *dungeon* (("ノード0の説明" (s . 1)) ("ノード1の説明" (n . 0) (s . 2) (e . 3)) ("ノード2の説明" (n . 1) (w . 4)) ("ノード3の説明" (w . 1)) ("ノード4の説明" (e . 2))))
のように表すことにする。
それぞれのノードに関する情報を表すリストを要素とするリスト。
各ノードの説明の後に続くドット対は、向かう方角と行き先のノード番号。
ドット対をいじれば逆行出来ないようにも出来る。有向グラフってやつやね。
だからこの節のタイトルも「グラフ」なのかな?
プレイヤーの状態には現在の位置を保持するpositionという属性があるので、現在位置のノードへの参照を持たせる。
つまり、初期状態がノード0ならば、
(set-position! *player* (car *dungeon*))
ってな感じかな。
さて、行き先のノードは「ノード番号」で持っているので、このposition属性を更新するときのために、要素番号から要素への参照を取得する手続きが欲しい。
つまり、他のプログラミング言語でいうところの配列みたいな使い方をしたい。
んで、そのための手続きがlist-ref。使用例は
gosh> (list-ref '(foo bar baz) 1) bar gosh> (list-ref '(foo bar baz) 0) foo gosh> (list-ref '(foo bar baz) 3) *** ERROR: argument out of range: 3 Stack Trace: _______________________________________
こんな感じ。
さて、このlist-refを使えば、プレイヤーを動かすための手続きmove!は次のように書ける、と。
(define move! (lambda (player direction) (let ((currentPos (get-position player))) (cond ((assoc direction (cdr currentPos)) => (lambda (p) (set-position! player (list-ref *dungeon* (cdr p))))) (else (print "そちらには移動できません")))) #t))
せっかくノードの説明もあるし、set-position!の後にそれを出力する手続きをかませば、移動後に移動先の説明を出すようにできる。
;; 現在位置の情報を出力 (define describe (lambda (player) (print (car (get-position player)))))
ってことで、9章おしまい。