changeset 71:3eca3030c175 misc

Added a simplistic Perl-based utility for generating Mage-guild related statistics and graphs in HTML + image formats from log file input.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 09 Apr 2010 10:49:31 +0000
parents 56c00d32d570
children bc05f9d391bb
files magestats.pl
diffstat 1 files changed, 277 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/magestats.pl	Fri Apr 09 10:49:31 2010 +0000
@@ -0,0 +1,277 @@
+#!/usr/bin/perl -w
+use strict;
+use Data::Dumper;
+use GD::Graph;
+use GD::Graph::linespoints;
+
+my %spell_data = (
+  "acid" => [ "acid blast",              "acid storm" ],
+  "fire" => [ "lava blast",              "lava storm" ],
+  "elec" => [ "electrocution",           "lightning storm" ],
+  "pois" => [ "summon carnal spores",    "killing cloud" ],
+  "cold" => [ "cold ray",                "hailstorm" ],
+  "mana" => [ "golden arrow",            "magic eruption" ],
+  "asph" => [ "blast vacuum",            "vacuum globe" ],
+);
+
+my $progName = "magestats.pl";
+my $opt_cachefile;
+my $opt_verbosity = 1;
+my $opt_prefix = "magestats";
+my $opt_imgfmt = "png";
+my $opt_noinput = 0;
+my $opt_width = 500;
+my $opt_height = 250;
+
+sub mlog($$)
+{
+  my $level = shift;
+  my $msg = shift;
+  print STDERR "* $msg\n" if ($opt_verbosity >= $level);
+}
+
+my $opt_mode = shift or die(
+"Magestats v0.3 by ccr/TNSP (C) 2010 TNSP
+
+Usage: $progName dump [options] < logfile
+       $progName stats [options] < logfile
+
+ -v                Verbose mode
+ -c <cachefile>    Specify a cache file to restore from
+ -p <prefix>       Output filename prefix ('$opt_prefix')
+ -t <png|gif>      Image format to be used ('$opt_imgfmt')
+ -s <WxH>          Graph dimensions in pixels ($opt_width x $opt_height)
+ -n                No input (only handle cache, if specified)
+");
+
+die("Invalid operation mode '$opt_mode'!\n") unless ($opt_mode =~ /^(dump|stats)$/);
+
+while (my $opt = shift) {
+  if ($opt eq "-c") {
+    # Restore cache from file
+    $opt_cachefile = shift or die("-c option requires an argument.\n");
+  } elsif ($opt eq "-v") {
+    $opt_verbosity++;
+  } elsif ($opt eq "-t") {
+    $opt_imgfmt = shift or die("-t option requires an argument.\n");
+    die("Invalid format '$opt_imgfmt' specified!\n") unless ($opt_imgfmt =~ /^(png|gif)$/);
+  } elsif ($opt eq "-p") {
+    $opt_prefix = shift or die("-p option requires an argument.\n");
+  } elsif ($opt eq "-n") {
+    $opt_noinput = 1;
+  }
+}
+
+
+###
+### Construct full data structure
+###
+mlog(1, "Initializing structures.");
+my $spells = {};
+foreach my $type (keys %spell_data) {
+  my $src = $spell_data{$type};
+
+  foreach my $spell (@{$src}) {
+    $spells->{$spell}{"type"} = $type;
+    $spells->{$spell}{"blasts"} = 0;
+  }
+
+  $spells->{$type}{"essence"}{"increase"} = 0;
+  $spells->{$type}{"essence"}{"blasts"}{"single"} = [];
+  $spells->{$type}{"essence"}{"blasts"}{"area"} = [];
+
+  $spells->{$type}{"reagents"} = 0;
+
+  $spells->{$type}{"single"} = $src->[0];
+  $spells->{$type}{"area"} = $src->[1];
+  
+  push(@{$spells->{"single"}}, $src->[0]);
+  push(@{$spells->{"area"}}, $src->[1]);
+}
+
+sub get_spell_type($)
+{
+  my $spell = $_[0];
+  die("get_spell_type($spell): No such spell.\n") unless exists($spells->{$spell}{"type"});
+  return $spells->{$spell}{"type"};
+}
+
+
+###
+### Read cache
+###
+if (defined($opt_cachefile)) {
+  mlog(1, "Restoring cache from '$opt_cachefile'.");
+  open(CACHE, "<", $opt_cachefile) or die("Could not open cache file '$opt_cachefile'!\n");
+  my $s = <CACHE>;
+  close(CACHE);
+  eval $s;
+}
+
+
+###
+### Scan input for blasts etc.
+###
+my $match_single = join("|", @{$spells->{"single"}});
+my $match_area = join("|", @{$spells->{"area"}});
+my $essence_flag = 0;
+my $last_spell = "";
+
+if ($opt_noinput) {
+ 
+} else {
+mlog(1, "Parsing log from stdin...");
+while (defined(my $s = <STDIN>)) {
+  if ($s =~ /^You watch with selfpride as your ($match_single) hits / || $s =~ /^You hit .+ with your ($match_area)\.$/) {
+    $last_spell = $1;
+
+    $spells->{$last_spell}{"blasts"}++;
+    $spells->{"total"}{"blasts"}++;
+
+    if ($essence_flag) {
+      my $type = get_spell_type($last_spell);
+      $spells->{$type}{"essence"}{"increase"}++;
+
+      foreach my $class ("single", "area") {
+        my $name = $spells->{$type}{$class};
+        push(@{$spells->{$type}{"essence"}{"blasts"}{$class}}, $spells->{$name}{"blasts"});
+        
+#        foreach my $crit ("1", "2", "3", "") {
+#          push(@{$spells->{$type}{"essence"}{"crits_".$crit}{$class}}, $spells->{$name}{"crits_".$crit});
+#        }
+      }
+
+      $essence_flag = 0;
+    }
+  } elsif ($s =~ /^Your knowledge in elemental powers helps you to save the reagent for further use\./) {
+    $spells->{get_spell_type($last_spell)}{"reagents"}++;
+    $spells->{"total"}{"reagents"}++;
+  } elsif ($s =~ /^You feel your skills in handling elemental forces improve\./) {
+    $essence_flag = 1;
+    $spells->{"total"}{"essence"}++;
+  }
+}
+}
+
+
+###
+### Dump cache to stdout
+###
+if ($opt_mode eq "dump") {
+  $Data::Dumper::Indent = 0;
+  $Data::Dumper::Useqq  = 1;
+  $Data::Dumper::Purity = 1;
+  
+  my $dumper = Data::Dumper->new([$spells], ["spells"]);
+  print $dumper->Dump();
+  exit;
+}
+
+
+###
+### Output statistics
+###
+if ($opt_mode eq "stats") {
+my $filename = $opt_prefix.".html";
+open(OUT, ">", $filename) or die("Could not create output file '$filename'.\n");
+
+mlog(1, "Outputting stats HTML to '$filename'");
+
+print OUT qq|
+<html>
+<head>
+ <title>Mage statistics</title>
+</head>
+<body>
+
+<h1>Mage statistics</h1>
+
+<table border=\"1\">
+<tr>
+ <th>Type</th>
+ <th>Blasts</th>
+ <th>Reagents saved</th>
+ <th>Essence gained</th>
+ <th>Blasts per essence gain</th>
+</tr>
+|;
+
+foreach my $type (sort { $a cmp $b } keys %spell_data) {
+  my $s = $spells->{$type}{"single"};
+  my $a = $spells->{$type}{"area"};
+  
+  print OUT "<tr><td>$type</td><td>
+  <table border=\"1\"><th></th><th>Single</th><th>Area</th></tr>
+  <tr><th>Name</th><td>$s</td><td>$a</td></tr>
+  <tr><th>Blasts</th><td>".$spells->{$s}{"blasts"}."</td><td>".$spells->{$a}{"blasts"}."</td></tr>
+  </table>";
+
+  my $total_blasts = $spells->{$s}{"blasts"} + $spells->{$a}{"blasts"};
+  
+  printf OUT "</td><td><b>%d</b> (%1.2f %%)</td><td>%d</td><td>",
+  $spells->{$type}{"reagents"},
+  ($total_blasts > 0) ? ($spells->{$type}{"reagents"} * 100.0) / $total_blasts : 0.0,
+  $spells->{$type}{"essence"}{"increase"};
+  
+  if (exists($spells->{$type}{"essence"}{"blasts"})) {
+    print OUT "<img src=\"".$opt_prefix."_".$type.".".$opt_imgfmt."\" alt=\"?\" />";
+  }
+  
+  print OUT "
+  </td>
+  </tr>\n";
+}
+
+print OUT "</table>
+
+<p>
+Total blasts: <b>".$spells->{"total"}{"blasts"}."</b><br />
+Total reagents saved: <b>".$spells->{"total"}{"reagents"}."</b><br />
+Total essence gained: <b>".$spells->{"total"}{"essence"}."</b><br />
+</p>
+
+</body>
+</html>
+";
+
+mlog(1, "Outputting graphs '".$opt_prefix."_*.".$opt_imgfmt."'...");
+
+foreach my $type (sort { $a cmp $b } keys %spell_data) {
+  my $s = $spells->{$type}{"single"};
+  my $a = $spells->{$type}{"area"};
+  
+  my $graph = GD::Graph::linespoints->new($opt_width, $opt_height);
+  $graph->set(
+    y_label => 'Blasts',
+    x_label => 'Essence gained', 
+  );
+
+  my @titles = ();
+  my @total = ();
+  for (my $i = 1; $i <= $spells->{$type}{"essence"}{"increase"}; $i++) {
+    push(@titles, "+".$i);
+  }
+
+  for (my $i = 0; $i < $spells->{$type}{"essence"}{"increase"}; $i++) {
+    push(@total, $spells->{$type}{"essence"}{"blasts"}{"single"}[$i] + $spells->{$type}{"essence"}{"blasts"}{"area"}[$i]);
+  }
+  
+  my @data = (
+    \@titles,
+    $spells->{$type}{"essence"}{"blasts"}{"single"},
+    $spells->{$type}{"essence"}{"blasts"}{"area"},
+    \@total,
+  );
+  
+  my $gd = $graph->plot(\@data) or mlog(0, "Error creating graph ($type): ".$graph->error);
+
+  if ($gd) {
+    open(IMG, ">", $opt_prefix."_".$type.".".$opt_imgfmt) or die("Error openiong file ".$!."\n");
+    binmode IMG;
+    print IMG $gd->gif if ($opt_imgfmt eq "gif");
+    print IMG $gd->png if ($opt_imgfmt eq "png");
+    close IMG;
+  }
+}
+
+}