Mercurial > hg > batmud > misc
view magestats.pl @ 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 | |
children | bc05f9d391bb |
line wrap: on
line source
#!/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; } } }