Node.js
Selenium
Tesseract.js
PORTDay 20

弊社の新卒タイピング課題「寿司打」を自動化?してみた

今日はPORT株式会社でエンジニアをしている@tetsuya-ogawaが担当します。

突然ですが、みなさん寿司打をご存知でしょうか?
寿司打とはタイピングゲームです。回転寿司のように問題が流れてきて、時間内にタイプできれば点数がもらえるっていう仕組みです。
弊社ではこの寿司打を用いた伝統の新卒課題があります。

スクリーンショット 2017-12-08 16.49.20.png

課題の概要

3つのモードがあり、弊社では高級モードで15,000点以上を獲るっていう新卒の課題が課されました。
15,000点以上の結果画面のスクリーンショットを提出してクリアになります。
タイピングが得意な人はすぐクリアできると思いますが、弊社では何人もの新卒が苦しめられてきたそうです。

今回はこの課題クリアを自動化してみようと思います。

問題解決の手法

課題はスクリーンショットをエビデンスとしているので、15,000点をとる過程はみられません。結果をみられています。ですので、seleniumを使って自動でタイピングさせることにしました。

ver 1

index.js
var webdriver = require('selenium-webdriver'),
    By = webdriver.By,
    until = webdriver.until;

//WebブラウザーはChrome
var driver = new webdriver.Builder().
   withCapabilities(webdriver.Capabilities.chrome()).
   build();
driver.manage().timeouts().implicitlyWait(3000);
driver.switchTo().defaultContent();
var $ = driver.findElement.bind(driver);


driver.get('http://neutral.x0.com/home/sushida/play1.html');

~~ 省略 ~~

// タイピングを制御する部分
for(var i = 0; i<100000; i++){
  driver.actions()
    .sendKeys('abcdefghijklmnopqrstuvwxyz-?!,')
    .perform();
}

実行の様子

shida.mov.gif

結果

スクリーンショット 2017-12-08 18.00.26.png

60,000点に届く素晴らしい記録を叩き出すことができました!!
しかし、これは寿司打で出てくる全てのキーを片っ端から連続で打ち込んでいるため、平均タイプ数が文字通り人間業ではないし、ミスタイプ数もとんでもないことになっています。笑
これでは使い物になりません。

ver 2

ver 1からの反省として「ミスタイプが多い」点が挙げられます。ミスを少なく確実に自動タイプしていくには方法として、最初に上がったのは、

スクリーンショットを撮る -> 解析 -> タイプ

という方法です。

この時画像解析に使ったライブラリが、Tesseract.jsです。
日本語の解析精度ははまちまちですが、アルファベットの解析精度はかなり高いので、使わせていただきました!
今回はnpmパッケージからインストールしましたが、CDNも利用できます。

index.js
var webdriver = require('selenium-webdriver'),
    By = webdriver.By,
    until = webdriver.until;

var Tesseract = require('tesseract.js');


//WebブラウザーはChrome
var driver = new webdriver.Builder().
   withCapabilities(webdriver.Capabilities.chrome()).
   build();
driver.manage().timeouts().implicitlyWait(3000);
driver.switchTo().defaultContent();
var $ = driver.findElement.bind(driver);

let counter = 0;

function screenShot(callback) {
    counter++;
    driver.takeScreenshot().then(function(data) {
        var base64Data = data.replace(/^data:image\/png;base64,/, "");
        require('fs').writeFile(counter+'.png', base64Data, 'base64', function (err) {
            if (err) console.log(err);
        });
    });
    setTimeout(function(){ callback(counter) },400);
}

function sendText(counter) {
    Tesseract.recognize(counter+'.png',{ lang:'eng' }).then(function(e){
        var text = e.text.replace(/Q/g,'').replace(/—/g,'-');
        driver.actions().sendKeys(text).perform();
        console.log(text);
    });

}

driver.get('http://neutral.x0.com/home/sushida/play1.html').then(function() {
    process.stdin.on('data', function (data) {
      screenShot(sendText)
    });
});

実行時に、ブラウザを最大化してスクショをとってしまうと、余計な文字列も解析してしまうとともに、解析の時間が増えてしまうため、ブラウザの幅を縮めて対象文字列だけスクショを取るように調整します。

実行の様子

suhida-kai.mov.gif

コンソールで標準入力を受け取るとスクショをとり、その画像を解析しています。
コンソールに表示させている解析した文字列を見ると、たまに読み取れない時がありますが、読み取れた時は、ほぼ間違いなく解析できており、寿司打の問題もどんどんクリアできています。

結果
スクリーンショット_2017-12-10_15.09.04.png

15,000点をいい感じにクリアする記録を叩き出すことができました。平均キータイプ数やミスタイプ数ともに自然ですね。これで無事課題クリアといけそうです。

終わりに

今回はSeleniumとTesseract.jsを用いて弊社の新卒課題を自動化しました。
実行環境のスペックにもよると思いますが、15,000点ならなんとかクリアできる範囲と言えそうです。
来年の新卒や新卒担当の人事が、この記事を見つけないことを祈りながら締めたいと思います。

最後になりますが、PORT株式会社では自社サービスを支えてくれる優秀なRubyエンジニアを募集しています(Rubyエンジニア以外も)。
もくもく会も行なっていますので、こちらもぜひ!