MobstarworldをWWW::Mechanizeでオートプレイ

MobstarworldというTwitterと連動したゲームがあります。twitterのアカウント権限を乱用しているんじゃ無いかと色々と騒がれていますが、まぁゲーム部分は普通にプレイできるんでやってみても良いかと思います。

で、このゲーム。やってみると解るのですが、所謂レベル上げクリックゲームの一種です。幾つかのパラメータを上手く成長させながら、ミッションをクリアするのが目的です(ボタンをクリックするだけ)。ミッションをクリアするのにエネルギーを消費しますが、エネルギーは時間が経つと回復します。

つまりこのゲームをプレイするという事は、以下の手順を何度も行う事になります。

  1. サイトを訪問
  2. パラメータ上げ活動
  3. エネルギーを消費してミッションをクリア
  4. 一定時間放置してエネルギー回復

しかもエネルギーは上限を超えては回復しませんので、上手くプレイする為には、出来るだけ高い頻度でサイトを訪問する必要があります。

特に面白くは無いんですが、ミッションをクリアすると入手できるアイテムが増えたりと一定のコレクター要素を含んでおり、やや中毒性を持っていると考えられ、徒に時間を消費してしまう可能性があります。

というわけで前置きが長くなりましたが、僕は別にクリックゲームを遊びたい訳ではなく、クリア結果を知りたいだけなので、練習も込めてWWW::Mechanizeにオートプレイさせる事にしました。行っている事は以下の手順になります。

  1. 2~5を繰り返す
  2. お金があればアイテムを買う
  3. 体力があれば他のユーザと戦う
  4. エネルギーがあればミッションを行う
  5. random(500)秒スリープ
#!/usr/bin/perl
# -*- coding: utf-8 -*-

use strict;
use warnings;
use Perl6::Say;

use WWW::Mechanize;
use Web::Scraper;
use YAML;
use Text::Trim;

# このインスタンスをGlobal変数として扱う
my $mech = WWW::Mechanize->new;
$mech->agent_alias('Mac Safari'); # ボットだとばれないように

# ログイン
login('user', 'pass'); # twitter アカウントのユーザとパス

# 1. 基本行動はjobをこなし続ける。
# 2. お金が余ったら物件購入
# 3. staminaがあれば戦う
# 4. ライフが危なくなったら回復
while (1) {
    say "processing...";
    
    # statusを取得する
    my $status = status($mech->content);

    # cure life
    if ($status->{health} < 30) {
        hospital();
    }

    # buy
    if ($status->{money} > 11000) {
        say "buying pistol and house. remain " . $status->{money};
        buy();
    }

    # Fight
    if ($status->{stamina} > 0) {
        fight($status);
    }

    # dojob
    doJob($status);

    sleep(rand(500)); # ボットだとばれないように
}

# ログイン処理
sub login {
    my ($user, $password) = @_;
    $mech->get('http://www.playmobsterworld.com/');
    exit unless $mech->success;
    $mech->follow_link(url_regex => qr/twitter.com/);
    exit unless $mech->success;
    $mech->form_id('login_form');
    $mech->set_visible($user, $password);
    $mech->submit;
    exit unless $mech->success;
    $mech->follow_link(text_regex => qr/click here/i);
    exit unless $mech->success;
}

# $10000の家と$1000のピストルを買う
sub buy {
    $mech->follow_link(text_regex => qr/Property/i);
    $mech->form_number(5);
    $mech->submit;
    $mech->follow_link(text_regex => qr/Inventory/i);
    $mech->form_number(3);
    $mech->submit;
    $mech->back;
}

# リストの先頭と戦う
sub fight {
    $mech->follow_link(text_regex => qr/Fight/i);
    my $list = fightList($mech->content);
    foreach my $target (@{ $list->{mobs} }) {
        say "finght with " . $target->{name} . " level:" . $target->{level};
        # 先頭の相手と戦う
        $mech->form_number(1);
        $mech->submit;
        last;
    }
    $mech->back;
}

# 回復する
sub hospital {
    $mech->follow_link(text_regex => qr/(Hospital)/i);
    $mech->form_number(1);
    $mech->submit;
    $mech->back;
}

# 仕事をこなす
sub doJob {
    my $status = shift;

    $mech->follow_link(text_regex => qr/jobs/i);
    last unless $mech->success;
    my $list   = jobList($mech->content);

    my $i = 0;  
    foreach my $job (@{ $list->{jobs} }) {
        $i++;
        if ($job->{'mastery'} ne 'Completed' && $job->{mobsters} < = $status->{mobs}) {
            if ($job->{'energy'} < = $status->{'energy'}) {
                # do job
                say "do " . $job->{name};
                #$mech->form_number($job->{id});
                $mech->form_number($i);
                $mech->submit;
                say "failed to submit " unless $mech->success;
                #say $mech->content;
            }
            else {
                say("not enough energy " . $status->{'energy'})
            }
            last;
        }
    }
}

# get people list to fight
sub fightList {
    my $html = shift;
    my $scraper = scraper {
        process '//table[@class="full_page"]/tr[@class!="header_row"]', 'mobs[]' => scraper {
            process '//td[2]/a/span', 'name' => 'TEXT';
            process '//td[2]', 'level'  => sub {
                my $html = $_->as_HTML;
                $html =~ /Level ([0-9]+)/;
                return $1;
            };
        };
    };
    return $scraper->scrape($html);
}

# get joblist
sub jobList {
    my $html = shift;
    my $scraper = scraper {
        process '//table[@class="full_page"]/tr[@class!="header_row"]', 'jobs[]' => scraper {
            process '//form/input', 'id' => '@value';
            process '//td[1]', 'name' => sub {
                my $html = $_->as_HTML;
                $html =~ s/
.+//g; $html =~ s///g; return trim($html); }; process '//td[1]', 'mastery' => sub { my $text = $_->as_text; $text =~ s/.+Job Mastery://g; return trim($text); }; process '//td[3]/span[1]', 'energy' => 'TEXT'; process '//td[3]/span[2]', 'mobsters' => 'TEXT'; }; }; return $scraper->scrape($html); } # get current status sub status { my $html = shift; my $scraper = scraper { process '//span[@id="cash"]', 'money' => sub { my $money = $_->as_text; $money =~ s/[\$,]//gm; return $money; }; process '//span[@id="energy"]', 'energy' => sub { return trim($_->as_text); }; process '//span[@id="stamina"]', 'stamina' => 'TEXT'; process '//span[@id="health"]', 'health' => 'TEXT'; process '//table[@class="nav_bar"]/tr/td[9]/a', 'mobs' => sub { my $text = $_->as_text; $text =~ /My Mob \(([0-9]+)\)/; return $1; }; }; return $scraper->scrape($html); }

もっと効率的なプレイも出来ると思うのですが、面倒になったのでこの辺で止めます。これでも多分人間がプレイしてたらあり得ない効率でゲームを進められるはずです。今のところ1.5日くらい回してまして、1日1回見に行くのがちょっと楽しみになるかもしれませんね。

あともし怒られたりbanされたりしても責任持てないので、利用は自己責任でお願い致しますです。

Leave a Reply

Your email address will not be published. Required fields are marked *