chiark / gitweb /
Merge branch 'ceb'
authorIan Jackson <ian@liberator.relativity.greenend.org.uk>
Sat, 22 Aug 2009 11:52:56 +0000 (12:52 +0100)
committerIan Jackson <ian@liberator.relativity.greenend.org.uk>
Sat, 22 Aug 2009 11:52:56 +0000 (12:52 +0100)
14 files changed:
yarrg/Commods.pm
yarrg/commod-email-processor
yarrg/database-info-fetch
yarrg/db-idempotent-populate
yarrg/devel-notes [new file with mode: 0644]
yarrg/master-info.txt
yarrg/pages.c
yarrg/structure.c
yarrg/test-yppedia-chart [new file with mode: 0644]
yarrg/web/lookup
yarrg/web/query_age
yarrg/web/query_commod
yarrg/web/tabsort [new file with mode: 0644]
yarrg/yppedia-chart-parser [new file with mode: 0755]

index 0f7ebd63a253919301b58c90855c42284c5295f7..13f581241fa1bd770e166e7e1cf4e581e36c473c 100644 (file)
@@ -49,13 +49,17 @@ BEGIN {
 }
 
 our %oceans; # eg $oceans{'Midnight'}{'Ruby'}{'Eta Island'}= $sources;
-our %commods; # eg $commods{'Fine black cloth'}= $sources;
 our %clients; # eg $clients{'ypp-sc-tools'}= [ qw(last-page) ];
 our %routes; # eg $routes{'Midnight'}{'Orca'}{'Tinga'}= $sources  NB abbrevs!
 our %route_mysteries; # eg $route_mysteries{'Midnight'}{'Norse'}= 3
 # $sources = 's[l]b';
 #       's' = Special Circumstances; 'l' = local ; B = with Bleach
 
+our %commods;
+# eg $commods{'Fine black cloth'}{Srcs}= $sources;
+# eg $commods{'Fine black cloth'}{Mass}= 700 [g]
+# eg $commods{'Fine black cloth'}{Volume}= 1000 [ml]
+
 our (%pctb_commodmap,@pctb_commodmap);
 
 my %colours; # eg $colours{'c'}{'black'}= $sources
@@ -116,10 +120,30 @@ sub parse_info1 ($$) {
     $ca= sub {
        my ($s,$ss) = @_;
 #print "ca($s)\n";
-       if ($s !~ m/\%(\w+)/) { $commods{ucfirst $s} .= $ss; return; }
+       if ($s !~ m/\%(\w+)/) {
+           my ($name, $props) = $s =~
+               /^(\S[^\t]*\S)\t+(\S[^\t]*\S)$/
+               or die "bad commodspec $s";
+           my $ucname= ucfirst $name;
+           $commods{$ucname}{Srcs} .= $ss;
+           my $c= $commods{$ucname};
+           $c->{Volume}= 1000;
+           foreach my $prop (split /\s+/, $props) {
+               if ($prop =~ m/^([1-9]\d*)(k?)g$/) {
+                   $c->{Mass}= $1 * ($2 ? 1000 : 1);
+               } elsif ($prop =~m/^([1-9]\d*)l$/) {
+                   $c->{Volume}= $1 * 1000;
+               } else {
+                   die "unknown property $prop for $ucname";
+               }
+           }
+           die "no mass for $ucname" unless defined $c->{Mass};
+           return;
+       }
        die "unknown $&" unless defined $colours{$1};
-       foreach my $c (keys %{ $colours{$1} }) {
-           &$ca($`.$c.$', $ss .'%'. $colours{$1}{$c});
+       my ($lhs,$pctlet,$rhs)= ($`,$1,$');
+       foreach my $c (keys %{ $colours{$pctlet} }) {
+           &$ca($lhs.$c.$rhs, $ss .'%'. $colours{$pctlet}{$c});
        }
     };
     foreach (@rawcm) { &$ca($_,$src); }
@@ -196,16 +220,16 @@ sub parse_info_serverside_ocean ($) {
 
 sub parse_pctb_commodmap () {
     undef %pctb_commodmap;
-    foreach my $commod (keys %commods) { $commods{$commod} =~ s/b//; }
+    foreach my $commod (keys %commods) { $commods{$commod}{Srcs} =~ s/b//; }
 
-    my $c= new IO::File '_commodmap.tsv' or die $!;
+    my $c= new IO::File '_commodmap.tsv';
     if (!$c) { $!==&ENOENT or die $!; return 0; }
 
     while (<$c>) {
        m/^(\S.*\S)\t(\d+)\n$/ or die "$_";
        die if defined $pctb_commodmap{$1};  $pctb_commodmap{$1}= $2;
        die if defined $pctb_commodmap[$2];  $pctb_commodmap[$2]= $1;
-       $commods{$1} .= 'b';
+       $commods{$1}{Srcs} .= 'b';
     }
     $c->error and die $!;
     close $c or die $!;
@@ -217,7 +241,7 @@ sub get_our_version ($$) {
     $aref->{"${prefix}name"}= 'ypp-sc-tools yarrg';
     $aref->{"${prefix}fixes"}= 'lastpage';
 
-    my $version= `git-describe --tags HEAD`; $? and die $?;
+    my $version= `git-describe --tags HEAD || echo 0unknown`; $? and die $?;
     chomp($version);
     $aref->{"${prefix}version"}= $version;
     return $aref;
@@ -303,7 +327,8 @@ sub cgipostform ($$$) {
        return $';
     } else {
        my $resp= $ua->request($req);
-       die $resp->status_line unless $resp->is_success;
+       die $resp->status_line."\n".$resp->content."\n "
+           unless $resp->is_success;
        return $resp->content();
     }
 }
index b395aa8d851fc1ede7bc385968b20b65d8758eca..1cff462cda1c67d451ee8064b9ac952e8e096cae 100755 (executable)
@@ -123,33 +123,38 @@ sub main () {
             $islandid, $mid,
             map { $md{$_} } (qw(timestamp clientspec serverspec)));
 
-    my (%sth, %sub_cs, %cache_cs, %sth_insert);
+    my (%sth, %sub_cs, %cache_cs, %sth_insert, %sth_lookup);
 
     $sth_insert{'stall'}= $dbh->prepare(
                 "INSERT OR IGNORE
                         INTO stalls
                         (islandid, stallname) VALUES ($islandid, ?)
                 ");
-    $sth_insert{'commods'}= $dbh->prepare(
+    $sth_lookup{'stall'}= $dbh->prepare(
+                "SELECT stallid FROM stalls
+                       WHERE islandid == $islandid AND stallname == ?
+                ");
+    $sth_insert{'commod'}= $dbh->prepare(
                 "INSERT OR IGNORE
                         INTO commods
                         (commodname) VALUES (?)
                 ");
+    $sth_lookup{'commod'}= $dbh->prepare(
+                "SELECT commodid FROM commods
+                       WHERE commodname == ?
+                ");
 
     foreach my $cs (qw(stall commod)) {
-       my $sth_lookup= $dbh->prepare(
-                "SELECT ${cs}id FROM ${cs}s WHERE ${cs}name == ?;
-                ");
        $sub_cs{$cs}= sub {
            my ($name)= @_;
            my $r= $cache_cs{$cs}{$name};
            return $r if defined $r;
-           $sth_lookup->execute($name) or die;
-           ($r)= $sth_lookup->fetchrow_array();
+           $sth_lookup{$cs}->execute($name) or die;
+           ($r)= $sth_lookup{$cs}->fetchrow_array();
            if (!defined $r) {
                $sth_insert{$cs}->execute($name);
-               $sth_lookup->execute($name) or die;
-               ($r)= $sth_lookup->fetchrow_array();
+               $sth_lookup{$cs}->execute($name) or die;
+               ($r)= $sth_lookup{$cs}->fetchrow_array();
                die unless defined $r;
            }
            $cache_cs{$cs}{$name}= $r;
index 1e789b88fcb389686675f07393e1d0fab7a48ac1..5e0576095a0b23ac7aa837a1d91ff72132f0c3c2 100755 (executable)
@@ -159,7 +159,7 @@ sub main__comparesources () {
                sub { });
     for_commods(sub {
                    my ($commod)= @_;
-                   my $srcs= $commods{$commod};
+                   my $srcs= $commods{$commod}{Srcs};
                    compare_sources_one($srcs, "commodity $commod");
                });
 }
index 96df020f842de9ab78e0e59035f7ab5122313db3..80ded466aefeb4ab89899fae5218dd7da2537e72 100755 (executable)
@@ -103,12 +103,26 @@ $dbh->commit;
 #---------- commodity list ----------
 
 {
-    my $sth= $dbh->prepare(<<'END')
- INSERT OR IGNORE INTO commods (commodname) VALUES (?);
+    my $insert= $dbh->prepare(<<'END')
+ INSERT OR IGNORE INTO commods
+     (unitmass,
+      unitvolume,
+      commodname)
+     VALUES (?,?,?);
+END
+    ;
+    my $update= $dbh->prepare(<<'END')
+ UPDATE commods
+     SET unitmass = ?,
+         unitvolume = ?
+     WHERE commodname = ?
 END
     ;
     foreach my $commod (sort keys %commods) {
-       $sth->execute($commod);
+       my $c= $commods{$commod};
+       my @qa= ($c->{Mass}, $c->{Volume}, $commod);
+       $insert->execute(@qa);
+       $update->execute(@qa);
     }
     $dbh->commit;
 }
diff --git a/yarrg/devel-notes b/yarrg/devel-notes
new file mode 100644 (file)
index 0000000..27335dd
--- /dev/null
@@ -0,0 +1,15 @@
+To remedy bug fixed in 01c14767c024ac56686dbbfcd88d9f3a0b4b1574,
+did this:
+
+sqlite> begin;
+sqlite> insert or ignore into stalls select null, buy.islandid, stalls.stallname from buy, stalls using (stallid);
+sqlite> insert or ignore into stalls select null, sell.islandid, stalls.stallname from sell, stalls using (stallid);
+sqlite> update buy set stallid = (select stallid from stalls where stalls.islandid == buy.islandid and stalls.stallname == (select stallname from stalls as bad where buy.stallid == bad.stallid));
+sqlite> update sell set stallid = (select stallid from stalls where stalls.islandid == sell.islandid and stalls.stallname == (select stallname from stalls as bad where sell.stallid == bad.stallid));
+sqlite> commit;
+
+And to check that it worked:
+
+sqlite> select * from buy offers, stalls using (stallid) where offers.islandid != stalls.islandid group by offers.islandid;
+sqlite> select * from sell offers, stalls using (stallid) where offers.islandid != stalls.islandid group by offers.islandid;
+sqlite> 
index eb8d66629a89d015b5313a47884021402a7d80fb..312e81c820652375b529136834c0a2adb5903e51 100644 (file)
@@ -1,13 +1,13 @@
 
 commods
- %c dye
- %c enamel
- %c paint
+ kraken's blood                1kg
+ %c dye                        1kg
+ %c enamel             5kg
+ %c paint              1200g 1l
 
- %c cloth
- fine %c cloth
-
- %g gems
+ %c cloth              700g
+ fine %c cloth         700g
+ sail cloth            700g
 
 %c
  aqua
@@ -38,6 +38,17 @@ commods
  white
  yellow
 
+commods
+ %g gems               10kg
+ diamonds              10kg
+ emeralds              10kg
+ moonstones            10kg
+ opals                 10kg
+ pearls                        10kg
+ rubies                        10kg
+ sapphires             10kg
+ topazes               10kg
+
 %g
  amber
  amethyst
@@ -51,67 +62,62 @@ commods
  tigereye
 
 commods
- bananas
- broom flower
- butterfly weed
- carambolas
- chalcocite
- coconuts
- cowslip
- cubanite
- diamonds
- durians
- elderberries
- emeralds
- fine rum
- gold nuggets
- gold ore
- grog
- hemp
- hemp oil
- indigo
- iris root
- iron
- kraken's blood
- lacquer
- large cannon balls
- leushite
- lily of the valley
- limes
- lobelia
- lorandite
- madder
- mangos
- masuyite
- medium cannon balls
- moonstones
- nettle
- old man's beard
- opals
- papagoite
- passion fruit
- pearls
- pineapples
- pokeweed berries
- pomegranates
- rambutan
- rubies
- sail cloth
- sapphires
- sassafras
- serandite
- sincosite
- small cannon balls
- stone
- sugar cane
- swill
- tellurium
- thorianite
- topazes
- varnish
- weld
- wood
- yarrow
+ swill                 1kg
+ grog                  1kg
+ fine rum              1kg
+
+ broom flower          200g
+ butterfly weed                100g
+ cowslip               700g
+ elderberries          700g
+ indigo                        700g
+ iris root             300g
+ lily of the valley    300g
+ lobelia               200g
+ madder                        400g
+ nettle                        300g
+ old man's beard       800g
+ pokeweed berries      300g
+ sassafras             500g
+ weld                  300g
+ yarrow                        200g
+
+ bananas               125kg 100l
+ coconuts              125kg 100l
+ limes                 125kg 100l
+ mangos                        125kg 100l
+ pineapples            125kg 100l
+
+ carambolas            125kg 100l
+ durians               125kg 100l
+ passion fruit         125kg 100l
+ pomegranates          125kg 100l
+ rambutan              125kg 100l
+
+ chalcocite            5700g
+ cubanite              4700g
+ gold nuggets          400g
+ leushite              4400g
+ lorandite             5500g
+ masuyite              5100g
+ papagoite             3300g
+ serandite             3400g
+ sincosite             3000g
+ tellurium             6200g
+ thorianite            100g
+
+ small cannon balls    7100g
+ medium cannon balls   14200g 2l
+ large cannon balls    21300g 3l
+
+ hemp                  125kg 250l
+ hemp oil              1kg
+ iron                  7800g
+ lacquer               1kg
+ stone                 2600g
+ sugar cane            50kg 100l
+ varnish               1kg
+ wood                  175kg 250l
 
 ocean Midnight
  Coral
index 2e3edde72d4b3fb68ecd48f20e430cac1739252b..8f1332cd41750978fe0c2fe2d6b40067145009de 100644 (file)
@@ -156,26 +156,26 @@ static void check_not_disturbed(void) {
 
 static void send_key(KeySym sym) {
   check_not_disturbed();
-  XTestFakeKeyEvent(disp, keycode(sym),1, 10);
-  XTestFakeKeyEvent(disp, keycode(sym),0, 10);
+  XTestFakeKeyEvent(disp, keycode(sym),1, 0);
+  XTestFakeKeyEvent(disp, keycode(sym),0, 0);
 }
-static void mouse_1_updown_here(void) {
+static void send_mouse_1_updown_here(void) {
   check_not_disturbed();
-  XTestFakeButtonEvent(disp,1,1, 10);
-  XTestFakeButtonEvent(disp,1,0, 10);
+  XTestFakeButtonEvent(disp,1,1, 0);
+  XTestFakeButtonEvent(disp,1,0, 0);
 }
-static void mouse_1_updown(int x, int y) {
+static void send_mouse_1_updown(int x, int y) {
   check_not_disturbed();
   int screen= XScreenNumberOfScreen(attr.screen);
   int xpos, ypos;
   translate_coords_toroot(x,y, &xpos,&ypos);
   XTestFakeMotionEvent(disp, screen, xpos,ypos, 0);
-  mouse_1_updown_here();
+  send_mouse_1_updown_here();
 }
 static void pgdown_by_mouse(void) {
   check_not_disturbed();
   debugf("PAGING   Mouse\n");
-  mouse_1_updown_here();
+  send_mouse_1_updown_here();
   sync_after_input();
 }
 
@@ -350,12 +350,14 @@ static void wait_for_stability(Snapshot **output,
          "  last_input=%f previously=%p `%s'\n",
          last_input, previously, doing);
 
-  double min_interval= 0.025;
+  double max_interval= 5.000;
+  double min_interval= 0.100;
   for (;;) {
     progress_spinner("%s",doing);
     
     double since_last_input= timestamp() - last_input;
     double this_interval= min_interval - since_last_input;
+    if (this_interval > max_interval) this_interval= max_interval;
 
     if (this_interval >= 0)
       delay(this_interval);
@@ -496,7 +498,7 @@ static void set_focus_commodity(void) {
 
   debugf("PAGING set_focus\n");
 
-  mouse_1_updown(commod_focus_point.x, commod_focus_point.y);
+  send_mouse_1_updown(commod_focus_point.x, commod_focus_point.y);
   sync_after_input();
 
   delay(0.5);
@@ -552,16 +554,17 @@ static void prepare_ypp_client(void) {
   Rect sunshine= find_sunshine_widget();
 
   progress("poking client...");
-  mouse_1_updown((sunshine.tl.x   + sunshine.br.x) / 2,
-                (sunshine.tl.y*9 + sunshine.br.y) / 10);
+  send_mouse_1_updown((sunshine.tl.x   + sunshine.br.x) / 2,
+                     (sunshine.tl.y*9 + sunshine.br.y) / 10);
+  sync_after_input();
 
   free(test);
 
   wait_for_stability(&current,0,0, "checking basic YPP client screen...");
-  mouse_1_updown(250, wheight-10);
-  mouse_1_updown_here();
-  mouse_1_updown_here();
-  XSync(disp,False);
+  send_mouse_1_updown(250, wheight-10);
+  send_mouse_1_updown_here();
+  send_mouse_1_updown_here();
+  sync_after_input();
   check_not_disturbed();
   send_key(XK_slash);
   send_key(XK_w);
@@ -626,7 +629,7 @@ void take_screenshots(void) {
     npages++;
   }
   progress("finishing with the YPP client...");
-  mouse_1_updown(commod_focuslast_point.x, commod_focuslast_point.y);
+  send_mouse_1_updown(commod_focuslast_point.x, commod_focuslast_point.y);
   sync_after_input();
   send_pgdown_torestore();
   sync_after_input();
index f10addc56eed9c65f77ede4809407333a4ca780a..99042d0c74961c5b064c19875b8af0c89ce37dca 100644 (file)
@@ -313,12 +313,16 @@ void find_structure(const CanonImage *im,
 
 #define CHECK_STRIP_BORDER(tlbr,xy,increm)             \
   do {                                                 \
-    Point csb_p;                                       \
+    Point csb_p, csb_p2;                               \
     Rect csb_r;                                                \
     csb_p= s.mr.tl;                                    \
     csb_p.x++; csb_p.y++;                              \
+    csb_p2= csb_p;                                     \
+    csb_p2.x++; csb_p2.y++;                            \
     csb_p.xy= s.mr.tlbr.xy;                            \
-    if (get_p(csb_p)=='+') {                           \
+    csb_p2.xy= s.mr.tlbr.xy;                           \
+    if (get_p(csb_p)=='+' &&                           \
+       get_p(csb_p2)=='+') {                           \
       csb_r= s.mr;                                     \
       csb_r.tl.xy= csb_p.xy;                           \
       csb_r.br.xy= csb_p.xy;                           \
diff --git a/yarrg/test-yppedia-chart b/yarrg/test-yppedia-chart
new file mode 100644 (file)
index 0000000..08c3fbf
--- /dev/null
@@ -0,0 +1,579 @@
+<div style="{{Chart style|65|65}}">
+{{Chart head}}
+<!--Ruby-->
+{{Chart label|23|6|[[Image:Ruby.png]]'''''[[Ruby Archipelago (Midnight)|Ruby Archipelago]]'''''}}
+ <!-- Islands -->
+{{Chart island icon|18|1|Olivia Isle|Midnight|out|sincosite, tellurium, sugar cane, lily of the valley}}
+{{Chart island icon|25|4|Cranberry Island|Midnight|lg_col|hemp, wood}}
+{{Chart island icon|23|2|Midsummer|Midnight|med_col|nettle, hemp}}
+{{Chart island icon|25|4|Cranberry Island|Midnight|lg_col|hemp, wood}}
+{{Chart island icon|22|7|Eta Island|Midnight|lg_col|wood}}
+{{Chart island icon|17|6|Lynx Island|Midnight|med|chalcocite, papagoite, stone}}
+{{Chart island icon|18|11|Islay of Luthien|Midnight|lg_col|sugar cane, iron, elderberries, serandite}}
+{{Chart island icon|27|12|Jorvik Island|Midnight|lg_col|wood, stone, cowslip}}
+ <!--Olivia-Midsummer-->
+{{Chart league|18|1|-|indianred}}
+{{Chart league|20|1|\|indianred}}
+{{Chart league|21|2|-|indianred}}
+ <!--Midsummer-Cranberry-->
+{{Chart league solid|24|3|\|indianred}}
+{{Chart league solid|23|2|\|indianred}}
+ <!--Cranberry-Eta-->
+{{Chart league solid|24|4|/|indianred}}
+{{Chart league solid|23|5|/|indianred}}
+{{Chart league solid|22|6|/|indianred}}
+ <!--Lynx-Eta-->
+{{Chart league solid|17|6|-|firebrick}}
+{{Chart league solid|19|6|\|firebrick}}
+{{Chart league solid|20|7|-|firebrick}}
+ <!--Olivia-Lynx-->
+{{Chart league|17|1|/|firebrick}}
+{{Chart league|17|2|\|firebrick}}
+{{Chart league|17|3|/|firebrick}}
+{{Chart league|17|4|\|firebrick}}
+{{Chart league|17|5|/|firebrick}}
+ <!--Lynx-Luthien-->
+{{Chart league|16|6|/|firebrick}}
+{{Chart league|16|7|\|firebrick}}
+{{Chart league|16|8|/|firebrick}}
+{{Chart league|16|9|\|firebrick}}
+{{Chart league|17|10|\|firebrick}}
+ <!--Luthien-Eta-->
+{{Chart league solid|18|10|/|indianred}}
+{{Chart league solid|19|9|/|indianred}}
+{{Chart league solid|20|8|/|indianred}}
+{{Chart league solid|21|7|/|indianred}}
+ <!--Luthien-Jorvik-->
+{{Chart league solid|18|11|-|firebrick}}
+{{Chart league solid|20|11|-|firebrick}}
+{{Chart league solid|22|11|-|firebrick}}
+{{Chart league solid|24|11|\|firebrick}}
+{{Chart league solid|25|12|-|firebrick}}
+ <!--Eta-Jorvik-->
+{{chart league solid|22|7|\|indianred}}
+{{chart league solid|23|8|\|indianred}}
+{{chart league solid|24|9|\|indianred}}
+{{chart league solid|25|10|\|indianred}}
+{{chart league solid|26|11|\|indianred}}
+ <!--Jorvik training route-->
+{{Chart league|29|12|o|gray}}
+{{chart league|26|13|o|gray}}
+{{Chart league|28|11|o|gray}}
+ <!--Interarches-->
+<!--Jorvik-Turtle-->
+{{Chart league|27|12|\|gold}}
+{{Chart league|28|13|\|gold}}
+{{Chart league|29|14|\|gold}}
+{{Chart league|30|15|\|gold}}
+{{Chart league|31|16|\|gold}}
+{{Chart league|32|17|\|gold}}
+<!--Luthien-Nuptial-->
+{{Chart league|17|11|/|gold}}
+{{Chart league|17|12|\|gold}}
+{{Chart league|17|13|/|gold}}
+{{Chart league|17|14|\|gold}}
+{{Chart league|17|15|/|gold}}
+{{Chart league|16|16|/|gold}}
+{{Chart league|15|17|/|gold}}
+{{Chart league|14|18|/|gold}}
+{{Chart league|13|19|/|gold}}
+{{Chart league|12|20|/|gold}}
+
+<!-- Pearl -->
+{{Chart label|0|17|[[Image:Pearl.png]]'''''[[Pearl Archipelago (Midnight)|Pearl Archipelago]]'''''}}
+<!--Islands of Pearl-->
+{{Chart island icon|6|23|Zeta Island|Midnight|lg_col|wood, old man's beard, weld, tellurium}}
+{{Chart island icon|1|26|Tadpole Isle|Midnight|out|hemp, pokeweed berries}}
+{{Chart island icon|2|19|Cleopatra's Pearls|Midnight|med_col|sugar cane, elderberries, cowslip, chalcocite}}
+{{Chart island icon|10|25|Ostreum Island|Midnight|med_col|iron, iris root}}
+{{Chart island icon|7|18|O'Reilly Island|Midnight|out|butterfly weed, stone, and masuyite}}
+{{Chart island icon|6|29|Frond Island|Midnight|out_col|hemp, pokeweed berries, butterfly weed, leushite}}
+{{Chart island icon|12|21|Nuptial Island|Midnight|out|lily of the valley}}
+<!--Nuptial-Zeta-->
+{{Chart league solid|11|21|/|blue}}
+{{Chart league solid|9|22|-|blue}}
+{{Chart league solid|8|22|/|blue}}
+{{Chart league solid|6|23|-|blue}}
+<!--Zeta-Tadpole-->
+{{Chart league solid|1|25|/|blue}}
+{{Chart league solid|2|24|/|blue}}
+{{Chart league solid|3|24|-|blue}}
+{{Chart league solid|5|23|/|blue}}
+<!--Zeta-Cleo-->
+{{Chart league solid|5|22|\|turquoise}}
+{{Chart league solid|4|21|\|turquoise}}
+{{Chart league solid|3|20|\|turquoise}}
+{{Chart league solid|2|19|\|turquoise}}
+<!--Zeta-Ostreum-->
+{{Chart league solid|6|23|\|turquoise}}
+{{Chart league solid|7|24|\|turquoise}}
+{{Chart league solid|8|25|-|turquoise}}
+<!--Tadpole-Frond-->
+{{Chart league|1|26|\|green}}
+{{Chart league|2|27|\|green}}
+{{Chart league|3|28|\|green}}
+{{Chart league|4|29|-|green}}
+<!--Frond-Ostreum-->
+{{Chart league solid|6|28|/|green}}
+{{Chart league solid|7|27|/|green}}
+{{Chart league solid|8|26|/|green}}
+{{Chart league solid|9|25|/|green}}
+<!--Cleo-O'Reilly-->
+{{Chart league|2|19|-|orange}}
+{{Chart league|4|19|-|orange}}
+{{Chart league|6|18|/|orange}}
+<!--O'Reilly-Nuptial-->
+{{Chart league|7|18|\|green}}
+{{Chart league|8|19|\|green}}
+{{Chart league|9|20|\|green}}
+{{Chart league|10|21|-|green}}
+
+<!--Ostreum-Wrasse-->
+{{Chart league|10|25|\|gold}}
+{{Chart league|11|26|\|gold}}
+{{Chart league|12|27|\|gold}}
+{{Chart league|13|28|\|gold}}
+{{Chart league|14|29|\|gold}}
+{{Chart league|15|30|-|gold}}
+
+<!--Emerald-->
+{{Chart label|14|38|[[Image:Emerald.png|Emerald gem]]'''''[[Emerald Archipelago (Midnight)|Emerald Archipelago]]'''''}}
+<!--Islands-->
+{{Chart island icon|17|30|Wrasse Island|Midnight|lg_col|iron, chalcocite, pokeweed berries, lobelia}}
+{{Chart island icon|18|35|Gaea Island|Midnight|lg_col|hemp, iron, sugar cane}}
+{{Chart island icon|23|38|Epsilon Island|Midnight|lg_col|hemp, sugar cane, sincosite}}
+{{Chart island icon|18|43|Tinga Island|Midnight|lg_col|hemp, indigo, madder, sugar cane}}
+{{Chart island icon|25|44|Spring Island|Midnight|lg_col|iron, wood, tellurium}}
+{{Chart island icon|27|40|Emperor Island|Midnight|med_col|stone, serandite, sugar cane}}
+{{Chart island icon|26|31|Guava Island|Midnight|lg_col|weld, wood, stone}}
+
+<!--Wrasse-Gaea-->
+{{chart league solid|16|30|/|orange}}
+{{chart league solid|16|31|\|orange}}
+{{chart league solid|16|32|/|orange}}
+{{chart league solid|16|33|\|orange}}
+{{chart league solid|17|34|\|orange}}
+<!--Wrasse-Guava-->
+{{chart league solid|17|30|-|orange}}
+{{chart league solid|19|30|-|orange}}
+{{chart league solid|21|30|-|orange}}
+{{chart league solid|23|30|\|orange}}
+{{chart league solid|24|31|-|orange}}
+<!--Guava training points-->
+{{chart league|25|30|o|gray}}
+{{chart league|28|31|o|gray}}
+{{chart league|27|32|o|gray}}
+<!--Gaea-Eps-->
+{{chart league solid|18|35|\|green}}
+{{chart league solid|19|36|\|green}}
+{{chart league solid|20|37|\|green}}
+{{chart league solid|21|38|-|green}}
+<!--Eps-Tinga-->
+{{chart league solid|22|38|/|green}}
+{{chart league solid|21|39|/|green}}
+{{chart league solid|20|40|/|green}}
+{{chart league solid|19|41|/|green}}
+{{chart league solid|18|42|/|green}}
+<!--Tinga-Spring-->
+{{chart league solid|18|43|-|gold}}
+{{chart league solid|20|43|-|gold}}
+{{chart league solid|22|43|\|gold}}
+{{chart league solid|23|44|-|gold}}
+<!--Spring training route-->
+{{chart league|24|43|o|gray}}
+{{chart league|27|44|o|gray}}
+{{chart league|26|45|o|gray}}
+<!--Spring-Emperor-->
+{{chart league solid|25|43|/|green}}
+{{chart league solid|26|42|/|green}}
+{{chart league solid|26|41|\|green}}
+{{chart league solid|26|40|/|green}}
+<!--Emperor-Epsilon-->
+{{chart league solid|23|38|\|green}}
+{{chart league solid|24|39|\|green}}
+{{chart league solid|25|40|-|green}}
+<!--Epsilon-Guava-->
+{{chart league solid|23|37|/|green}}
+{{chart league solid|24|36|/|green}}
+{{chart league solid|25|35|/|green}}
+{{chart league solid|25|34|\|green}}
+{{chart league solid|25|33|/|green}}
+{{chart league solid|25|32|\|green}}
+{{chart league solid|25|31|/|green}}
+
+<!--Guava-Cnossos-->
+{{chart league|26|30|/|gold}}
+{{chart league|27|29|/|gold}}
+{{chart league|28|28|/|gold}}
+{{chart league|29|27|/|gold}}
+{{chart league|30|27|-|gold}}
+{{chart league|32|26|/|gold}}
+
+<!--Diamond-->
+{{Chart label|41|23|[[Image:Diamond.png]]'''''[[Diamond Archipelago (Midnight)|Diamond Archipelago]]'''''}}
+<!--Islands-->
+{{Chart island icon|33|18|Turtle Island|Midnight|lg_col|iron, wood, stone}}
+{{Chart island icon|38|19|Papaya Island|Midnight|lg_col|hemp, wood, cowslip, lily of the valley, cubanite}}
+{{Chart island icon|42|21|Byrne Island|Midnight|lg_col|sugar cane, iron, sincosite}}
+{{Chart island icon|38|25|Alpha Island|Midnight|lg_col|no commodities}}
+{{Chart island icon|43|26|Oyster Island|Midnight|lg_col|hemp, elderberries}}
+{{Chart island icon|33|26|Cnossos Island|Midnight|lg_col|stone, tellurium, serandite}}
+{{Chart island icon|37|30|Winter Solstice|Midnight|lg_col|sugar cane, hemp, stone, madder}}
+<!--Cnossos Training-->
+{{chart league|31|26|o|gray}}
+{{chart league|34|25|o|gray}}
+<!--Cnossos-Winter-->
+{{chart league solid|33|26|\|turquoise}}
+{{chart league solid|34|27|\|turquoise}}
+{{chart league solid|35|28|\|turquoise}}
+{{chart league solid|36|29|\|turquoise}}
+<!--Cnossos-Alpha-->
+{{chart league solid|33|26|-|turquoise}}
+{{chart league solid|35|26|-|turquoise}}
+{{chart league solid|37|25|/|turquoise}}
+<!--Alpha-Winter-->
+{{chart league|38|27|o|gray}}
+{{chart league|37|28|o|gray}}
+<!--Cnossos-Turtle-->
+{{chart league solid|32|25|\|blue}}
+{{chart league solid|32|24|/|blue}}
+{{chart league solid|32|23|\|blue}}
+{{chart league solid|32|22|/|blue}}
+{{chart league solid|32|21|\|blue}}
+{{chart league solid|32|20|/|blue}}
+{{chart league solid|32|19|\|blue}}
+{{chart league solid|32|18|/|blue}}
+<!--Turtle Training-->
+{{chart league|31|18|o|gray}}
+{{chart league|34|17|o|gray}}
+{{chart league|34|19|o|gray}}
+<!--Turtle-Papaya-->
+{{chart league solid|33|18|-|gold}}
+{{chart league solid|35|18|\|gold}}
+{{chart league solid|36|19|-|gold}}
+<!--Papaya-Byrne-->
+{{chart league solid|38|19|\|gold}}
+{{chart league solid|39|20|\|gold}}
+{{chart league solid|40|21|-|gold}}
+<!--Byrne Training-->
+{{chart league|41|20|o|gray}}
+{{chart league|44|21|o|gray}}
+{{chart league|43|22|o|gray}}
+<!--Alpha-Turtle-->
+{{chart league solid|37|24|\|blue}}
+{{chart league solid|36|23|\|blue}}
+{{chart league solid|35|22|\|blue}}
+{{chart league solid|34|21|\|blue}}
+{{chart league solid|33|20|\|blue}}
+<!--Alpha-Byrne-->
+{{chart league solid|38|24|/|orange}}
+{{chart league solid|39|23|/|orange}}
+{{chart league solid|40|22|/|orange}}
+{{chart league solid|41|21|/|orange}}
+<!--Winter-Oyster-->
+{{chart league solid|37|29|/|turquoise}}
+{{chart league solid|38|28|/|turquoise}}
+{{chart league solid|39|27|/|turquoise}}
+{{chart league solid|40|27|-|turquoise}}
+{{chart league solid|42|26|/|turquoise}}
+<!--Oyster Training-->
+{{chart league|42|25|o|gray}}
+{{chart league|44|25|o|gray}}
+{{chart league|45|26|o|gray}}
+<!--Alpha-Oyster-->
+{{chart league solid|38|25|-|turquoise}}
+{{chart league solid|40|25|\|turquoise}}
+{{chart league solid|41|26|-|turquoise}}
+<!--Papaya-Remora-->
+{{chart league|38|18|/|gold}}
+{{chart league|39|17|/|gold}}
+{{chart league|40|16|/|gold}}
+{{chart league|41|15|/|gold}}
+{{chart league|42|14|/|gold}}
+{{chart league|43|13|/|gold}}
+{{chart league|43|12|\|gold}}
+{{chart league|43|11|/|gold}}
+<!--Byrne-Remora-->
+{{chart league|42|20|/|gold}}
+{{chart league|43|19|/|gold}}
+{{chart league|43|18|\|gold}}
+{{chart league|43|17|/|gold}}
+{{chart league|43|16|\|gold}}
+{{chart league|43|15|/|gold}}
+{{chart league|43|14|\|gold}}
+
+ <!--Sapphire-->
+{{chart label|45|14|[[Image:Sapphire.png]]'''''[[Sapphire Archipelago (Midnight)|Sapphire Archipelago]]'''''}}
+ <!--Islands-->
+{{Chart island icon|49|12|Beta Island|Midnight|lg_col|yarrow, sugar cane, wood}}
+{{Chart island icon|47|8|The Horseshoe Crabs|Midnight|out|leushite, papagoite, butterfly weed}}
+{{Chart island icon|55|14|Iris Island|Midnight|out_col|lorandite, papagoite, iron}}
+{{Chart island icon|44|11|Remora Island|Midnight|out_col|pokeweed berries, iris root}}
+{{Chart island icon|57|10|Uxmal Island|Midnight|lg|sugar cane, tellurium, nettle, cowslip, stone}}
+{{Chart island icon|52|9|Verdant Atoll|Midnight|out|old man's beard, madder, nettle}}
+{{Chart island icon|59|16|Vernal Equinox|Midnight|med_col|sincosite, thorianite, sugar cane, hemp, broom flower}}
+<!--Remora-Horseshoe Crabs-->
+{{chart league|44|10|/|gold}}
+{{chart league|45|9|/|gold}}
+{{chart league|46|8|/|gold}}
+<!--Remora-Beta-->
+{{chart league solid|44|11|-|gold}}
+{{chart league solid|46|11|\|gold}}
+{{chart league solid|47|12|-|gold}}
+<!--Beta-Verdant-->
+{{chart league solid|49|11|/|gold}}
+{{chart league solid|50|10|/|gold}}
+{{chart league solid|51|9|/|gold}}
+<!--Horseshoe Crabs-Verdant-->
+{{chart league|47|8|-|green}}
+{{chart league|49|8|\|green}}
+{{chart league|50|9|-|green}}
+<!--Extinct Points-->
+{{chart league|48|9|o|gray}}
+{{chart league|47|10|o|gray}}
+{{chart league|49|10|o|gray}}
+{{chart league|48|11|o|gray}}
+{{chart league|51|8|o|gray}}
+{{chart league|53|10|o|gray}}
+{{chart league|54|11|o|gray}}
+{{chart league|55|12|o|gray}}
+{{chart league|57|14|o|gray}}
+{{chart league|58|15|o|gray}}
+{{chart league|51|12|o|gray}}
+<!--Beta-Iris-->
+{{chart league solid|49|12|\|indianred}}
+{{chart league solid|50|13|-|indianred}}
+{{chart league solid|52|13|\|indianred}}
+{{chart league solid|53|14|-|indianred}}
+<!--Iris-Vernal-->
+{{chart league|55|14|\|firebrick}}
+{{chart league|56|15|\|firebrick}}
+{{chart league|57|16|-|firebrick}}
+<!--Verdant-Uxmal-->
+{{chart league|52|9|-|firebrick}}
+{{chart league|54|9|\|firebrick}}
+{{chart league|55|10|-|firebrick}}
+<!--Iris-Uxmal-->
+{{chart league|56|10|/|firebrick}}
+{{chart league|56|11|\|firebrick}}
+{{chart league|56|12|/|firebrick}}
+{{chart league|55|13|/|firebrick}}
+
+<!--Spring-Heph-->
+{{chart league|24|44|/|gold}}
+{{chart league|24|45|\|gold}}
+{{chart league|25|46|\|gold}}
+{{chart league|26|47|\|gold}}
+{{chart league|27|48|\|gold}}
+{{chart league|28|49|\|gold}}
+{{chart league|29|50|\|gold}}
+{{chart league|30|51|\|gold}}
+
+<!--Jet-->
+{{Chart label|34|50|[[Image:Jet.png]] <b>''[[Jet Archipelago (Midnight)|Jet Archipelago]]''</b>}}
+<!--Islands-->
+{{Chart island icon|31|52|Hephaestus' Forge|Midnight|lg_col|iron, tellurium}}
+{{Chart island icon|31|58|Namath Island|Midnight|lg_col|sugar cane, wood, madder}}
+{{Chart island icon|35|60|Rhinoceros Ridge|Midnight|out|lily of the valley, hemp, stone, lorandite}}
+{{Chart island icon|37|58|Lagniappe Island|Midnight|lg_col|market: sugar cane, butterfly weed, sincosite}}
+{{Chart island icon|38|53|Xi Island|Midnight|lg_col|sugar cane, stone, elderberries}}
+{{Chart island icon|44|51|Chaparral Island|Midnight|out_col|wood, old man's beard, broom flower}}
+{{Chart island icon|42|55|Eclipse Island|Midnight|out_col|cowslip, hemp}}
+{{Chart island icon|43|58|Dugong Island|Midnight|out|pokeweed berries, iron, wood}}
+<!--Heph-Namath-->
+{{chart league solid|30|52|/|blue}}
+{{chart league solid|30|53|\|blue}}
+{{chart league solid|30|54|/|blue}}
+{{chart league solid|30|55|\|blue}}
+{{chart league solid|30|56|/|blue}}
+{{chart league solid|30|57|\|blue}}
+<!--Namath-Rhino-->
+{{chart league solid|31|58|\|orange}}
+{{chart league solid|32|59|\|orange}}
+{{chart league solid|33|60|-|orange}}
+<!--Rhino-Lagniappe-->
+{{chart league solid|35|59|/|orange}}
+{{chart league solid|36|58|/|orange}}
+<!--Heph-Xi-->
+{{chart league solid|31|52|-|indianred}}
+{{chart league solid|33|52|-|indianred}}
+{{chart league solid|35|52|\|indianred}}
+{{chart league solid|36|53|-|indianred}}
+<!--Lagniappe-Xi-->
+{{chart league solid|37|57|/|green}}
+{{chart league solid|37|56|\|green}}
+{{chart league solid|37|55|/|green}}
+{{chart league solid|37|54|\|green}}
+{{chart league solid|37|53|/|green}}
+<!--Lagniappe-Dugong-->
+{{chart league solid|37|58|-|gold}}
+{{chart league solid|39|58|-|gold}}
+{{chart league solid|41|58|-|gold}}
+<!--Xi-Chap-->
+{{chart league solid|38|53|-|orange}}
+{{chart league solid|40|52|/|orange}}
+{{chart league solid|41|52|-|orange}}
+{{chart league solid|43|51|/|orange}}
+<!--Dugong-Eclipse-->
+{{chart league|42|57|\|gray}}
+{{chart league|41|56|\|gray}}
+{{chart league|41|55|/|gray}}
+<!--Eclipse-Chap-->
+{{chart league|43|52|\|gray}}
+{{chart league|42|54|/|gray}}
+{{chart league|43|53|/|gray}}
+
+<!--Chap-Monsoon-->
+{{chart league|44|50|/|gold}}
+{{chart league|45|49|/|gold}}
+{{chart league|46|48|/|gold}}
+{{chart league|47|47|/|gold}}
+{{chart league|47|46|\|gold}}
+{{chart league|47|45|/|gold}}
+
+ <!--Coral-->
+{{chart label|45|37|[[Image:Coral.png]]'''''[[Coral Archipelago (Midnight)|Coral Archipelago]]'''''}}
+ <!--Islands-->
+{{Chart island icon|51|32|Park Island|Midnight|lg_col|lobelia, stone, wood}}
+{{Chart island icon|62|33|Meke Island|Midnight|out_col|cubanite, iron, leushite, masuyite, sugar cane}}
+{{Chart island icon|57|36|Angelfish Island|Midnight|out_col|hemp, serandite, tellurium}}
+{{Chart island icon|53|38|Delta Island|Midnight|lg_col|sugar cane, weld}}
+{{Chart island icon|47|42|Macaw Island|Midnight|out|cowslip, hemp, sassafras}}
+{{Chart island icon|48|45|Monsoon Island|Midnight|med|butterfly weed, hemp, iron, papagoite}}
+{{Chart island icon|54|43|Turongo Island|Midnight|out|nettle, stone, sugar cane}}
+{{Chart island icon|58|45|Durian Island|Midnight|out|lorandite, old man's beard, pokeweed berries, sugar cane}}
+<!--Monsoon-Macaw-->
+{{chart league|47|44|\|gray}}
+{{chart league|46|43|\|gray}}
+{{chart league|46|42|/|gray}}
+<!--Monsoon-Turongo-->
+{{chart league|48|45|-|gray}}
+{{chart league|50|44|/|gray}}
+{{chart league|51|44|-|gray}}
+{{chart league|53|43|/|gray}}
+<!--Turongo-Durian-->
+{{chart league|54|43|\|gray}}
+{{chart league|55|44|\|gray}}
+{{chart league|56|45|-|gray}}
+<!--Extinct points-->
+{{chart league|52|45|o|gray}}
+{{chart league|54|45|o|gray}}
+{{chart league|56|43|o|gray}}
+{{chart league|57|42|o|gray}}
+{{chart league|57|44|o|gray}}
+<!--Macaw-Delta-->
+{{chart league solid|47|41|/|green}}
+{{chart league solid|48|40|/|green}}
+{{chart league solid|49|39|/|green}}
+{{chart league solid|50|39|-|green}}
+{{chart league solid|52|38|/|green}}
+<!--Delta-Angelfish-->
+{{chart league solid|53|37|/|gray}}
+{{chart league solid|54|37|-|gray}}
+{{chart league solid|56|36|/|gray}}
+<!--Angelfish-Meke-->
+{{chart league|57|35|/|orange}}
+{{chart league|58|34|/|orange}}
+{{chart league|59|34|-|orange}}
+{{chart league|61|33|/|orange}}
+<!--Angelfish-Turongo-->
+{{chart league|56|37|\|green}}
+{{chart league|56|38|/|green}}
+{{chart league|56|39|\|green}}
+{{chart league|56|40|/|green}}
+{{chart league|55|41|/|green}}
+{{chart league|54|42|/|green}}
+<!--Delta-Park-->
+{{chart league solid|52|37|\|gray}}
+{{chart league solid|51|36|\|gray}}
+{{chart league solid|50|35|\|gray}}
+{{chart league solid|50|34|/|gray}}
+{{chart league solid|50|33|\|gray}}
+{{chart league solid|50|32|/|gray}}
+<!--Park-Angelfish-->
+{{chart league solid|51|32|\|gray}}
+{{chart league solid|52|33|\|gray}}
+{{chart league solid|53|34|\|gray}}
+{{chart league solid|54|35|\|gray}}
+{{chart league solid|55|36|-|gray}}
+<!--Park-Oyster-->
+{{chart league|49|32|-|gold}}
+{{chart league|48|31|\|gold}}
+{{chart league|47|30|\|gold}}
+{{chart league|46|29|\|gold}}
+{{chart league|45|28|\|gold}}
+{{chart league|44|27|\|gold}}
+{{chart league|43|26|\|gold}}
+
+<!--Tinga-Orca-->
+{{chart league|17|43|/|gold}}
+{{chart league|15|44|-|gold}}
+{{chart league|14|44|/|gold}}
+{{chart league|13|45|/|gold}}
+{{chart league|12|46|/|gold}}
+{{chart league|11|47|/|gold}}
+{{chart league|10|48|/|gold}}
+{{chart league|9|49|/|gold}}
+
+ <!--Opal-->
+{{chart label|8|55|[[Image:Opal.png]]'''''[[Opal Archipelago (Midnight)|Opal Archipelago]]'''''}}
+
+ <!--Islands-->
+{{Chart island icon|2|53|Norse Island|Midnight|out|papagoite, serandite, stone}}
+{{Chart island icon|2|57|Waterberry|Midnight|out|elderberries, hemp, wood}}
+{{Chart island icon|3|60|Boyle Island|Midnight|out|cubanite, sugar cane}}
+{{Chart island icon|8|61|Flow|Midnight|out|cowslip, stone, thorianite, yarrow}}
+{{Chart island icon|9|58|Oseberg Island|Midnight|out|butterfly weed, iris root, leushite}}
+{{Chart island icon|7|54|Nu Island|Midnight|lg_col|hemp, nettle, sugar cane}}
+{{Chart island icon|9|50|Orca Island|Midnight|out_col|iron, tellurium}}
+{{Chart island icon|12|53|Endurance Island|Midnight|lg_col|none}}
+
+<!--Orca-Endurance-->
+{{chart league solid|9|50|\|green}}
+{{chart league solid|10|51|\|green}}
+{{chart league solid|11|52|\|green}}
+<!--Orca-Nu-->
+{{chart league solid|8|50|/|green}}
+{{chart league solid|8|51|\|green}}
+{{chart league solid|8|52|/|green}}
+{{chart league solid|7|53|/|green}}
+<!--Orca-Norse-->
+{{chart league|6|51|-|gray}}
+{{chart league|5|51|/|gray}}
+{{chart league|3|52|-|gray}}
+{{chart league|2|52|/|gray}}
+<!--Norse-Nu-->
+{{chart league solid|2|53|-|turquoise}}
+{{chart league solid|4|53|\|turquoise}}
+{{chart league solid|5|54|-|turquoise}}
+<!--Norse-Waterberry-->
+{{chart league|1|53|/|gold}}
+{{chart league|1|54|\|gold}}
+{{chart league|1|55|/|gold}}
+{{chart league|1|56|\|gold}}
+<!--Waterberry-Boyle-->
+{{chart league|1|57|/|gray}}
+{{chart league|1|58|\|gray}}
+{{chart league|2|59|\|gray}}
+<!--Boyle-Flow-->
+{{chart league|3|60|-|orange}}
+{{chart league|5|60|\|orange}}
+{{chart league|6|61|-|orange}}
+<!--Endurance-Nu-->
+{{chart league solid|7|54|-|green}}
+{{chart league solid|9|54|-|green}}
+{{chart league solid|11|53|/|green}}
+<!--Nu-Oseberg-->
+{{chart league solid|6|54|/|gray}}
+{{chart league solid|6|55|\|gray}}
+{{chart league solid|7|56|\|gray}}
+{{chart league solid|8|57|\|gray}}
+<!--Oseberg-Flow-->
+{{chart league|8|58|/|orange}}
+{{chart league|8|59|\|orange}}
+{{chart league|8|60|/|orange}}
+</div>
+<small>Standalone points mark extinct routes</small>
+
+[[Category:Maps|Midnight Ocean]]
+[[Category:Midnight Ocean maps|*]]
index 0b6466e3f6a4fc53a61fc4202643fddc67f3b438..a56adf7a2e7d8ee2b45e047ba30d85c92e72f017 100755 (executable)
@@ -66,6 +66,12 @@ my %styles;
                                [ 'sell',       'Sell offers only' ],
                        ],
                QuerySpecific => 1,
+       }, {    Name => 'ShowBlank',
+               Before => '',
+               Values => [     [ 'omit', 'Omit islands with no offers' ],
+                               [ 'show', 'Show all islands' ],
+                       ],
+               QuerySpecific => 1,
        });
 
 foreach my $var (@vars) {
index 77d750c8c95ec2c0386b79f785111df215851e0b..28d6a5f667074203c49451d522ad55b7d9fb1254 100644 (file)
@@ -78,27 +78,39 @@ $sth->execute();
 
 <h1>Market data age</h1>
 
-<table>
+<table id="ts_table">
 <tr>
 <th>Archipelago
 <th>Island
 <th>Age
 </tr>
 % my %da_ages;
-% $da_ages{'daid_loaded'}= 0;
+% my %ts_sortkeys;
+% $da_ages{'id_loaded'}= 0;
 % while ($row=$sth->fetchrow_hashref) {
-%      my $elid= "daid_$row->{'islandid'}";
+%      my $rowid= "id_$row->{'islandid'}";
+%      my $cellid= "c$rowid";
 %      my $age= $now - $row->{'timestamp'};
-%      $da_ages{$elid}= $age;
-<tr> <td><% $row->{'archipelago'} |h
- %>  <td><% $row->{'islandname'} |h
- %>  <td id="<% $elid %>"><% $prettyprint_age->($age) %> </tr>
+%      $ts_sortkeys{'0'}{$rowid}= $row->{'archipelago'};
+%      $ts_sortkeys{'1'}{$rowid}= $row->{'islandname'};
+%      $da_ages{$rowid}= $age;
+<tr id=<% $rowid %>
+   > <td><% $row->{'archipelago'} |h
+  %> <td><% $row->{'islandname'} |h
+  %> <td id="<% $cellid %>"><% $prettyprint_age->($age) %> </tr>
 % }
 </table>
 
+<& tabsort, table => 'ts_table', cols => [
+       {}, {},
+       { DoReverse => 1,
+         Numeric => 1,
+         SortKey => "da_ages[rowid]" }]
+  &>
+
 <p>
 Time since this page loaded:
-<span id="daid_loaded">(not known; times above not updating)</span>
+<span id="cid_loaded">(not known; times above not updating)</span>
 
 <form action="lookup" method="get">
 <input type=submit name=submit value="Reload">
@@ -106,25 +118,32 @@ Time since this page loaded:
 </form>
 
 <&| script &>
+  ts_sortkeys= <% to_json_protecttags(\%ts_sortkeys) %>;
   da_ages= <% to_json_protecttags(\%da_ages) %>;
 
   function da_Refresh() {
     var now= Date.now();
     debug('updating now='+now);
-    for (var elid in da_ages) {
-      var el= document.getElementById(elid);
-      var oldage= da_ages[elid];
+    for (var rowid in da_ages) {
+      var oldage= da_ages[rowid];
+      var cellid= 'c'+rowid;
+      var el= document.getElementById(cellid);
       var age= oldage + (now - da_pageload) / 1000;
-      var newhtml= <% $meta_prettyprint_age->('age','Math.floor','+') %>
+      var newhtml= <% $meta_prettyprint_age->('age','Math.floor','+') %>;
 % if ($ARGS{debug}) {
-      if (elid == 'daid_loaded')
-       debug('element elid='+elid+' oldage='+oldage+' age='+age+': '+newhtml);
+      if (cellid == 'cid_loaded')
+       debug('element rowid='+rowid+' cellid='+cellid
+               +' oldage='+oldage+' age='+age+': '+newhtml);
 % }
       el.innerHTML= newhtml;
     }
   }
 
-  window.onload= da_Refresh;
+  function all_onload() {
+    ts_onload__ts_table();
+    da_Refresh();
+  }
+  window.onload= all_onload;
   window.setInterval(da_Refresh, 10000);
 </&script>
 
index aeee3cee83d0bf8288ee79903ec005e20b36f4fc..052d019f998b137cd103f806c46bbbb0f3e0fd4a 100644 (file)
@@ -52,6 +52,7 @@ my $qa= \%ARGS;
 <h1>Commodity enquiry</h1>
 
 % $prselector->('BuySell');
+% $prselector->('ShowBlank');
 
 %#---------- textbox, user enters route as string ----------
 % if (!$qa->{Dropdowns}) {
@@ -88,42 +89,139 @@ $someresults->();
 
 #---------- actually compute the results and print them ----------
 
+my $onloads= "";
+
 foreach my $bs (split /_/, $ARGS{BuySell}) {
        $bs =~ m/^(buy|sell)$/ or die;
        $bs= $1;
+       my ($ascdesc) = ($bs eq 'buy')
+               ? ('DESC')
+               : ('ASC');
+       my $joinkind= $ARGS{ShowBlank} eq 'show'
+               ? 'LEFT OUTER JOIN' : 'INNER JOIN';
+       my $islands= $dbh->prepare(
+       "SELECT islands.islandid AS islandid, archipelago, islandname,
+                       sum(qty) as tqty
+               FROM islands $joinkind $bs offers
+               ON islands.islandid == offers.islandid AND commodid == ?
+               GROUP BY islands.islandid,
+               ORDER BY archipelago, islandname"
+               );
 
-       my $sth= $dbh->prepare(
-       "SELECT archipelago, islandname, stallname, price, qty
-               FROM $bs NATURAL JOIN stalls NATURAL JOIN islands
-               WHERE commodid = ?"
+       my $offers= $dbh->prepare(
+       "SELECT stallname, price, qty
+               FROM $bs NATURAL JOIN stalls
+               WHERE commodid = ? AND islandid = ?
+               ORDER BY price $ascdesc"
                );
        # fixme this query is utterly wrong
 
-       $sth->execute($commodid);
-
 </%perl>
 
 <h2>Offers to <% uc $bs |h %> <% $commodname |h %></h2>
 
-<table>
+<table id="<% $bs %>_table">
 <tr>
+<th colspan=3>
+<th colspan=2>Prices
+<th colspan=3>Quantity at price
+<tr id="<% $bs %>_table_thr">
 <th>Archipelago
 <th>Island
-<th>Best stall
-<th>Best price
-<th>Qty at best
+<th>Stall(s)
+<th>Best
+<th>Median
+<th>Best
+<th>+/-10%
+<th>Any
 </tr>
-%      my $row;
-%      while ($row=$sth->fetchrow_hashref) {
-<tr> <td><% $row->{'archipelago'} %>
-     <td><% $row->{'islandname'} %>
-     <td><% $row->{'stallname'} %>
-     <td><% $row->{'price'} %>
-     <td><% $row->{'qty'} %>
+%      $islands->execute($commodid);
+%      my $island;
+%      my %ts_sortkeys;
+%      while ($island= $islands->fetchrow_hashref) {
+%              my $islandid= $island->{'islandid'};
+%              $offers->execute($commodid, $islandid);
+%              my ($offer, $bestprice, $marginal, @beststalls);
+%              my $tqty= $island->{'tqty'};
+%              my $cqty= '';
+%              my $bestqty= '';
+%              my $approxqty= '';
+%              my $median= '-';
+%              while ($offer= $offers->fetchrow_hashref) {
+%                      my $price= $offer->{'price'};
+%                      my $qty= $offer->{'qty'};
+%                      length $bestqty or $bestprice= $price;
+%                      if ($price == $bestprice) {
+%                              $bestqty += $qty;
+%                              push @beststalls, $offer->{'stallname'};
+%                      }
+%                      $cqty += $qty;
+%                      if ($cqty*2 >= $tqty && $median eq '-') {
+%                              $median= $price;
+%                      }
+%                      if ($bestprice*9 <= $price*10 and
+%                          $price*10 <= $bestprice*11) {
+%                              $approxqty += $qty;
+%                      }
+%              }
+%              my $stallname;
+%
+%              my $rowid= "id_${bs}_$islandid";
+%              my $s= [ ];
+%
+%              $s->[2]= sprintf "%06d", scalar @beststalls;
+%              if (!@beststalls) {
+%                      $stallname= '-';
+%              } elsif (@beststalls==1) {
+%                      $stallname= $beststalls[0];
+%                      $s->[2] .= " $stallname";
+%              } else {
+%                      $stallname= sprintf "%d offers", scalar @beststalls;
+%              }
+%
+%              $cqty == $tqty or die "$bs $cqty $tqty $commodid $islandid ";
+<tr id=<% $rowid %> >
+     <td><% $s->[0]= $island->{'archipelago'} |h %>
+     <td><% $s->[1]= $island->{'islandname'} |h %>
+     <td><%          $stallname |h %>
+     <td><% $s->[3]= (length $bestqty ? $bestprice : '-') %>
+     <td><% $s->[4]= $median %>
+     <td><% $s->[5]= $bestqty %>
+     <td><% $s->[6]= $approxqty %>
+     <td><% $s->[7]= $cqty %>
 </tr>
+%              for my $cix (0..$#$s) {
+%                      $ts_sortkeys{$cix}{$rowid}= $s->[$cix];
+%              }
 %      }
 </table>
 
+<& tabsort,    table => "${bs}_table", sortkeys => "${bs}_sortkeys",
+               throw => "${bs}_table_thr", cols => [
+       {}, {},
+       { DoReverse => 1 },
+       { DoReverse => 1, Numeric => 1, MapFn => "ts_Pricemap_${bs}" },
+       { DoReverse => 1, Numeric => 1, MapFn => "ts_Pricemap_${bs}" },
+       { DoReverse => 1, Numeric => 1 },
+       { DoReverse => 1, Numeric => 1 },
+       { DoReverse => 1, Numeric => 1 },
+       ] &>
+<&| script &>
+  <% $bs %>_sortkeys= <% to_json_protecttags(\%ts_sortkeys) %>;
+  function ts_Pricemap_<% $bs %>(price) {
+    if (price=='-') { return <% $bs eq 'buy' ? '-1' : '99999999' %>; }
+    return price;
+  }
+</&>
+%      $onloads .= "    ts_onload__${bs}_table();\n";
+
 <%perl>
 }
 </%perl>
+
+<&| script &>
+  function all_onload() {
+<% $onloads %>
+  }
+  window.onload= all_onload;
+</&>
diff --git a/yarrg/web/tabsort b/yarrg/web/tabsort
new file mode 100644 (file)
index 0000000..ad25f86
--- /dev/null
@@ -0,0 +1,139 @@
+<%doc>
+
+ This is part of the YARRG website.  YARRG is a tool and website
+ for assisting players of Yohoho Puzzle Pirates.
+
+ Copyright (C) 2009 Ian Jackson <ijackson@chiark.greenend.org.uk>
+ Copyright (C) 2009 Clare Boothby
+
+  YARRG's client code etc. is covered by the ordinary GNU GPL (v3 or later).
+  The YARRG website is covered by the GNU Affero GPL v3 or later, which
+   basically means that every installation of the website will let you
+   download the source.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+ Yohoho and Puzzle Pirates are probably trademarks of Three Rings and
+ are used without permission.  This program is not endorsed or
+ sponsored by Three Rings.
+
+
+ This Mason component generates Javascript for sorting tables in
+ DHTML.
+
+
+</%doc>
+
+<%args>
+$table => 'ts_table'
+$sortkeys => 'ts_sortkeys'
+$throw => undef
+$cols
+</%args>
+
+<%doc>
+       NoSort
+       DoReverse
+</%doc>
+
+<&| script &>
+
+%      my $sortfn= "ts_sort__$table";
+function <% $sortfn %>(compar) {
+  debug('sorting compar='+compar);
+  var table= document.getElementById('<% $table %>');
+  var firstrow= table.getElementsByTagName('tr').item(0);
+  var tbody= firstrow.parentNode
+  var rows= tbody.childNodes
+  var newrows= new Array;
+  for (var rowix=0; rowix < rows.length; rowix++) {
+    var row= rows.item(rowix);
+    debug('process row '+rowix+' [[ '+row+' ]] id='+row.id)
+    if (!row.id) continue;
+%      if (defined $throw) {
+    if (row.id == '<% $throw %>') continue;
+%      }
+    if (row.tagName != 'TR') continue;
+    newrows.push(row);
+  }
+  newrows.sort(compar);
+  for (var rowix=0; rowix < newrows.length; rowix++) {
+    var row= newrows[rowix];
+    debug('add row '+rowix+' [[ '+row+' ]]');
+    tbody.appendChild(row);
+  }
+}
+
+% my %add_heads;
+% foreach my $cix (0..$#$cols) {
+%      my $col= $cols->[$cix];
+%      my $thhtml= '';
+%      next if $col->{NoSort};
+
+%      my $mapfn= "ts_compar${cix}_map__$table";
+function <% $mapfn %>(rowelement) {
+  var rowid = rowelement.id;
+%      if ($col->{SortKey}) {
+  return <% $col->{SortKey} %>;
+%      } else {
+%              my $sk= "$sortkeys"."[$cix][rowid]";
+%              if ($col->{MapFn}) {
+  return <% $col->{MapFn} %>(<% $sk %>);
+%              } else {
+  return <% $sk %>;
+%              }
+%      }
+}
+
+%      my $comparefn= "ts_compar${cix}_cmp0__$table";
+function <% $comparefn %>(a,b) {
+  var a_key = <% $mapfn %>(a);
+  var b_key = <% $mapfn %>(b);
+%      if ($col->{Numeric}) {
+  return a_key - b_key
+%      } else {
+  if (a_key < b_key) return -1;
+  if (a_key > b_key) return +1;
+  return 0;
+%      }
+}
+
+%      foreach my $reverse (qw(0 1)) {
+%              my $tcomparefn= "ts_compar${cix}_cmp${reverse}__$table";
+%              if ($reverse) {
+%                      next unless $col->{DoReverse};
+function <% $tcomparefn %>(a,b) { return -<% $comparefn %>(a,b); }
+%              }
+%              $thhtml .= "<a href=\"javascript:$sortfn($tcomparefn)\">".
+%                              ($reverse ? '&or;' : '&and;'). '</a>';
+%      }
+%      if (length $thhtml) {
+%              $add_heads{$cix}= $thhtml;
+%      }
+% }
+
+function ts_onload__<% $table %>() {
+  var ts_add_heads= <% to_json_protecttags(\%add_heads) %>;
+  var ctr= document.getElementById('<% defined($throw) ? $throw : $table %>');
+  var firstth= ctr.getElementsByTagName('th').item(0);
+  var thlist= firstth.parentNode.getElementsByTagName('th');
+  debug('thlist='+thlist);
+  debug('thlist.item(2)=' + thlist.item(2));
+  for (var cix in ts_add_heads) {
+    var ah = ts_add_heads[cix];
+    debug('appending to cix='+cix+' ah='+ah);
+    thlist.item(cix).innerHTML += ah;
+  }
+}
+</&>
diff --git a/yarrg/yppedia-chart-parser b/yarrg/yppedia-chart-parser
new file mode 100755 (executable)
index 0000000..2da027e
--- /dev/null
@@ -0,0 +1,302 @@
+#!/usr/bin/perl
+
+use strict (qw(vars));
+use warnings;
+
+use Graph::Undirected;
+
+use CommodsDatabase;
+
+my $widists= Graph::Undirected->new();
+my $wiarchs= Graph::Undirected->new();
+my @wiarchlabels;
+my %wiisland2node;
+my %winode2island;
+my %wiisland2arch;
+my %winode2lines;
+my %wiccix2arch;
+
+my $dbdists= Graph::Undirected->new();
+my %dbisland2arch;
+
+my %msgcount;
+sub perr ($$) { print STDERR "$_[0]: $_[1]\n"; $msgcount{$_[0]}++; }
+sub warning ($) { perr("warning",$_[0]); }
+sub error   ($) { perr("error",  $_[0]); }
+sub change  ($) { perr("change", $_[0]); }
+
+if ($ARGV[0] eq '--debug') {
+    shift @ARGV;
+    open DEBUG, ">&STDOUT" or die $!;
+    select(DEBUG); $|=1;
+} else {
+    open DEBUG, ">/dev/null" or die $!;
+}
+select(STDOUT); $|=1;
+
+my $parity;
+sub nn_xy ($$) {
+    my ($x,$y) = @_;
+    my $tp= (0+$x ^ 0+$y) & 1;
+    defined $parity or $parity=$tp;
+    $tp==$parity or warning("line $.: parity error $x,$y is $tp not $parity");
+    my $n= "$_[0],$_[1]";
+    $winode2lines{$n}{$.}++;
+    return $n;
+}
+
+sub parse_yppedia_map () {
+    # We don't even bother with tag soup; instead we do line-oriented parsing.
+
+    while (<>) {
+       s/\<--.*--\>//g;
+       s/^\s*//; chomp; s/\s+$//; s/\s+/ /g;
+       s/\<\/?(?:b|em)\>//g;
+       s/\{\{Chart\ style\|[^{}]*\}\}//g;
+       next unless m/\{\{/; # only interested in chart template stuff
+
+       my ($x,$y, $arch,$island,$solid,$dirn);
+       my $nn= sub { return nn_xy($x,$y) };
+    
+       if (($x,$y,$arch) =
+           m/^\{\{ chart\ label \|(\d+)\|(\d+)\| .*
+                   \'\[\[ [^][\']* \| (\S+)\ archipelago \]\]\'*\}\}$/xi) {
+           printf DEBUG "%2d,%-2d arch %s\n", $x,$y,$arch;
+           push @wiarchlabels, [ $x,$y,$arch ];
+       } elsif (($x,$y,$island) =
+           m/^\{\{ chart\ island\ icon \|(\d+)\|(\d+)\|
+                   ([^| ][^|]*[^| ]) \| .*\}\}$/xi) {
+           my $n= $nn->();
+           $wiisland2node{$island}= $n;
+           $winode2island{$n}= $island;
+           $widists->add_vertex($n);
+           $wiarchs->add_vertex($n);
+#print "\$g->add_vertex('$n');\n";
+           printf DEBUG "%2d,%-2d island %s\n", $x,$y,$island;
+       } elsif (($solid,$x,$y,$dirn) =
+           m/^\{\{ chart\ league((?:\ solid)?) \|(\d+)\|(\d+)\|
+                   ([-\/\\o]) \| .*\}\}$/xi) {
+           next if $dirn eq 'o';
+
+           my ($bx,$by) = ($x,$y);
+           if ($dirn eq '-') { $bx+=2; }
+           elsif ($dirn eq '\\') { $bx++; $by++; }
+           elsif ($dirn eq '/') { $x++; $by++; }
+           else { die; }
+
+           $widists->add_weighted_edge($nn->(), nn_xy($bx,$by), 1);
+           $wiarchs->add_edge($nn->(), nn_xy($bx,$by)) if $solid;
+           $wiarchs->add_edge($nn->(), nn_xy($bx,$by)) if $solid;
+#print "\$g->add_edge('".$nn->()."','".nn_xy($bx,$by)."');\n" if $solid;
+
+           printf DEBUG "%2d,%-2d league %-6s %s\n", $x,$y,
+               $solid?'solid':'dotted', $dirn;
+       } elsif (
+           m/^\{\{ chart\ head \}\}$/xi
+                ) {
+           next;
+       } else {
+           warning("line $.: ignoring incomprehensible: $_");
+       }
+    }
+}
+
+sub parse_database_map () {
+    my ($row,$sth);
+    $sth= $dbh->prepare('SELECT islandname, archipelago FROM islands');
+    $sth->execute();
+    while ($row= $sth->fetchrow_hashref) {
+       print DEBUG "database-island $row->{'islandname'}".
+                    " $row->{'archipelago'}\n";
+       $dbisland2arch{$row->{'islandname'}}= $row->{'archipelago'};
+    }
+    $sth= $dbh->prepare('SELECT dist, a.islandname a, b.islandname b
+                               FROM dists
+                               JOIN islands AS a ON dists.aiid==a.islandid
+                               JOIN islands AS b ON dists.biid==b.islandid');
+    $sth->execute();
+    while ($row= $sth->fetchrow_hashref) {
+       $dbdists->add_weighted_edge($row->{'a'}, $row->{'b'}, $row->{'dist'});
+    }
+}                       
+
+sub process_yppedia_graphs () {
+    # Prune the LP database by eliminating boring intermediate vertices
+    foreach my $delete ($widists->vertices()) {
+       next if exists $winode2island{$delete};
+       my @neigh= $widists->neighbours($delete);
+       next unless @neigh==2;
+#      my @aneigh= $wiarchs->has_vertex($delete)
+#          ? $wiarchs->neighbours($delete) : ();
+#      next unless @aneigh==0 || @aneigh==2;
+       my $weight= 0;
+       map { $weight += $widists->get_edge_weight($delete, $_) } @neigh;
+       $widists->add_weighted_edge(@neigh, $weight);
+       $widists->delete_vertex($delete);
+       printf DEBUG "%-5s elide %5s %-5s %2d\n", $delete, @neigh, $weight;
+    }
+
+    # Check that it's connected.
+    foreach my $cc ($widists->connected_components()) {
+       next if 2*@$cc > $widists->vertices();
+       my $m= "disconnected league point(s):";
+       foreach my $n (@$cc) {
+           $m .= "\n    LP $n, def. yppedia line(s): ".
+               join(',', sort keys %{ $winode2lines{$n} });
+       }
+       warning($m);
+    }
+
+    # Compute all-pairs-shortest-paths on dist, which is the
+    # actual distances between all LPs.
+    my $wialldists= $widists->APSP_Floyd_Warshall();
+
+    # Assign archipelago labels to groups of islands
+    foreach my $label (@wiarchlabels) {
+       my ($ax,$ay,$arch) = @$label;
+       my $best_ccmulti= -1;
+       my $best_d2= 0;
+       my $best_n;
+#      print DEBUG "$ax,$ay arch-island-search $arch\n";
+       $ay += 1;  $ax += 2;  # coords are rather to the top left of label
+       foreach my $vertex ($wiarchs->vertices()) {
+           next unless exists $winode2island{$vertex};
+           my $ccix= $wiarchs->connected_component_by_vertex($vertex);
+           my @cc= $wiarchs->connected_component_by_index($ccix);
+           my $ccmulti= @cc > 1;
+           my ($vx,$vy) = split /,/, $vertex;
+           my $d2= ($vx-$ax)*($vx-$ax) + ($vy-$ay)*($vy-$ay);
+           my $cmp= $ccmulti <=> $best_ccmulti
+               ||   $best_d2 <=> $d2;
+           printf DEBUG "%2d,%-2d arch-island-search %5s d2=%4d ccix=%-2d".
+                        " cc=%2d ccmulti=%d cmp=%-2d %s\n",
+               $ax,$ay, $vertex, $d2, $ccix, scalar(@cc), $ccmulti, $cmp,
+               $winode2island{$vertex};
+           next unless $cmp > 0;
+           $best_n=       $vertex;
+           $best_d2=      $d2;
+           $best_ccmulti= $ccmulti;
+       }
+       die 'no island vertices?!' unless defined $best_n;
+       printf DEBUG "%2d,%-2d arch-island-select %-5s d2=%-2d %-10s %s\n",
+           $ax,$ay, $best_n, $best_d2, $arch, $winode2island{$best_n};
+       my $ccix= $wiarchs->connected_component_by_vertex($best_n);
+       my $desc= join "\n", map {
+           my $in= $winode2island{$_};
+           "    LP $_". (defined $in ? ", $in" : "");
+       } sort $wiarchs->connected_component_by_index($ccix);
+
+       if (exists $wiccix2arch{$ccix}) {
+           error("archipelago determination failed, wrongly merged:\n".
+                 "    archipelago $arch\n".
+                 "    archipelago $wiccix2arch{$ccix}\n".
+                 $desc);
+           next;
+       }
+       $wiccix2arch{$ccix}= $arch;
+#      print "$ccix $arch ::\n$desc\n";
+    }
+
+    # Assign islands not labelled above to archipelagoes.
+    #
+    # We do this by, for each connected component (set of islands
+    # linked by purchaseable charts), searching for the nearest other
+    # connected component which has already been assigned an arch.
+    # `Nearest' means shortest distance of unpurchaseable charts, in
+    # leagues.
+
+    # fixme need some hints
+
+    # we need only consider vertices which weren't `boring intermediate
+    # vertices' (removed during optimisation as being of order 2)
+    my @ccs_useful= map {
+       [ grep { $widists->has_vertex($_) } @$_ ]
+    } $wiarchs->connected_components();
+
+    foreach my $sourceccix (0..$#ccs_useful) {
+       next if defined $wiccix2arch{$sourceccix};
+
+       my $sourcecc= $ccs_useful[$sourceccix];
+       my @islandnodes= grep { $winode2island{$_} } @$sourcecc;
+       next unless @islandnodes; # don't care, then
+
+       my $best_dist= 9999999;
+       my $best_target;
+       foreach my $targetccix (0..$#ccs_useful) {
+           next unless defined $wiccix2arch{$targetccix}; # not helpful
+           my $targetcc= $ccs_useful[$targetccix];
+           foreach my $target (@$targetcc) {
+               foreach my $source (@$sourcecc) {
+                   my $target_dist= $wialldists->path_length($target,$source);
+                   next if $target_dist >= $best_dist;
+                   $best_dist= $target_dist;
+                   $best_target= $target;
+               }
+           }
+       }
+#      die "no possible target ?!" unless defined $best_target;
+#
+#      printf DEBUG "
+#
+#    foreach my $node (sort keys %winode2island) {
+#      my $island= $winode2island{$node};
+#      my $arch= winode2arch($node);
+#      next if defined $arch;
+#      my $ccix= $wiarchs->connected_component_by_vertex($node);
+#      my @cc= $wiarchs->connected_component_by_index($ccix);
+#      @cc= grep { defined $winode2island{$_} } @cc;
+#      # We search for the best:
+#      #      - member of this connected component
+    }
+}
+
+sub winode2arch ($) {
+    my ($node) = @_;
+    my $ccix= $wiarchs->connected_component_by_vertex($node);
+    return $wiccix2arch{$ccix};
+}
+sub wiisland2arch ($) {
+    my ($island) = @_;
+    my $node= $wiisland2node{$island};
+    die "$island ?" unless defined $node;
+    return winode2arch($node);
+}
+
+sub compare_island_lists () {
+    foreach my $island (sort keys %dbisland2arch) {
+       my $node= $wiisland2node{$island};
+       if (!defined $node) {
+           error("would delete island: $island");
+           next;
+       }
+       my $wiarch= winode2arch($node);
+       if (!defined $wiarch) {
+           error("island has no arch: $island");
+           next;
+       }
+       my $dbarch= $dbisland2arch{$island};
+       if ($wiarch ne $dbarch) {
+           change("change archipelago from $dbarch to $wiarch".
+                  " for island $island");
+       }
+    }
+    foreach my $island (sort keys %wiisland2node) {
+       my $dbarch= $dbisland2arch{$island};
+       if (!defined $dbarch) {
+           my $wiarch= wiisland2arch($island);
+           if (!defined $wiarch) {
+               error("new island has no arch: $island");
+               next;
+               # We check arches of non-new islands above
+           }
+           change("new island in $wiarch: $island");
+       }
+    }
+}
+
+db_setocean('Midnight');
+db_connect();
+parse_yppedia_map();
+parse_database_map();
+process_yppedia_graphs();
+compare_island_lists();