挨拶
メリークリスマス! Adevent Calendarの最終日です。ポケモントレーナーの石田です。
僕はクリスチャンではありませんが、アドベントカレンダーのラストを飾ることになってしまいました。そんな僕ですが、クリスマス・イヴのイヴはeveningで、アダムとイヴのイヴとは関係ありませんので恋人の日的な扱いではなくて家族と静かに過ごすべきだし、25日はキリストが生まれた日ではないし、サンタクロースが赤い服を著ているのはコカ・コーラのせいではなくて赤い服を着ているサンタクロースはコカ・コーラ以前から記録にあるよねみたいなことは知っているので、ちゃんとクリスマスに歩みよって厳かに締め括りたいなと思います。ORASではメガタブンネのパーティをまだ作れていませんが、今日の夜はしっかりデリバードを厳選したいなと思います。ダブル想定で、|タスキ|おくびょう|CS中心の調整振り|ねこだま|ここかぜ|みちづれ|まもる、みたいな構成にして、ランダム対戦で、クリスマスの夜にデリバードから悪夢をお届け出来るところまでイケたら良いなと。
ロードマップ
- 前提条件となるポケモンのステータスの仕様を説明する
- 仕様に合わせてベースとなるクラス構造を考える
- メジャーどころを抜くためのS振り調整計算機を作る<= イマココ
- 不利な相手を想定した耐久調整用計算機を作る
前回はオブジェクト指向とクラス設計について説明しました。今回は実際に、それらを用いてS振りについて考えていきたいと思います。
実行環境について
ソースはGitHubにあります。リポジトリ内にVagrantで実行可能なように諸々入っています。./vm ディレクトリにVagrantFileと、./playbookにAnsibleのplaybookがあり、Vagrantで立ち上げてAnsibleを実行すれば環境が作れるようになっています。このplaybookはansible-galaxyのgeerlingguy.phpとgeerlingguy.php-xdebugに依存しています。適宜インストールして実行してください。Ansibleの実行時引数にはdevelop=trueが必要です。環境が整ったら、自身のhostsに 192.168.33.12 を pokemon.local を追加すれば実行出来ると思います。また、このプログラムはThe Pokémon RESTful APIを利用しています。
Macならば、必要そうなコマンドは以下です。内容を理解した上で自己責任でお試しください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
## 適当なディレクトリへ移動しておいてgit cloneです git clone https://github.com/t-ishida/pokemon-calculator.git ## Vagrantが入ってなければ brew install vagrant
## Ansibleが入ってなければ brew install ansible ## これらのAnsible Galaxyが入てなければ ansible-galaxy install geerlingguy.php ansible-galaxy install geerlingguy.php-xdebug ## VagrantのイメージでCentOS65が入ってなければ ## もし、上手くいかなければ add する名前とか変えた方が良いかも知れません vagrant box add centos65 https://github.com/2creatives/vagrant-centos/releases/download/v6.5.1/centos65-x86_64-20131205.box ## vmディレクトリに移動したあと、vmを起動します ## cd vm ## 立ち上げる前に他にvmが立ち上がってないか、 ## VMのIPアドレスがかぶってないか等確認してください。 vagrant up ## 再度、作業ディレクトリの直下に移動したあと、ansibleを実行です ## cd ../ ## 実行時に known_hostsのfingerprintがーみたいなエラーになったら、 ## その辺は「vagrant known_hosts」みたいなのでググって理解して修正してください ## ## Vagrantを初めて入れた人や、普段vagrant sshしか使わない人などは、 ## IPアドレスでssh接続するために .ssh/config へ 設定が必要かも知れません。 ## セキュリティ的にはアレですが、192.168.33.* はvagrantでしか使わないようであれば、 ## 以下のように設定してしまうのもアリです。自己責任で。 ## ## Host 192.168.33.* ## User vagrant ## IdentityFile ~/.vagrant.d/insecure_private_key ## StrictHostKeyChecking no ## UserKnownHostsFile=/dev/null ## ansible-playbook -i vm/hosts playbook/all.yml --extra-vars="develop=true" |
S振りって?
役割論理で考えればS振りはありえませんが、今回は一般的な育成論を題材としてオンバーンを例に上げてS振りについて説明します。さて、オンバーンの種族値を見てみましょう。
H | 85 |
---|---|
A | 70 |
B | 80 |
C | 97 |
D | 80 |
S | 123 |
「ちょっと良い位の耐久を持った高速アタッカー」という印象を受けたんじゃないかと思います。さて、こいつをどう使ったら良いだろうか?ということで、誰もがイメージするであろうところの「このSとドラゴンであることを生かして、先発で出して最速でりゅうせいぐんを撃てるドラゴンキラーとして運用しよう」ということにします。先にも書きましたが、ここで「りゅうせいぐんwwww最速wwwww」と脊髄反射でCSぶっぱして許されるのは中学生までです。節度の有る紳士的なポケモントレーナーとしては「誰を抜いて、誰を落とし、何を耐えるか?」について無駄の無い振り方を考えるべきです。この「誰を抜いて?」を考えるのがS振りです。ここではSから調整を始めていますが「ドラゴン最速でりゅうせいぐんを撃ちたいオンバーンなので、Sから考え始める」のであって、常にSから考えれば良いというものではありません。「耐久を生かして耐えてから返り撃ちにしたい」のであれば「耐久と火力から調整を始めて、余りでSも調整する」という流れもあり得ます。
S振りについて考える
実際に、Sから考え始めるにあたって誰を抜くのか?ということになりますが種族値だけで言えばオンバーンは既にドラゴン最速です(メガ進化を除く)。しかし、それは種族値の話で、実数値には性格補正や努力値、アイテムが絡んできます。とりあえず、オンバーンの無振りの実数値を見てみましょう。http://pokemon.local/sample/Sample1を表示してみてください。これは以下のプログラムで表示してます。今回は簡略化のためにアイテムについては考慮外とします。アイテムを考慮する場合も、そんなに難しくはないはずなので、どうしたら良いのか考えてみてください。
actionの内容
1 2 3 4 5 6 7 8 9 10 11 12 |
public function run() { // 715はオンバーンです。 // APIからid:715のデータを取りだします。 // repositoryは IDを渡すとAPIから種族値をfillしたPokemonのインスタンスを返却するファクトリです $pokemon = $this->repository->build(715); // 50LVに固定 $pokemon->setLevel(50); // おくびょう $pokemon->setTemperament(Temperament::valueOf('COWARDLY')); return new Sample1View($pokemon); } |
コメント「おくびょう」と書いてある部分ですが、これは性格をセットしています。性格のクラスはどうなっているか?というとclasses/temperament/Temperament.php に有ります。これは、Enumという列挙型を模したクラスです。性格ごとの補正掛率が定義されています。中身について、くどくど説明しようかと思ったのですが、何も難しくないので実際に実装を見てもらった方が良いかなと思います。また、$this->repositoryについては、コメントで書いてある以上のことをしておらず、本題とはあまり関係ないので説明しません。各自で見ておいてください。
Viewの内容
HTML部分は省略してますが、こんな雰囲気です。
1 2 3 4 5 6 |
<?php $this->assign($response->getPokemon()->getHpValue())?> <?php $this->assign($response->getPokemon()->getAttackValue())?> <?php $this->assign($response->getPokemon()->getDefenceValue())?> <?php $this->assign($response->getPokemon()->getSpecialAttackValue())?> <?php $this->assign($response->getPokemon()->getSpecialDefenceValue())?> <?php $this->assign($response->getPokemon()->getSpeedValue())?> |
さて本題はviewに現れるgetHpValueとgetAttackValueの辺りです。これは実数値を取り出すメソッドになっており、Pokemonクラスに定義されています。以下がその内容です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public function getHpValue () { return $this->hp->calcParameter($this->level,0); } public function getAttackValue () { return $this->attack->calcParameter( // レベルです $this->level, // 性格による掛率ですね $this->temperament->getAttackReviseBudget() ); } |
それぞれHPとその他のパラメータは、それぞれParameterとParameterを継承したHPクラスのオブジェクトです。calcParameterは、それぞれ以下のように定義されています。第一回で説明した式をそのままコードにしたものですね。実際の式と見比べてみると良いでしょう。
Parameter
1 2 3 4 5 6 7 8 |
// familyPoint = 種族値 // giftPoint = 固体値(デフォルトで31を代入しています(0-31までの32段階)。6V前提で計算したいため) // basePiont = 基礎ポイント(努力値) public function calcParameter ($level, $revise) { return intval(intval(((intval( $this->familyPoint * 2 + $this->giftPoint + $this->basePoint / 4 ) * $level / 100) + 5) * $revise)); } |
HP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// familyPoint = 種族値 // giftPoint = 固体値(デフォルトで31を代入しています(0-31までの32段階)。6V前提で計算したいため) // basePiont = 基礎ポイント(努力値) // @override // 親を呼ばない実装になっています。 // これは基本的には、あまり良くないオーバーライドです。 // これが現れた場合には"抽象クラス"の取扱いが正解となる場合があります。 // 抽象クラス化するのであれば、どのような実装になるのかを考えてみましょう public function calcParameter($level, $revise) { return intval(intval( $this->familyPoint * 2 + $this->giftPoint + $this->basePoint / 4 ) * $level / 100) + 10 + $level; } |
さて、実際にどのように計算しているのかが掴めたのではないかと思います。では、実際に無振りの実数値であるところの、157ってどれくらいなの?というのを見てみましょう。http://pokemon.local/sample/TargetListにアクセスしてみてください。この一覧はclasses/speed/TargetLoader.phpの中にあります。内容は高速アタッカーで有名どころの実数値をかき集めたマスタです。さて、オンバーンは性格補正有りの無振りで、S補正有り最速90族まで抜けることを確認出来たのではないかと思います。ターゲットはオンバーンを除けば最速であるところのラティオスの最速なので、最速110族抜きの調整をすれば目的は達成です。ORAS環境におけるメガラティオスになってもSは上がらないので考慮しなくても良いです。例外として、メガジュカインはオンバーンより速いのですが、こいつについてはメガ進化してくれなければ、りゅうせいぐんは刺さらないし、考慮すると複雑になり過ぎるため、ここでは見なかったことにしましょう。http://pokemon.local/CalcSpeedを見てください。「実数値178の最速110族を抜くには156振れば良い」という計算結果が出ています。では、どうしているのかを見ていいきましょう。
responses/templates/CalcSpeedView.phpを見てみると、以下のような記述があります。
1 2 3 4 5 6 |
<?php // 同値では抜けない ?> <?php if ($response->getPokemon()->getMaxSpeedValue() <= $row->getSpeed()):?> - <?php else:?> <?php $this->assign($response->getPokemon()->calcTargetSpeed($row->getSpeed()))?> <?php endif;?> |
では、呼び出しているcalcTargetSpeedはというと、Pokemonクラスの中の以下のメソッドですね。
1 2 3 4 5 6 7 8 |
public function calcTargetSpeed ($target) { return $this->speed->calcBasePoint( $target, $this->level, $this->temperament->getSpeedReviseBudget() ); } |
では、そのcalcBasePointは、というと以下のようになっています。$targetを抜くために、どれだけの基礎ポイントが必要かを計算しています。メンバ変数を計算に利用しているため、$backUpとか現れて妙なコードになってしまっていますが、そこは御愛嬌ということで。データを保持するクラスと計算するクラスを分ければ問題は解決するのですが、それだと「データとロジックをセットにしてクラスにする」という基本から外れてしまうので、ここではこのままにしておきます。機会があれば「リファクタリングする」というネタも取り扱いたいですね。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public function calcBasePoint ($target, $level, $revise) { $backUp = $this->basePoint; $result = -1; for ($i = 0; $i <= 252; $i++) { $this->basePoint = $i; if ($this->calcParameter($level, $revise) > $target) { $result = $i; break; } } $this->basePoint = $backUp; return $result; } |
さて、ここまででオンバーンのSに振るべき努力値の算出が出来ました。しかし、このままでは火力が足りません。相手より速く動けても相手を一撃で落とせなければ返り討ちにされてしまうかも知れません。次回は、どれだけCに振れば良いのか?を算出するためのプログラムを作っていきたいと思います。
まとめ
前回までのクラス構造の考え方に続いて、今回は実際にプログラムを起こしてみました。classesの中身をソリッドに保ったままViewをレンダリングさせるために即席で作った風のMVCな構造が出来ており若干戸惑ったかも知れません。しかし、中身を見てみればそんなに難しくはないと思います。CalcSpeedでは、おくびょうなオンバーン固定になっていますが、性格やIDをパラメータで受け取ってコントロールすれば他のポケモンで他の性格でも計算出来ることは想像に難くないと思います。またアイテムについては算出済みの実数値に掛率がかかるだけなのでマスタの定義さえしてしまえば、これの追加も難しくないでしょう。良かったら、forkして遊んで見てください。ただ、記事の内容と合わなくなるのでPull Requestは送られてもマージできませんので悪しからず。元のリポジトリへのマージは出来ないのは申し訳ないのですが、そのまま自身のリポジトリを進めて機能追加をしたり、デザインを当て込んだりは勿論、それを「一般公開したい」というのもOKです。このソースはMITライセンスです。さて、次回は耐久調整の予定ですが、その前に耐久調整に使える努力値の量を決めるためにCを先に振らないとダメですね。次で終われるのかな……
- 前提条件となるポケモンのステータスの仕様を説明する
- 仕様に合わせてベースとなるクラス構造を考える
- メジャーどころを抜くためのS振り調整計算機を作る
- 不利な相手を想定した耐久調整用計算機を作る <= ココ
アライドアーキテクツではポケモントレーナーと連載記事をシレっと1年近く放置しておいて、いきなり再開してもへっちゃらな心の強さを持ったエンジニアを募集しております
なるべく他のメンバーは書かないであろう流行りとは程遠いところや、 枯れたキーワードについて書いていこうと思います。 Perl / Java / PHP 及び周辺技術に興味があります。