Mercurial > hg > batmud > misc
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; + } +} + +}