テストコードが無くてPHP7へのバージョンアップが出来ない?ボットで解決しました!

Yuki IwakamiPosted by

今回の記事は、昨年、弊社で関わっているすべてのゲームタイトルのPHPのバージョンをPHP5.6系からPHP7.2系に上げたことについて、PHPカンファレンス仙台2019でも発表した「テストコードが無くてPHP7へのバージョンアップが出来ない?ボットで解決しました!」について深掘りしたものになります。

https://speakerdeck.com/sgeengineer/tesutokodogawu-kutephp7hefalsebaziyonatupugachu-lai-nai-botutodejie-jue-simasita
こちらがぺちコンで登壇したときの資料になります。

ぺちコン仙台(PHPカンファレンス仙台のこと)ではPHP7化の背景やスケジュール、その効果、またボットを作る背景、内容、効果などを発表で紹介しました。

今回の記事では、ボットについてより詳しく紹介したいと思います。

発表した内容について

簡単に発表した内容を書いてみます。

  • PHP5.6.31からPHP7.2.5にバージョンアップした
  • チームのリリース希望は2ヶ月以内
  • テストコードは存在しなかった
  • デバッガーさんの工数があまり確保できないためボットによるデバッグを行った
  • ボットのおかげでバグがたくさん取れた
  • 約1ヶ月の対応期間後リリースした
  • ユーザの皆様に影響のあるバグが出なかった
  • Webサーバの負荷が下がりコストが66%も削減できた

ゲームの紹介

PHP7.2にバージョンアップしたのはグリフォンで関わっているゲームタイトル全てに対して行いましたが、今回のぺちコンでは、グリフォンで開発・運用を行っている「不良遊戯シャッフル・ザ・カード」(以下、不良遊戯)を例に挙げ紹介をしました。

ボットの言語仕様とか拡張機能の紹介

今回作成したボットについて紹介したいと思います。

  • Google Chrome Extensionsの利用
  • JavaScriptによるDOMやURLの取得・操作

不良遊戯も含め、バージョンアップを行ったゲームはすべてパソコン、スマートフォン、フィーチャーフォンで遊べるブラウザゲームでした。そのため、今回使用したものは、Google Chrome Extensionsを使用し、Google Chromeブラウザ上で動くボットの作成を行いました。Google Chrome Extensionsで作ったものは一般的にはChrome拡張機能と呼ばれると思います。

また、使用している言語はJavaScriptで、ブラウザ上で動くJavaScriptのように扱うことができるため、DOMの取得、操作はもちろん、URLの取得などブラウザ上でJavaScriptが扱えることはだいたいできるかと思います。

例えば、トップページにおいてマイページにページ遷移する場合

if (url = '/top') {
  $('a.mypage').click();
  return;
}

のようにかけると思います。(例題のため、簡易的な書き方にしていますので、実際に動くコードとは異なります。)

シナリオの作成ツール

ボットのシナリオを作成するときは、状態遷移図を作成するような形でシナリオを組み立てられるようにしようと考えました。

  • 画像のようにGUI上で状態遷移図を作成できるツールを用意します(ボット作成ツール)
  • JSON形式で出力するようにしてシナリオを保存することができるようにする。もちろんそのデータをインポートすることもできる
  • そのJSONデータを用いてボットが動く

このようなフローでシナリオを作成できるように考えました。

今回のぺちコンでは、このボット作成ツールの実装まではいきませんでしたが、頭の中で状態遷移図をイメージしながら、JSONデータを手動で作成してボットのシナリオを作成しました。

ボットのシナリオの種類

ボットがどのような行動するかを説明したいと思います。

不良遊戯ではメインの遊び方がGvGであります。GvGとは、Guild Vs Guildの略で最大26人のチーム同士が毎日同じ時間に30分間戦い合うというゲームです。戦いでは、たくさんあるスキル(技)を出しながら戦います。そのため、例えばこのスキルが発動中にこのスキルを発動などのようにたくさんのスキルを組み合わせたものがものすごいたくさんあり、ゲームとしては複雑ではあるのですが、エンジニアにとってもロジックが複雑になり、またデバッガーさんにとってもその組み合わせすべてをデバッグするのは時間的にも不可能に近くなっています。

今回のデバッグのために作成したシナリオは以下の2つになります。

  1. インゲームをいろいろ回ってくれるボット
  2. アウトゲームをいろいろ回ってくれるボット

これらについて説明します。

1.インゲームをいろいろ回ってくれるボット

1つ目の「インゲームをいろいろ回ってくれるボット」は、上でも説明した、たくさんの複雑なスキルの大量の組み合わせをデバッグするためのものになります。

たくさんのスキルを打つ

たくさんのスキルを全組み合わせの挙動を確認するためのアルゴリズムとしては、一つ一つ組み合わせをやってみるというのも有効かとは思うのですが、作成のコストもかかるとは思いますが、実際に動かしたときに、組み合わせの種類が想像以上にあると考えられる。

例えば、前衛/後衛というステータスがあったり、特殊な効果をユーザに与えることができその状態でのスキル効果の確認があったりと自分の方でも把握しきれない程の組み合わせがある。そこで今回選択した手法として、とにかくランダムに動くをずっと行う。この手法で時間をかけてデバッグすることで、自分でも把握しきれない組み合わせもこなしてくれるはずです。

今回はそのようなボットを作成して、リリース直前まで24時間ずっと動かし続けました。

master/slaveを分ける

また、複数人の対戦をするために、ボットも複数用意する必要があります。ただそのときに「デバッグ機能を利用してゲームを始める」という動作をすべてのボットがする必要がありません。どれか1つのボットだけがゲームを始めるデバッグ機能を扱うだけでいいのです。今回はそのデバッグ機能を扱うボットをmasterと呼び、それ以外はslaveと呼ぶことにします。

masterがゲームを始めるまではslaveは待機する、といったシナリオも書く必要があります。

2.アウトゲームをいろいろ回ってくれるボット

2つ目の「アウトゲームをいろいろ回ってくれるボット」は、インゲームのGvG以外の機能を網羅的に遊んでくれるようなボットです。

例えば、URLを取得して、そのURLがマイページだった場合は、以下のようなシナリオが書けるかと思います。

  • レイドボスが出現していたら攻撃しに行く
  • クエストに行く体力があればクエストにいく
  • GvGが始まっていればGvGに参戦する

このようにマイページ以外でもそれぞれのページごとに同じようなシナリオを作成することで、連続して遷移するようなボットが完成します。このようにしてアウトゲームのシナリオを網羅的に作成していきます。

実装について

ここまでの文章を呼んでいただければきっとざっくりとした実装イメージはついたかなと思います。実際にどのようにして実装していくかを簡単に説明していきたいと思います。

jQueryの導入

DOMを取得/操作するのにはjQueryは便利なものです。そのjQueryを導入する場合は以下のことをするだけで完了するかと思います。

  • jQueryのファイルを配置する
  • manifest.jsonにjQueryを追加する

jQueryのファイルを配置する

jQueryのソースファイルをダウンロードしてきます。そのファイルを例えば、jsディレクトリ下に配置します。

.
├── js
│   └── jquery.js
└── manifest.json

manifest.jsonにjQueryを追加する

manifest.jsonのcontent_scriptsの中にjquery.jsを追加してあげます。

{
  ・・・
  "content_scripts": [
    {
      "js": [
        "js/jquery.js",
        ・・・
      ]
    }
  ],
  ・・・
}

DOMの取得

jQueryの導入ができたらDOMの取得などの操作は簡単かと思います。

例えば、以下のようにすることでマイページへのリンクを取得することができると思います。

var mypage_button = $('a.mypage')[0]

例えば、以下のようにすることで、spanタグに囲まれた体力などのステータスを取得することも可能かと思います。

var hp = $('span.hp').eq(0).text()

リンクのクリック

DOMやページの状態を取得することができたら、あとはその状態に合わせてページを遷移するだけです。そのときに一番やりやすいのだと、click()があるかなと思います。

例えば、上の例で取得したマイページへのリンクをクリックする場合は以下のような書き方でページ遷移ができると思います。

mypage_button.click()

おまけ機能

ボットのシナリオを動かす以外にも便利な機能をつけてみました。

  • データの保存機能
  • スクショ機能
  • チャットの投稿機能

データの保存機能

chrome.storageを使ってデータを保存しておきます。

インゲームを動いてくれるボットで例えると・・・

  • masterだったらデバッグ機能を利用してゲームを始める
  • slaveだったらゲームが始まるまで待機する

のように、一部の動作だけmasterとslaveで分ける必要があるときに、is_masterのようにパラメータを持ち、「masterとしてボットを動かすか」という項目を設定することで処理が可能となります。

アウトゲームを動いてくれるボットで例えると・・・

  • 今はクエストにはいかないようにする
  • GvGでは特殊な攻撃のみにする

このような項目の設定をボットを動かすときに指定することができます。

スクショ機能

エラー発生時にスクショを取る機能になります。

chrome.tabs.captureVisibleTabを利用して現在画面に表示されている画面をキャプチャを行います。

また、1画面に収まらないくらいに縦長のページに関しては、

  1. スクショを取る
  2. 1画面分下にスクロールする
  3. 1に戻る

のようにして、複数回に分けて撮影し、それらの画像データを結合させて一枚の画像にします。そのようにして1枚の縦長の画像を作成することができます。

コマンドの紹介

今回作成したボットのシナリオ上で設定するコマンドをいくつか紹介したいと思います。

[next]コマンド

次の状態に遷移することができます。

[name]コマンドと[jump]コマンド

nameコマンドで設定した名前をjumpコマンドで指定することで、そこの状態にジャンプすることができる。状態遷移図上だと矢印だけで書くことができるとは思いますが、分岐する処理では必要になるコマンドになります。

[if]コマンドと[else]コマンド

プログラミングのifと同じように扱えるコマンドです。条件分岐するときに使います。

[badge]コマンド

Chrome Extensionsのbadge(アイコンの上に出る文字のこと)に指定した文字を表示させることができる。

[back]コマンド

ブラウザバックするコマンドです。

[reload]コマンド

ページをリロードするコマンドです。

[script]コマンド

javascriptをそのまま実行します。万能ですが使いすぎに注意です。

[alert]コマンド

javascriptのalertに指定した文字を表示させます。デバッグ時など、処理を止めたいときに使用することが多いです。

[capture]コマンド

スクショを撮ることができます。

コマンドを利用した例

上で紹介した例を使って、アウトゲームで動くボットのマイページでの処理の一部が以下のようになります。

{
  {
    name: 'mypage',
    next: true,
  },
  {
    badge: 'gvg',
    if: options.is_arena + " && " + button.mypage_to_arena_active, scripts: [
      button.mypage_to_arena_active + click,
    ],
    jump: 'end',
    else: {
      next: true,
    },
  },
  {
    badge: 'クエスド',
    if:  options.is_use_ap_item + " || " + value.mypage_ap + ">=30",
    scripts:[
      button.mypage_to_quest + click,
    ],
    jump: 'end',
    else: {
      next: true,
    },
  },
}

buttonはDOMの指定した要素を取得できるコマンドで主にはボタンなどのページ遷移するDOMを定義したものを格納しています。例えば、button.mypage_to_arena_activeと指定することで、参戦可能状態のGvG参戦ボタンのDOMを取得することができます。

valueはDOMから値や文字などを取得できるコマンドを定義したもので、例えばvalue.mypage_apはマイページでのAP(体力的なもの)の値を取得するコマンドになります。

optionsは保存されているデータを使用するコマンドで、例えばoptions.is_arenaはGvGに参戦するかを真理値で設定できる項目を作成してボットを動かすときに指定します。

したがって、上の例だと

  • URLを見てマイページだったときにjump:”mypage”のような書き方をすることでここの処理に入ってくる
  • GvG参戦する設定にしていて、参戦可能ボタンが存在する、すなわちGvGに参戦できる状態であればGvGの参戦ボタンをクリックする
  • GvGに参戦しない場合は次の処理
  • 回復アイテムを使用する設定(options.is_use_ap_item)のとき、またはAPが30以上ある場合、マイページからクエストに行くボタン(button.mypage_to_quest)をクリックする
  • クエストに行かない場合は次の処理

のような動きをすることができます。

最後に

今回、ボットを使ったデバッグをしたことで以下のような良かった点があげられました。

  • ボットによるデバッグでGvG部分のバグがたくさんとることができた
  • ボットがバグをとってくれたおかげで、デバッガーさんによるデバッグ時にバグ報告がほぼなかった
  • リリース後、ユーザの皆様に影響のあるバグがなかった
  • ボットなら24時間365日休まずデバッグしてくれる
  • 効果がとてもよかったため、別のゲームでのPHPバージョンアップ時はデバッグ用PCのスペックを大きくアップしてもらえた
  • 今回作成したボットは、他のブラウザゲームやブラウザ上で動くWebサービスなどの場合にも活用できると思う

また、発表スライドもアップする予定なので、そちらを参考にしながらこちらの記事を見ると理解が深まるかと思います。