有限ステートマシンで遊ぶ(2)
MzSchemeの構造体を使って有限ステートマシン作りしてみました。らくちん過ぎる。
状態(state)
状態は4つあります。
ゲームステート V 戦闘ステート <--> 宿屋ステート V 死亡ステート
デザパタで言うと、Stateパターンと同じような感じです。
HPによって状態(ステート)を変えていきます。HPが少なくなってきたら宿屋へ移動。元気になったら戦闘へ。HPがゼロ以下になったら、死んじゃいます。悲しいです。
また、状態を切り替えるときにもメッセージを表示出来るようにしました。状態の中にstart、current、endという状態が入ってます。
へっぽこ AI RPG
動きはこんな感じ。
===== tanakaのへっぽこ AI RPG ===== さぁ冒険だ〜 いざ戦いへ〜 ゴブリンと戦います。 戦闘 : -7のダメージを受けました。 HP : 13 戦闘 : -11のダメージを受けました。 HP : 2 モウダメ。 ねむぃよぉ・・・ 宿屋で休みます。 宿屋 : 9回復しました。 HP : 11 宿屋 : 5回復しました。 HP : 16 宿屋 : 6回復しました。 HP : 22 あぁ、よく寝た! いざ戦いへ〜 ゴブリンと戦います。 戦闘 : -7のダメージを受けました。 HP : 15 戦闘 : -9のダメージを受けました。 HP : 6 モウダメ。 ねむぃよぉ・・・ 宿屋で休みます。 宿屋 : 5回復しました。 HP : 11 宿屋 : 9回復しました。 HP : 20 宿屋 : 2回復しました。 HP : 22 あぁ、よく寝た! いざ戦いへ〜 ゴブリンと戦います。 戦闘 : -11のダメージを受けました。 HP : 11 戦闘 : -13のダメージを受けました。 HP : -2 モウダメ。 死亡しました。 ===== GAME OVER ===== bye!
あぁぁ死んじゃった・・・ゴブリンつえぇぇぇ。
コード
残念ながらMzScheme専用です。MzSchemeの構造体はヤバイ。
#!/usr/bin/mzscheme -f ;; player-struct (define-struct player (name state hp)) (define (add-hp! player hp) (set-player-hp! player (+ hp (player-hp player)))) ;; message (define (message . args) (display (apply format args)) (newline)) ;; state-struct (define-struct state (start current end)) (define (execute player state) (if (string? state) (message state) (state player))) (define (change-state! player state) (execute player (state-end (player-state player))) (set-player-state! player state) (execute player (state-start (player-state player)))) ;; state (define (start-game player) (message "===== ~aのへっぽこ AI RPG =====\n" (player-name player)) (change-state! player fight)) (define (fight-current player) (let ((damage (- (random 15)))) (add-hp! player damage) (message "戦闘 : ~aのダメージを受けました。 HP : ~a" damage (player-hp player)) (cond ((<= (player-hp player) 0) (change-state! player die)) ((< (player-hp player) 10) (change-state! player sleep))))) (define (sleep-current player) (let ((damage (random 10))) (add-hp! player damage) (message "宿屋 : ~a回復しました。 HP : ~a" damage (player-hp player)) (cond ((> (player-hp player) 20) (change-state! player fight))))) (define (bye!) (message "bye!")) (define game (make-state 'empty start-game "さぁ冒険だ〜")) (define fight (make-state "いざ戦いへ〜\nゴブリンと戦います。" fight-current "モウダメ。")) (define sleep (make-state "ねむぃよぉ・・・\n宿屋で休みます。" sleep-current "あぁ、よく寝た!")) (define die (make-state "死亡しました。\n===== GAME OVER =====" 'empty 'empty)) ;; main (define (main player n) (if (or (= n 0) (eq? die (player-state player))) (bye!) (begin (execute player (state-current (player-state player))) (main player (- n 1))))) (main (make-player 'tanaka game 20) 20) (exit)
たったこれだけで、AIプログラミング出来ちゃうSchemeに感動です。
AIエンジン自体はexecuteとchange-stateだけ。前回よりちょっと複雑です。
やばいAI楽しい。やっぱりSchemeだよ。
あ
状態をツリーにしたら楽しそうだ。
戦闘の状態の中に、攻撃な状態があったり、魔法な状態があったりする。攻撃の中に剣で攻撃したり、矢で攻撃したり・・・うっほ。再帰だ。
しかし、再帰にも問題点があるな。攻撃中に死亡したとすると、戦闘状態を抜けなければならない。つまり、大域ジャンプが必要となってしまう。継続を使えばその点は解消出来るけど・・・継続か・・・考え中。
あぁ、違うな
状態ははツリーじゃない。「グラフ」。例えるならネットみたいなもの。検索欄に「ボクノス」って入れたら、ボクノスにたどり着ける。網の目のようになってるんだ。つまり、どの状況からでもゲームオーバーに飛べる。
ツリーじゃ表現出来ない。グラフだグラフ・・・まだリスト修行が必要だ・・・。