changeset 101:592d3b3b7898

implemented the WAR robot
author "Rex Tsai <chihchun@kalug.linux.org.tw>"
date Wed, 29 Oct 2008 12:05:36 +0800
parents ed6160630329
children 94c9cde99e19
files Ikariam.pm warfare.pl warfare.yaml
diffstat 3 files changed, 272 insertions(+), 89 deletions(-) [+]
line wrap: on
line diff
--- a/Ikariam.pm	Wed Oct 29 03:20:53 2008 +0800
+++ b/Ikariam.pm	Wed Oct 29 12:05:36 2008 +0800
@@ -8,7 +8,7 @@
 use Class::DBI::AutoLoader (
     dsn       => 'dbi:SQLite:dbname=ikariam.sqlite',
     options   => { RaiseError => 1 },
-    tables    => ['cities', 'island', 'user', 'ally'],
+    tables    => ['cities', 'island', 'user', 'ally', 'report'],
     use_base  => 'Class::DBI::SQLite',
     namespace => 'Ikariam',
 );
@@ -97,6 +97,8 @@
 use HTML::TagParser;
 use XML::LibXML qw(:encoding);
 use IO::Uncompress::Gunzip qw(gunzip $GunzipError) ;
+# use Time::ParseDate;
+use POSIX;
 use utf8;
 
 sub new
@@ -177,15 +179,16 @@
         ]);
     my $extractor = new Ikariam::Extractor(content => $res->content);
     my $result = $extractor->{doc}->find('//table[@class="table01"][2]//tr');
+
     foreach my $tr ( @$result ) {
         my %user;
+        my $extractor = new Ikariam::Extractor(content => $tr->toString(1));
 
-        $extractor->{doc} = $tr;
         my $href = $extractor->find('//td[@class="action"]/a/@href');
         if($href =~ /index\.php\?view=sendMessage&with=(\d+)&oldView=highscore/) {
             $user{'id'} = $1;
-            # $user{'name'} = $extractor->find('//td[@class="name"]');
             $user{'name'} = $user;
+            $user{'name'} = $extractor->find('//td[@class="name"]');
 
             $user{'ally'} = $extractor->find('//td[@class="allytag"]/a/text()');
             my $allyHref = $extractor->find('//td[@class="allytag"]/a/@href');
@@ -197,6 +200,7 @@
             $users{$user{'id'}} = \%user;
         }
     }
+    # print(Dumper(\%users));
     return \%users;
 }
 
@@ -412,11 +416,22 @@
     return $out;
 }
 
-sub checkCity {
+sub plunderCity {
+    my $self = shift;
+    my $cityId = shift;
+    $self->{mech}->get(sprintf("http://%s/index.php?view=plunder&destinationCityId=%d", $self->{server}, $cityId));
+    $self->{mech}->submit_form(
+        form_number => 1,
+        fields      => {
+            cargo_army_302 => '2',
+        }
+    );
+}
+
+sub changeCity {
     my $self = shift;
     my $cityId = shift;
 
-    # search for goods
     $self->{mech}->get(sprintf("http://%s/index.php", $self->{server}));
     $self->{mech}->submit_form(
         form_number => 2,
@@ -424,6 +439,14 @@
             cityId => $cityId,
         }
     );
+}
+
+sub checkCity {
+    my $self = shift;
+    my $cityId = shift;
+
+    $self->changeCity($cityId);
+
     my $res = $self->{mech}->get(sprintf("http://%s/index.php?view=city&id=%d", $self->{server}, $cityId));
     my $extractor = new Ikariam::Extractor(content => $res->content);
 
@@ -478,102 +501,119 @@
 sub checkMilitaryAdvisorMilitaryMovements  {
     my $self = shift;
     # TODO
-    # http://%s/index.php?view=militaryAdvisorMilitaryMovements
     my $res = $self->{mech}->get(sprintf('http://%s/index.php?view=militaryAdvisorMilitaryMovements', $self->{server}));
     my $extractor = new Ikariam::Extractor(content => $res->content);
 
-    my @homeland = $extractor->find('//div[@id="homeland"]');
-    my @elsewhere = $extractor->find('//div[@id="elsewhere"]');
-    $self->{'military'}->{attacks} = $#elsewhere+1;
-    $self->{'military'}->{wars} = $#homeland+1;
+    $self->{'military'}->{wars} = 0;
+    $self->{'military'}->{attack} = 0;
+
+    foreach (qw/homeland elsewhere/) {
+        $self->{'military'}->{$_} = ();
+        my $result = $extractor->{doc}->find(sprintf('//div[@id="%s"]//ul[@id="battleReports"]/li[@class="enroute"]', $_));
+        foreach my $div ( @$result ) {
+            my $extractor = new Ikariam::Extractor(content => $div->toString(1));
+            my $f = $extractor->find('//div[@class="report"]/a[1]/@href');
+            my $t = $extractor->find('//div[@class="report"]/a[2]/@href'),
+            $f = $1 if($f =~ /\?view=island&cityId=(\d+)/);
+            $t = $1 if($t =~ /\?view=island&cityId=(\d+)/);
+
+            push @{$self->{'military'}->{$_}}, { from => $f, to => $t};
+            if($_ eq 'homeland') {
+                $self->{'military'}->{wars}++;
+            } else {
+                $self->{'military'}->{attack}++;
+            }
+        }
+    }
+    # print(Dumper($self->{'military'}));
+}
+
+sub checkMilitaryAdvisorReportView {
+    my $self = shift;
+    my $combatId = shift;
+    my $res = $self->{mech}->get(sprintf('http://%s/index.php?view=militaryAdvisorReportView&detailedCombatId=%s', $self->{server}, $combatId));
+    my $extractor = new Ikariam::Extractor(content => $res->content);
+
+    my %report;
 
-    # there are some details.
-#<div id="homeland" class="content">
-#                <ul id="battleReports">
-#                    <li class="groundfight">
-#                        <div class="type"/>
-#                        <div class="report">
-#                            <table cellspacing="0" cellpadding="0" class="repTable">
-#                                <tbody><tr>
-#                                    <th colspan="2">
-#                                        攻擊 卍严破碎虛空焱卐                                    </th>
-#                                </tr>
-#                            </tbody></table>
-#                        </div>
-#                        <br class="clearfloat"/>
-#                    </li>
-#					<li class="enroute" style="height: 52px;">
-#						<script type="text/javascript">
-#							Event.onDOMReady(function() {
-#								getCountdown({
-#									enddate: 1224863393,
-#									currentdate: 1224862910,
-#									el: "researchCountDown11396440"
-#									}, 3, " ", "", true, true);
-#								});
-#							</script>
-#                        <div class="type">
-#                            <div id="researchCountDown11396440" class="time">5分 46秒</div><div class="undefined"> Finished 23:49:23</div>
-#                        </div>
-#                        <div class="report">
-#來自 <a href="?view=island&amp;cityId=98496">松鼠城</a> 的 3 兵力正在前往 <a href="?view=island&amp;cityId=82959">卍严破碎虛空焱卐</a> 的途中。                        </div>
-#                        <div class="action">
-#                            <div class="return">
-#                                <a href="?action=transportOperations&amp;function=forceReturn&amp;actionRequest=46cc5fdb56b8cb0e65e2116ff9f61512&amp;eventId=11396440&amp;oldView=militaryAdvisorMilitaryMovements" title="返回">返回</a>
-#                            </div>
-#                        </div>
-#                        <br class="clearfloat"/>
-#                    </li>
-#		        </ul>
-#</div>
+    $report{id} = $combatId;
+    my $c = $extractor->{doc}->toString(1);
+    
+    # FIXME 城鎮  6 級)的城牆( 2 級)為防禦部隊增加了 7% 的防禦力。
+    while($c =~ /城鎮\s+(\d+)/gs) {
+        $report{cityLevel} = $1;
+        $report{wallLevel} = $2;
+    }
+    
+    $report{attacker} = $extractor->find('//div[@id="troopsReport"]//table[@id="result"]//th[@class="attackers"]');
+    $report{defender} = $extractor->find('//div[@id="troopsReport"]//table[@id="result"]//th[@class="defenders"]');
+    $report{winner} = $extractor->find('//div[@id="troopsReport"]//table[@id="result"]//td[@class="winner"]');
 
+    $report{targetCity} = $extractor->find('//div[@id="troopsReport"]/div/h3/a/text()');
+    my $href = $extractor->find('//div[@id="troopsReport"]/div/h3/a/@href');
+    if($href =~ /index\.php\?view=island&id=(\d+)&selectCity=(\d+)/) {
+       $report{island} = $1;
+       $report{city} = $2;
+    }
+    $report{gold} = $extractor->find('//div[@id="troopsReport"]//ul[@class="resources"]/li[@class="gold"]');
+    $report{wood} = $extractor->find('//div[@id="troopsReport"]//ul[@class="resources"]/li[@class="wood"]');
+    $report{crystal} = $extractor->find('//div[@id="troopsReport"]//ul[@class="resources"]/li[@class="glass"]');
+    $report{wine} = $extractor->find('//div[@id="troopsReport"]//ul[@class="resources"]/li[@class="wine"]');
+    $report{sulfur} = $extractor->find('//div[@id="troopsReport"]//ul[@class="resources"]/li[@class="sulfur"]');
 
-#    <div id="elsewhere" class="content">
-#                <ul id="battleReports">
-#                    <li class="groundfight">
-#                        <div class="type"/>
-#                        <div class="report">
-#                            <table cellspacing="0" cellpadding="0" class="repTable">
-#                                <tbody><tr>
-#                                    <th colspan="2">
-#                                        攻擊 Rex                                    </th>
-#                                </tr>
-#                            </tbody></table>
-#                        </div>
-#                        <br class="clearfloat"/>
-#                    </li>
-#                    <li class="enroute">
-#                        <script type="text/javascript">
-#												Event.onDOMReady(function() {
-#													getCountdown({
-#														enddate: 1224877160,
-#														currentdate: 1224861666,
-#														el: "researchCountDown159638"
-#														}, 3, " ", "", true, true);
-#													});
-#                        </script>
-#                        <div class="type">
-#                            <div id="researchCountDown159638" class="time">4時 17分 35秒</div>
-#                        </div>
-#                        <div class="report">
-#來自 <a href="?view=island&amp;cityId=6078">水晶交換各種資源</a> 的 3 兵力正在前往 <a href="?view=island&amp;cityId=6077">Rex</a> 的途中。                        </div>
-#                    </li>
-#		        </ul>
-#            </div>
+    foreach(qw/gold wood crystal wine sulfur attacker defender winner/) {
+        $report{$_} =~ s/^.*?:\s+//;
+        $report{$_} =~ s/\s+$//;
+        $report{$_} =~ s/,//g;
+    }
+    return \%report;
 }
 
 sub checkMilitaryAdvisorCombatReports {
     my $self = shift;
-    my $content;
-    my $res = $self->{mech}->get(sprintf('http://%s/index.php?view=militaryAdvisorCombatReports', $self->{server}));
-    gunzip \$res->content => \$content 
-        or die "gunzip failed: $GunzipError\n";
+    my $page = shift || 0;
+
+    my $res = $self->{mech}->get(sprintf('http://%s/index.php?view=militaryAdvisorCombatReports&start=%s', $self->{server}, $page));
+    my $extractor = new Ikariam::Extractor(content => $res->content);
+
+    my $result = $extractor->{doc}->find('//div[@id="troopsOverview"]//table/tr[position()<=10]');
+    foreach my $tr ( @$result ) {
+        my $trExtractor = new Ikariam::Extractor(content => $tr->toString(1));
+        my $date = $trExtractor->find('//td[@class="date"]');
+        $date =~ s/^\s+//g; $date =~ s/\s+$//g;
+
+
+        my $href = $trExtractor->find('//td/a/@href');
+        if($href =~ /index.php\?view=militaryAdvisorReportView&combatId=(\d+)/) {
+            my $report = Ikariam::Report->retrieve($1);
+            if(!$report) {
+                my $report_hash = $self->checkMilitaryAdvisorReportView($1);
+                $report = Ikariam::Report->insert($report_hash);
 
-    # TODO
+                if($date =~ /(\d+)\.(\d+)\. (\d+):(\d+)/) {
+                    my $unixtime = mktime (0, $4, $3, $1, ($2-1), '108');
+                    $report->set('date', $unixtime);
+                }
+                $report->set('time', time);
 
-    # list down reports.
-    # /index.php?view=militaryAdvisorReportView&amp;combatId=1887662
-    # //form[@id='finishedReports']//a
+                $report->update();
+            } else {
+                return;
+            }
+        } else {
+            return;
+        }
+    }
+
+    my @moreCombats = $extractor->find('//div[@id="troopsOverview"]//table/tr[position()>10]//a/@href');
+    foreach (@moreCombats){
+        last if(/^javascript/);
+        if(/\?view=militaryAdvisorCombatReports&start=(\d+)/) {
+            next if($1 le $page);
+            $self->checkMilitaryAdvisorCombatReports($1);
+            last;
+        }
+    }
 }
 
 sub checkTownHall {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/warfare.pl	Wed Oct 29 12:05:36 2008 +0800
@@ -0,0 +1,136 @@
+#!/usr/bin/perl
+use strict;
+use Ikariam;
+use Data::Dumper;
+use Decision::ParseTree q{ParseTree};
+use YAML qw/LoadFile Dump DumpFile/;
+
+package Ikariam::Warfare::Rules;
+use strict;
+use Data::Dumper;
+
+Ikariam::User->has_many(cities => 'Ikariam::Cities');
+Ikariam::User->has_a(ally => 'Ikariam::Ally');
+Ikariam::Cities->has_a(user => 'Ikariam::User');
+Ikariam::Cities->has_a(island => 'Ikariam::Island');
+Ikariam::Cities->set_sql(sheeps => qq {
+        SELECT cities.cityId
+          FROM user, cities 
+         WHERE user.id == cities.user 
+           AND user.army_score_main == 0
+           AND cities.island IN (SELECT island.id FROM island WHERE island.x <= ? AND island.x >= ? AND island.y <= ? AND island.y >= ? )
+         ORDER BY cities.citylevel * (cities.citylevel - 1) * user.trader_score_secondary / 10000 DESC LIMIT 10
+    }
+);
+Ikariam::Report->set_sql(attack => qq {
+        SELECT report.id
+          FROM report
+         WHERE report.city = ?
+           AND report.date >= ?
+    }
+);
+
+sub new {
+    my ( $class, $i ) = @_;
+    my $self = {
+        ikariam => $i,
+    };
+    return bless $self, $class;
+}
+
+sub is_attacked {
+    my ($self, $city) = @_;
+    return ($self->{'ikariam'}->{'military'}->{attacks} > 0 ) ? 1 : 0;
+}
+
+sub is_actionPoint_enough {
+    my ($self, $city) = @_;
+    return ($city->{actionPoints} > ($city->{maxActionPoints}/2) ) ? 1 : 0;
+}
+
+sub is_army_available {
+    my ($self, $city) = @_;
+    return ($city->{army}->{Swordsman} > 2 ) ? 1 : 0;
+}
+
+
+sub locateVictim {
+    my ($self, $city, $x, $y) = @_;
+    my @cities = Ikariam::Cities->search_sheeps(($x + 6), ($x - 6), ($y + 6), ($y - 6));
+
+    foreach my $city (@cities) {
+        my $sheep = $city->user;
+        my $island = $city->island;
+
+        # Ignore 假期模式
+        next if($city->status eq 'v');
+        unless ($city->status eq 'i') {
+            unless ($sheep->allyId == '0') {
+                my $ally = $sheep->ally;
+                # Ignore 聯盟人數大於10
+                next if(defined($ally) && $ally->members > 10);
+            }
+        }
+
+        # check if we over-attacked
+        my $c = Ikariam::Report->search_attack($city->cityId, time - 24*60*60 - 7*60*60)->count();
+        # check the current plunders
+        foreach ($self->{ikariam}->{'military'}->{'homeland'}) {
+            $c++ if($_{to} eq $city->cityId);
+        }
+        next if($c >= 4);
+
+        # {
+        # if(1) {
+            my $capture = $city->citylevel * ($city->citylevel - 1) * $sheep->trader_score_secondary / 10000;
+            my $line = sprintf("%d %s [%d,%d] %s %s/%s", 
+                $capture, $city->status, 
+                $island->x, $island->y,
+                $city->cityname,
+                $sheep->name, $sheep->ally);
+            printf("%s\n", $line);
+        # }
+        return $city->cityId;
+    }
+    return undef;
+}
+
+sub engagement {
+    my ($self, $cityId, $x, $y) = @_;
+    my $victim = $self->locateVictim($cityId, $x, $y);
+    if(defined($victim)) {
+        $self->{ikariam}->changeCity($cityId);
+        $self->{ikariam}->plunderCity($victim);
+    }
+}
+
+1;
+
+package main;
+
+if($#ARGV != 1) { die("Usage: $0 x y\n"); }
+my ($x, $y) = @ARGV;
+
+our $i = new Ikariam($::server, $::user, $::pass);
+our $rules = Ikariam::Warfare::Rules->new($i);
+
+$i->login;
+my $cities = $i->check;
+$i->checkMilitaryAdvisorCombatReports();
+print Dump($i->{'military'});
+
+foreach my $cityId (keys(%$cities)) {
+    # build and upgrade for cities
+    my $tree  = LoadFile('warfare.yaml');
+    $cities->{$cityId}->{parse_path} = [];
+    $cities->{$cityId}->{parse_answer} = undef;
+    if(ParseTree($tree, $rules, $cities->{$cityId}) eq 'engagement') {
+        $rules->engagement($cityId, $x, $y);
+        sleep(30);
+        $i->checkMilitaryAdvisorMilitaryMovements();
+    }
+    print Dump($cities->{$cityId}->{parse_path});
+}
+DumpFile("warfare-dump.yaml", $cities);
+
+$i->logout;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/warfare.yaml	Wed Oct 29 12:05:36 2008 +0800
@@ -0,0 +1,7 @@
+---
+- is_attacked:
+    0:
+       - is_actionPoint_enough:
+          1:
+             - is_army_available:
+                1: engagement