#!/usr/bin/perl
-
-# Updater for island topology
+#
+# Normally run from
+# update-master-info
+#
+# usage: ./yppedia-chart-parser <Oceanname>
+# updates OCEAN-Oceanname.db and _ocean-<oceanname>.txt
+# from YPPedia (chart and ocean page) and source-info.txt
# This is part of ypp-sc-tools, a set of third-party tools for assisting
# players of Yohoho Puzzle Pirates.
# are used without permission. This program is not endorsed or
# sponsored by Three Rings.
-# usage: ./yppedia-chart-parser OCEAN
-
use strict (qw(vars));
use warnings;
my $wialldists;
my %wtisland2arch;
-my $dbdists= Graph::Undirected->new();
+my $dbdists;
my %dbisland2arch;
+my @msgkinds= qw(change warning error);
my %msgs;
+my %msgprinted;
+my %msgkindprinted;
sub pmsg ($$) { push @{ $msgs{$_[0]} }, "$_[0]: $_[1]\n"; }
sub warning ($) { pmsg("warning",$_[0]); }
sub error ($) { pmsg("error", $_[0]); }
sub change ($) { pmsg("change", $_[0]); }
sub print_messages () {
- foreach my $k (qw(change warning error)) {
- my $m= $msgs{$k};
- next unless $m;
- print sort @$m or die $!;
+ foreach my $k (@msgkinds) {
+ my $ms= $msgs{$k};
+ next unless $ms;
+ foreach my $m (sort @$ms) {
+ next if $msgprinted{$m};
+ print $m or die $!;
+ $msgprinted{$m}++;
+ $msgkindprinted{$k}++;
+ }
}
}
sub progress ($) { print "($_[0])\n"; }
}
}
-sub database_fetch_ocean () {
- 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 database_graph_spr () {
- $dbspr= shortest_path_reduction('db',$dbdists);
-}
-
sub yppedia_graphs_add_shortcuts () {
# We add edges between LPs we know about, as you can chart
# between them. Yppedia often lacks these edges.
}
}
-parse_info_serverside();
+#========== database handling ==========
+
+sub database_fetch_ocean () {
+ my ($row,$sth);
+ $sth= $dbh->prepare('SELECT islandname, archipelago FROM islands');
+ $sth->execute();
+ undef %dbisland2arch;
+ $dbdists= Graph::Undirected->new();
+ 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'});
+ }
+}
-progress("reading database");
+sub database_graph_spr () {
+ $dbspr= shortest_path_reduction('db',$dbdists);
+}
-db_setocean($ocean);
-db_connect();
-database_fetch_ocean();
+sub database_do_updates () {
+ my $addisland= $dbh->prepare(<<'END')
+ INSERT OR IGNORE INTO islands (islandname, archipelago) VALUES (?, ?);
+END
+ ;
+ foreach my $island (sort keys %wiisland2node) {
+ my $wiarch= wiisland2arch($island);
+ $addisland->execute($island, $wiarch);
+ }
+
+ db_doall(<<END)
+ DELETE FROM dists;
+ DELETE FROM routes;
+END
+ ;
+ my $adddist= $dbh->prepare(<<'END')
+ INSERT INTO dists VALUES
+ ((SELECT islandid FROM islands WHERE islandname == ?),
+ (SELECT islandid FROM islands WHERE islandname == ?),
+ ?);
+END
+ ;
+ my $addroute= $dbh->prepare(<<'END')
+ INSERT INTO routes VALUES
+ ((SELECT islandid FROM islands WHERE islandname == ?),
+ (SELECT islandid FROM islands WHERE islandname == ?),
+ ?);
+END
+ ;
+ foreach my $ia (sort keys %wiisland2node) {
+ my $na= $wiisland2node{$ia};
+ foreach my $ib (sort keys %wiisland2node) {
+ my $nb= $wiisland2node{$ib};
+ my $apdist= $ia eq $ib ? 0 : widist($na,$nb);
+ die "$ia $ib" unless defined $apdist;
+ my $sprdist= $wispr->get_edge_weight($ia,$ib);
+ die "$ia $ib $apdist $sprdist" if
+ defined($sprdist) && $sprdist != $apdist;
+
+ $adddist->execute($ia,$ib,$apdist);
+ $addroute->execute($ia,$ib,$sprdist) if defined $sprdist;
+ }
+ }
+
+ # select ia.islandname, ib.islandname, d.dist from dists as d, islands as ia on d.aiid = ia.islandid, islands as ib on d.biid = ib.islandid order by ia.islandname, ib.islandname;
+
+}
+
+#========== update _ocean-*.txt ==========
+
+our $localtopo_path;
-progress("computing database spr"); database_graph_spr();
+sub localtopo_rewrite () {
+ $localtopo_path= '_ocean-'.(lc $ocean).'.txt';
+ my $fh= new IO::File "$localtopo_path.tmp", 'w';
+ print $fh "# autogenerated - do not edit\n" or die $!;
+ print $fh "ocean $ocean\n" or die $!;
+ my %arches;
+ foreach my $isle (sort keys %wtisland2arch) {
+ my $arch= $wtisland2arch{$isle};
+ push @{ $arches{$arch} }, $isle;
+ }
+ foreach my $arch (sort keys %arches) {
+ print $fh " $arch\n" or die $!;
+ foreach my $isle (@{ $arches{$arch} }) {
+ print $fh " $isle\n" or die $!;
+ }
+ }
+ print $fh "\n" or die $!;
+ close $fh or die $!;
+}
+
+sub localtopo_commit () {
+ rename "$localtopo_path.tmp", $localtopo_path or die $!;
+}
+
+#========== main program ==========
+
+parse_info_serverside();
progress("fetching yppedia chart"); yppedia_ocean_fetch_chart();
progress("adding shortcuts"); yppedia_graphs_add_shortcuts();
progress("setting archs from labels"); yppedia_archs_chart_labels();
progress("setting archs from nearby"); yppedia_archs_fillbynearest();
progress("computing yppedia spr"); yppedia_graph_spr();
-
progress("fetching yppedia ocean text"); yppedia_ocean_fetch_text();
-progress("comparing islands"); compare_island_lists();
-progress("comparing distances"); compare_distances();
+db_setocean($ocean);
+db_connect();
+my $iteration=0;
+for (;;) {
+ progress("reading database");
+ database_fetch_ocean();
+ progress("computing database spr"); database_graph_spr();
+
+ progress("comparing islands"); compare_island_lists();
+ progress("comparing distances"); compare_distances();
+
+ print "\n";
+ print_messages();
+
+ foreach my $k (@msgkinds) {
+ my $n= $msgkindprinted{$k};
+ next unless $n;
+ printf STDERR "*** %d%s %ss\n", $n, $iteration?' additional':'', $k;
+ }
+
+ if ($msgs{'error'}) {
+ print STDERR "*** errors, aborting update\n";
+ exit 1;
+ }
+
+ if (!%msgkindprinted) {
+ progress("updating database"); database_do_updates();
+ progress("updating _ocean-*.txt"); localtopo_rewrite();
+ progress("committing database"); $dbh->commit();
+ progress("committing _ocean-*.txt"); localtopo_commit();
+ exit 0;
+ }
+ $dbh->rollback();
+
+ my $default= !$msgkindprinted{'warning'};
+ printf STDERR "*** confirm update %s ? ", $default?'(y/n)':'(n/y)';
+
+ $!=0; my $result= <STDIN>; defined $result or die $!;
+ $result =~ s/\s//g;
+ $result= $default?'y':'n' if !length $result;
+ $result= $result =~ m/^y/i;
+
+ if (!$result) {
+ printf STDERR "*** updated abandoned at your request\n";
+ exit 1;
+ }
+
+ print "\n";
+ undef %msgkindprinted;
+ $iteration++;
+}
print_messages();