}
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
$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); }
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 $!;
$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;
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();
}
}
$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;
sub { });
for_commods(sub {
my ($commod)= @_;
- my $srcs= $commods{$commod};
+ my $srcs= $commods{$commod}{Srcs};
compare_sources_one($srcs, "commodity $commod");
});
}
#---------- 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;
}
--- /dev/null
+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>
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
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
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
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();
}
" 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);
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);
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(¤t,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);
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();
#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; \
--- /dev/null
+<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|*]]
[ '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) {
<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">
</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>
<h1>Commodity enquiry</h1>
% $prselector->('BuySell');
+% $prselector->('ShowBlank');
%#---------- textbox, user enters route as string ----------
% if (!$qa->{Dropdowns}) {
#---------- 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;
+</&>
--- /dev/null
+<%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 ? '∨' : '∧'). '</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;
+ }
+}
+</&>
--- /dev/null
+#!/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();