changeset 87:4c82cf844aa8

counting navy expenditure
author "Rex Tsai <chihchun@kalug.linux.org.tw>"
date Sun, 26 Oct 2008 15:02:06 +0800
parents a8acf50bf7bb (diff) f2a7de8ac5c1 (current diff)
children 0fa9cd836e1e
files Ikariam.pm agent.pl
diffstat 9 files changed, 755 insertions(+), 357 deletions(-) [+]
line wrap: on
line diff
--- a/Ikariam.pm	Thu Oct 23 00:59:20 2008 +0800
+++ b/Ikariam.pm	Sun Oct 26 15:02:06 2008 +0800
@@ -13,6 +13,79 @@
     namespace => 'Ikariam',
 );
 
+package Ikariam::Extractor;
+use strict;
+use Data::Dumper;
+use XML::LibXML;
+use IO::Uncompress::Gunzip qw(gunzip $GunzipError) ;
+use Carp;
+use utf8;
+
+sub new {
+    Carp::croak("Options should be key/value pairs, not hash reference") if ref($_[1]) eq 'HASH'; 
+
+    my($class, %conf) = @_;
+
+    my $self = bless {
+        doc => undef,
+        gzip => 1,
+    }, $class;
+
+    $self->{gzip} = $conf{'gzip'} if(defined($conf{'gzip'}));
+    $self->parse($conf{'content'}) if(defined($conf{'content'}));
+
+    return $self;
+}
+
+# parse($Content);
+sub parse {
+    my ($self, $content) = @_;
+    my $string;
+    my $parser = XML::LibXML->new('1.0','utf-8');
+
+    if($self->{gzip} == 1) {
+        gunzip \$content => \$string
+            or die "gunzip failed: $GunzipError\n";
+    } else {
+        $string = $content;
+    }
+
+    $self->{doc} = $parser->parse_html_string ($string, { suppress_errors => 1 });
+    return;
+}
+
+# find($XPathQuery);
+sub find {
+    my $self = shift;
+    my $query = shift;
+    my $out = [];
+
+    my $result = $self->{doc}->find($query);
+
+    return undef unless defined($result);
+
+    if ( $result->isa( 'XML::LibXML::NodeList' ) ) {
+        return () if($result->size() == 0);
+        foreach ( @$result ) {
+            # $_ is XML::LibXML::Element, XML::LibXML::Node
+            my $literal = $_->to_literal() , "\n";
+            utf8::encode($literal);
+            # warn $_->toString(1) , "\n";
+            return $literal unless wantarray;
+            push( @$out, $literal);
+        }
+    } else {
+        Carp::croak("Unsupported data type"); 
+    }
+    # TODO
+    # XML::LibXML::Literal
+    # XML::LibXML::Boolean
+
+    return @$out;
+}
+
+1;
+
 package Ikariam;
 use strict;
 use Data::Dumper;
@@ -22,7 +95,9 @@
 use HTTP::Cookies;
 use WWW::Mechanize;
 use HTML::TagParser;
+use XML::LibXML qw(:encoding);
 use IO::Uncompress::Gunzip qw(gunzip $GunzipError) ;
+use utf8;
 
 sub new
 {
@@ -82,6 +157,7 @@
         view => 'highscore'
         ]);
 
+    # TODO rewrite with Xpath
     my $c;
     my $status = gunzip \$res->content => \$c 
         or die "gunzip failed: $GunzipError\n";
@@ -269,6 +345,12 @@
     my $param = shift;
     my $cityId = shift;
 
+    # TODO: 應該扣除買船之黃金, 以免誤計
+    # TODO: 加上所有之船隻
+    # my @cargoCost = qw/160 244 396 812 1240 1272 1832 1888 3848 3972 5204 5384 6868 7120 8864 9200 11268 11712 14108 14680 23320 24288 28664 29880 34956 36468 42348 44212 51024 53308 61236 64024 73096 76468 87020 91088 103224 116524 122072 137432 180060 202132 211964 237444 249108 278276 292076 306623 321963 338138 355198 373191 392171 412195 433320 455612 479135 503962 530166 557828 587031 617863 650420 684802 721113 759466 799981 842783 888005 935790 986286 1039654 1096062 1155689 1218724 1285369 1355837 1430353 1509159 1592508 1680670 1773932 1872597 1976989 2087448 2204338 2328045 2458976 2597567 2744276 2899594 3064040 3238163 3422550 3617820 3824635 4043693 4275738 4521561 4782000 5057946 5350345 5660202 5988585 6336630 6705540 7096598 7511164 7950683 8416694 8910828 9434823 9990523 10579889 11205006 11868090 12571498 13317734 14109462 14949514/;
+    # $city->{transporters}->{sum}
+ 
+    # TODO: 找買船最便宜之城市
     my @locations = @{$self->{'cities'}->{$cityId}->{locations}};
     foreach (1..2) {
         if($locations[$_] eq 'port') {
@@ -291,9 +373,9 @@
         $position = $_ if($locations[$_] eq $type);
     }
 
-    if($position == -1)
-    {
+    if($position == -1) {
         foreach (0..$#locations) {
+            # XXX
             next if($_ <= 2 && ($self->{buildingIDs}->{$type} ne "workshop-fleet" &&
                     $self->{buildingIDs}->{$type} ne "shipyard"));
             if($locations[$_] eq undef) {
@@ -303,27 +385,17 @@
             }
         }
     } else {
-        $self->{mech}->add_header( Referer =>  
-            sprintf("http://%s/index.php?view=%s&id=%s&position=%d", $self->{server}, $type, $cityId, $position));
-        my $res = $self->{mech}->post(sprintf('http://%s/index.php', $self->{server}), [
-            action => 'CityScreen',
-            'function' => 'upgradeBuilding',
-            id => $cityId,
-            position => $position,
-            level => $self->{'cities'}->{$cityId}->{buildings}->{$type},
-            oldView => $type,
-            ]);
-#        my $content;
-#        gunzip \$res->content => \$content 
-#            or die "gunzip failed: $GunzipError\n";
-#        print ($content);
+        my $res = $self->{mech}->get (sprintf("http://%s/index.php?view=%s&id=%s&position=%d", $self->{server}, $type, $cityId, $position));
+        my $url = Ikariam::Extractor->new(content => $res->content)->find('//a[@title="升級建築物"]/@href');
+        $self->{mech}->get($url) if(defined($url));
     }
 }
 
 sub run {
     my $self = shift;
+    my $param = shift;
     # defense.
-    die("Not implemented");
+    die("run $param not implemented yet.");
 }
 
 sub research
@@ -334,12 +406,6 @@
 
     # check if we are researching the same stuff
     my $res =  $self->{mech}->get(sprintf('http://%s/index.php?action=CityScreen&function=changeResearch&id=%s&researchType=%s', $self->{server}, $cityId, $type));
-
-#    my $content;
-#    gunzip \$res->content => \$content 
-#        or die "gunzip failed: $GunzipError\n";
-#
-#    print ($content);
 }
 
 sub checkResearch {
@@ -347,186 +413,406 @@
     my $cityId = shift;
 
     my $res = $self->{mech}->get(sprintf('http://%s/index.php?view=researchOverview&id=%s', $self->{server}, $cityId));
+    
+    my @urls = Ikariam::Extractor->new(content => $res->content)->find('//ul[@class="explored"]//a/@href');
+    my $out = {};
+    foreach(@urls) {
+        if(/view=researchDetail&id=\d+&position=\d+&researchId=(\d+)$/) {
+            # we ignore the chinese name of technology researched.
+            @$out{$1} = 1;
+        }
+    }
+    return $out;
+}
 
-    my $content;
-    gunzip \$res->content => \$content 
-        or die "gunzip failed: $GunzipError\n";
-    my $html = HTML::TagParser->new($content);
+sub checkCity {
+    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,
+        fields      => {
+            cityId => $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);
+
+    $self->{'cities'}->{$cityId}->{resources}->{gold} = $extractor->find('//span[@id="value_gold"]/text()');
+    $self->{'cities'}->{$cityId}->{resources}->{gold} =~ s/,//g;
+
+    $self->{'cities'}->{$cityId}->{name} = $extractor->find('//span[@class="city"]/text()');
 
-    my @elems = $html->getElementsByAttribute('class', 'explored');
+    $self->{'cities'}->{$cityId}->{construction} = defined($extractor->find('//*[@class="constructionSite"]')) ? 1 : 0;
+
+    # maxCapacity
+    my $page = $extractor->{doc}->toString(1);
+    if($page =~ /maxCapacity : {\s+wood: (\d+),\s+wine: (\d+),\s+marble: (\d+),\s+crystal: (\d+),\s+sulfur: (\d+)\s+}/s) {
+        $self->{'cities'}->{$cityId}->{maxCapacity}->{wood} = $1;
+        $self->{'cities'}->{$cityId}->{maxCapacity}->{wine} = $2;
+        $self->{'cities'}->{$cityId}->{maxCapacity}->{marble} = $3;
+        $self->{'cities'}->{$cityId}->{maxCapacity}->{crystal} = $4;
+        $self->{'cities'}->{$cityId}->{maxCapacity}->{sulfur} = $5;
+    }
 
-    my $out = {};
-    foreach my $elem (@elems) {
-        my @items = getElementsByTagName($elem, "a");
-        foreach my $item (@items) {
-            if($item->getAttribute('href') =~ /view=researchDetail&id=\d+&position=\d+&researchId=(\d+)$/) {
-                @$out{$1} = $item->innerText();
+    foreach my $good (qw/wood wine marble crystal sulfur/) {
+        $self->{'cities'}->{$cityId}->{resources}->{$good} = $extractor->find(sprintf('//span[@id="value_%s"]', $good));
+        $self->{'cities'}->{$cityId}->{resources}->{$good} =~ s/,//g;
+    }
+
+    foreach my $i (0..14) {
+        my @buildings = $extractor->find(sprintf('//li[@id="position%s"]/@class', $i));
+        foreach my $building (@buildings) {
+            if (!($building =~ /buildingGround/) && !($building =~ /townhall/)) {
+                $self->{'cities'}->{$cityId}->{locations}[$i] = $building;
+
+                my $span;
+                my @spans = $extractor->find(sprintf('//li[@id="position%s"]//span[@class="textLabel"]/text()', $i));
+                if($#spans >= 1) {
+                    $span = $spans[1];
+                } else {
+                    $span = $spans[0];
+                }
+                my (undef, undef, $level) = split(/ /, $span);
+                $self->{'cities'}->{$cityId}->{buildings}->{$building} = $level;
             }
         }
     }
-    return $out;
+
+    $self->{'cities'}->{$cityId}->{transporters}->{avail} = $extractor->find('//span[@id="value_transAvail"]/text()');
+    $self->{'cities'}->{$cityId}->{transporters}->{sum} = $extractor->find('//span[@id="value_transSum"]/text()');
+    $self->{'cities'}->{$cityId}->{transporters}->{sum} =~ s/[\(|\)]//g;
+    $self->{'cities'}->{$cityId}->{maxActionPoints} = $extractor->find('//span[@id="value_maxActionPoints"]');;
+}
+
+
+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;
+
+    # 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>
+
+
+#    <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>
+}
+
+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";
+
+    # TODO
+
+    # list down reports.
+    # /index.php?view=militaryAdvisorReportView&amp;combatId=1887662
+    # //form[@id='finishedReports']//a
+}
+
+sub checkTownHall {
+    my $self = shift;
+    my $cityId = shift;
+
+    my $res = $self->{mech}->get(sprintf('http://%s/index.php?view=townHall&id=%d', $self->{server}, $cityId));
+    my $extractor = new Ikariam::Extractor(content => $res->content);
+
+    # //div[@id='SatisfactionOverview']//div[@class='value']
+    # //div[@id='SatisfactionOverview']//div[@class='text']
+    $self->{"cities"}->{$cityId}->{happiness} = $extractor->find('//div[@id="SatisfactionOverview"]//div[@class="value"]');
+
+    # 取 SatisfactionOverview 中的其他值
+    # 一個城鎮的市民滿意度結合了多方面的因素
+    # check happiness
+    # Happiness = Basic bonuses (196 + Capital Bonus + Holiday Bonus(25)) + 
+    #             Wine (Tavern Base(12*level) + 
+    #             Tavern Bonus(80*step)) + 
+    #             Culture (Museum Base(20*level) + 
+    #             Cultural Goods Bonus(50*Cultural Goods)) - 
+    #             Population (population) - 
+    #             Corruption (Corruption rate * population)
+    #
+    # Growth Rate = Happiness * 0.02
+
+    # Space, 房屋數
+    $self->{'cities'}->{$cityId}->{"space"} = {};
+    $self->{'cities'}->{$cityId}->{"space"}->{'total'} = $extractor->find('//span[@class="value total"]/text()');
+    $self->{'cities'}->{$cityId}->{"space"}->{'occupied'} = $extractor->find('//span[@class="value occupied"]/text()');
+
+    my @values = $extractor->find('//div[@id="CityOverview"]//span[@class="value"]');
+    $self->{'cities'}->{$cityId}->{"growth"} = $values[0];
+    $self->{'cities'}->{$cityId}->{"incomegold"} = $values[1];
+
+    $self->{'cities'}->{$cityId}->{corruption} = $extractor->find('//li[@class="corruption"]//span[@title="目前腐敗程度"]');
+    $self->{'cities'}->{$cityId}->{corruption} =~ s/%//g;
+
+    my @citizens_type = qw/citizens woodworkers specialworkers scientists/;
+    @values = $extractor->find('//div[@id="PopulationGraph"]//span[@class="count"]');
+    foreach my $i (0..$#citizens_type)
+    {
+        $self->{'cities'}->{$cityId}->{'citizens'}->{$citizens_type[$i]} = $values[$i];
+        $self->{'cities'}->{$cityId}->{'citizens'}->{total} += $values[$i];
+    }
+}
+
+
+sub checkArmies
+{
+    my $self = shift;
+    my $cityId = shift;
+    my %force_types;
+
+    $force_types{'army'} = [ qw/undef undef Slinger Swordsman Phalanx Ram Archer Catapult Gunsman Mortar SteamGiant Gyrocopter Bombardier Doctor Cook/ ];
+    $force_types{'fleet'} = [ qw/undef undef Ram-Ship BallistaShip Flamethrower CatapultShip MortarShip PaddleWheelRam DivingBoat/ ];
+
+    foreach my $x (qw/army fleet/) {
+        $self->{'cities'}->{$cityId}->{$x} = {};
+        my $res = $self->{mech}->get(sprintf('http://%s/index.php?view=cityMilitary-%s&id=%d', $self->{server}, $x, $cityId));
+        my @numbers = Ikariam::Extractor->new(content => $res->content)->find('//table//tr/td');
+        foreach my $j (0..$#{$force_types{$x}}) {
+            next if(!defined($force_types{$x}[$j]));
+            if ($numbers[$j] == '-') {
+                $self->{'cities'}->{$cityId}->{$x}->{$force_types{$x}[$j]} = 0;
+            } else {
+                $self->{'cities'}->{$cityId}->{$x}->{$force_types{$x}[$j]} = $numbers[$j];
+            }
+        }
+    }
 }
 
 sub check
 {
     my $self = shift;
 
+    # MilitaryAdvisor
+    $self->checkMilitaryAdvisorMilitaryMovements();
+    $self->checkMilitaryAdvisorCombatReports();
+    $self->{'research'} = $self->checkResearch((keys(%{$self->{'cities'}}))[0]);
+
+    # alerts
+    # //li[@id="advCities"]/a[@class="normalalert"]
+    # //li[@id="advMilitary"]/a[@class="normalalert"]
+    # //li[@id="advResearch"]/a[@class="normalalert"]
+    # //li[@id="advDiplomacy"]/a[@class="normalalert"]
+
     # looking for cities
     foreach my $cityId (keys(%{$self->{'cities'}})) 
     {
-        # search for goods
-        my $res = $self->{mech}->post(sprintf('http://%s/index.php', $self->{server}), [
-            action => 'header',
-            cityId => $cityId,
-            function => 'changeCurrentCity',
-            id => $cityId,
-            oldView => 'city',
-        ]);
-
-        my $content;
-        gunzip \$res->content => \$content 
-            or die "gunzip failed: $GunzipError\n";
-
-        my $html = HTML::TagParser->new($content);
-        my @elems;
+        $self->checkCity($cityId);
+        $self->checkTownHall($cityId);
+        $self->checkArmies($cityId);
+    }
+    return $self->{'cities'};
+}
 
-        my ($elem) = $html->getElementsByAttribute("id", "value_gold");
-        $self->{'cities'}->{$cityId}->{resources}->{gold} = $elem->innerText();
-        $self->{'cities'}->{$cityId}->{resources}->{gold} =~ s/,//g;
-
-        my ($elem) = $html->getElementsByAttribute("class", "city");
-        $self->{'cities'}->{$cityId}->{name} = $elem->innerText();
+sub getNetIncome {
+    my $self = shift;
+    my $cityId = shift;
 
-        my ($elem) = $html->getElementsByAttribute("class", 'constructionSite');
-        $self->{'cities'}->{$cityId}->{construction} = 0;
-        $self->{'cities'}->{$cityId}->{construction} = 1 if(defined($elem));
+    Carp::croak("must assign cityId") if !defined($cityId);
 
-        # check goods
-        foreach my $good (qw/wood wine marble crystal sulfur/) {
-            my ($elem) = $html->getElementsByAttribute("id", "value_" . $good);
-            $self->{'cities'}->{$cityId}->{resources}->{$good} = $elem->innerText();
-            $self->{'cities'}->{$cityId}->{resources}->{$good} =~ s/,//g;
-        }
+    # 扣除研發每人花費 8 GOLD
+    return ($self->{cities}->{$cityId}->{citizens}->{total}*4 - $self->{cities}->{$cityId}->{citizens}->{scientists}*8 );
+}
 
-        # search locations
-        foreach my $i (0..14) {
-            my ($elem) = $html->getElementsByAttribute("id", "position" . $i);
-            my $building = $elem->getAttribute('class');
-            if (!($building =~ /buildingGround/)) {
-                $self->{'cities'}->{$cityId}->{locations}[$i] = $building;
-                my $span = getElementsByAttribute($elem, "class", "textLabel");
-                my (undef, undef, $level) = split(/ /, $span->innerText());
-                $self->{'cities'}->{$cityId}->{buildings}->{$building} = $level;
-            }
-        }
+sub getNavyExpenditure {
+    my $self = shift;
+    my $cityId = shift;
+    my $cities = $self->{cities};
 
-        # transporters
-        my ($elem) = $html->getElementsByAttribute("class", 'transAvail');
-        $self->{'cities'}->{$cityId}->{transporters}->{avail} = $elem->innerText();
-        my ($elem) = $html->getElementsByAttribute("class", 'transSum');
-        $self->{'cities'}->{$cityId}->{transporters}->{sum} = $elem->innerText();
-        $self->{'cities'}->{$cityId}->{transporters}->{sum} =~ s/\(//;
-        $self->{'cities'}->{$cityId}->{transporters}->{sum} =~ s/\)//;
-
-        $res = $self->{mech}->get(sprintf('http://%s/index.php?view=militaryAdvisorMilitaryMovements', $self->{server}, $cityId));
-        gunzip \$res->content => \$content 
-            or die "gunzip failed: $GunzipError\n";
-
-        $self->{'cities'}->{$cityId}->{force}->{attacks} = $1 if($content =~ /敵人攻擊: (\d+)/);
-        $self->{'cities'}->{$cityId}->{force}->{wars} = $1 if($content =~ /我方軍隊行程: (\d+)/);
-        # if($content =~ /更新戰鬥報告: (\d+)/);
-        # if($content =~ /新的戰鬥報告: (\d+)/);
-
-        # sub checkTownHall {
-        $res = $self->{mech}->get(sprintf('http://%s/index.php?view=townHall&id=%d', $self->{server}, $cityId));
-        gunzip \$res->content => \$content 
-            or die "gunzip failed: $GunzipError\n";
+    my $ships = {
+        CargoShip => {n => "CargoShip",a => 0,d => 0,s => 4,c => "Steamship",v => 20,A => 0,D => 0},
+        'Ram-Ship' => {n => "Ram-Ship",p => 6,w => 56,S => 21,b => "34m",u => 20,m => 1,o => 3,a => 16,d => 16,A => 4,D => 4,s => 10,c => "Sailer",v => 10},
+        BallistaShip => {n => "BallistaShip",p => 5,w => 72,S => 29,b => "47m",u => 24,m => 3,o => 5,a => 20,d => 28,A => 5,D => 7,s => 11,c => "Sailer",v => 8,x => "Resistance"},
+        Flamethrower => {n => "Flamethrower",p => 5,w => 105,S => 77,b => "1h 55m",u => 45,m => 5,o => 7,a => 40,d => 40,A => 10,D => 10,s => 12,c => "Steamship",v => 8,x => "Assault"},
+        CatapultShip => {n => "CatapultShip",p => 10,w => 173,S => 76,b => "3h 11m",u => 57,m => 7,o => 10,a => 60,d => 60,A => 12,D => 12,s => 16,c => "Sailer",v => 6},
+        MortarShip => {n => "MortarShip",p => 22,w => 456,S => 282,b => "3h 38m",u => 130,m => 12,o => 15,a => 160,d => 160,A => 35,D => 35,s => 14,c => "Steamship",v => 4},
+        PaddleWheelRam => {n => "PaddleWheelRam",i => "steamboat",p => 12,w => 513,S => 167,b => "4h 8m",u => 114,m => 10,o => 13,a => 100,d => 90,A => 20,D => 18,s => 13,c => "Steamship",v => 8,x => "Assault"},
+        DivingBoat => {n => "DivingBoat",i => "submarine",p => 16,w => 493,C => 378,b => "5h 5m",u => 126,m => 15,o => 16,a => 110,d => 155,A => 20,D => 30,s => 10,c => "Steamship",v => 2,x => "Resistance"}
+    };
+    my $cost = 0;
+    foreach(keys(%{$cities->{$cityId}->{fleet}})) {
+        $cost += $cities->{$cityId}->{fleet}->{$_} * $ships->{$_}->{u};
+    }
+    return $cost;
+}
 
-        # check happiness
-        # Happiness = Basic bonuses (196 + Capital Bonus + Holiday Bonus(25)) + 
-        #             Wine (Tavern Base(12*level) + 
-        #             Tavern Bonus(80*step)) + 
-        #             Culture (Museum Base(20*level) + 
-        #             Cultural Goods Bonus(50*Cultural Goods)) - 
-        #             Population (population) - 
-        #             Corruption (Corruption rate * population)
-        #
-        # Growth Rate = Happiness * 0.02
-        $html = HTML::TagParser->new($content);
-
-        my @happiness = ("ecstatic", "happy", "neutral", "sad", "outraged");
-        foreach my $j (0..$#happiness) {
-            my ($elem) = $html->getElementsByAttribute("class", sprintf("happiness happiness_%s", $happiness[$j]));
-            if(defined($elem)) {
-                $self->{'cities'}->{$cityId}->{happiness} = $j;
-                $self->{'cities'}->{$cityId}->{happiness_text} = $happiness[$j];
-            }
-        }
+sub getMilityExpenditure {
+    my $self = shift;
+    my $cityId = shift;
+    my $cities = $self->{cities};
+    my $troops = {
+        # p 需要人口
+        # w 木頭
+        # S 硫磺
+        # b 製造時間
+        # u 維持費用
+        # m 最低軍營等級
+        # o 最佳軍營等級
+        # a 攻擊力
+        # d 防守力
+        # A 
+        # D 
+        # s 耐力
+        # c 種類
+        # v speed
+        # x 加成 Assault, Resistance
+        Slinger => {n=>"Slinger",p=>1,w=>40,b=>"12m",u=>8,m=>1,o=>4,a=>7,d=>6,A=>2,D=>2,s=>10,c=>"Human",v=>20},
+        Swordsman => {n=>"Swordsman",p=>2,w=>47,S=>16,b=>"17m",u=>16,m=>3,o=>5,a=>18,d=>14,A=>4,D=>3,s=>12,c=>"Human",v=>20,x=>"Assault"},
+        Phalanx => {n=>"Phalanx",p=>4,w=>104,S=>64,b=>"40m",u=>24,m=>4,o=>7,a=>24,d=>40,A=>6,D=>10,s=>14,c=>"Human",v=>20,x=>"Resistance"},
+        Ram => {n=>"Ram",p=>8,w=>198,S=>128,b=>"42m",u=>52,m=>5,o=>8,a=>14,d=>18,A=>3,D=>4,s=>16,c=>"Machina",v=>20,x=>"Ram"},
+        Archer => {n=>"Archer",p=>4,w=>172,S=>86,b=>"49m",u=>32,m=>7,o=>10,a=>40,d=>40,A=>10,D=>10,s=>12,c=>"Human",v=>20},
+        Catapult => {n=>"Catapult",p=>10,w=>342,S=>232,b=>"49m",u=>72,m=>10,o=>14,a=>36,d=>28,A=>9,D=>7,s=>16,c=>"Machina",v=>20,x=>"Ram"},
+        Gunsman => {n=>"Gunsman",i=>"marksman",p=>7,w=>355,S=>154,b=>"1h 23m",u=>58,m=>14,o=>18,a=>80,d=>64,A=>18,D=>14,s=>10,c=>"Human",v=>20},
+        Mortar => {n=>"Mortar",p=>12,w=>1325,S=>938,b=>"1h 53m",u=>128,m=>19,o=>21,a=>64,d=>64,A=>15,D=>15,s=>16,c=>"Machina",v=>20,x=>"Ram"},
+        SteamGiant => {n=>"SteamGiant",i=>"steamgiant",p=>6,w=>1150,S=>716,b=>"1h 45m",u=>68,m=>16,o=>20,a=>100,d=>140,A=>20,D=>30,s=>14,c=>"Machina",v=>20,x=>"Resistance"},
+        Gyrocopter => {n=>"Gyrocopter",p=>8,w=>1250,S=>670,b=>"1h 2m",u=>97,m=>12,o=>16,a=>112,d=>112,A=>25,D=>25,s=>12,c=>"Machina",v=>20},
+        Bombardier => {n=>"Bombardier",p=>24,w=>2270,S=>878,b=>"2h 10m",u=>228,m=>22,o=>24,a=>200,d=>165,A=>45,D=>35,s=>14,c=>"Machina",v=>20,x=>"Assault"},
+        Doctor => {n=>"Doctor",i=>"medic",p=>6,w=>640,C=>361,b=>"1h 2m",u=>244,m=>11,o=>12,a=>4,d=>28,A=>0,D=>0,s=>14,c=>"Human",v=>20,x=>"Healer"},
+        Cook => {n=>"Cook",p=>4,w=>520,W=>103,b=>"38m",u=>138,m=>8,o=>8,a=>6,d=>26,A=>0,D=>0,s=>16,c=>"Human",v=>20,x=>"Regeneration"}
+    };
 
-        # Space, 房屋數
-        $self->{'cities'}->{$cityId}->{"space"} = {};
-        foreach my $j (qw/occupied total/) {
-            my ($elem) = $html->getElementsByAttribute("class", sprintf("value %s", $j));
-            if(defined($elem)) {
-                $self->{'cities'}->{$cityId}->{"space"}->{$j} = $elem->innerText();
-            }
-        }
-
-        # Actions
-        # <span id="value_maxActionPoints">1</span>
-
-        # <li class="incomegold incomegold_negative">
-        # <li class="incomegold incomegold_positive">
-        # <span class="value">-178</span>
-
-        my ($elem) = $html->getElementsByAttribute("title", "目前腐敗程度");
-        if(defined($elem)) {
-            $self->{'cities'}->{$cityId}->{corruption} = $elem->innerText();
-            $self->{'cities'}->{$cityId}->{corruption} =~ s/%//g;
-        }
+    my $cost = 0;
+    foreach(keys(%{$cities->{$cityId}->{army}})) {
+        # printf("%s %d = %d\n", $_, $cities->{$cityId}->{army}->{$_},  $cities->{$cityId}->{army}->{$_} * $troops->{$_}->{u});
+        $cost += $cities->{$cityId}->{army}->{$_} * $troops->{$_}->{u};
+    }
+    return $cost;
+}
 
-        # countCiizens
-        my @citizens_type = qw/citizens woodworkers specialworkers scientists/;
-        @elems = $html->getElementsByAttribute('class', 'count');
-        $self->{'cities'}->{$cityId}->{'citizens'} = {};
-        $self->{'cities'}->{$cityId}->{'citizens'}->{total} = 0;
+sub blanceHurmanResource {
+    my $self = shift;
+    my $cityId = shift;
+    my $workersRatio = {
+        'citizens' => 0.4,
+        'specialworkers' => 0.3,
+        'woodworkers' => 0.7,
+    };
 
-        foreach my $i (0..$#citizens_type)
-        {
-            $self->{'cities'}->{$cityId}->{'citizens'}->{$citizens_type[$i]} = $elems[$i]->innerText();
-            $self->{'cities'}->{$cityId}->{'citizens'}->{total} += $elems[$i]->innerText();;
-        }
+    my $netincome = $self->getNetIncome($cityId);
 
-        # } 
-        
-        $self->{'cities'}->{$cityId}->{'research'} = $self->checkResearch($cityId);
+    # --- HR ---
+    # 扣除研發,四成種田生產,剩下 3:7 挖資源
+    # 四成收入中可用兩成做軍事用途
+    # 生產共四成 
+    my $produceworkers = int(($netincome * $workersRatio->{citizens}) / 4);
+
+    # 換成生產人力
+    my $freePeople = $self->{cities}->{$cityId}->{citizens}->{total} - ($produceworkers + $self->{cities}->{$cityId}->{scientists});
 
-        # sub checkArmies {
-        my %force_types;
-        $force_types{'army'} = [ qw/undef undef Slinger Swordsman Phalanx Ram Archer Catapult Gunsman Mortar SteamGiant Gyrocopter Bombardier Doctor Cook/ ];
-        $force_types{'fleet'} = [ qw/undef undef Ram-Ship BallistaShip Flamethrower CatapultShip MortarShip PaddleWheelRam DivingBoat/ ];
-        foreach my $x (qw/army fleet/)
-        {
-            $self->{'cities'}->{$cityId}->{$x} = {};
-            # search army
-            $res = $self->{mech}->get(sprintf('http://%s/index.php?view=cityMilitary-%s&id=%d', $self->{server}, $x, $cityId));
-            gunzip \$res->content => \$content 
-                or die "gunzip failed: $GunzipError\n";
+    # XXX
+    # 需計算資源開挖上限, 依照島等級區分
+    # 木頭
+#   create_slider({
+#            dir : 'ltr',
+#            id : "default",
+#            maxValue : 367,
+#            overcharge : 0,
+#            iniValue : 367,
+#            bg : "sliderbg",
+#            thumb : "sliderthumb",
+#            topConstraint: -10,
+#            bottomConstraint: 344,
+#            bg_value : "actualValue",
+#            bg_overcharge : "overcharge",
+#            textfield:"inputWorkers"
+#    });
 
-            $html = HTML::TagParser->new($content);
-            @elems = $html->getElementsByTagName('td');
-            foreach my $j (0..$#{$force_types{$x}}) {
-                next if($force_types{$x}[$j] eq 'undef');
-                if($elems[$j]->innerText() == '-') {
-                    $self->{'cities'}->{$cityId}->{$x}->{$force_types{$x}[$j]} = 0;
-                } else {
-                    $self->{'cities'}->{$cityId}->{$x}->{$force_types{$x}[$j]} = $elems[$j]->innerText();
-                }
-            }
-        }
-    }
-    # print Dumper($self->{'cities'});
-    return $self->{'cities'};
+#    	create_slider({
+#            dir : 'ltr',
+#            id : "default",
+#			maxValue : 367,
+#			overcharge : 0,
+#			iniValue : 367,
+#			bg : "sliderbg",
+#			thumb : "sliderthumb",
+#			topConstraint: -10,
+#			bottomConstraint: 344,
+#			bg_value : "actualValue",
+#			bg_overcharge : "overcharge",
+#			textfield:"inputWorkers"
+#	});
+
+    Carp::carp(sprintf("Suggested HR palnning - produce: %s wood %s special %s\n", $produceworkers, int($freePeople*0.3), int($freePeople*0.7)));
 }
 
 sub logout
@@ -543,22 +829,14 @@
         name => $self->{user},
         password => $self->{pass},
         ]);
-    my $c;
-    my $status = gunzip \$res->content => \$c 
-        or die "gunzip failed: $GunzipError\n";
+
+    my @cities = Ikariam::Extractor->new(content => $res->content)->find('//option[@class="avatarCities coords"]/@value');
 
-    if($c =~ /錯誤!/)
-    {
-        die ("password error\n");
-    } else {
-        my $html = HTML::TagParser->new($c);
-        my @elems;
-        
-        @elems = $html->getElementsByAttribute("class", "avatarCities coords");
-        foreach my $elem (@elems) {
-            # my cities
-            $self->{'cities'}->{$elem->getAttribute('value')} = {};
-        }
+    if($#cities<0) {
+        die ("login failed\n");
+    }
+    foreach(@cities) {
+        $self->{'cities'}->{$_} = {};
     }
 }
 
--- a/agent.pl	Thu Oct 23 00:59:20 2008 +0800
+++ b/agent.pl	Sun Oct 26 15:02:06 2008 +0800
@@ -4,21 +4,23 @@
 use Ikariam;
 use Data::Dumper;
 use Decision::ParseTree q{ParseTree};
-use YAML qw/LoadFile Dump/;
+use YAML qw/LoadFile Dump DumpFile/;
 
 package Ikariam::Cities::Rules;
 use strict;
 use Data::Dumper;
 
 sub new {
-    my ( $class ) = @_;
-    my $self = {};
+    my ( $class, $i ) = @_;
+    my $self = {
+        ikariam => $i,
+    };
     return bless $self, $class;
 }
 
 sub is_attacked {
     my ($self, $city) = @_;
-    return ($city->{force}->{attacks} > 0 ) ? 1 : 0;
+    return ($self->{'ikariam'}->{'military'}->{attacks} > 0 ) ? 1 : 0;
 }
 
 sub is_constructing {
@@ -28,13 +30,15 @@
 
 sub is_wall_enough {
     my ($self, $city) = @_;
-    # TODO 應該以防禦力計算
+    # http://ikariam.wikia.com/wiki/Wall_Defense
+    # Basic rule - the wall level must equale or large then townHall level.
     return ($city->{buildings}->{wall} >= $city->{buildings}->{townHall} ?  1 : 0);
 }
 
 sub is_space_enough {
     my ($self, $city) = @_;
     # TODO 應該以消耗率/時間計算
+    # $self->{ikariam}->{growth}
     return ($city->{space}->{total} <= ($city->{space}->{occupied}+6) ?  0 : 1)
 }
 
@@ -52,13 +56,19 @@
 sub is_warehouse_enough {
     my ($self, $city) = @_;
     # TODO 以速率計算容納率
-    # XXX: not implemented yet.
     return 1;
 }
 
 sub is_risk {
     my ($self, $city) = @_;
     # TODO 計算可搶劫比例, 城牆防護, 軍事分數
+    # 
+    # my $capture = $city->{buildings}->{townHall} * ($city->{buildings}->{townHall} - 1) * $city->{resource}->gold / 10000;
+    # 計算軍人消耗
+    #
+    # 方陣兵, 1.58 軍分, 24 維護費
+    # 劍士, 4.64 軍分, 16 維護費
+    # army_score_main > (occupied * 0.3)
     return 0;
 }
 
@@ -88,81 +98,154 @@
 
 sub is_expansion_researched {
     my ($self, $city) = @_;
-    return (defined($city->{research}->{1030}) ?  1 : 0);
+    # $self->{'ikariam'}->
+    return (defined($self->{'ikariam'}->{research}->{1030}) ?  1 : 0);
+}
+
+sub is_culturalexchange_researched {
+    my ($self) = @_;
+    return (defined($self->{'ikariam'}->{research}->{1040}) ?  1 : 0);
 }
 
 sub is_wealth_researched {
-    my ($self, $city) = @_;
-    return (defined($city->{research}->{2030}) ?  1 : 0);
+    my ($self) = @_;
+    return (defined($self->{'ikariam'}->{research}->{2030}) ?  1 : 0);
 }
 
-sub is_professionalarmy_researched {
-    my ($self, $city) = @_;
-    return (defined($city->{research}->{4030}) ?  1 : 0);
+sub is_winepress_researched {
+    my ($self) = @_;
+    return (defined($self->{'ikariam'}->{research}->{2040}) ?  1 : 0);
 }
 
 sub is_paper_researched {
-    my ($self, $city) = @_;
-    return (defined($city->{research}->{3020}) ?  1 : 0);
+    my ($self) = @_;
+    return (defined($self->{'ikariam'}->{research}->{3020}) ?  1 : 0);
+}
+
+sub is_espionage_researched {
+    my ($self) = @_;
+    return (defined($self->{'ikariam'}->{research}->{3030}) ?  1 : 0);
+}
+
+sub is_invention_researched {
+    my ($self) = @_;
+    return (defined($self->{'ikariam'}->{research}->{3040}) ?  1 : 0);
+}
+
+sub is_professionalarmy_researched {
+    my ($self) = @_;
+    return (defined($self->{'ikariam'}->{research}->{4030}) ?  1 : 0);
 }
 
 sub is_drydock_researched {
-    my ($self, $city) = @_;
-    return (defined($city->{research}->{4010}) ?  1 : 0);
-}
-
-sub is_winepress_researched {
-    my ($self, $city) = @_;
-    return (defined($city->{research}->{2040}) ?  1 : 0);
-}
-
-sub is_invention_researched {
-    my ($self, $city) = @_;
-    return (defined($city->{research}->{3040}) ?  1 : 0);
+    my ($self) = @_;
+    return (defined($self->{'ikariam'}->{research}->{4010}) ?  1 : 0);
 }
 
 sub is_barracks_level_enough {
     my ($self, $city) = @_;
-    return 0 if(!defined($city->{building}->{barracks}));
-    return ($city->{building}->{barracks} >= 3 ? 1 : 0);
+    return 0 if(!defined($city->{buildings}->{barracks}));
+    # 方陣兵需要 level 4
+    # optimum is 5
+    return ($city->{buildings}->{barracks} >= 4 ? 1 : 0);
 }
 
 sub is_shipyard_level_enough {
     my ($self, $city) = @_;
-    return 0 if(!defined($city->{building}->{shipyard}));
-    return ($city->{building}->{shipyard} >= 2 ? 1 : 0);
+    return 0 if(!defined($city->{buildings}->{shipyard}));
+    # optimum is 5
+    return ($city->{buildings}->{shipyard} >= 3 ? 1 : 0);
     return 0;
 }
 
 sub rule_engagement
 {
     my ($self, $city) = @_;
-    # XXX
+    # TODO
     # 計算距離, 可搶劫比例, 是否有聯盟, 軍事分數 (win rate)
 }
 
 sub rule_resource
 {
-    # XXX
+    # TODO 
+}
+
+# navy
+sub rule_navyExpenditure
+{
+    my ($self, $cityId) = @_;
+    # move this to somewhere else.
+    my $workersRatio = {
+        'citizens' => 0.4,
+        'specialworkers' => 0.3,
+        'woodworkers' => 0.7,
+    };
+
+    my $currentCost = $self->{'ikariam'}->getNavyExpenditure($cityId);
+    my $netincome = $self->{'ikariam'}->getNetIncome($cityId);
+    
+    # 軍費為 兩成 淨收入
+    # 陸軍佔用 0.8
+    # 海軍佔用 0.2
+    my $militaryExpenditure = int($netincome * 0.2 * 0.2);
+
+    if($currentCost < $militaryExpenditure) {
+        printf("Current navy expenditure total=%s, affordable %s\n", $currentCost, $militaryExpenditure);
+    }
+}
+
+# army
+sub rule_milityExpenditure
+{
+    my ($self, $cityId) = @_;
+    # move this to somewhere else.
+    my $workersRatio = {
+        'citizens' => 0.4,
+        'specialworkers' => 0.3,
+        'woodworkers' => 0.7,
+    };
+
+    my $currentCost = $self->{'ikariam'}->getMilityExpenditure($cityId);
+    my $netincome = $self->{'ikariam'}->getNetIncome($cityId);
+    
+    # 軍費為 兩成 淨收入
+    # 陸軍佔用 0.8
+    # 海軍佔用 0.2
+    my $militaryExpenditure = int($netincome * 0.2 * 0.8);
+
+    if($currentCost < $militaryExpenditure) {
+        printf("Current army expenditure total=%s, affordable %s\n", $currentCost, $militaryExpenditure);
+    }
 }
 1; 
 
 
 package main;
+my $verbose = 1;
+
 our $i = new Ikariam($::server, $::user, $::pass);
 $i->login;
 my $cities = $i->check;
 
-my $rules = Ikariam::Cities::Rules->new;
-my $tree  = LoadFile('building.yaml');
-# print Dumper($tree);
+# Genereic rules for both overall and city level arranagement.
+my $rules = Ikariam::Cities::Rules->new($i);
+# blanace resources, arrange defance 
+my $tree  = LoadFile('overall.yaml');
+my $decision;
+$decision->{parse_path} = [];
+$decision->{parse_answer} = undef;
+my $action = ParseTree($tree, $rules, $decision);
+triggerAction($action, (keys(%$cities))[0]) if(defined($action));
+print STDERR Dump($decision) if(defined($verbose));
+
 # show cities.
 foreach my $cityId (keys(%$cities)) {
     printf("%s http://%s/index.php?view=city&id=%d\n", 
         $cities->{$cityId}->{name}, $::server, $cityId);
     printf("construction: %s\n", $cities->{$cityId}->{construction});
     
-    foreach my $thing (qw/resources space force buildings citizens army fleet/) {
+    # foreach my $thing (qw/resources space wars buildings citizens army fleet/) {
+    foreach my $thing (qw/resources space buildings citizens army fleet/) {
         printf("<%s>\n", uc($thing));
         foreach my $i (keys(%{$cities->{$cityId}->{$thing}})) {
             printf("%s %s, ", $i, $cities->{$cityId}->{$thing}->{$i});
@@ -170,35 +253,34 @@
         print("\n");
     }
     print("\n");
-    # print(Dumper($cities));
 
-    # make decisions
+    # maybe this should be moved to Rules.
+    $i->blanceHurmanResource($cityId);
+    # milityExpenditure
+    $rules->rule_milityExpenditure($cityId);
+    $rules->rule_navyExpenditure($cityId);
 
-    # for the Decision Tree
+    # build and upgrade for cities
+    $tree  = LoadFile('city.yaml');
     $cities->{$cityId}->{parse_path} = [];
     $cities->{$cityId}->{parse_answer} = undef;
-    while (my $action = ParseTree($tree, $rules, $cities->{$cityId}))
-    {
-        # TODO: remove the last rule, if the result is fallback
+    while (my $action = ParseTree($tree, $rules, $cities->{$cityId})) {
         triggerAction($action, $cityId);
-        if($cities->{$cityId}->{parse_answer} ne "fallback") {
-            last ;
-        } else {
-
-        }
+        last;
     }
     # Debug
-    # print(Dumper($cities->{$cityId}->{parse_path}));
+    # print(Dump($cities->{$cityId}->{parse_path})) if(defined($verbose));
 }
+# print(Dump($cities)) if(defined($verbose));
+DumpFile("dump.yaml", $cities);
 
 $i->logout;
 
-
 sub triggerAction {
     my ($action, $cityId) = @_;
 
     my ($func, $param) = split(/_/,$action);
-    printf('$i->%s("%s", %s);'. "\n\n", $func, $param, $cityId);
-    eval(sprintf('$i->%s("%s", %s);', $func, $param, $cityId));
-    warn $@ if $@;
+    printf('$i->%s("%s", %s);'. "\n", $func, $param, $cityId);
+    # eval(sprintf('$i->%s("%s", %s);', $func, $param, $cityId));
+    # warn $@ if $@;
 }
--- a/building.yaml	Thu Oct 23 00:59:20 2008 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,107 +0,0 @@
----
-# 基本建設規則
-# 檢查是否被攻擊
-- is_attacked:
-    # we are in Peace :D
-    0:
-      - is_constructing:
-         # already building something.
-         1:
-           # adjust human resources
-           # 
-           # 相關基礎研究 (technologies tree)
-           # 財富
-            - is_wealth_researched:
-               0: research_economy
-            # 擴張
-            - is_expansion_researched:
-               0: research_seafaring
-            # 造紙
-            - is_paper_researched:
-               0: research_knowledge
-            # 正規軍
-            - is_professionalarmy_researched:
-               0: resaerch_military
-            # 釀酒
-            - is_winepress_researched:
-               0: research_economy
-            # 發明
-            - is_invention_researched:
-               0: research_knowledge
-         # let's find something to build up
-         0:
-            - is_gold_enoughforcargo:
-               1: increaseTransporter
-            - is_wall_enough:
-               0: build_wall
-            - is_space_enough:
-               0: 
-                  - is_resource_enoghforHall:
-                     1: build_townHall
-            - is_corruption:
-               1: build_governorsresidence
-            # 倉庫庫存量
-            - is_warehouse_enough:
-               0: build_warehouse
-            - is_risk:
-               1: 
-                  - is_shipyard:
-                     0: 
-                        - is_drydock_researched: 
-                           0: resaerch_military
-                           1: build_shipyard
-                           # build one board
-            - is_happiness:
-               0: 
-                  - is_winepress_researched:
-                     # 0: research_economy
-                     1: build_tavern
-
-            # TODO
-            # http://ikariam.wikia.com/wiki/List_of_buildings
-            # http://ikariam.wikia.com/wiki/Technology_Tree
-            # is_conservation_researched
-            #    -build_warehouse
-            # build_academy
-            # build_palace (Expansion, Capital only)
-            # build_embassy (副城, 不建)
-
-            - is_professionalarmy_researched:
-               1:
-                  - is_shipyard_level_enough:
-                     1:
-                        - build_shipyard
-            - is_professionalarmy_researched:
-               1:
-                  - is_barracks_level_enough:
-                     1: 
-                        - build_barracks
-            # is_invention_researched
-            # biuld_workshop 
-            # biuld_hideout
-            #
-            # build_museum
-
-            # 皇宮
-            # 副城, 不建 ...
-    # 採取防禦措施
-    1: run_defense
-    # 若軍分 0, 進行焦土政策 針對特定城市 錢全部買船 拆港
-    # increaseTransporter
-    # destroy TradingPort
-    #
-    # 計算出兵數量、到達時間與調動時間
-    # 調動軍隊防護 (加入封鎖列表)
-
-    # (保留兵力) 出兵對方城鎮, 拆港, keep gold
-    # (保留兵力) 出兵任一城鎮, 拆港, keep gold
-
-# blocking
-# 計算軍方比例
-# 對方軍分 0, # 海軍一日三次, 每次八小時 # 陸軍每日三次, 八小時一次
-# 對方平等軍分, 半夜偷襲
-# 對方高軍分 # 累積分數
-#
-# balance resources
-# 從其他城移動資源到特定城市
-# 預先計算可能需要的資源
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/city.yaml	Sun Oct 26 15:02:06 2008 +0800
@@ -0,0 +1,65 @@
+---
+# city level
+# 基本建設規則
+# 若糟攻擊則暫停建設
+- is_attacked:
+    # we are in Peace :D
+    0:
+      # adjust human resources
+      - is_constructing:
+         0:
+            # XXX: 這樣會一口氣買同等城市數量的船耶
+            # - is_gold_enoughforcargo:
+               # 1: increaseTransporter
+            - is_wall_enough:
+               0: build_wall
+            - is_space_enough:
+               0: 
+                  - is_resource_enoghforHall:
+                     1: build_townHall
+            - is_corruption:
+               1: build_governorsresidence
+            # 倉庫庫存量
+            - is_warehouse_enough:
+               0: build_warehouse
+            - is_happiness:
+               0: 
+                  - is_winepress_researched:
+                     1: build_tavern
+
+            # TODO
+            # http://ikariam.wikia.com/wiki/List_of_buildings
+            # http://ikariam.wikia.com/wiki/Technology_Tree
+            # is_conservation_researched
+            #    -build_warehouse
+            # build_academy
+            # build_palace (Expansion, Capital only)
+            # build_embassy (副城, 不建)
+
+            - is_risk:
+               1: 
+                  - is_professionalarmy_researched:
+                     1:
+                        - is_shipyard_level_enough:
+                           0: build_shipyard
+                        - is_barracks_level_enough:
+                           0: build_barracks
+                  - is_shipyard_level_enough:
+                     0: 
+                        - is_drydock_researched: 
+                           # requireed more army ?
+                           1: build_shipyard
+
+# 應該把 Barracks 建到最佳等級
+# 1:
+#    - build one board
+            # 要蓋到何種程度?
+            # is_invention_researched
+            #    1: 
+            #       - build_workshop 
+            # is_espionage_researched
+            #    1: 
+            #       - build_hideout
+            # is_culturalexchange_resaerched
+            #    1: 
+            #       - build_museum
--- a/enemy.pl	Thu Oct 23 00:59:20 2008 +0800
+++ b/enemy.pl	Sun Oct 26 15:02:06 2008 +0800
@@ -32,8 +32,8 @@
         }
         printf("Level: %s\n", $city->citylevel);
         printf("Island: %s\n", $island->name);
-        printf("Location: [%s:%s] http://s2.ikariam.tw/index.php?view=island&id=%s&selectCity=%d\n", 
-            $island->x, $island->y, $island->id, $city->cityId);
+        printf("Location: [%s:%s] http://%s/index.php?view=island&id=%s&selectCity=%d\n", 
+            $island->x, $island->y, $::server, $island->id, $city->cityId);
         printf("Tradegood: %s\n", $tradegoodText[$island->tradegood]);
         printf("Wonder: %s\n", $wonderText[$island->wonder]);
         printf("capture score: %d\n\n", $capture);
--- a/freeland.pl	Thu Oct 23 00:59:20 2008 +0800
+++ b/freeland.pl	Sun Oct 26 15:02:06 2008 +0800
@@ -4,6 +4,8 @@
 use Data::Dumper;
 
 package main;
+my @tradegoodText = qw/NULL 葡萄酒 大理石 水晶 硫磺/;
+my @wonderText = qw/NULL 赫菲斯拓斯的熔爐 蓋亞的神殿 狄奧尼索斯的花園 雅典娜的神殿 赫秘士的神殿 阿瑞斯的要塞 波賽頓的神殿 克羅瑟斯的神殿/;
 
 if($#ARGV != 1) {
     die("Usage: $0 x y\n");
@@ -23,6 +25,7 @@
     }
 );
 
+# 
 my @islands = Ikariam::Island->retrieve_from_sql(qq{
         tradegood == 2
     AND people < 16
@@ -37,19 +40,25 @@
         foreach my $city (@cities) {
             my $island = Ikariam::Island->retrieve($city->island);
             my $user = Ikariam::User->retrieve($city->user);
-            printf("%s (%d) \"%s\" %d [%d,%d] http://s2.ikariam.tw/index.php?view=island&id=%d&selectCity=%d\n",
+            printf("%s (%d) \"%s\" %d [%d,%d] http://%s/index.php?view=island&id=%d&selectCity=%d\n",
                 $user->name, $user->score,
                 $city->cityname, $city->citylevel, 
                 $island->x, $island->y,
+                $::server,
                 $island->id,
                 $city->cityId
             );
         }
 } else {
+    # 依照距離列表
     foreach my $island (@islands)
     {
-        printf("[%d:%d] http://s2.ikariam.tw/index.php?view=island&id=%d%s\n",
+        printf("%d [%d:%d] %s %s http://%s/index.php?view=island&id=%d%s\n",
+            (abs($x-$island->x) + abs($y-$island->y)),
             $island->x, $island->y,
+            $tradegoodText[$island->tradegood],
+            $wonderText[$island->wonder],
+            $::server,
             $island->id,
         );
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hunting.yaml	Sun Oct 26 15:02:06 2008 +0800
@@ -0,0 +1,34 @@
+---
+# 基本建設規則
+# 若糟攻擊則不建設
+- is_attacked:
+   0:
+# 計算當下戰爭次數
+# 本城之戰爭數
+# 維護費 / gold 比例
+
+# blocking (打到死政策)
+# 計算軍方比例
+# 對方軍分 0, 海軍一日三次, 每次八小時, 陸軍每日三次, 八小時一次
+# 對方平等軍分, 半夜偷襲
+# 對方高軍分, 累積分數後反攻
+
+# hunting
+# 分數低於我 0.1
+# 軍分低於我 0.3
+# 搶奪數量
+# 出戰比例運算
+#   間諜
+#   軍分成績
+
+   1: run_defense
+    # 遭受攻擊 採取防禦措施
+    # 若軍分 0, 進行焦土政策 針對特定城市 錢全部買船 拆港
+    # increaseTransporter
+    # destroy TradingPort
+    #
+    # 計算出兵數量、到達時間與調動時間
+    # 調動軍隊防護 (加入封鎖列表)
+
+    # (保留兵力) 出兵對方城鎮, 拆港, keep gold
+    # (保留兵力) 出兵任一城鎮, 拆港, keep gold
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/overall.yaml	Sun Oct 26 15:02:06 2008 +0800
@@ -0,0 +1,36 @@
+---
+# country level management.
+- is_attacked:
+   # we are in Peace :D
+   0:
+     # 相關基礎研究 (technologies tree)
+     # 財富
+     - is_wealth_researched:
+        0: research_economy
+     # 擴張
+     - is_expansion_researched:
+        0: research_seafaring
+     # 造紙
+     - is_paper_researched:
+        0: research_knowledge
+     # 正規軍
+     - is_professionalarmy_researched:
+        0: resaerch_military
+     # 釀酒
+     - is_winepress_researched:
+        0: research_economy
+     # 發明 (兵工廠)
+     - is_invention_researched:
+        0: research_knowledge
+     # 異國文化
+     - is_culturalexchange_researched:
+        0: research_seafaring
+     # is_reousrce_balanced.
+        # 酒館
+        # 學院
+     # balance resources
+     # 從其他城移動資源到特定城市
+     # 預先計算可能需要的資源
+   # let's find something to build up
+   1: run_defence
+   # we are being attacked!!!!!!!!!!!!!
--- a/sheep.pl	Thu Oct 23 00:59:20 2008 +0800
+++ b/sheep.pl	Sun Oct 26 15:02:06 2008 +0800
@@ -83,14 +83,15 @@
 
             next if($capture < 100);
 
-            $line = sprintf("%d %s army %d %s/%s(%d),", 
+            $line = sprintf("%d %s score %d army %d %s/%s(%d),", 
                 $capture,
-                $c->status, $sheep->army_score_main, $sheep->name, $sheep->ally, $members);
+                $c->status, $sheep->score, $sheep->army_score_main, $sheep->name, $sheep->ally, $members);
 
-            $line .= sprintf("\"%s\" %d [%d,%d] %s http://s2.ikariam.tw/index.php?view=island&id=%d&selectCity=%d\n",
+            $line .= sprintf("\"%s\" %d [%d,%d] %s http://%s/index.php?view=island&id=%d&selectCity=%d\n",
                 $c->cityname, $c->citylevel, 
                 $island->x, $island->y,
                 $tradegoodText[$island->tradegood],
+                $::server,
                 $island->id,
                 $c->cityId
             );