CPUスクリプト覚書~最低限動作CPU作成~
【実践~最低限動作のCPUを作る~】
前回のAIスクリプトについて★マークの部分だけ編集すれば
CPUは問題なく動作すると書きました。
ってわけで、
まずはその最低限動作するCPUを作ってみます。
関数の説明とかは公式リファレンスを見よ。
ちなみに、
「スキン設定用iniファイル」と「CPUスクリプト」は出来ているものとします。
もちろんskin直下にディレクトリも作ってある感じで。
ここでのメインは「AIスクリプト」の編集です。
~編集その前に~
luaの記述についてある程度書いておいたほうがいいかもしれない。
AI記述する上で最低限使うやつだけ紹介していきます。
<記述基礎>
・命令文の最後に「;」を記述する。メンドイけど忘れずに。
・文字列はダブルクォートで囲む感じなのか?
・文字列結合は「..」で行なう感じなのか?
例)
out("はろわ" .. "(ハローワーク)") ;
→「はるわ(ハローワーク)」って出力される。
<変数定義>
・型はあまり意識する必要なし。ABCD独自のクラスは意識しまくれ。
・「nil」は空と言う意味。NULLとかと同じだとえばいい気がする。
・関数内で完結する変数は「local」って指定する。
・なんかインクリメントできないみたい。めんでぇ
例)
local testvalue = nil ;
→関数内で完結する変数「testvalue」を定義し、値を「nil(空)」にする。
<テーブル型>
・配列とかそこらへん全部テーブル型で処理される
・{}で囲んで複数値を指定する
・INDEXは「1」からみたい
・参照は[]でINDEX指定する感じ
・INDEXは文字でも出来るみたい。数字のヤツとは別物みたい。
・#をつけて参照すると要素数が参照できるぽい
例)
local testtable = {123, 567, nil, "TESTEST"} ;
testtable["abc"] = 9999 ;
↓
testtable[2] → 「567」
#testtable → 「4」
testtable["abc"] → 「9999」
<メンバ変数・メンバ関数>
・メンバ変数は{クラス}.{メンバ変数名}で参照するみたい
・メンバ関数は{クラス}:{メンバ関数}で使用するみたい
・クラスは当然インスタンス化しておく必要がある感じ
例)
local cpu = Player(ID.CPU) ; -- Playerクラスインスタンス
cpu.PID → メンバ変数「PID」参照
cpu:GetHP() → メンバ関数「GetHP()」使用
<条件文>
・記述は、if (条件) then ~ elseif (条件) then ~ else ~ end ;
・比較演算子の等号は「==」、不等号は「~=」なところが注意
<繰り返し文>
・記述は、for 変数=開始値 , 終了値 do ~ end ;
・あんまり注意点はないわ
とりあえずここら辺だけ覚えておけばおk。
もっと細かい説明はLuaリファレンスを見てください。
----------------------------------------------------------
では、ABCD_AIの記述に参ります。
①AI_INFOの編集
まずはAI_INFOを編集します。
っていってもデッキの名前と説明文書けばいいだけです。適当で良いです。
余裕がある人は笑いを取ることを目指そう。
これは関数じゃないのでreturnとかもしないで良いです
②CreateDeckの編集
関数です。returnはデッキ内容そのものですので、
return後の値指定で直接記述しちゃいます。
記述方法はCardData関数を使用します。
「*」記号でデッキ投入枚数も指定できるのでラクチンです。
↓↓こんな感じで↓↓
return {
-- デッキ内のカードリスト設定
CardData("BT_001J") * 20 , -- 炎のクリスタル
CardData("BT_002J") * 10 , -- 水のクリスタル
CardData("BT_003J") * 10 , -- 風のクリスタル
} ;
※40~100枚になるよう注意しませう
③InitCPUの編集
ここではCPUの基本動作を指定します。
きちんとAIを組みたかったらここでの動作指定は一切しません。
でも今回は簡単CPU作成!なのでここでの指定に頼り切ります。
で、動作指定ですが「Regist系」の関数を用いて動作を定義します。
Regist系は基本下記の3種類。
デッキに投入したカードにあわせて記載していきます。
・RegistCardUse(Card card , int type , int value , table arg)
→カード詠唱の設定です。対象を指定するカードとかは使用できないぽい
・RegistAttack(Card card , int type , int value , table arg)
→クリーチャー攻撃設定です。カードごとに個別に攻撃条件を指定できる
・RegistBlock(int type , table arg)
→クリーチャーブロック設定です。指定したタイプにのっとってブロックする
↓↓こんな感じで↓↓
-- 炎のクリスタル使用設定
RegistCardUse(CardData(EX.BT , 001) , USE_TYPE.BASIC_CRYSTAL , 10 , nil) ;
-- クリーチャーブロック設定(先手優先で)
RegistBlock(BLOCK_TYPE.FIRST_ATK , nil) ;
-- クリーチャーアタック設定
RegistAttack(CardData() , ATTACK_TYPE.AUTO , 30 , nil) ;
④GameOverの編集
これは空スクリプトでも結構書いてある場所です。
メッセージ部分だけ書き換えてくれればおk。
⑤ExecCPUの編集
メインです。戻り値にTrueかFalse返さなきゃいけないのに注意。
(なんか動作起こしたらTrue返すんだよ!)
しかし、今回の簡単CPUではあんまり使いません。
今回ここで定義するのは次の動作のみです。
・対象を指定するソーサリーの詠唱
・スキルの使用
・ドローストックの使用
このうち、ドローストックについてはもう記述してあります。
あとはソーサリーとスキルです。
スキルについてはタイプによって動作を分けたりする必要があってメンドイので
ここでは指定しない。簡単CPUなんだからスキルなんて高等なことしないの!!
つーわけで、
ソーサリーの詠唱ロジックだけ紹介します。
結構複雑っていうか面倒な手順踏んでる。別関数化したほうがいいかもネン
↓↓こんな感じ↓↓
-- 手札に存在するか検索
local cpu = Player(ID.CPU) ;
local hands = {} ; -- 手札定義
local num = cpu:GetHand(hands) ; -- 手札取得
local tgtcard = CardData("BT_012J") ; -- 検索するカード(せっかくだから火炎弾で)
for i=1 , #hands do -- 手札分ループ
if(hands[i].Ex==tgtcard.Ex) and (hands[i].CID==tgtcard.CID) then -- EXとCIDで検索HITチェック
if(hands[i]:CanCast(ID.CPU)) then -- 詠唱可能かチェック
local tgtclist = hands[i]:GetTargetable(ID.CPU) ; -- 対象リストを取得
if (tgtclist~=nil) then -- 対象リストがnil(空)でない場合
local tgtcid = math.random(#tgtclist) ; -- 対象リストからランダムで1つ選ぶ
if (hands[i]:Cast(ID.CPU,{tgtclist[tgtcid]})) then -- 詠唱動作指定
result = true ; -- ExecCPU関数の戻り値指定
end ;
break ; -- ループ脱出
end ;
end ;
end ;
end ;
⑥CheckReDrawの編集
Trueを返したらリドローする。
リドローの指針を条件指定しておいて、リドロー指定する。
特に無視して常にfalseを返すのでもいい。リドローしないだけだから。
一応クリスタル0個だったらリドローみたいなロジックだけ書いておく
といっても上のソーサリー詠唱とあんま変わらんかったわ。
↓↓こんな感じ↓↓
-- ReDraw条件
local cpu = Player(ID.CPU) ;
local hands = {} ; -- 手札定義
local num = cpu:GetHand(hands) ; -- 手札取得
local tgtcard = CardData("BT_001J") ; -- 検索するカード(炎のクリスタル)
local tgtcnt = 0 ; -- 検索HIT数
for i=1 , #hands do -- 手札分ループ
if(hands[i].Ex==tgtcard.Ex) and (hands[i].CID==tgtcard.CID) then -- EXとCIDで検索HITチェック
tgtcnt = tgtcnt + 1 ; -- カウントアップ
end ;
end ;
if (tgtcnt==0) then -- 0個だったらリドロー
ret = true ;
end ;
⑦SelectBlockの編集
ブロッククリーチャーの選択です。
ブロッククリーチャーはdestBlockに格納し、
戻り値はカウンタークリーチャーになります。
プレイヤーがブロック側の時に編集しないよう注意!
とはいえ、RegistBlockを指定していればほとんど書くことありません。
なので、カウンターしたい時だけ編集します。
適当なロジック例を出しておきます。
↓↓こんな感じ↓↓
local counter = nil ;
local cpu = Player(ID.CPU) ;
if(attack:GetController()==ID.CPU) then
-- CPU攻撃
-- 基本何もしない(勝手にプレイヤーのブロッククリーチャーを指定しない)
else
-- CPUブロック
--
local cres = {} -- クリーチャー
local num = cpu:GetCreature(cres) ; -- クリーチャー取得
for i=1 , #cres do -- クリーチャー分ループ
local c = Creature(cres[i]) ; -- クリーチャーインスタンス生成
if(c:CanAttack(true)) then -- カウンターモードで攻撃可能チェック
counter = cres[i] ; -- 攻撃可能ならカウンターに
break ; -- ループ脱出
end ;
end ;
end ;
return counter ;
⑧SelectBattleSpell編集
使用するバトルスペルを返します。
使わなければ何も返す必要なし。
まぁ一応ロジック例を出しておく。
↓↓こんな感じ↓↓
local ret = nil ;
local cpu = Player(ID.CPU) ;
-- 攻撃側によって使用バトルスペルを設定する
if(attack:GetController()==ID.CPU) then
-- CPUが攻撃
-- 攻撃時のやつまで書くのははメンドイからパス
else
-- CPUがブロック
--
local hands = {} ; -- 手札定義
local num = cpu:GetHand(hands) ; -- 手札取得
local tgtcard = CardData("BT_057J") ; -- 検索するカード(突然死で)
for i=1 , #hands do -- 手札分ループ
if(hands[i].Ex==tgtcard.Ex) and (hands[i].CID==tgtcard.CID) then -- EXとCIDで検索HITチェック
if(hands[i]:CanCast(ID.CPU)) then -- 詠唱可能かチェック
if(cpu:GetHP()<=10) then -- 自HP10以下の時のみ突然死を使う。
ret = handc ;
end ;
end ;
end ;
end ;
end ;
return ret ;
!!!!!!!ここまでで完成!!!!!!!
----------------------------------------------------------
じゃー早速動かしてみます。
「SkinSelector.exe」で作ったやつをぶっこんで、
ABCD本体起動して、「OfflineDuel」を選べば出てくるはず。
後は対戦するだけです。
エラーが出たら泣いてエラー箇所を探します。
エラー記述に行数が出てたりするのでそれを参考にします。
まぁ大体記述ミスとかそこらへんだと思うよ。
DP()関数でデバッグプリントを指定している場合、
本体の「config.ini」の「DebugConsole = 0」を1にすれば見れる。
んでもって「DebugOutLog = 0」を1にすればファイル出力もしてくれる。
ちょーうれしい。
あと、手軽に構文チェックを行ないたい場合、
空想具現化プログラミングさんのところからLua5.1.3(lua.org版)を落として
コマンドプロンプトで
.\lua5.1\bin\luac5.1.exe {luaソースファイル}
ってやれば構文チェックしてくれる。
ちょーべんり。
こんなとこですかね。
慣れてきたら独自の条件での動作を追加していってみてくださいな。
以上。
| 固定リンク


コメント