graphplay to draw SVG graphs of gameplay statistics
[perl/schtarr.git] / graphplay
1 #!/usr/bin/env perl
2 use strict;
3 use warnings;
4
5 use Template;
6 use List::Util;
7 use Data::Dumper;
8
9 my %area = (
10         fieldxmin => 45,
11         fieldxmax => 1590,
12         fieldymin => 10,
13         fieldymax => 575,
14         xmax => 1600,
15         ymax => 600,
16 );
17 $area{fieldxsize} = $area{fieldxmax} - $area{fieldxmin};
18 $area{fieldysize} = $area{fieldymax} - $area{fieldymin};
19
20 my @lines;
21 my @max = (0) x 4;
22 my $maxtime = 0;
23 my $player = 0;
24
25 for my $input (@ARGV) {
26         open my $datafile, '<', $input or next;
27         my @data;
28         my $lasttime;
29         while (defined ($_ = readline $datafile)) {
30                 my ($frame, $time, @vars) = split /\t/, $_;
31                 $frame =~ /^\d+:$/ or next;
32                 if ($time !~ /^\d+$/) {
33                         defined $lasttime or next;
34                         $time = $lasttime + 1;
35                         $lasttime = undef;
36                 } else {
37                         $lasttime = $time;
38                 }
39                 /^\d+$/ or $_ = undef for @vars;
40                 push @{$data[$time]} => \@vars;
41                 defined $vars[$_] and $vars[$_] > $max[$_] and $max[$_] = $vars[$_]
42                         for 0 .. $#vars;
43         }
44         $maxtime = $#data if $#data > $maxtime;
45         close $datafile;
46
47         for my $time (0 .. $#data) {
48                 my $vals = $data[$time];
49                 for my $subtime (0 .. $#$vals) {
50                         defined $vals->[$subtime]->[$_] and push @{$lines[$_][$player]} => [
51                                 ($time + $subtime/@$vals), # x
52                                 $vals->[$subtime]->[$_] # y
53                         ] for 0 .. 3;
54                 }
55         }
56
57         $player++;
58 }
59
60 my @norm = ((map {$area{fieldysize} / $_} @max), $area{fieldxsize} / $maxtime);
61
62 my $xsteps = 25;
63 my $xstep = $maxtime / $xsteps;
64 my @xaxis = map [$area{fieldxmin} + $_ * $norm[4], sprintf '%d:%02d', $_/3600, $_/60%60],
65         map $_*$xstep, 0 .. $xsteps-1;
66
67 my $ysteps = 10;
68 my @yaxis;
69 for my $i (0 .. 3) {
70         my $ystep = $max[$i] / $ysteps;
71         $ystep = 10 ** int (log($ystep * 4) / log(10));
72         $ystep /= 2 while $ystep * $ysteps > $max[$i];
73         $yaxis[$i] = [
74                 map [$area{fieldymax} - $_ * $norm[$i], $_],
75                         map $_*$ystep, 0 .. int $max[$i] / $ystep
76         ];
77 }
78
79 undef $/;
80 my $svg = Template->new({POST_CHOMP => 1, BLOCKS => {timeline => readline DATA}});
81
82 for ([0 => "min", "Minerals"], [1 => "gas", "Gas"], [2 => "unit", "Units"]) {
83         my ($line, $filename, $title) = @$_;
84         open my $output, '>', "$filename.svg";
85         $svg->process('timeline', {
86                 title => "$title timeline for /replay/20080201md4x4",
87                 paths => [
88                         map { "\n\tM " . join "\tL ", map sprintf("%s %s\n",
89                                 $area{fieldxmin} + $_->[0] * $norm[4],
90                                 $area{fieldymax} - $_->[1] * $norm[$line]
91                         ), grep defined, @$_ } @{$lines[$line]}
92                 ],
93                 xaxis => \@xaxis,
94                 yaxis => $yaxis[$line],
95                 area => \%area,
96         }, $output) or die $svg->error;
97 }
98
99 __DATA__
100 <?xml version="1.0"?>
101 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
102  "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
103
104 <?xml-stylesheet href="http://sc.shiar.net/apm.css" type="text/css"?>
105
106 <svg width="100%" height="100%" viewBox="0 0 [% area.xmax %] [% area.ymax %]"
107  xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
108 <title>[% title %]</title>
109
110 <rect x="0" y="0" width="[% area.xmax %]" height="[% area.ymax %]" class="svgBackground" />
111 <rect x="[% area.fieldxmin %]" y="[% area.fieldymin %]" width="[% area.fieldxsize %]" height="[% area.fieldysize %]" class="graphBackground" />
112
113 <path d="M[% area.fieldxmin %] [% area.fieldymin %] v[% area.fieldysize %]" class="axis" id="xAxis"/>
114 [% FOREACH entry = xaxis %]
115 <text x="[% entry.0 %]" y="[% area.fieldymax + 15 %]" class="xAxisLabels"> [% entry.1 %]</text>
116 [% END %]
117
118 <path d="M[% area.fieldxmin %] [% area.fieldymax %] h[% area.fieldxsize %]" class="axis" id="yAxis"/>
119 [% FOREACH entry = yaxis %]
120 <path d="M[% area.fieldxmin %] [% entry.0 %] h[% area.fieldxsize %]" class="guideLines" />
121 <text x="[% area.fieldxmin - 5 %]" y="[% entry.0 %]" class="yAxisLabels">[% entry.1 %]</text>
122 [% END %]
123
124 <g class="data">
125 [% count = 0 %]
126 [% FOREACH path = paths %][% count = count + 1 %]
127 <path d="[% path %]" class="line[% count %]" />
128 [% END %]
129 </g>
130
131 </svg>
132