たんばりんニュースブログ

当て付け怪文書海老ブログではない

【翻訳】ポッ拳の使用率分析ツール作成

今回はプログラミング記事になります。


突然ですが、ポッ拳のキャラクター使用率が気になった時ってありませんか?

「ランキングは〇〇が多いなー」
「今日は〇〇によく当たるなー」

そう思っていても、母数が少ない以上は気のせいかも?で終わってしまう。
そこで、載っているランキング全てを集計して統計を取るプログラムを作成しました。

1.データを集める

今回の目的であるランキング集計ですが、まずnintendo switchからPCへのデータの受け渡し方法が大きな壁となりました。

前回上位100名では手入力で正の字をメモ用紙に書いて…という感じだったのですが、今回はそうもいきません。

何故なら項目数が前回の7.5倍。

250x3ランキング=750項目もあります。

流石に正の字を書くのはめんどくさそうです…

ここで、単一換字式で1文字に置き換え、辞書型として保持する方法を思いつきました。
人間に分かりづらくても後でデータを簡単に成形できますし、等倍文字にする事で何キャラ分出来たのかチェックしやすいという利点があります。

前回のワークフロー

ランキングを見る
全部の項目を確認するまで以下を繰り返す

  • リストから該当するポケモンを探す
  • 正の字を1画記入してカウント

終わったら全体の何%か計算してメモ
多い順にPCへ手入力

問題点
紙データのため統計時にPCへ入力する必要がある
記入ミスがあった時、書いた順が分からないので整合性チェックが出来ない
ミスする可能性のある課程が多い
ぶっちゃけめんどい

今回挑戦するワークフロー

ランキングを見る
全部の項目を確認するまで以下を繰り返す

  • ポケモン選択画面に対応したキーを押す
  • 10ページ毎に見直し

出来た文字列から文字の出現率を解析
出来たリストを出現率の降順でソート
文字をキャラ名に置換してリストを出力

これなら多くの手順を自動化できそうです。
文字列という形式なので整合性がチェックできます。

早速入力してみました。

累計ポイント世界ランキング(1〜250位)
vgqfzqzzzruhqjzeduaqbgymcrmwzjmgbqfrcgmvgbzhhemuygvwnbugqwcvzveczhbmcnvxvnqqtmjsxzfestgnxezunbqsrzggrsnzgsjdebwzdxqbxzqzjvmfwtznbrzzfqcfsrafdnuuxhafrtgzhgbwhasbmrqwnmxqsqzrarhhmzhmzbdnszfvzushqntzggnwgfsturdwzqunedfbqmuzewxrtfcjgvnwvdqtwyezqxznrvmjtt

累計勝利数世界ランキング(1〜250位)
qqvgzzafzuqrbzzmhxbqjuezwnnzdcyhghhrmzzgbzgqxmjgwfurmwycgemvbvxcvxvesqsgatnvgwvzujmcbrwtqrvmsmvnnbcugxarqeshthfzusfbbzedzqgfmzwqgzzsnxnffgusfdzjsrxfzumwzqrnnrtdnrjgfrwdrqxtgrbrahcndshqmbfwuqrtnhshtgzqamrhbgzzwwqmqyrtzwqfzwvsghzhdtyfebntshqzrnevbbcywc

トレーナー世界ランキング(1〜250位)
zrngzdnqxyxmfmzwxvfqnwwhadfaxnreztburuuzqzrvscvtahzryjmztbgqbsxjcfzrutrwxrnnrzdsucyrsvcscueqvyzbmssbuqtdjzwqhzhbhtmssybhwztvhztztrsscgbsxengqstbmqweqdnzfqztcqdqdjgbrsnrnrrdchmmsfuecjxmftgyqwunbmmmwfuhrznvfdrrfzsjbqxrcgjztxcerfertthrnhhurxjtumwjfxzxyt

1ページ5秒、見直し含めて1ランキング10分以内で終わりました。
ここを自動化したい気持ちがあったのですが、PCとswitchを繋ぐ黒い差込口みたいなパーツが手元になかったので断念。
webカメも無いので手打ちという結果になりました。

2.自動で統計する

前置きが長くなりましたが、これが本記事のメインです。

LINQでソートすると楽かな?とふと思いつき、C#でプログラミングしてみました。

そして出来上がったものはこちらです。

using System;
using System.Collections.Generic;
using System.Linq;

namespace SimpleSubstitutionCipher
{
    class ConvertCharacterList
    {
        //Read Users Input
        static string readText()
        {
            Console.WriteLine("\n--------------------------------\n");
            var originalText = Console.ReadLine();
            Console.WriteLine("\n--------------------------------\n");
            return originalText;
        }

        //Output Console
        static void WriteAllStatistics(Dictionary<char, string> name, Dictionary<char, float> stat)
        {
            //var sort = stat.OrderBy((x) => x.Value);
            var sort = stat.OrderByDescending((x) => x.Value);
            foreach (var st in sort)
                Console.WriteLine($"{name[st.Key]}  \t: {stat[st.Key]}%");
        }

        static void Main(string[] args)
        {
            var Statistics = new Dictionary<char, float>();
            var CharacterList
                = new Dictionary<char, string>()
                {
                    {'q',"Darkrai"},{'w',"Blaziken"},{'e',"Pikachu"},{'r',"Lucario"},{'t',"Gardevoir"},{'y',"Masked"},{'u',"Scizor"},
                    {'a',"Crogunk"},{'s',"Sceptile"},{'d',"Genger"},{'f',"Desidueye"},{'g',"Machamp"},{'h',"Braixen"},{'j',"Empoleon"},
                    {'z',"Mewtwo"},{'x',"Chandelure"},{'c',"Suicune"},{'v',"Weavile"},{'b',"Charizard"},{'n',"Garchomp"},{'m',"Dark M2"},
                };

            //1.Read users input
            var originalText = readText();

            //2.Substitution
            foreach (var ch in CharacterList)
            {
                float ratio = (float)originalText.Count(c => c == ch.Key) / (float)originalText.Length * 100;
                Statistics.Add(ch.Key, ratio);
            }

            //3.Output console
            WriteAllStatistics(CharacterList, Statistics);

            Console.WriteLine("\n--------------------------------\n");
            Console.WriteLine("Statistics Complete!! (Press Any Key)");
            Console.ReadKey();
        }
    }
}

動作チェック。

問題なく統計情報が出力できました。

3.解説

using System;
using System.Collections.Generic;
using System.Linq;

今回は辞書型を使うためのSystem.Collections.Generic;と
ソートするためのSystem.Linq;を読み込みました。




        //Read Users Input
        static string readText()
        {
            Console.WriteLine("\n--------------------------------\n");
            var originalText = Console.ReadLine();
            Console.WriteLine("\n--------------------------------\n");
            return originalText;
        } 

ユーザー入力を返すモジュールです。
少し装飾がありますが、やっている事はシンプルですが。
入力を待ち、決定された文字列をstring型で返します。



        //Output Console
        static void WriteAllStatistics(Dictionary<char, string> name, Dictionary<char, float> stat)
        {
            //var sort = stat.OrderBy((x) => x.Value);
            var sort = stat.OrderByDescending((x) => x.Value);
            foreach (var st in sort)
                Console.WriteLine($"{name[st.Key]}  \t: {stat[st.Key]}%");
        }

ソートして出力します。
ラムダ式LINQのwhere拡張を組み合わせると降順ソートが1行で可能です。

sort変数をコメントアウトしている行に切り替えると、昇順になります。
後半は結果出力です。
後述しますが、nameとstatは辞書型の変数です。また、statのKeyがキャラクターに当てはめた文字になります。
そのため、nameとstatの情報をst.Keyで取り出す事でソートした結果が取得できます。




var Statistics = new Dictionary<char, float>();
            var CharacterList
                = new Dictionary<char, string>()
                {
                    {'q',"Darkrai"},{'w',"Blaziken"},{'e',"Pikachu"},{'r',"Lucario"},{'t',"Gardevoir"},{'y',"Masked"},{'u',"Scizor"},
                    {'a',"Crogunk"},{'s',"Sceptile"},{'d',"Genger"},{'f',"Desidueye"},{'g',"Machamp"},{'h',"Braixen"},{'j',"Empoleon"},
                    {'z',"Mewtwo"},{'x',"Chandelure"},{'c',"Suicune"},{'v',"Weavile"},{'b',"Charizard"},{'n',"Garchomp"},{'m',"Dark M2"},
                };

先述した辞書型の中身はこうなっています。
ゲーム内のキャラ選択画面における上段、中段、下段で行を分けてみました。
statには統計結果がfloatで入るので、まだ空の状態です。



            //1.Read users input
            var originalText = readText();

            //2.Substitution ratio
            foreach (var ch in CharacterList)
            {
                float ratio = (float)originalText.Count(c => c == ch.Key) / (float)originalText.Length * 100;
                Statistics.Add(ch.Key, ratio);
            }

            //3.Output console
            WriteAllStatistics(CharacterList, Statistics);

メイン処理です。
先ほどのモジュールを呼び出しています。
2番目が肝となる処理で、ここで統計のパーセンテージを出して辞書型に格納しています。

最後に完了のメッセージを出して終了します。

手入力した部分と例外処理が無いのが反省点でしょうか。
辞書にない文字を入力しても無視する作りにはなっていると思ったので記述はしていません。