changeset 52:d2ac1e198ce4

implement a new agent based on Decision Tree (Decision::ParseTree)
author "Rex Tsai <chihchun@kalug.linux.org.tw>"
date Mon, 20 Oct 2008 19:07:53 +0800
parents bc31729f29f4
children 336164c9a983
files Ikariam.pm agent.pl building.yaml inference.pl sheep.pl
diffstat 5 files changed, 293 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/Ikariam.pm	Sat Oct 18 22:24:03 2008 +0800
+++ b/Ikariam.pm	Mon Oct 20 19:07:53 2008 +0800
@@ -241,6 +241,97 @@
     return @cities;
 }
 
+sub build {
+    my $self = shift;
+    my $type = shift;
+    my $cityId = shift;
+
+    die ("only wall are implmeneted") unless ($type eq "wall");
+    die ("we don't know about this city") unless(defined($self->{'cities'}->{$cityId}));
+
+    my $position = -1;
+    my @locations = @{$self->{'cities'}->{$cityId}->{locations}};
+    foreach (0..$#locations) {
+        $position = $_ if($locations[$_] == $type);
+    }
+
+    if($position == -1)
+    {
+        warn("we don't have the building yet.");
+    } else {
+        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,
+            ]);
+    }
+}
+
+sub run {
+    my $self = shift;
+    # defense.
+    die("Not implemented");
+}
+
+sub research
+{
+    my $self = shift;
+    my $type = shift;
+    my $cityId = shift;
+
+    # 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 {
+    my $self = shift;
+    my $cityId = shift;
+
+    my $res = $self->{mech}->get(sprintf('http://%s/index.php?view=researchOverview&id=%s', $self->{server}, $cityId));
+
+    my $content;
+    gunzip \$res->content => \$content 
+        or die "gunzip failed: $GunzipError\n";
+    my $html = HTML::TagParser->new($content);
+
+    my @elems = $html->getElementsByAttribute('class', 'explored');
+
+    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();
+            }
+        }
+    }
+    return $out;
+}
+
+sub checkCity
+{
+    my $self = shift;
+    my $cityId = shift;
+
+    my $res = $self->{mech}->post(sprintf('http://%s/index.php', $self->{server}), [
+        action => 'header',
+        cityId => $cityId,
+        function => 'changeCurrentCity',
+        id => $cityId,
+        oldView => 'city',
+        ]);
+    # XXX
+}
+
 sub check
 {
     my $self = shift;
@@ -286,7 +377,7 @@
         foreach my $i (0..14) {
             my ($elem) = $html->getElementsByAttribute("id", "position" . $i);
             my $building = $elem->getAttribute('class');
-            if ($building ne "buildingGround land") {
+            if (!($building =~ /buildingGround/)) {
                 $self->{'cities'}->{$cityId}->{locations}[$i] = $building;
                 my $span = getElementsByAttribute($elem, "class", "textLabel");
                 my (undef, undef, $level) = split(/ /, $span->innerText());
@@ -303,7 +394,7 @@
         # if($content =~ /更新戰鬥報告: (\d+)/);
         # if($content =~ /新的戰鬥報告: (\d+)/);
 
-        # check townHall
+        # 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";
@@ -351,7 +442,7 @@
             $self->{'cities'}->{$cityId}->{corruption} =~ s/%//g;
         }
 
-        # count
+        # countCiizens
         my @citizens_type = qw/citizens woodworkers specialworkers scientists/;
         @elems = $html->getElementsByAttribute('class', 'count');
         $self->{'cities'}->{$cityId}->{'citizens'} = {};
@@ -363,14 +454,11 @@
             $self->{'cities'}->{$cityId}->{'citizens'}->{total} += $elems[$i]->innerText();;
         }
 
-        # production
-        # skin/resources/icon_gold.gif
-        # skin/resources/icon_wood.gif
-        # skin/resources/icon_sulfur.gif (?)
-        # skin/resources/icon_research.gif
+        # } 
+        
+        $self->{'cities'}->{$cityId}->{'research'} = $self->checkResearch($cityId);
 
-
-        # check armies
+        # 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/ ];
@@ -432,14 +520,16 @@
 }
 
 sub getElementsByTagName {
-    my $element    = shift;
+    my $element = shift;
     my $tagname = lc(shift);
     my ( $flat, $cur ) = @$element;
 
     my $out = [];
     for( ; $cur <= $#$flat ; $cur++ ) {
-        next if ( $flat->[$cur]->[001] ne $tagname );
-        next if $flat->[$cur]->[000];                 # close
+        last if ($flat->[ $cur + 1 ]->[001] eq $element->tagName() );
+        next if ($flat->[$cur]->[001] ne $tagname );
+        next if $flat->[$cur]->[000]; # close
+
         my $elem = HTML::TagParser::Element->new( $flat, $cur );
         return $elem unless wantarray;
         push( @$out, $elem );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent.pl	Mon Oct 20 19:07:53 2008 +0800
@@ -0,0 +1,145 @@
+#!/usr/bin/perl
+
+use strict;
+use Ikariam;
+use Data::Dumper;
+use Decision::ParseTree q{ParseTree};
+use YAML qw/LoadFile Dump/;
+
+package Ikariam::Cities::Rules;
+use strict;
+use Data::Dumper;
+
+sub new {
+    my ( $class ) = @_;
+    my $self = {};
+    return bless $self, $class;
+}
+
+sub is_Attacked {
+    my ($self, $city) = @_;
+    return ($city->{force}->{attacks} > 0 ) ? 1 : 0;
+}
+
+sub is_constructing {
+    my ($self, $city) = @_;
+    return ($city->{construction} > 0 ) ? 1 : 0;
+}
+
+sub is_wall_enough {
+    my ($self, $city) = @_;
+    # TODO 應該以防禦力計算
+    return ($city->{buildings}->{wall} >= $city->{buildings}->{townHall} ?  1 : 0);
+}
+
+sub is_space_enough {
+    my ($self, $city) = @_;
+    # TODO 應該以消耗率/時間計算
+    return ($city->{space}->{total} <= ($city->{space}->{occupied}+6) ?  1 : 0)
+}
+
+sub is_corruption {
+    my ($self, $city) = @_;
+    return 0;
+}
+
+sub is_happiness
+{
+    my ($self, $city) = @_;
+    # TODO 以 fuzzy 取出合理 happiness 值
+    return ($city->{happiness} >= 2 ?  1 : 0)
+}
+
+sub is_warehouse_enough
+{
+    my ($self, $city) = @_;
+    # TODO 以速率計算容納率
+    # XXX: not implemented yet.
+    return 1;
+}
+
+sub is_risk
+{
+    my ($self, $city) = @_;
+    # TODO 計算可搶劫比例, 城牆防護, 軍事分數
+    return 0;
+}
+
+sub is_shipyard
+{
+    return 1;
+}
+
+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 rule_engagement
+{
+    my ($self, $city) = @_;
+    # XXX
+    # 計算距離, 可搶劫比例, 是否有聯盟, 軍事分數 (win rate)
+}
+
+sub rule_resource
+{
+    # XXX
+}
+1; 
+
+
+package main;
+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);
+# 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/) {
+        printf("<%s>\n", uc($thing));
+        foreach my $i (keys(%{$cities->{$cityId}->{$thing}})) {
+            printf("%s %s, ", $i, $cities->{$cityId}->{$thing}->{$i});
+        }
+        print("\n");
+    }
+    print("\n");
+    # print(Dumper($cities));
+
+    # make decisions
+
+    # for the Decision Tree
+    $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
+        triggerAction($action, $cityId);
+        last;
+    }
+    # Debug
+    print(Dumper($cities->{$cityId}->{parse_path}));
+}
+
+$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 $@;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/building.yaml	Mon Oct 20 19:07:53 2008 +0800
@@ -0,0 +1,39 @@
+---
+# 基本建設規則
+# 檢查是否被攻擊
+- is_Attacked:
+    # we are in Peace :D
+    0:
+      - is_constructing:
+         # already building something.
+         1: 
+         # let's find something to build up
+         0:
+            # 確認是否為主城
+            # 確認是否有學院
+            - is_wall_enough:
+               0: build_wall
+            - is_space_enough:
+               0: build_cityhall
+            - is_corruption:
+               1: build_governorsresidence
+            # 倉庫庫存量
+            - is_warehouse_enough:
+               0: build_warehouse
+            - is_risk:
+               1: 
+                  - is_shipyard:
+                     0: 
+                        - is_drydock_researched: 
+                           0: resaerch_drydock
+                           1: build_shipyard
+            - is_happiness:
+               0: 
+                  - is_winepress_researched:
+                     0: research_economy
+                     1: build_tavern
+                     # reduce_trvern
+            # 是主城, 可隨意建任一尚未搭建之建築
+            # 副城, 不建 ...
+    # 採取防禦措施
+    1: run_defense
--- a/inference.pl	Sat Oct 18 22:24:03 2008 +0800
+++ b/inference.pl	Mon Oct 20 19:07:53 2008 +0800
@@ -13,11 +13,6 @@
         $::server, $city, $cities->{$city}->{name}, $msg);
 }
 
-our $i = new Ikariam($::server, $::user, $::pass);
-$i->login;
-$cities = $i->check;
-$i->logout;
-
 sub rule_happiness
 {
     my $id = shift;
@@ -111,6 +106,11 @@
     print("\n");
 }
 
+our $i = new Ikariam($::server, $::user, $::pass);
+$i->login;
+$cities = $i->check;
+$i->logout;
+
 # space
 # Checking rules
 foreach my $procedure (qw/rule_war rule_happiness rule_corruption rule_building rule_space rule_resource/) {
--- a/sheep.pl	Sat Oct 18 22:24:03 2008 +0800
+++ b/sheep.pl	Mon Oct 20 19:07:53 2008 +0800
@@ -37,7 +37,7 @@
         SELECT user.id 
           FROM user, cities 
          WHERE user.id == cities.user 
-           AND user.trader_score_secondary >= 50000 
+           AND user.trader_score_secondary >= 20
            AND user.army_score_main <= 100
            AND cities.island IN (SELECT island.id FROM island WHERE island.x <= ? AND island.x >= ? AND island.y <= ? AND island.y >= ? )
     }