chiark / gitweb /
Found bug in Graph::Undirected
[ypp-sc-tools.web-live.git] / yarrg / yppedia-chart-parser
1 #!/usr/bin/perl
2
3 use strict (qw(vars));
4 use warnings;
5
6 use Graph::Undirected;
7
8 use CommodsDatabase;
9
10 my $widists= Graph::Undirected->new();
11 my $wiarchs= Graph::Undirected->new(unionfind => 1);
12 my @wiarchlabels;
13 my %wiisland2node;
14 my %winode2island;
15 my %wiisland2arch;
16 my %winode2lines;
17 my %wiccix2arch;
18
19 my $dbdists= Graph::Undirected->new();
20 my %dbisland2arch;
21
22 my $warnings=0;
23 sub warning ($) {
24     my ($m) = @_;
25     print STDERR "warning: $m\n";
26     $warnings++;
27 }
28 my $errors=0;
29 sub error ($) {
30     my ($m) = @_;
31     print STDERR "error: $m\n";
32     $errors++;
33 }
34
35 #open PO, ">/dev/null" or die $!;
36 open PO, ">&STDOUT" or die $!;
37 select(PO); $|=1;
38 select(STDOUT); $|=1;
39
40 my $parity;
41 sub nn_xy ($$) {
42     my ($x,$y) = @_;
43     my $tp= (0+$x ^ 0+$y) & 1;
44     defined $parity or $parity=$tp;
45     $tp==$parity or warning("line $.: parity error $x,$y is $tp not $parity");
46     my $n= "$_[0],$_[1]";
47     $winode2lines{$n}{$.}++;
48     return $n;
49 }
50
51 sub parse_yppedia_map () {
52     # We don't even bother with tag soup; instead we do line-oriented parsing.
53
54     while (<>) {
55         s/\<--.*--\>//g;
56         s/^\s*//; chomp; s/\s+$//; s/\s+/ /g;
57         s/\<\/?(?:b|em)\>//g;
58         s/\{\{Chart\ style\|[^{}]*\}\}//g;
59         next unless m/\{\{/; # only interested in chart template stuff
60
61         my ($x,$y, $arch,$island,$solid,$dirn);
62         my $nn= sub { return nn_xy($x,$y) };
63     
64         if (($x,$y,$arch) =
65             m/^\{\{ chart\ label \|(\d+)\|(\d+)\| .*
66                     \'\[\[ [^][\']* \| (\S+)\ archipelago \]\]\'*\}\}$/xi) {
67             printf PO "%d,%d arch %s\n", $x,$y,$arch;
68             push @wiarchlabels, [ $x,$y,$arch ];
69         } elsif (($x,$y,$island) =
70             m/^\{\{ chart\ island\ icon \|(\d+)\|(\d+)\|
71                     ([^| ][^|]*[^| ]) \| .*\}\}$/xi) {
72             my $n= $nn->();
73             $wiisland2node{$island}= $n;
74             $winode2island{$n}= $island;
75             $widists->add_vertex($n);
76             $wiarchs->add_vertex($n);
77             printf PO "%d,%d island %s\n", $x,$y,$island;
78         } elsif (($solid,$x,$y,$dirn) =
79             m/^\{\{ chart\ league((?:\ solid)?) \|(\d+)\|(\d+)\|
80                     ([-\/\\o]) \| .*\}\}$/xi) {
81             next if $dirn eq 'o';
82
83             my ($bx,$by) = ($x,$y);
84             if ($dirn eq '-') { $bx+=2; }
85             elsif ($dirn eq '\\') { $bx++; $by++; }
86             elsif ($dirn eq '/') { $x++; $by++; }
87             else { die; }
88
89             $widists->add_weighted_edge($nn->(), nn_xy($bx,$by), 1);
90             $wiarchs->add_edge($nn->(), nn_xy($bx,$by)) if $solid;
91
92             printf PO "%d,%d league %s %s \n", $x,$y,
93                 $solid?'solid':'dotted', $dirn;
94         } elsif (
95             m/^\{\{ chart\ head \}\}$/xi
96                  ) {
97             next;
98         } else {
99             warning("line $.: ignoring incomprehensible: $_");
100         }
101     }
102 }
103
104 sub parse_database_map () {
105     my ($row,$sth);
106     $sth= $dbh->prepare('SELECT islandname, archipelago FROM islands');
107     $sth->execute();
108     foreach $row ($sth->fetchrow_hashref) {
109         $dbisland2arch{$row->{'islandname'}}= $row->{'archipelago'};
110     }
111     $sth= $dbh->prepare('SELECT dist, a.islandname a, b.islandname b
112                                 FROM dists
113                                 JOIN islands AS a ON dists.aiid==a.islandid
114                                 JOIN islands AS b ON dists.biid==b.islandid');
115     $sth->execute();
116     foreach $row ($sth->fetchrow_hashref) {
117         $dbdists->add_weighted_edge($row->{'a'}, $row->{'b'}, $row->{'dist'});
118     }
119 }                        
120
121 sub process_yppedia_graphs () {
122     # Prune the LP database by eliminating boring intermediate vertices
123     foreach my $delete ($widists->vertices()) {
124         next if exists $winode2island{$delete};
125         my @neigh= $widists->neighbours($delete);
126         next unless @neigh==2;
127 #       my @aneigh= $wiarchs->has_vertex($delete)
128 #           ? $wiarchs->neighbours($delete) : ();
129 #       next unless @aneigh==0 || @aneigh==2;
130         my $weight= 0;
131         map { $weight += $widists->get_edge_weight($delete, $_) } @neigh;
132         $widists->add_weighted_edge(@neigh, $weight);
133         $widists->delete_vertex($delete);
134 #       print PO "$delete elide $weight\n";
135     }
136
137     # Check that it's connected.
138     foreach my $cc ($widists->connected_components()) {
139         next if 2*@$cc > $widists->vertices();
140         my $m= "disconnected league point(s):";
141         foreach my $n (@$cc) {
142             $m .= "\n    LP $n, def. yppedia line(s): ".
143                 join(',', sort keys %{ $winode2lines{$n} });
144         }
145         warning($m);
146     }
147
148     # Compute all-pairs-shortest-paths on dist, which is the
149     # actual distances between all LPs.
150     my $wialldists= $widists->APSP_Floyd_Warshall();
151
152     # Compute arch's
153     foreach my $label (@wiarchlabels) {
154         my ($ax,$ay,$arch) = @$label;
155         my $d2best= 9999999;
156         my $best;
157         foreach my $vertex ($wiarchs->vertices()) {
158             next unless exists $winode2island{$vertex};
159             my ($vx,$vy) = split /,/, $vertex;
160             my $d2= ($vx-$ax)*($vx-$ax) + ($vy-$ay)*($vy-$ay);
161             next unless $d2 < $d2best;
162             $best= $vertex;
163             $d2best= $d2;
164         }
165         die 'no island vertices?!' unless defined $best;
166         printf PO "%d,%d arch-select-island %s %s\n",
167             $ax,$ay, $arch, $winode2island{$best};
168         my $ccix= $wiarchs->connected_component_by_vertex($best);
169         my $desc= join "\n", map {
170             my $in= $winode2island{$_};
171             "    LP $_". (defined $in ? ", $in" : "");
172         } sort $wiarchs->connected_component_by_index($ccix);
173
174         if (exists $wiccix2arch{$ccix}) {
175             error("architecture determination failed:\n".
176                   "    archipelago $arch\n".
177                   "    archipelago $wiccix2arch{$ccix}\n".
178                   $desc);
179             next;
180         }
181         $wiccix2arch{$ccix}= $arch;
182         print "$ccix $arch ::\n$desc\n";
183     }
184 }
185
186 sub compare_island_lists () {
187 #    foreach my $island (keys %dbisland2arch) {
188 #       next if exists $winode2island
189 #       error("
190 }
191
192 db_setocean('Midnight');
193 db_connect();
194 parse_yppedia_map();
195 parse_database_map();
196 process_yppedia_graphs();
197 compare_island_lists();
198
199 printf "%d %d %d %d\n", $wiarchs->has_edge('32,17','33,18'),
200     $wiarchs->connected_component_by_vertex('32,17'),
201     $wiarchs->connected_component_by_vertex('33,18'),
202     $wiarchs->same_connected_components('32,17','33,18');