MUGEN ( フォント環境:MS ゴシック 標準 サイズ10 )   擬似学習AI、記憶システムの話、またその応用案 byADI ( 学習:Learning 記憶:Storage 記録:Record とか。 System) 2:40 2009/10/23 Anim= 19:14 2009/10/19 また最後尾に。 20:40 2009/10/18 最後尾に 21:41 2009/10/16 AnimTiemは基本マイナス数値。 9:31 2009/09/22 基本終わり 20:56 2010/04/29 PowerMax監視記述を改良  ※記憶システムそのものは「限界への挑戦」であり、  ※ 娯楽性を追及する為にあるわけではありません。  ※ただ、適度な動作を行うよう調整を行えば、それは娯楽たりえますが。  ※Helper管理ですと、ラウンド毎にリセットされますが、  ※ 本体管理となると設定次第でその試合中いつまでも保持します。  ※その場合、相手によってリセットを行うよう注意。  ※( 相手が始めての出場 の場合などを条件に。 )  ※試したことのないものも含まれて居ます。  ■記憶システムの概念  特定状況になった時   >数値化を行ってVarへ情報を書き込み、   >その情報を元に必要な動作を行う。  それが記憶システムの概念です。  例えば、  「State,200で攻撃が当たらなかった時」  >「当たらなかった事をVarへ記録し」  >「State,200を控える」  という具合。 ( あくまで大雑把な例。 )  システムの種類は  ・ガードの失敗を記憶する。  ・投げ技を記憶する。  ・相手の攻撃技を記録する。  ・相手の当身投げ、当身返し技を記憶する。  ・攻撃の失敗を記憶する。  など、「いくらでも応用することが可能」  ただし記述や設計に隙があると、そこを突いた「記憶システム殺し」も可能ですし、  乱用して隙が無さ過ぎる、というのも娯楽の観点からすれば考えもの。  人間は楽にできて単純なプログラムにできないことを補う為、   と言ったような、理由でもなければ乱用するのは好ましくない。  こうしたシステムを用いたAIが無節操に氾濫してしまった場合、   その対抗策として「対記憶システム型キャラ」も出現しうる、と私は考えている。  対記憶システム殺しの動作や娯楽を考えた場合は、   記憶に確率を設定し、適度な弱体化を計るなどが望まれる。  あるいは、何度も記憶していなければ、動作を行わないなど。 ( 1発で覚えず、繰り返されることで覚える形は「人間的な動作」とも見える。 )  動作条件側では、1F反応などを抑制するなどの遅延処理などもある。 ( いわゆる超反応の抑制 )  これらの情報はその試合分しか覚えられないが、   それらを人の手で最初から記述する方法も存在しはする。  それが、[P2Name]などを使った、個別対応記述である。 ( こちらは記憶システムよりも、もっと扱いに注意しなければならない代物。 )  ■記憶の情報  ID,StateNo,Time,P2BodyDist X,P2Dist Y  Anim,AnimTime,AnimElemTime  Hitdefattr,GetHitVar  StateType,MoveType,Vel X,Vel Y  などなど。  基本的は相手の状態を正確に察知することだが、   Varなどによる管理は個別の対応が必要になるため、一律の対応はできない。 ( そうした一律の対応が困難な状態を意図的に作り出すのが、対記憶システム )  また情報が安定しない為、飛び道具に対しての記録は困難である。  ■「ガードの失敗」の記憶について  ガード状態でありながら、攻撃をガードできなかった時、   自身のしていたガードの上下と相手のStateNoを変数化して記録し、   その攻撃に対し記憶したガードを行わないようにする。  いわゆる「中段・下段」や「ガード不可」に対し、   AIでは十分な対応ができないことが、「理由」にできる。  P2Nameよりも柔軟な対応が可能である為、   特殊な技でないかぎり、覚えさえすれば対応が可能。  勿論、中段失敗→下段失敗→ ガード不可判定にするのかどうかなど調整は必要。  ■「投げ技」の記憶について  投げ技を判別できた時、そのStateNoなどを記憶し、   反撃が可能なら反撃、回避が可能であれば回避動作を行う。  「ガード不可」と同じような感覚で注意をすること。  ただし、判別や回避が不可能である場合の調整、処理なども必要となる。  特殊な投げ技に対して対応できない、といったことが頻発することもありうる。  ■「相手の攻撃技」の記憶について  攻撃を受けたとき、StateNoなどを記憶する。  投げ技に限定をしない、相手の攻撃を記憶するもの。  こちらはただ単なるガードや回避だけでなく、   当身返し系(※いわゆる当身技)を確実に発動させるためなどにも使われる。  それ以外では前もって発動しておく必要のある特殊ガードなどの確実な発動、   あるいは相手の攻撃を知っておくことでの攻撃動作の調整など、   幅が広い分、使い勝手も広い。  ただし、これらの理由は「より確実な行動を行う」である為、乱用は厳禁。 ( P2Nameでの情報管理はもっての他である。 )  特に当身返し系は「外れた場合の隙」が「リスク」である為、   ハイリターンの当身返しをノーリスクでも行いうるなどは、   「高ゲージ技をノーゲージで使える」のと同様の状態である。 ( 人間には確実な反応が困難である為に隙が明確なリスクとなるが、   プログラムは確実な情報さえあれば確実に行えてしまう。 )  特殊ガードも、リスクとリターンに配慮すること。  どの情報を取得するかと言った調整、選択は難しいが、   相手の攻撃から動作の調整を行うなど確実でなくとも注意はすること。  ちなみに調整の内容は、相手の始動技が早い場合ガードを優先したり、   反対に遅い場合攻撃を優先したり、発生率の調整など。  ■「相手の当身返し系」の記憶について  当身技にはまった時、当身中のStateNoを記憶し、   次からはそのState中攻撃を行わないようにする、といったもの。  ガード不可などと同様に、AIでの対処が難しいことが理由にできる。 ( 例えば超必殺技での暗転当身へ攻撃をしようとする人間は居ないが、   AIの場合判別ができず、攻撃をしてしまうことがある。 )  記憶の為に最低一度は食らわなければならない為、   封殺できるわけではないが、何度も当たるヘマもしづらくなる。  しかし当身判定の無くなるタイミングをつかめるわけでは無い為、   隙を攻撃させようとする場合、また当身を食らう危険性がある。 ( (abs(EnemyNear,AnimTime) < *攻撃判定F*)などでアニメーションの終わりは狙えるが。 )  人の反応に近づける場合は「出始めには攻撃を試みてしまう」ように、   相手のTimeを条件として反応を抑制するといいかもしれない。  少なくとも当身を分かるような状態で突っ込むことを、人は滅多にしない。  ■「攻撃の失敗」を記憶する  通常当たりうるはずの攻撃が当たらずに終わった時、   相手のStateNoを記憶し、そのStateNo中同様の攻撃を行わない、といったもの。  例えば相手の無敵回避技などを覚え、無闇な攻撃を行わない、など。  あるいは、ある系統の攻撃を失敗してる場合にその技を自重するといったことも。   こちらもAIでの対処が難しいため、ということが理由にできる。  ヘルパーなどで全画面判定のエフェクトの無い攻撃を行い、   無敵かどうかを判定するという方法もあるらしいが、私は知らない。  攻撃の失敗を記憶する場合、体格上「当たらない攻撃」も多少は減らせる。 ( コンボの失敗なども考慮すれば、同じ失敗をしにくくなる。 )  また同じ失敗をしないようにする、という点は人間らしいとも言え、   「失敗の修正」の範疇と言えなくも無い。  条件や修正の行動など考慮すべき点は細かいが、相手の技を殺したりせず、   自分の技を極端に引き出すわけでは無いなど、大勢に影響を与えない為、   その程度の使い方であれば、さほど問題にはならないかと、私は思う。  気をつけるに越したことはないが。 ■  ■記憶システムの構築方法  1.知りたい情報を定め、情報を取得する条件を決める    ○対当身>当身技を受けた時    ○対ガード不可>ガード状態から攻撃で崩された時  2.情報が本当に取得できているかを、デバック表示で確認する。    ヘルパーの場合はヘルパー側で設定する。    取得できていなければ、条件を変えて工夫を行う。  3.本当に必要なときのみ取得しているかをデバック表示で確認する。    条件が甘い場合、記憶を二度したりするなどの不具合がおきる。    また関係の無いときに取得してしまうと、後々辛い。  4.その情報を元に、対応した動作が行えるかを確認する。    デバック表示やAIでの反応を用いて調べることになる。    複数記憶の参照などの大きな処理は、一括して行う方が良い。  5.実際に戦闘を行わせ、理想的な行動を起こしているかを確認する。    想定外の動作で妙な反応を起こすこともありうる為、    ある程度複数の相手と戦い、正常に作動しているかを調べる。  6.AIの強さなどの調整を行う。    学習AI全開は強すぎることもある。(特にリスクを消す当身返し系)    望みどおりの強さになるよう調整を行う、ということ。  MUGENのCNSのプログラムについて、よく知っていないとかなり難しいが、  「条件を付け→情報を取得→情報を変数記憶(+過去記憶に反映)   (記憶)→記憶引き出し→動作に反映」  というだけの話である為、プログラムの勝手さえ分かれば出来なくはない。  ただし使える変数、Varの数は60個(0~59)までと無制限ではない為、   情報1つ1つにVar1個ずつを使っているとすぐに限界が来てしまう。  その為、Var1個に2〜3個の情報を入れるなどの工夫をし、   また不要な情報は除外するなど、使うVarを削減すること。 ( 例えば、Varを一回4個使うとなると、記憶10回分でも残りは20個。   それらをそれぞれ2個のVarに纏められれば、残り40個と余裕が大きい。 )  ちなみにFvarの数を考慮していないのは、FvarはFloat型の変数で、   記憶などの桁1つ1つが重要になるような精密な処理向かない為。  桁数が少なければ使える  、かもしれないが重要な情報処理には不向きかと。 ■  ■対記憶システム型キャラの概論  必要となる基本動作はそれぞれのStateだが、   技などは全て同一のStateで行うようにしたキャラ、など。  Varを2つほどStateNo代わりにすることで可能。  Cmd記述の条件の最後尾に[Trigger* = 1||Var(**):=xx]と書くか、   VarsetをしてCmd記述の最後尾にVarを確認してChangeStateを行う、など。  Varを2つ、と言ったのは「技確認用Var」と「技動作用Var」の2つ。  それらを同一Varで行う場合、リセットのタイミングが難しい為、   まずCmdで「技確認用Var」に数値を入れ、   技Stateの最初で「技動作用Var」へ技確認用Varの数値を代入し   同タイミングで技確認用Varのリセットを行う。  これならば数値の変化による動作の不具合を減らせる、はずである。  そうした場合、StateNoで行う記憶システムが上手く機能しなくなる。  それに記憶システムが対策を行う場合、管理Varを知らなければならず、   「P2Name」などの直接的な対策以外は困難となる。  それ以上いくと、Nameを変更すると言った対策となり、あとはいたちごっこだろう。  同一State以外で私の思いつく方法としては、かなり強引になるが  「同じ技の記述を複数State用意し、   Var変数によって毎回異なるStateの技を繰り出し、   一度記憶されても確実に対応できないようにする。」  などの方法がある。  Anim番号(アニメーション番号)を記憶するタイプを考慮した場合には、Animを複製する、  もしくは同Anim内で、Type=ChangeAnimによってアニメーションの管理を行う、など。  例えばその為に作られたわけではないが、同じStateで強弱の異なる技を繰り出したり、   同じAnimでタイプの違う技を出したりする技も「記憶システム殺しの技」と言える。  こういったキャラ側による対抗策が存在する為、AI制作者は注意すること。  追記:ChangeStateは、Anim=というオプションがあるらしい。     Animを変えるだけなら、Varを1つも使わずに済む。  Var1個に複数の情報を入れている相手に限定されるが、   Statedefの限界数は、Var変数の限界数と同数である為、   ステート番号を極めて高い数字にする、という方法もある。 ■ ■簡単な方の話  ■感情システム・傾向変化  記憶システムに近い物として、特殊な状態になった時に、   正確な記憶をせず、特定の数値を変動させ行動傾向を変化させる、   というタイプのシステムも存在する。  どういう役目であるか、と言えば記憶システムに近しい。   それと同様「状況を察知して動作を変化させる」ものだ。  「攻撃を優先する大胆側」と「防御を優先する慎重側」の併用なら   「ガードから、くらい状態へ移った時」などに大胆側へ、   「攻撃動作からくらい状態へ移った時」などに慎重側へ変化させるなど。  個別としては、投げが失敗した時、投げの確率を下げる為の変化、   あるいは地上で投げ技をくらった時、バックステップなど、色々とある。  他には相手のスーパーアーマーを察知したり、   ゲージの量と消費を監視してゲージの増加量を把握するなど。  ピンポイントでも、ワンパターンなやられ方を発見したとき、   特定の動作から何度も攻撃を同じように食らう場合、   その系統の行動を一時的にでも低確率にするなど。  記憶システムよりも変数(Var)が少なくすみ、   簡単で作りやすく、その上記憶システムより幅広く対応が可能で、   またこれを騙すのは手操作以外難しいという代物。  欠点としては記憶システムと同様、システムを理解している場合に、   騙される可能性を持っている点だが、それは容易なことでない。  まして、これに対するシステムなどは、目に見えた悪影響がでかねない。  細密なシステムでない分、対応できる範囲は広いという強みを持っている。  >必殺技の成否を記録しておき、行動に反映させるなど。  ■ゲージ効率の異常を感知  またパワーゲージ量(Power)を確認し、   「異常な増加がある場合、それを感知してゲージ技を多用する」   というのも簡単ながら記憶システムの一種です。 ;Var(31)を使っているが、使う場合は使われていないVarに変えること。 ;置く場所は常時監視の-2,ステートが好ましい。あるいはAI用ヘルパー [State -2, Power Max Flag Check] Type = VarSet var(31) = 9 ;ゲージMAXフラグ Trigger1 = Var(31) = 9 ; フラグあり Trigger2 = Power > 999999 ; 100本以上はMAXとほぼ同義かと。 Trigger3 = Var(31) = 0 ; Power代入フラグさえ無い。 Trigger3 = 0&& Var(31) := Power*10 + 1 ; Power代入フラグ Trigger4 = Var(31):= Power*10 +( (Var(31)%10) + (Floor(Var(31)/10)+1000 < Power) ) ;1Fで1本以上の増加に反応 Trigger4 = Var(31) % 10 > 2 ;2回分の記憶でMAXフラグ Trigger5 = RoundState < 2 && Power = PowerMax;試合開始前・パワー全快 Trigger5 = RoundNo = 1 ;最初のラウンド(Helperで監視する場合不要) Trigger5 = 0 && Var(31):= Power*10 + 2 ; 1回分の記憶にする。 IgnoreHitPause = 1 [State 60000, Power Max Flag Reset] Type = VarSet var(31) = Power * 10 + 2 ;記憶一回分に戻す TriggerAll = var(31) = 9 ;ゲージMAXフラグ Trigger1 = Power < 1000 && Enemy,Power < 1000 ;両者1本未満 Trigger2 = Power < 500 ;ゲージがほぼ枯渇 ;仕組み ; Var(31)の1桁目を記憶回数フラグにし、2桁目より上にPowerを代入する。 ; >毎フレーム、ゲージの量を監視して1Fに1本(1000)以上の上昇を確認した場合、 ;   MAXの可能性があるとしてフラグに1ポイント加算。 ; >合計2ポイント加算されたらMAXと判定し、Var(31)を9にする。 ; >またそれ以外にもゲージが100本以上の場合ほぼ同義としてVar(31)を9に。 ; >その他、ラウンドの試合開始前からゲージ量が最大の場合はフラグ1回分とする。 ; ゲージの量が少なくなってきた場合、MAXでないと判断してフラグを1回分に戻す。 ;  これで、「Var(31)=9は、ゲージマックス状態」と判断できます。  下側の記述でゲージが減っている場合も感知できます。  ただこの記述は若干甘くて、減っているのに気づかないこともありえますが。  ■素案メモ:相手のステートを記録し異常を感知するとか。  アーマー状態などの感知に?  Root,MoveGuarded && ((Enemy,StateNo!=[150,153])||(Enemy(NumEnemy>1),StateNo!=[150,153])) ;MoveGuarded&&相手がガード状態でない  Root,MoveHit && ((Enemy,StateNo!=[5000,5099])||(Enemy(NumEnemy>1),StateNo!=[5000,5099])) ;MoveHit&&相手がくらい状態でない  うーん。。。