graphplay to draw SVG graphs of gameplay statistics
authorShiar <shiar@shiar.org>
Wed, 6 Feb 2008 21:39:30 +0000 (22:39 +0100)
committerShiar <shiar@shiar.org>
Thu, 7 Feb 2008 23:05:13 +0000 (00:05 +0100)
Parses game stats output of capture script, and draws progression of
minerals, gas, and units over time per player.

SVG is simple enough to write manually (template at *DATA), but
produces essentially the same as SVG::TT::Graph::TimeSeries would
(except much faster and easier to understand/modify).

graphplay [new file with mode: 0755]

diff --git a/graphplay b/graphplay
new file mode 100755 (executable)
index 0000000..d6baebf
--- /dev/null
+++ b/graphplay
@@ -0,0 +1,132 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+use Template;
+use List::Util;
+use Data::Dumper;
+
+my %area = (
+       fieldxmin => 45,
+       fieldxmax => 1590,
+       fieldymin => 10,
+       fieldymax => 575,
+       xmax => 1600,
+       ymax => 600,
+);
+$area{fieldxsize} = $area{fieldxmax} - $area{fieldxmin};
+$area{fieldysize} = $area{fieldymax} - $area{fieldymin};
+
+my @lines;
+my @max = (0) x 4;
+my $maxtime = 0;
+my $player = 0;
+
+for my $input (@ARGV) {
+       open my $datafile, '<', $input or next;
+       my @data;
+       my $lasttime;
+       while (defined ($_ = readline $datafile)) {
+               my ($frame, $time, @vars) = split /\t/, $_;
+               $frame =~ /^\d+:$/ or next;
+               if ($time !~ /^\d+$/) {
+                       defined $lasttime or next;
+                       $time = $lasttime + 1;
+                       $lasttime = undef;
+               } else {
+                       $lasttime = $time;
+               }
+               /^\d+$/ or $_ = undef for @vars;
+               push @{$data[$time]} => \@vars;
+               defined $vars[$_] and $vars[$_] > $max[$_] and $max[$_] = $vars[$_]
+                       for 0 .. $#vars;
+       }
+       $maxtime = $#data if $#data > $maxtime;
+       close $datafile;
+
+       for my $time (0 .. $#data) {
+               my $vals = $data[$time];
+               for my $subtime (0 .. $#$vals) {
+                       defined $vals->[$subtime]->[$_] and push @{$lines[$_][$player]} => [
+                               ($time + $subtime/@$vals), # x
+                               $vals->[$subtime]->[$_] # y
+                       ] for 0 .. 3;
+               }
+       }
+
+       $player++;
+}
+
+my @norm = ((map {$area{fieldysize} / $_} @max), $area{fieldxsize} / $maxtime);
+
+my $xsteps = 25;
+my $xstep = $maxtime / $xsteps;
+my @xaxis = map [$area{fieldxmin} + $_ * $norm[4], sprintf '%d:%02d', $_/3600, $_/60%60],
+       map $_*$xstep, 0 .. $xsteps-1;
+
+my $ysteps = 10;
+my @yaxis;
+for my $i (0 .. 3) {
+       my $ystep = $max[$i] / $ysteps;
+       $ystep = 10 ** int (log($ystep * 4) / log(10));
+       $ystep /= 2 while $ystep * $ysteps > $max[$i];
+       $yaxis[$i] = [
+               map [$area{fieldymax} - $_ * $norm[$i], $_],
+                       map $_*$ystep, 0 .. int $max[$i] / $ystep
+       ];
+}
+
+undef $/;
+my $svg = Template->new({POST_CHOMP => 1, BLOCKS => {timeline => readline DATA}});
+
+for ([0 => "min", "Minerals"], [1 => "gas", "Gas"], [2 => "unit", "Units"]) {
+       my ($line, $filename, $title) = @$_;
+       open my $output, '>', "$filename.svg";
+       $svg->process('timeline', {
+               title => "$title timeline for /replay/20080201md4x4",
+               paths => [
+                       map { "\n\tM " . join "\tL ", map sprintf("%s %s\n",
+                               $area{fieldxmin} + $_->[0] * $norm[4],
+                               $area{fieldymax} - $_->[1] * $norm[$line]
+                       ), grep defined, @$_ } @{$lines[$line]}
+               ],
+               xaxis => \@xaxis,
+               yaxis => $yaxis[$line],
+               area => \%area,
+       }, $output) or die $svg->error;
+}
+
+__DATA__
+<?xml version="1.0"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+
+<?xml-stylesheet href="http://sc.shiar.net/apm.css" type="text/css"?>
+
+<svg width="100%" height="100%" viewBox="0 0 [% area.xmax %] [% area.ymax %]"
+ xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<title>[% title %]</title>
+
+<rect x="0" y="0" width="[% area.xmax %]" height="[% area.ymax %]" class="svgBackground" />
+<rect x="[% area.fieldxmin %]" y="[% area.fieldymin %]" width="[% area.fieldxsize %]" height="[% area.fieldysize %]" class="graphBackground" />
+
+<path d="M[% area.fieldxmin %] [% area.fieldymin %] v[% area.fieldysize %]" class="axis" id="xAxis"/>
+[% FOREACH entry = xaxis %]
+<text x="[% entry.0 %]" y="[% area.fieldymax + 15 %]" class="xAxisLabels"> [% entry.1 %]</text>
+[% END %]
+
+<path d="M[% area.fieldxmin %] [% area.fieldymax %] h[% area.fieldxsize %]" class="axis" id="yAxis"/>
+[% FOREACH entry = yaxis %]
+<path d="M[% area.fieldxmin %] [% entry.0 %] h[% area.fieldxsize %]" class="guideLines" />
+<text x="[% area.fieldxmin - 5 %]" y="[% entry.0 %]" class="yAxisLabels">[% entry.1 %]</text>
+[% END %]
+
+<g class="data">
+[% count = 0 %]
+[% FOREACH path = paths %][% count = count + 1 %]
+<path d="[% path %]" class="line[% count %]" />
+[% END %]
+</g>
+
+</svg>
+