#!/usr/bin/perl # # Copyright (C) 2002 gl00on.net Enterprises (http://gl00on.net). # # released under the terms of the GNU GPL. # # FIX: # - geometric mean calculation # # TODO: # - per-user pages # - http://www.statslab.cam.ac.uk/~eva/nethack/ways_to_die.html stats :) # auch: http://nethack.devnull.net/archive/2002/standings.html # DATA GATHERING OPTIONS # score file name $scores = "/var/games/nethack/record"; # log file name $logfile = "/var/games/nethack/logfile"; # set this to 1 to not include games with 0 points in the stats $skip_zeros = 1; # don't include games with more than this number of player deaths # (assuming that they were in explore / debug mode). 0 to disable. # setting this to a non-zero value is only useful when you use a # nethack logfile instead of a scorefile, as the scorefile will not # contain games ended in explore / debug mode, anyway. $deaths_allowed = 0; # set this to not include games where player quit $skip_quitted = 0; # STATISTICS OUTPUT OPTIONS # include player names where appropriate? $players = 0; # SCORE TABLE OUTPUT OPTIONS # set this to include seperators in the score table at 1m, # 100k, 10k and 1k points. $score_lines = 1; # maximum number of scores to display. 0 for unlimited. $max_places = 0; # minimum score to display. $min_score = 0; # GENERAL GRAPHICS OUTPUT OPTIONS # image width $graph_width = 800; # image height $graph_height = 400; # line thickness $graph_linewidth = 1; # MOVING AVERAGE GRAPHICS OPTIONS # compute moving average for how many games at a time? $average_smoothing = 25; # blend in moving average graph? $average_blendin = 1; # --- NO NEED TO CONFIGURE ANYTHING BELOW THIS LINE --- use Math::BigInt; use GD::Graph::lines; use GD::Graph::bars; use Socket; # for AF_INET $oldclass{"A"} = "Arc"; $oldclass{"B"} = "Bar"; $oldclass{"C"} = "Cav"; $oldclass{"E"} = "Elf"; $oldclass{"H"} = "Hea"; $oldclass{"K"} = "Kni"; $oldclass{"P"} = "Pri"; $oldclass{"R"} = "Rog"; $oldclass{"S"} = "Sam"; $oldclass{"T"} = "Tou"; $oldclass{"V"} = "Val"; $oldclass{"W"} = "Wiz"; $classname{""} = "All classes"; $classname{"Arc"} = "Archaeologist"; $classname{"Bar"} = "Barbarian"; $classname{"Cav"} = "Caveman"; $classname{"Elf"} = "Elf"; $classname{"Hea"} = "Healer"; $classname{"Kni"} = "Knight"; $classname{"Mon"} = "Monk"; $classname{"Pri"} = "Priest"; $classname{"Ran"} = "Ranger"; $classname{"Rog"} = "Rogue"; $classname{"Sam"} = "Samurai"; $classname{"Tou"} = "Tourist"; $classname{"Val"} = "Valkyrie"; $classname{"Wiz"} = "Wizard"; $cols = 12; sub calc_and_print_score_line { local *FILE = shift; my ($class, $player, $topic) = @_; if($player eq "") { if($class eq "") { ($_max_entries, $_max_player) = get_maxentries(\%class_entries); $total_players = (scalar keys %player_entries) - 1; $_max_entries .= "
(" . $_max_player . ")" if($players); } else { $total_players = (scalar keys %{$class_entries{$class}}) - 1; # FIXME: this is inefficient at least. foreach $__ (sort { $class_entries{$class}{$b} <=> $class_entries{$class}{$a} } keys %{$class_entries{$class}}) { if($__ ne "") { $_max_entries = $class_entries{$class}{$__}; $_max_entries .= "
($class_)" if($players && ($player eq "")); last; } } } } else { $total_players = "-"; $_max_entries = "-"; } if($class ne "") { $_high = $pc_high{$player}{$class}; $_high .= "
(", $class_highp{$class}, ")" if($players && ($player eq "")); $mean = $pc_score{$player}{$class} / $class_entries{$class}{$player}; $med = compute_median($class_scores{$class}{$player}); $std = compute_stddev($class_scores{$class}{$player}, $mean); # $har = compute_harmonicmean($class_scores{$class}{$player}); # $geo = compute_geometricmean($class_scores{$class}{$player}); $_max = $pc_max{$player}{$class}; $_max .= "
(", $class_maxp{$class}, ")" if($players && ($player eq "")); $entries = $class_entries{$class}{$player}; $asc = 0 + $pc_asc{$player}{$class}{$player}; $amuletd = 0 + $pc_amuletd{$player}{$class}; $totalscore = $pc_score{$player}{$class}; } else { $_high = $p_high{$pname}; $_high .= "
($highplayer)" if($players && ($pname eq "")); $mean = $p_score{$pname} / $player_entries{$pname}; $med = compute_median($class_scores{""}{$pname}); $std = compute_stddev($class_scores{""}{$pname}, $mean); # $geo = compute_geometricmean($class_scores{""}{$pname}); # $har = compute_harmonicmean($class_scores{""}{$pname}); $_max = $p_max{$pname}; ($players && ($pname eq "")) && print $_max .= "
($maxtotalplayer)"; $entries = $player_entries{$pname}; $asc = 0 + $p_asc{$pname}; $amuletd = 0 + $p_amuletd{$pname}, $totalscore = $p_score{$pname}; } print_table_line( FILE, $topic, $entries, $total_players, $_max_entries, $asc, $amuletd, $totalscore, $_high, int $mean, # int $geo, # int $har, $med, $std, $_max ); } sub write_stats { my ($filename, $class) = @_; open OUTFILE, ">", $filename; print OUTFILE "\n"; print OUTFILE "\n"; print OUTFILE "Nethack Stats\n"; print OUTFILE "\n"; print OUTFILE "\n"; print OUTFILE "

Nethack Stats: All players / " . $classname{$class} . "

\n"; print OUTFILE "
\n";
	print OUTFILE "\n";
	print OUTFILE "\n";
	print OUTFILE "\n";
	print OUTFILE "\n";
	print OUTFILE "\n";
	print OUTFILE "\n";
	print OUTFILE "\n";
	print OUTFILE "\n";
	print OUTFILE "\n";
	print OUTFILE "\n";
	print OUTFILE "\n";
#	print OUTFILE "\n";
#	print OUTFILE "\n";
	print OUTFILE "\n";
	print OUTFILE "\n";
	print OUTFILE "\n";
	print OUTFILE "\n";
	print OUTFILE "\n";

	if($class eq "") {
		foreach $pname (sort keys %player_entries) {
		
			print_pname_row(OUTFILE, ($pname eq "") ? "All players" : $pname);
			calc_and_print_score_line(OUTFILE, "", $pname, " All classes");

			foreach(sort keys %classname) {
				next unless $class_entries{$_}{$pname};
				calc_and_print_score_line(OUTFILE, $_, $pname, " $_ (" . $classname{$_} . ") ");
			}
		}

	} else {

		print_pname_row(OUTFILE, $classname{$class});

		foreach $pname (sort keys %{$class_entries{$class}}) {
			calc_and_print_score_line(OUTFILE, $class, $pname, " " . ($pname eq "" ? "All players" : $pname) . "");
		}

	}
	
	print OUTFILE "\n";
	print OUTFILE "
Player
Class
total
games
total
players
most games
(same player)
ascen-
sions
deaths w/
amulet
total scorehighscoremean
score
geom.
mean
harm.
mean
median
score
standard
deviation
maximum
total HP


\n"; print OUTFILE "
\n"; write_graphs(OUTFILE, $class, $player); write_scoretable(OUTFILE, $class, $player); print OUTFILE "\n"; print OUTFILE "\n"; } sub write_graphs { local *FILE = shift; my ($class, $player) = @_; foreach("scoremovingaverage", "scoreplot", "totalscoredevelopment", "meanscoredevelopment", "highscoredevelopment", "scorespectrum") { print FILE "
\n"; print FILE "

\n"; print FILE "\"Total\n"; print FILE "

\n"; print FILE "
\n"; } } sub print_pname_row { local *FILE = shift; print FILE "" . $_[0] . "\n"; } sub print_table_line { local *FILE = shift; print FILE "\n"; print FILE "" . shift(@_) . "\n"; print FILE "$_\n" foreach(@_); print FILE "\n"; } sub get_maxentries { my ($hashref) = @_; foreach $_c (keys %$hashref) { foreach $_p (keys %{$hashref->{$_c}}) { next if($_p eq ""); if($hashref->{$_c}{$_p} > $_max_entries) { $_max_entries = $hashref->{$_c}{$_p}; $_max_player = $_p; } } } return ($_max_entries, $_max_player); } sub compute_stddev { my ($arrayref, $mean) = @_; my $tmp; $tmp += (($_ - $mean) ** 2) foreach(@$arrayref); return int sqrt($tmp / scalar @$arrayref); } sub compute_median { ($arrayref) = @_; $count = scalar @$arrayref; if($count % 2) { return $arrayref->[$count / 2 - 0.5]; } else { return int(($arrayref->[($count / 2) - 1] + $arrayref->[$count / 2]) / 2); } } sub compute_harmonicmean { my ($arrayref) = @_; my $mean = 0; foreach(@$arrayref) { return undef if($_ == 0); $mean += (1 / $_); } return undef if($mean == 0); return (scalar(@$arrayref) / $mean); } sub compute_geometricmean { my ($arrayref) = @_; my $mean = new Math::BigInt("1"); $mean *= $_ foreach(@$arrayref); $mean **= (1 / scalar @$arrayref); return ($mean ** (1 / scalar @$arrayref)); } sub write_scoretable { local *FILE = shift; my ($class, $player) = @_; my $place, $points_1m_reached = 0, $points_100k_reached = 0, $points_10k_reached = 0, $points_1k_reached = 0; print FILE "

Nethack Highscores: "; if($player eq "") { print FILE "All players"; } else { print FILE $player; } print FILE " / "; if($class eq "") { print FILE "All classes"; } else { print FILE $classname{$class}; } print FILE "

\n"; print FILE "\n"; print FILE "\n"; print FILE "\n"; print FILE "\n"; print FILE "\n"; print FILE "\n"; print FILE "\n"; print FILE "\n"; print FILE "\n"; print FILE "\n"; foreach $ref (sort { $b->{"score"} <=> $a->{"score"} } %allscores) { # I probably have a thinko in the data gathering code somewhere. next if($ref->{"score"} == 0); next if(($player ne "") && ($ref->{"player"} ne $player)); next if(($class ne "") && ($ref->{"class"} ne $class)); last if($ref->{"score"} < $min_score); $omit_death = 0; $omit_lev = 0; $tmp = 0; if($score_lines) { if(($ref->{"score"} < 1000000) && !$points_1m_reached) { print FILE "\n" if($place); $points_1m_reached = 1; } if(($ref->{"score"} < 100000) && !$points_100k_reached) { print FILE "\n" if($place); $points_100k_reached = 1; } if(($ref->{"score"} < 10000) && !$points_10k_reached) { print FILE "\n" if($place); $points_10k_reached = 1; } if(($ref->{"score"} < 1000) && !$points_1k_reached) { print FILE "\n" if($place); $points_1m_reached = 1; $points_100k_reached = 1; $points_10k_reached = 1; $points_1k_reached = 1; } } print FILE "\n"; print FILE "\n"; print FILE "\n"; print FILE "\n\n"; print FILE "\n"; print FILE ""; last if($max_places && ($place >= $max_places)); } print FILE "\n"; print FILE "
No.PointsNameDeathHP[max]





", ++$place, "", $ref->{"score"}, "", $ref->{"player"}, "", $ref->{"plinfo"}, " "; foreach($ref->{"death"}) { m/killed by/i && print FILE "died "; m/petrified by/i && print FILE "turned to stone "; m/choked on/i && do { print FILE "choked on "; if($ref->{"gender"} =~ m/fem/i) { print FILE "her "; } else { print FILE "his "; } print FILE "food "; }; m/ascended/i && do { print FILE "ascended to demigod"; if($ref->{"gender"} =~ m/fem/i) { print FILE "dess"; } print FILE "-hood"; $omit_death = 1; $omit_lev = 1; }; m/quit/i && do { print FILE "quit "; $omit_death = 1; }; m/poisoned/i && print FILE "was poisoned "; m/starved/i && do { print FILE "starved to death "; $omit_death = 1; }; m/crushed/i && print FILE "was crushed to death "; m/tower of flame/i && print FILE "died "; } unless($omit_lev) { foreach ($ref->{"dnum"}) { ($_ == 0) && do { print FILE "in The Dungeons of Doom "; $tmp++; }; ($_ == 1) && do { print FILE "in Gehennom "; $tmp++; }; ($_ == 2) && do { print FILE "in The Gnomish Mines "; $tmp++; }; ($_ == 3) && do { print FILE "in The Quest "; $tmp++; }; ($_ == 4) && do { print FILE "in Sokoban "; $tmp++; }; ($_ == 5) && do { print FILE "in Fort Ludios "; $tmp++; }; ($_ == 6) && do { print FILE "in Vlad's Tower "; $tmp++; }; ($_ == 7) && do { foreach($ref->{"dlev"}) { ($_ == -1) && print FILE "on the Elemental Plane of Earth "; ($_ == -2) && print FILE "on the Elemental Plane of Air "; ($_ == -3) && print FILE "on the Elemental Plane of Fire "; ($_ == -4) && print FILE "on the Elemental Plane of Water "; ($_ == -5) && print FILE "on the Astral Plane "; ($_ == 0) && print FILE "in a place that no mortal should ever reach "; (($_ < -5) || ($_ > 0)) && print FILE "in a realm beyond existance "; } $tmp++; }; # this whole $tmp thing is an ugly kludge. ($tmp == 0) && print FILE "in dungeon branch $_ "; } print FILE "on level ", $ref->{"dlev"}, ""; if($ref->{"dlev"} != $ref->{"mlev"}) { print FILE " [max. ", $ref->{"mlev"}. "]"; } } print FILE "."; # don't you just love regexes? ($d = $ref->{"death"}) =~ s/((on|by)( an?)? )(hallucinogen-distorted|invisible)*([^,]*(\,\s+the\s+shopkeeper)?)(\,\s+while\s+helpless)?$/$1$4$5<\/strong>$7/; print FILE "$d." unless($omit_death); print FILE " "; if($ref->{"hp"} > 0) { print FILE $ref->{"hp"}; } else { print FILE "-"; } print FILE "[", $ref->{"mhp"}, "]

\n"; } sub write_graph { ($dataref, $filename, $y_label, $title, $y_max_value) = @_; $graph = make_graph($y_label, $title, $y_max_value, scalar @{$dataref->[1]} - 1); open GRAPH, ">", "$filename"; binmode GRAPH; $gd = $graph->plot($dataref); print GRAPH $gd->png(); close GRAPH; } sub write_spectrum { ($dataref, $filename, $y_label, $title, $y_max_value, $x_label) = @_; $graph = make_spectrum($y_label, $title, $y_max_value, $x_label); open GRAPH, ">", "$filename"; binmode GRAPH; print GRAPH $graph->plot($dataref)->png(); close GRAPH; } sub make_graph { ($y_label, $title, $y_max_value, $x_max_value) = @_; $graph = GD::Graph::lines->new($graph_width, $graph_height); $graph->set( transparent => 0, interlaced => 0, x_label => '# Games', y_min_value => 0, box_axis => 1, long_ticks => 1, x_label_position => 1/2, x_tick_number => ($x_max_value > 10) ? 10 : $x_max_value, x_min_value => 0, x_max_value => $x_max_value, two_axes => 0, t_margin => 10, b_margin => 10, l_margin => 10, r_margin => 10, bgclr => 'black', fgclr => 'dgreen', labelclr => 'lgreen', axislabelclr => 'lgreen', legendclr => 'lgreen', valuesclr => 'lgreen', textclr => 'lgreen', dclrs => [ qw(lgreen) ], line_width => $graph_linewidth, y_tick_number => 5, y_number_format => "%d", x_number_format => "%d", y_label => $y_label, title => $title, y_max_value => $y_max_value ); return $graph; } sub make_spectrum { ($y_label, $title, $y_max_value, $x_label) = @_; $graph = GD::Graph::bars->new($graph_width, $graph_height); $graph->set( transparent => 0, interlaced => 0, x_label => $x_label, y_min_value => 0, box_axis => 1, long_ticks => 1, x_label_position => 1/2, two_axes => 0, t_margin => 10, b_margin => 10, l_margin => 10, r_margin => 10, bgclr => 'black', fgclr => 'dgreen', labelclr => 'lgreen', axislabelclr => 'lgreen', legendclr => 'lgreen', valuesclr => 'lgreen', textclr => 'lgreen', dclrs => [ qw(lgreen) ], line_width => $graph_linewidth, y_tick_number => ($y_max_value > 5) ? 5 : $y_max_value, y_number_format => "%d", y_label => $y_label, title => $title, y_max_value => $y_max_value ); return $graph; } open FH, "<", $scores or die "Can't read file."; while() { chomp; ($version, $points, $deathdungeonnum, $deathlev, $maxlvl, $hp, $totalhp, $deaths, $deathdate, $birthdate, $uid, $class, $plrace, $plgend, $plalign, $namedeath, @spillover) = split /\s+/; next if (($points == 0) && $skip_zeros); next if ($deaths_allowed && ($deaths > $deaths_allowed)); ($playername, @death) = split /,/, $namedeath; $death = join " ", @death; $death .= " " . (join " ", @spillover); next if(($death =~ m/quit/) && $skip_quitted); $scorelines{$_} = 1; $allscores{++$numscores}{"score"} = $points; $allscores{$numscores}{"dnum"} = $deathdungeonnum; $allscores{$numscores}{"dlev"} = $deathlev; $allscores{$numscores}{"mlev"} = $maxlvl; $allscores{$numscores}{"hp"} = $hp; $allscores{$numscores}{"mhp"} = $totalhp; $allscores{$numscores}{"player"} = $playername; $allscores{$numscores}{"plinfo"} = "-$class-$plrace-$plgend-$plalign"; $allscores{$numscores}{"gender"} = $plgend; $allscores{$numscores}{"class"} = $class; ($allscores{$numscores}{"death"} = $death) =~ s/^(.)/\U $1/; # number of entries for each player; number of players $player_entries{""}++; $player_entries{$playername}++; $class_entries{$class}{$playername}++; $class_entries{$class}{""}++; $name_entries{$playername}{$class}++; $name_entries{""}{$class}++; push @{$class_scores{""}{""}}, $points; push @{$class_scores{$class}{""}}, $points; push @{$class_scores{$class}{$playername}}, $points; push @{$class_scores{""}{$playername}}, $points; # total number of ascensions ($death =~ m/ascended/) && do { $p_asc{""}++; $pc_asc{""}{$class}++; $p_asc{$playername}++; $pc_asc{$playername}{$class}++; }; # total score $p_score{""} += $points; $pc_score{""}{$class} += $points; $p_score{$playername} += $points; $pc_score{$playername}{$class} += $points; # number of games >= 1M points ($points > 999999) && do { $p_mega{""}++; $pc_mega{""}{$class}++; $p_mega{$playername}++; $pc_mega{$playername}++; }; # highscore ($points > $p_high{""}) && do { $p_high{""} = $points; $highplayer = $playername; }; ($points > $pc_high{""}{$class}) && do { $pc_high{""}{$class} = $points; $class_highp{$class} = $playername; }; ($points > $p_high{$playername}) && do { $p_high{$playername} = $points; }; ($points > $pc_high{$playername}{$class}) && do { $pc_high{$playername}{$class} = $points; }; # max total HP ($totalhp > $p_max{""}) && do { $p_max{""} = $totalhp; $maxtotalplayer = $playername; }; ($totalhp > $pc_max{""}{$class}) && do { $pc_max{""}{$class} = $totalhp; $class_maxp{$class} = $playername; }; ($totalhp > $p_max{$playername}) && do { $p_max{$playername} = $totalhp; }; ($totalhp > $pc_max{$playername}{$class}) && do { $pc_max{$playername}{$class} = $totalhp; }; # number of deaths with the Amulet ($death =~ m/\(with the Amulet\)/) && do { $p_amuletd{""}++; $pc_amuletd{""}{$class}++; $p_amuletd{$playername}++; $pc_amuletd{$playername}{$class}++; }; } close FH; open FH, "<", $logfile or die "Can't open logfile"; while() { chomp; # check if this score should be considered (i.e., if it's in the scorefile, too) if($scorelines{$_}) { # setup work. ($version, $points, $deathdungeonnum, $deathlev, $maxlvl, $hp, $totalhp, $deaths, $deathdate, $birthdate, $uid, $class, $plrace, $plgend, $plalign, $namedeath, @spillover) = split /\s+/; ($playername, @death) = split /,/, $namedeath; $death = join " ", @death; $death .= " " . (join " ", @spillover); # calculcate highscores. $high = $points if($points > $high); $high_p{$playername} = $points if($points > $high_p{$playername}); $high_c{$class} = $points if($points > $high_c{$class}); $high_cp{$class}{$playername} = $points if($points > $high_cp{$class}{$playername}); $high_pc{$playername}{$class} = $points if($points > $high_pc{$playername}{$class}); # calculate total scores. $score += $points; $score_p{$playername} += $points; $score_c{$class} += $points; $score_cp{$class}{$playername} += $points; $score_pc{$playername}{$class} += $points; # update score spectrum. $graph_score_spectrum{int(log($points) / (log 10))}++; $graph_score_spectrum_c{$class}{int(log($points) / (log 10))}++; $graph_score_spectrum_p{$player}{int(log($points) / (log 10))}++; $graph_score_spectrum_cp{$class}{$player}{int(log($points) / (log 10))}++; $graph_score_spectrum_pc{$player}{$class}{int(log($points) / (log 10))}++; # score moving average. push @moving_average, $points; push @{$moving_average_c{$class}}, $points; push @{$moving_average_p{$playername}}, $points; push @{$moving_average_cp{$class}{$playername}}, $points; push @{$moving_average_pc{$playername}{$class}}, $points; # make sure the arrays containing the most recent data don't get too big. foreach $__ (\@moving_average, $moving_average_c{$class}, $moving_average_p{$playername}, $moving_average_cp{$class}{$playername}, $moving_average_pc{$playername}{$class}) { shift @$__ if(scalar(@$__) > $average_smoothing); } # update moving average highs and graph data. $_mv = 0; $_mv += $_ foreach(@moving_average); $val = ($_mv / (scalar @moving_average)); $moving_average_high = $val if($val > $moving_average_high); if(((scalar @moving_average) >= $average_smoothing) || $average_blendin) { push @graph_scores_moving, $val; } else { push @graph_scores_moving, undef; } $_mv = 0; $_mv += $_ foreach(@{$moving_average_c{$class}}); $val = ($_mv / (scalar @{$moving_average_c{$class}})); $moving_average_high_c{$class} = $val if($val > $moving_average_high_c{$class}); if(((scalar @{$moving_average_c{$class}}) >= $average_smoothing) || $average_blendin) { push @{$graph_scores_moving_c{$class}}, $val; } else { push @{$graph_scores_moving_c{$class}}, undef; } $_mv = 0; $_mv += $_ foreach(@{$moving_average_p{$playername}}); $val = ($_mv / (scalar @{$moving_average_p{$playername}})); $moving_average_high_p{$playername} = $val if($val > $moving_average_high_p{$playername}); if(((scalar @{$moving_average_p{$playername}}) >= $average_smoothing) || $average_blendin) { push @{$graph_scores_moving_p{$playername}}, $val; } else { push @{$graph_scores_moving_p{$playername}}, undef; } $_mv = 0; $_mv += $_ foreach(@{$moving_average_cp{$class}{$playername}}); $val = ($_mv / (scalar @{$moving_average_cp{$class}{$playername}})); $moving_average_high_cp{$class}{$playername} = $val if($val > $moving_average_high_cp{$class}{$playername}); if(((scalar @{$moving_average_cp{$class}{$playername}}) >= $average_smoothing) || $average_blendin) { push @{$graph_scores_moving_cp{$class}{$playername}}, $val; } else { push @{$graph_scores_moving_cp{$class}{$playername}}, undef; } $_mv = 0; $_mv += $_ foreach(@{$moving_average_pc{$playername}{$class}}); $val = ($_mv / (scalar @{$moving_average_pc{$playername}{$class}})); $moving_average_high_pc{$playername}{$class} = $val if($val > $moving_average_high_pc{$playername}{$class}); if(((scalar @{$moving_average_pc{$playername}{$class}}) >= $average_smoothing) || $average_blendin) { push @{$graph_scores_moving_pc{$playername}{$class}}, $val; } else { push @{$graph_scores_moving_pc{$playername}{$class}}, undef; } # update total score development. push @graph_scores, $score; push @{$graph_scores_c{$class}}, $score_c{$class}; push @{$graph_scores_p{$playername}}, $score_p{$playername}; push @{$graph_scores_cp{$class}{$playername}}, $score_cp{$class}{$playername}; push @{$graph_scores_pc{$playername}{$class}}, $score_pc{$playername}{$class}; # update indices. $num++; $num_c{$class}++; $num_p{$playername}++; $num_cp{$class}{$playername}++; $num_pc{$playername}{$class}++; push @graph_scores_index, $num; push @{$graph_scores_index_c{$class}}, $num_c{$class}; push @{$graph_scores_index_p{$playername}}, $num_p{$playername}; push @{$graph_scores_index_cp{$class}{$playername}}, $num_cp{$class}{$playername}; push @{$graph_scores_index_pc{$playername}{$class}}, $num_pc{$playername}{$class}; # mean score development; also calculate highest mean scores. $tmp = $score / $num; $tmp_c = $score_c{$class} / $num_c{$class}; $tmp_p = $score_p{$playername} / $num_p{$playername}; $tmp_cp = $score_cp{$class}{$playername} / $num_cp{$class}{$playername}; $tmp_pc = $score_pc{$playername}{$class} / $num_pc{$playername}{$class}; $mean_high = $tmp if($tmp > $mean_high); $mean_high_c{$class} = $tmp_c if($tmp_c > $mean_high_c{$class}); $mean_high_p{$playername} = $tmp_p if($tmp_p > $mean_high_p{$playername}); $mean_high_cp{$class}{$playername} = $tmp_cp if($tmp_cp > $mean_high_cp{$class}{$playername}); $mean_high_pc{$playername}{$class} = $tmp_pc if($tmp_pc > $mean_high_pc{$playername}{$class}); push @graph_scores2, $tmp; push @{$graph_scores2_c{$class}}, $tmp_c; push @{$graph_scores2_p{$playername}}, $tmp_p; push @{$graph_scores2_cp{$class}{$playername}}, $tmp_cp; push @{$graph_scores2_pc{$playername}{$class}}, $tmp_pc; # update highscore development. push @graph_scores3, $high; push @{$graph_scores3_c{$class}}, $high_c{$class}; push @{$graph_scores3_p{$playername}}, $high_p{$playername}; push @{$graph_scores3_cp{$class}{$playername}}, $high_cp{$class}{$playername}; push @{$graph_scores3_pc{$playername}{$class}}, $high_pc{$playername}{$class}; # update score plot. push @graph_scores5, $points; push @{$graph_scores5_c{$class}}, $points; push @{$graph_scores5_p{$playername}}, $points; push @{$graph_scores5_cp{$class}{$playername}}, $points; push @{$graph_scores5_pc{$playername}{$class}}, $points; } } close FH; foreach (keys %graph_score_spectrum) { push @graph_score_spectrum_index, ((10 ** $_) . " to " . (10 ** ($_ + 1))); push @graph_score_spectrum_data, $graph_score_spectrum{$_}; if($graph_score_spectrum{$_} > $max_spectrum) { $max_spectrum = $graph_score_spectrum{$_}; } } open OUTFILE, ">", "index.html"; print OUTFILE "\n"; print OUTFILE "\n"; print OUTFILE "Nethack statistics\n"; print OUTFILE "\n"; print OUTFILE "\n"; print OUTFILE "\n"; print OUTFILE "\n"; print OUTFILE "\n"; print OUTFILE "please use the menu frame to navigate. :)\n"; print OUTFILE "\n"; print OUTFILE "\n"; print OUTFILE "\n"; close OUTFILE; open OUTFILE, ">", "menu.html"; print OUTFILE "\n"; print OUTFILE "\n"; print OUTFILE "

Choose:

\n"; print OUTFILE "

All players

\n"; print OUTFILE "

\n"; print OUTFILE "

\n"; print OUTFILE "

\n"; print OUTFILE "

Info

\n"; print OUTFILE "\n"; print OUTFILE "

\n"; print OUTFILE "page generated on:"; print OUTFILE "

\n"; print OUTFILE "

\n"; print OUTFILE "" . localtime() . "\n"; print OUTFILE "

\n"; print OUTFILE "

\n"; print OUTFILE "by nhstats 0.1\n"; print OUTFILE "

\n"; print OUTFILE "
\n"; print OUTFILE "\n"; print OUTFILE "\n"; write_stats("stats.html", ""); foreach(keys %classname) { write_stats("$_-stats.html", $_) if(($_ ne "") && (keys %{$class_entries{$_}} > 0)); } close OUTFILE;