無印吉澤

ソフトウェア開発、運用管理(俗にいう DevOps)、クラウドコンピューティングなどについて、吉澤が調べたり試したことを書いていくブログです。

Blob をファイルとしてダウンロードさせるブックマークレットが「何もしてないのに壊れた」話

f:id:muziyoshiz:20180430135846p:plain

背景

私は、趣味で 艦これアーケードのプレイデータ管理ツール Admiral Stats というサービスを開発・運用しています。これは、

  • SEGA の艦これアーケード公式ページ から自分のプレイデータを JSON 形式でダウンロードするツール(通称エクスポータ)
  • プレイデータをアップロードしてもらい、それを可視化する Web アプリケーション

を組み合わせて実現しているサービスで、既に1年半以上運用しています。

このエクスポータは Ruby 版、PowerShell 版、ブックマークレット版を提供していて、一番使われているのはユーザにとって手軽なブックマークレット版です。去年9月の調査では、88%のユーザがブックマークレット版を使っていました。

ブックマークレット版のエクスポータにはさらに以下の2種類があります。いずれも、PCとスマホの両方で動く状態でした。

  • 自動アップロード用
    • エクスポートした JSON を、Admiral Stats に自動的にアップロードする
    • ダウンロード用よりもこちらの方が便利で、普通はこちらが使われている(はず)
  • ダウンロード用
    • エクスポートした JSON を、ローカルに .json ファイルとしてダウンロードする
    • Admiral Stats が終了した場合などのために、手元にファイルとして保存しておきたい人向け

今回はこのなかの、ブックマークレット版の「ダウンロード用」が何もしてないのに壊れたという話です。

何が起こったか

4/24 にユーザーの方から、「ダウンロード用のブックマークレットを実行しても、ファイルがダウンロードされない」という不具合報告を頂きました。詳しく情報を伺ってみると、

  • .json ファイルのダウンロードが始まる代わりに、本来ダウンロードしたい JSON そのものがページ内に表示されてしまう
  • ブラウザは Firefox 59.0.2
  • Admiral Stats 上のデータを見る限り、2018年3月29日まではダウンロードできていた

とのことで、僕の手元でも最新の Firefox で再現しました。また、Chrome でも同じく再現しました。

原因を調べたところ、a タグの download 属性が全く効いていないことがわかりました。

SEGA 公式サイトにログインしたあとに、このブックマークレットを実行すると、XMLHttpRequest を実行し、その結果を blob として取得します。そして、この blob の URL を参照する a タグを作り、自動的にクリックさせることでファイルのダウンロードを実現しています。コード中の以下の箇所です。

          var blob = new Blob([req.response]);
          if (window.navigator.msSaveBlob) {
            window.navigator.msSaveBlob(blob, fname);
          } else {
            var url = window.URL || window.webkitURL;
            var blobUrl = url.createObjectURL(blob);
            var a = document.createElement('a');
            document.body.appendChild(a);
            a.download = fileType + '_' + ymdhms + '.json';
            a.href = blobUrl;
            a.click();
            document.body.removeChild(a);
          }

https://github.com/muziyoshiz/admiral_stats_exporter_js/blob/v1.10.1/admiral_stats_exporter.js#L82

この download 属性が効いていないために、blob の表示ページに遷移してしまい、ブラウザの画面に JSON そのものが表示されてしまうようです。

しかし、このコードは元々普通に動いていました。それが突然なぜ……?

調査

最近のブラウザは自動アップグレードされてしまうので、なるべく古いバージョンのブラウザが残っていないかと手元の PC を漁って調べてみました。各バージョンでの動作結果はこちら。

ブラウザ リリース日 ブックマークレット(ダウンロード用)の動作
IE 11.371.16299.0 2013-11-08 成功
Microsoft Edge 41.16299.371.0 2017-11-05 成功
Firefox 47.0.1 2016-06-28 失敗
Firefox 59.0.2 2018-03-27 失敗
Chrome 65.0.3325.181 2018-03-06 失敗
Chrome 66.0.3359.117 2018-04-17 失敗
Safari 11.1 2018-02-22 失敗(ファイル名が unknown になる)

ダウンロード用エクスポータの公開日は 2016-10-19 でした。この時点では Chrome, Firefox, Edge, IE 11 で動いていて、特に動かないという報告もありませんでした。

ダウンロード用エクスポータに最後に機能追加したのは 2017-09-22 でした。普段の開発は Chrome で行っているので、この時点で、少なくとも Chrome では動いていた……はずです。

IE と Edge は window.navigator.msSaveBlob を使っているからか問題なし。Safari は当時からダウンロードが動かなかったので、状況変わってません。

Firefox は、かなり古いバージョンがインストールされたものが見つかったので動作確認したところ、エクスポータ公開日の 2016-10-19 より過去のバージョンなのに動きませんでした。ということは、SEGA 公式サイト側の動作が何か変わっている……?

Chrome は、2ヶ月以上古いバージョンが手元になかったので状況不明。しかし、Chrome 65 からクロスオリジンに対する a タグの download 要素はブロックされるようになったことがわかりました。

developers.google.com

Block cross-origin <a download>
To avoid what is essentially a user-mediated cross-origin information leakage, Blink will now ignore the presence of the download attribute on anchor elements with cross origin attributes. Note that this applies to HTMLAnchorElement.download as well as to the element itself.

今回のブックマークレットの場合、公式サイトと JSON の取得元は同一ドメインなので、このクロスオリジンに対する変更は関係ないのでは?と思ったのですが、以下のページによると手動設定された HTTP ヘッダーの種類によっては同一ドメインでもクロスオリジンの扱いになるようです。

developer.mozilla.org

ユーザーエージェントによって自動的に設定されたヘッダー (たとえば Connection、 User-Agent、 または Fetch 仕様書で "forbidden header name" として定義されている名前のヘッダー) を除いて、手動で設定できるヘッダーは、 Fetch 仕様書で "CORS-safelisted request-header" として定義されている以下のヘッダーだけです。
- Accept
- Accept-Language
- Content-Language
- Content-Type (但し、下記の要件を満たすもの)
- Last-Event-ID
- DPR
- Save-Data
- Viewport-Width
- Width

問題のブックマークレットは、JSON を取得する際に、CSRF チェックに引っかからないように X-Requested-With ヘッダを手動で設定しています。これが原因??

    req.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

https://github.com/muziyoshiz/admiral_stats_exporter_js/blob/v1.10.1/admiral_stats_exporter.js#L53

Chrome 64 以前で動くならコレが原因と断言できるのですが、手元の Chrome はすべて Chrome 65 以降にアップグレード済みだったので検証できず。

それとは別に、Google Chrome ver60からa要素のdownload属性が同一オリジンポリシーに厳密になった(Qiita) には、Chrome 60(2017-07-25 公開)からこの制限が入ったとの情報もあります。しかし、2017-09-22 に新機能の動作確認をしているので、そのときには動いていたはずなんですよね……。Deprecations and Removals in Chrome 60 にはそういう記載もなく、動作が変わった時期はよくわかりません。うーん。

対応

結局のところ、この問題が具体的にいつから発生したのかはわかりませんでした。しかし、モダンブラウザでは crossorigin 属性の付いたコンテンツの <a download> は許可しない方向に動いていることはわかりました。

そのため、この制限を何らかの方向で回避することは不可能と判断して、報告してくれたユーザーには代替案の利用をオススメしました。

こういうことが時々あるのが、Web アプリ開発の嫌なところですね……。

Admiral Stats のユーザ向けのまとめ

  • ブックマークレットでの SEGA 公式からの JSON ダウンロードは不可能になりつつある
  • ブラウザ側の機能制限によるものなので対策不可
  • 自動アップロード用のブックマークレットのほうが便利なので、そちらに乗り換えて欲しい(使い方
  • .json ファイルをダウンロードしたい場合は PowerShell 版などをどうぞ(使い方

Playbook 側から Ansible のバージョンを指定する方法(あるいは Ansible 2.x.0 を絶対使わせない方法)

f:id:muziyoshiz:20160331232512p:plain:w300

もうすぐ Ansible 2.5 がリリースされますね。僕もそろそろ Ansible 2.5 Porting Guide とか読み始めました。

ところで、僕は Ansible のバージョンが上がった最初のリリースでつまづくことが多くて、どうしてもしばらく様子見してしまいます。例えば、Ansible 2.4.0 では aws_s3 モジュールに不具合があって、既存の playbook が動かなくなったりしました(下記)。

github.com

しかし、いくら自分が注意していても、そういう経験の無い人が気軽に Ansible のバージョンを上げてしまい、あとから「動かなくなったんだけど」と言われることもあります。そんなわけで、playbook 側から実行環境の Ansible のバージョンを指定できないか?と考えてみたら、うまく動いたので紹介します。

対象読者

  • Ansible 2.x.0 を基本的に信用していない
  • 同じ Ansible playbook を操作・編集する人が自分以外にもいる
  • ある程度動作確認が終わってから、Ansible のバージョンを上げたい

実現方法

Ansible では、always という特別なタグを付けたタスクは「毎回必ず呼ばれるタスク」として扱われます。そこで、Ansible のバージョンをチェックするタスクを、この「毎回必ず呼ばれるタスク」として playbook に登録すれば、今回やりたいことを実現できます。

There is a special always tag that will always run a task, unless specifically skipped (--skip-tags always) Tags — Ansible Documentation

実行環境の Ansible のバージョン番号は、ansible_version という変数から参照できます。例えば、debug モジュールでこの変数を参照すると、以下のように出力されます。

- debug: msg="{{ ansible_version }}"
TASK [common : debug] ************************************************************************************************
ok: [localhost] => {
    "msg": {
        "full": "2.4.3.0",
        "major": 2,
        "minor": 4,
        "revision": 3,
        "string": "2.4.3.0"
    }
}

バージョン番号の4番目は個別に取得できませんが、そこまで確認する機会は考えにくいので大丈夫でしょう。

具体例

どの playbook からも常に呼ばれるロールを作ります。この例では "common" という名前にします。

まず、固定したいバージョンを表す変数 expected_ansible_version を作ります。今回は、Ansible 2.4.0 だけは使われたくないので、2.4.1 以降なら許すということにします。

roles/common/vars/main.yml

---
expected_ansible_version:
  major: 2
  minor: 4
  revision: 1

そして、この変数と ansible_version を比較するタスクを作ります。

roles/common/tasks/main.yml

---
- name: Ansible major & minor version check
  fail:
    msg:
      - Expected Ansible version is
        {{ expected_ansible_version.major }}.{{ expected_ansible_version.minor }},
        but actual version is {{ ansible_version.major }}.{{ ansible_version.minor }}
  when: not (expected_ansible_version.major == ansible_version.major
             and expected_ansible_version.minor == ansible_version.minor)
  run_once: True
  tags: always

- name: Ansible revision check
  fail:
    msg:
      - Expected Ansible version is
        {{ expected_ansible_version.major }}.{{ expected_ansible_version.minor }}.{{ expected_ansible_version.revision }}+,
        but actual version is {{ ansible_version.full }}
  when: not (expected_ansible_version.major == ansible_version.major
             and expected_ansible_version.minor == ansible_version.minor
             and expected_ansible_version.revision <= ansible_version.revision)
  run_once: True
  tags: always

この例では、revision(バージョン番号の3番目)が想定より大きい場合は許しています。また、エラーメッセージをわかりやすくするために、タスクを2個に分けていますが、Ansible revision check の方だけでも十分です。

あとは、その環境のすべての playbook で、ロールの先頭にこの common を追加すれば OK です。

playbook1.yml

---
- hosts: all
  roles:
    - common

こうすると、ansible-playbook コマンドでタグを指定してもしなくても、必ずバージョンチェックが実行されます。when でチェックを実行しているので、チェックに成功すると "skipping" と表示されます(この表示は若干わかりにくいので、他にいい方法があったら教えてください)。

$ ansible-playbook -i inventory -c local playbook1.yml

PLAY [all] ***********************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************
ok: [localhost]

TASK [common : Ansible major & minor version check] ******************************************************************
skipping: [localhost]

TASK [common : Ansible revision check] *******************************************************************************
skipping: [localhost]

TASK [common : Example task 1] ***************************************************************************************
ok: [localhost] => {
    "msg": "Example task 1 is executed."
}

TASK [common : Example task 2] ***************************************************************************************
ok: [localhost] => {
    "msg": "Example task 2 is executed."
}

PLAY RECAP ***********************************************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0

$ ansible-playbook -i inventory -c local playbook1.yml --tags=tag1

PLAY [all] ***********************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************
ok: [localhost]

TASK [common : Ansible major & minor version check] ******************************************************************
skipping: [localhost]

TASK [common : Ansible revision check] *******************************************************************************
skipping: [localhost]

TASK [common : Example task 1] ***************************************************************************************
ok: [localhost] => {
    "msg": "Example task 1 is executed."
}

PLAY RECAP ***********************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0

メジャーバージョンかマイナーバージョンが合わないと処理が止まります。例えば、Ansible 2.4.3 以上を指定したのに Ansible 2.5 で実行されると、以下のように表示されます。

$ ansible-playbook -i inventory -c local playbook1.yml --tags=tag1

PLAY [all] ***********************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************
ok: [localhost]

TASK [common : Ansible major & minor version check] ******************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": ["Expected Ansible version is 2.4, but actual version is 2.5"]}
    to retry, use: --limit @/Users/myoshiz/devel/ansible_version/playbook1.retry

PLAY RECAP ***********************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=1

同じく、リビジョンが小さすぎても止まります。例えば、Ansible 2.4.3 以上を指定したのに Ansible 2.4.0 で実行されると、以下のように表示されます。

$ ansible-playbook -i inventory -c local playbook1.yml --tags=tag1

PLAY [all] ***********************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************
ok: [localhost]

TASK [common : Ansible major & minor version check] ******************************************************************
skipping: [localhost]

TASK [common : Ansible revision check] *******************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": ["Expected Ansible version is 2.4.3+, but actual version is 2.4.0.0"]}
    to retry, use: --limit @/Users/myoshiz/devel/ansible_version/playbook1.retry

まとめ

Ansible は頻繁にバージョンアップされるので、特に問題がないなら、最新版に追従したほうがいいのは確かです。Ansible 2.5.0 を安心して使う気になれるまでの一時しのぎとして、よかったら試してみてください。

余談:Ansible 2.5 での仕様変更

Ansible 2.5 Porting Guide の冒頭に書いてありますが、include_tasks に付けられた属性(タグなど)の扱いが変わるようです。以下の記事で紹介した事象は、Ansible 2.4 でのみ発生する一時的な問題だったみたいですね。

muziyoshiz.hatenablog.com

2018-03-13追記

Ansible のバージョンチェックは1回で十分なので、run_once: True を付けました(参考)。

艦これアーケード第3回イベント「索敵機、発艦始め!」プレイデータ解析

f:id:muziyoshiz:20170128164643p:plain

はじめに

1年半くらい前から、艦これアーケードのプレイデータ管理ツール "Admiral Stats" を開発・運用しています。自分のプレイデータを記録するためにツールが欲しかったのと、他の人のプレイデータを見てみたかったというのが理由です。

そんなわけで今回の記事は、2017年11月30日から2018年1月9日まで開催されていた第3回イベント「索敵機、発艦始め!」のプレイデータ解析です。

最初に断っておくと、このプレイデータは、Admiral Stats を使ってくれているプレイヤーのものに限られています。そのため、プレイヤーの母集団と比べると、ヘビープレイヤーに寄った結果になっている可能性が高いです。それを念頭に置いて、大まかな傾向だけ参考にしてください。

第3回イベント「索敵機、発艦始め!」について

解析結果について話す前に、今回のイベントの簡単な紹介から。

今回は、第2回イベントと同様に「前段作戦」「後段作戦」に分かれたイベントでした。以下のように、前段作戦でクリアした難易度のみ、後段作戦でも出撃できます。詳しくは 第2回イベントのプレイデータ解析 に書きました。

f:id:muziyoshiz:20180223215553p:plain

今回のイベント期間は1ヶ月以上あり、しかも正月を挟んでいたので、ネット上でも期間の長さに関する不満は少なかったようです。第2回イベントは前段作戦36日、後段作戦21日で、期間が短いとの不満があったので、その点はかなり改善されていました。

  • 前段作戦:2017年11月30日 7:00 〜 2018年1月9日 23:59 (41日)
  • 後段作戦:2017年12月14日 7:00 〜 2018年1月9日 23:59 (27日)

また、今回はクリスマス前後に1週間、期間限定クリスマスフレーム艦娘カード が排出されるキャンペーン期間があり、イベントを早めに終わらせてしまった人もうまく集客していました。SEGA 恐るべし……。

  • クリスマスフレーム排出:2017年12月20日 7:00 〜 12月27日 6:59 (7日)

難易度については、レア艦娘がいなくてもできる攻略法(時間ギリギリまで空母で削ってから戦闘開始)が発見されたおかげで、時間さえかければ甲E-6クリアも簡単でした(※個人の感想です)。それ以外の正攻法でのクリアは、練度とプレイヤースキルがないと難しそうというか……僕はチャレンジしましたが無理でした。

また、今回は前段の報酬(天津風、Prinz Eugen)と後段の報酬(大鳳、Bismarck)がはっきり分かれていました。前回はこれが分かれてなくて、大和がいつまでも出ずにハマる人が多かったので、おそらくはその救済策ですね。

全体的に、第2回よりも改善されたイベントだったと思います。個人的には楽しかったですよ。

集計対象のプレイデータ

今回は前段作戦145名、後段作戦136名分のプレイデータが集まりました。第2回イベントより約50名プレイヤーが増えています。いつも Admiral Stats のご利用ありがとうございます。

集計対象としたプレイデータの詳しい情報は、以下の通りです。

  • 集計対象とした提督
    • 第3回イベントに参加して、Admiral Stats に1回以上プレイデータをアップロードした提督
  • 集計対象としたプレイデータ(イベント進捗情報、艦娘一覧など)
    • イベント攻略率と艦娘カード入手率は、2018年1月16日までにエクスポートされたプレイデータを集計
    • レベルや経験値については、各プレイヤーの、イベント終了時刻に最もプレイデータを集計

集計対象を1月16日までとしたのは、SEGA の公式サイト(提督情報ページ)でプレイデータを閲覧できたのがこの日までだったからです。イベント終了1週間後に消える、というのは第2回イベント時と一緒でした。

以下、プレイデータの解析結果です。

プレイデータの解析結果

イベント攻略率

第2回イベントと比べると、甲難易度の攻略率は大幅に上がりました。これは、開催期間が長かったことと、手軽な攻略法が存在したことが理由と思われます。

イベント 「甲」攻略率(前段) 「甲」攻略率(後段)
第2回 57.9 % 57.5 %
第3回 72.4 % 72.1 %

イベント攻略率:前段作戦の詳細

前段作戦の甲難易度をクリアしたのは、前段作戦に出撃した提督の 72.4 % でした。

また、甲難易度をクリアした提督のうち、63.8 % 以上がレアカード(甲種勲章がプリントされた艦娘カード)を求めて2周以上クリアしていました。100周超えている提督が複数居るのは、もう凄いとしか言いようがないですね……。

f:id:muziyoshiz:20180223211848p:plain
f:id:muziyoshiz:20180223213957p:plain:w675

詳細:イベント攻略率(索敵機、発艦始め! 前段作戦) - Admiral Stats

イベント攻略率:後段作戦の詳細

後段作戦の甲難易度をクリアしたのは、後段作戦に出撃した提督の 72.1 % でした。

また、甲難易度をクリアした提督の 63.3 % 以上が2周以上クリアしていました。難易度が高い分、前段作戦よりも周回数は減っていますが、それでも50周近く回ってる提督がいるのはすごいです。

f:id:muziyoshiz:20180223211947p:plain
f:id:muziyoshiz:20180223214024p:plain:w675

詳細:イベント攻略率(索敵機、発艦始め! 後段作戦) - Admiral Stats

艦娘カード入手率

以下は、このイベントでの新艦娘を入手できた提督の割合です。ちなみに、自分でドロップしたカードだけでなく、買ったり借りたりして読み込んだカードも「入手」に含みます。これは、公式プレイヤーズサイトの仕様による制限です。

作戦 図鑑 No. 艦名 N Nホロ N中破
前段 176 Prinz Eugen 84.1 % 20.3 % 7.2 %
前段 181 天津風 87.0 % 22.5 % 10.1 %
後段 153 大鳳 73.9 % 10.9 % 2.9 %
後段 171 Bismarck 73.9 % 10.9 % 2.9 %

この結果を見る限り、艦娘ごとのドロップ率に大きな偏りはなさそうです。後段作戦のほうが大鳳・Bismarck ともに入手率が低いのは、前段よりも周回するのが難しくて、片方の艦娘しか手に入らなかった提督が多かったということかもしれません。

それと、今回のイベントでは、前段・後段ともに夕立改二の限定カードがドロップしました。入手率は意外と高く、半分以上の提督の手に渡ったようです(でもうちにはいません……)。

作戦 図鑑 No. 艦名 入手率
前段/後段 144 夕立改二 50.7 %

さらに、今回のイベントでは、第2回イベントでドロップした以下の限定カードも再度ドロップしました。11/30 3:00時点のアクティブ提督(過去60日)の入手率と比較すると、イベント後には入手率が15〜20%程度上がったようです。

作戦 図鑑 No. 艦名 11/30時点の入手率 イベント終了時の入手率
前段 103 日向改 41.2 % 61.6 %
後段 102 伊勢改 42.0 % 57.2 %

艦隊司令部レベル、および経験値

ここから先は、Admiral Stats 上で表示していない(自動集計機能がまだない)集計結果の紹介です。

まず、前回と同様に、ステージのクリア回数とステージの難易度によって増える「艦隊司令部レベル」の分布を出してみました。

艦隊司令部レベル 提督数 割合 (参考)第2回終了時の割合
0 以上 10 未満 0 0.00% 3.16%
10 以上 20 未満 2 1.38% 1.05%
20 以上 30 未満 1 0.69% 2.11%
30 以上 40 未満 3 2.07% 5.26%
40 以上 50 未満 5 3.45% 5.26%
50 以上 60 未満 3 2.07% 10.53%
60 以上 70 未満 12 8.28% 7.37%
70 以上 80 未満 9 6.21% 7.37%
80 以上 90 未満 7 4.83% 8.42%
90 以上 100 未満 38 26.21% 29.47%
100 以上 110 未満 56 38.62% 18.95%
110 (上限) 9 6.21% 1.05%
総計 145 100.00% 100.00%

艦隊司令部レベルの平均値と中央値は以下の通りです。

艦隊司令部レベル 第3回終了時 第2回終了時
平均 90.1 77.0
中央値 99.0 87.0

艦隊司令部レベルを経験値に換算すると、次のようになりました。レベル99に必要な経験値は 1,000,000 なので、イベント参加提督の 51.72 % がレベル99以上ということになります。このプレイデータは、間違いなくヘビープレイヤーに寄ってますね……。

経験値 提督数 割合 (参考) 第2回終了時
0 以上 100000 未満 9 6.21% 14.74%
100000 以上 200000 未満 8 5.52% 14.74%
200000 以上 300000 未満 15 10.34% 8.42%
300000 以上 400000 未満 6 4.14% 6.32%
400000 以上 500000 未満 3 2.07% 6.32%
500000 以上 600000 未満 4 2.76% 3.16%
600000 以上 700000 未満 5 3.45% 6.32%
700000 以上 800000 未満 9 6.21% 3.16%
800000 以上 900000 未満 11 7.59% 3.16%
900000 以上 1000000 未満 0 0.00% 0.00%
1000000 以上 2000000 未満 30 20.69% 24.21%
2000000 以上 3000000 未満 12 8.28% 5.26%
3000000 以上 4000000 未満 11 7.59% 2.11%
4000000 以上 5000000 未満 8 5.52% 0.00%
5000000 以上 6000000 未満 14 9.66% 2.11%
総計 145 100.00% 100.00%
経験値 第3回終了時 第2回終了時
平均 1712572.4 808893.7
中央値 1000000.0 491500.0

イベントの間に6ヶ月空いたので、全体的にかなり底上げされてます。新規プレイヤーがあまり入ってこなかったとも解釈できますが、ただ、それは Admiral Stats のユーザに限った話かもしれません。

攻略度と艦隊司令部レベルの比較

甲難易度をクリアするのに必要なレベルを知るために、攻略度と、艦隊司令部レベルの関係を調べた結果が以下の表です。

前段 - 後段 攻略度 提督数 提督数 (割合) 最大 最小 平均 中央値 標準偏差
甲 - 甲 98 67.59% 110 62.0 99.1 101.0 10.7
甲 - 乙 2 1.38% 99 64.0 81.5 81.5 17.5
甲 - 丙 1 0.69% 72 72.0 72.0 72.0 -
甲 - 未出撃 4 2.76% 107 95.0 101.0 101.0 4.3
乙 - 乙 8 5.52% 100 41.0 82.1 95.5 20.8
乙 - 丙 3 2.07% 87 34.0 57.3 51.0 22.1
乙 - 未攻略 2 1.38% 97 63.0 80.0 80.0 17.0
乙 - 未出撃 1 0.69% 80 80.0 80.0 80.0 -
丙 - 丙 12 8.28% 99 37.0 63.7 64.5 18.7
丙 - 未攻略 4 2.76% 74 64.0 68.8 68.5 4.0
丙 - 未出撃 3 2.07% 102 54.0 85.3 100.0 22.2
未攻略 - 未攻略 6 4.14% 99 17.0 40.8 32.0 28.2
未攻略 - 未出撃 1 0.69% 98 98.0 98.0 98.0 -
全体 145 100.00% 110 17.0 90.1 99.0 21.3

「未出撃」とあるのは、後段作戦に出撃しなかったか、後段作戦の期間中に Admiral Stats にプレイデータをアップロードしなかったユーザです。

後段作戦の甲E-6をクリアした提督と、それ以外の提督で、レベル差があることがわかります。しかし、乙E-6までしかクリアしていない提督(表の「乙 - 乙」の行)も中央値はそれほど低くありません。このあたりの提督が、どういう理由で甲をクリアしなかったのかは気になるところです。

攻略度と艦娘レベルの比較

攻略に艦娘のレベルの高さは必須だったのかどうかを調べるために、各提督の、レベルが一定値(50, 70, 90, 99)以上の艦娘数を調べてみました。 すると、レベル90あたりが、関係がありそうな結果になりました。

以下の表は、各提督の攻略度と、Lv90以上の艦娘数の関係です。

前段 - 後段 攻略度 提督数 提督数 (割合) 最大 最小 平均 中央値 標準偏差
甲 - 甲 98 67.59% 119 0 17.5 9 21.0
甲 - 乙 2 1.38% 5 0 2.5 2.5 2.5
甲 - 丙 1 0.69% 0 0 0.0 0 -
甲 - 未出撃 4 2.76% 35 0 13.3 9 14.0
乙 - 乙 8 5.52% 8 0 1.6 1 2.5
乙 - 丙 3 2.07% 0 0 0.0 0 0.0
乙 - 未攻略 2 1.38% 3 0 1.5 1.5 1.5
乙 - 未出撃 1 0.69% 1 1 1.0 1 -
丙 - 丙 12 8.28% 5 0 0.4 0 1.4
丙 - 未攻略 4 2.76% 1 0 0.3 0 0.4
丙 - 未出撃 3 2.07% 13 0 8.0 11 5.7
未攻略 - 未攻略 6 4.14% 0 0 0.0 0 0.0
未攻略 - 未出撃 1 0.69% 0 0 0.0 0 -
全体 145 100.00% 119 0 12.5 3 19.0

甲難易度をクリアした艦隊とそうでない艦隊では、Lv90以上の艦娘数に明らかな違いがあることがわかります。

攻略度と空母の練度の比較

今回のイベントは、空母を育てていれば使える攻略法がありました。そこで、攻略度と、各提督が所持する空母のレベルに関係があったかを調べてみました。

以下は、攻略度と、各提督が所持する空母(正規空母、装甲空母、軽空母)の上位5隻の平均レベルを比較した結果です。

前段 - 後段 攻略度 提督数 提督数 (割合) 最大 最小 平均 中央値 標準偏差
甲 - 甲 98 67.59% 99 18.8 74.1 75.4 20.2
甲 - 乙 2 1.38% 40.4 29.2 34.8 34.8 5.6
甲 - 丙 1 0.69% 47.6 47.6 47.6 47.6 -
甲 - 未出撃 4 2.76% 93.2 39.4 74.2 82.1 20.9
乙 - 乙 8 5.52% 70.6 26.2 52.2 56.4 15.4
乙 - 丙 3 2.07% 37.6 19.6 29.7 31.8 7.5
乙 - 未攻略 2 1.38% 60.2 44.8 52.5 52.5 7.7
乙 - 未出撃 1 0.69% 63.6 63.6 63.6 63.6 -
丙 - 丙 12 8.28% 77.6 16 40.0 37.5 17.2
丙 - 未攻略 4 2.76% 49.8 35.4 43.0 43.3 5.1
丙 - 未出撃 3 2.07% 90.2 28.6 62.9 70 25.6
未攻略 - 未攻略 6 4.14% 64 10.8 25.0 16.9 18.4
未攻略 - 未出撃 1 0.69% 72.4 72.4 72.4 72.4 -
全体 145 100.00% 99 10.8 64.9 66.4 24.5

これを見ると、空母の平均レベルが高いほど、甲難易度を攻略できた可能性は高そうですね。ただ、平均レベルが18.8でも甲E-6をクリアできている提督がいるのはちょっと驚きです。空母のレベルが低くても行けたのか、他の方法で攻略したのか……?

まとめ

いくつかの方法で、甲種勲章を入手した提督と、そうでない提督のプレイデータを比較してみました。

今回は甲難易度をクリアできた提督が全体の70%以上と多かったため、「甲提督 vs それ以外」という比較はあまりうまく行きませんでした。「レベルがある程度高ければクリアできた」という感じでしたね。第2回イベントのように「大井改二・北上改二がいれば」「甲標的があれば」といったレベル以外の条件による影響は見つけられませんでした。

もし、「こういう観点で調べてほしい」といったアイディアがありましたら Admiral Stats のお知らせ用アカウント @admiral_stats までお寄せください。今後のネタにさせていただきます。

最後に、艦これアーケードをプレイしていて、こういうプレイデータに興味があったら是非 Admiral Stats を使ってみてください。よろしくお願いします。

おまけ:2/16 のケッコンカッコカリ実装時に、レベル99到達済みの艦娘数を集計した結果

あわせて読みたい

muziyoshiz.hatenablog.com

muziyoshiz.hatenablog.com

muziyoshiz.hatenablog.com

muziyoshiz.hatenablog.com

Ansible 2.4 で import_tasks/include_tasks に tags を付けるときの注意点

f:id:muziyoshiz:20160331232512p:plain:w300

前提:Ansible 2.4 から include は非推奨になった

Ansible 2.4 を使っていたら既に嫌というほど見てると思いますが、include を使うと以下のような警告が出るようになりました。

[DEPRECATION WARNING]: The use of 'include' for tasks has been deprecated. Use 'import_tasks' for static inclusions or 'include_tasks' for dynamic inclusions. This feature will be removed in a future release. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.

Ansible 2.4 から、いままで include が持っていた機能は以下の4つのアクションに分けられました。

Action Operation Reusable content
import_tasks static task
include_tasks dynamic task
import_playbook static playbook
include_playbook dynamic playbook
include (DEPRECATED) both static and dynamic task or playbook

この変更は、静的(static)な読み込みと動的(dynamic)な読み込み、そして読み込む対象(task ファイルか playbook ファイルか)を明確に区別することを目的に行われたようです。

static と dynamic の違い

ここで言う static と dynamic の意味は、公式サイトの Creating Reusable Playbooks によると次の通りです。

  • 静的な読み込み(static import)は、Playbook をパースする段階で、事前に実行される
  • 動的な読み込み(dynamic include)は、タスクを順に処理して、その行に来た段階で実行される

include は基本的には static import を行いますが、状況によって dynamic include を行うなどして動作がわかりづらいため、Ansible 2.8 で廃止予定とのことです。

じゃあ今までに書いた include はどうすればいいのか?というと、個人的な意見ですが、特に理由がなければ import_tasksimport_playbook に書き換えておけばいい と思います。dynamic include にすると文法ミスの事前検知が行われなくなるので、デバッグが面倒になります。

もっと詳しく知りたい方は、以下のページをどうぞ。@heriet さんによる Qiita の記事は、Ansible 2.2 の時点で書かれたものですが、具体例が多く、基本的な考え方の勉強になりました。

本題:import_tasks/include_tasks に tags を付けた場合の動作

ここまでは前提の話で、ここからやっと本題です。

以下のように import_tasks/include_tasks に tags を付けると、その中に含まれるすべてのタスクに同じタグを付けることができます。これは公式の Tags — Ansible Documentation にも書かれた方法です。

- import_tasks: main.yml
  tags: [ tag1 ]
- include_tasks: main.yml
  tags: [ tag1 ]

例えば、main.yml の中身が、

- debug: msg="Task 1"

- debug: msg="Task 2"

の場合、ansible-playbook --tags=tag1 で両方のタスクが実行されます。

しかし、何かの理由で Task 2 だけ実行したくなったとします(Ansible 使ってるとよくありますよね?)。そこで、

- debug: msg="Task 1"

- debug: msg="Task 2"
  tags: [ tag2 ]

と直して、ansible-playbook --tags=tag2 を実行すると、include_tasks を使った場合は Task 2 が実行されません。どうやら、以下のような動作の違いがあるようです。

  • import_tasks に tags を付けた場合は、「main.yml 内のすべてのタスクに追加するタグ」として扱われる
  • include_tasks に tags を付けた場合は、「include_tasks アクション自体に追加するタグ」のように扱われる

ローカルホストで実験

ローカルホストで簡単に実験できるのでやってみましょう。Gist にファイルを用意しました。

Example of import_tasks/include_tasks with tags

これらのファイルを同じディレクトリに置いて、以下のように実行してください。

$ ansible-playbook playbook1.yml --tags=tag1

Ansible 2.4.2.0 で実行した結果は、以下の通りです。include_tasks の場合だけ、tag2 を付けたタスク(Task 2)が無視されているのがわかります。

No. Playbook How to import --tags option Task 0 Task 1 Task 2
1 playbook1.yml include None ok ok ok
2 --tags=tag1 ok ok ok
3 --tags=tag2 - - ok
4 playbook2.yml import_tasks None ok ok ok
5 --tags=tag1 ok ok ok
6 --tags=tag2 - - ok
7 playbook3.yml include_tasks None ok ok ok
8 --tags=tag1 ok ok ok
9 --tags=tag2 - - -

まとめ:結局どうしたらいいのか?

import_tasksinclude_tasks を使った場合で、タグの扱いに微妙な違いがあることがわかりました。

上記の結果を見ると、「include_tasks 使うとタグが無視されることがあるのか。使うのやめよう」と思うかもしれません。しかし、以下のように書かれているのに、main.yml 内に tag1 以外のタグがついているというのは、それはそれで可読性に問題があります。

- import_tasks: main.yml
  tags: [ tag1 ]

そう考えると、結論としては「import_tasks/include_tasks で読み込むタスクにはタグを付けない」というルールを設けるのが良いのではないでしょうか。