pbot/applets/cjeopardy/cjeopardy_scores.pl

363 lines
13 KiB
Perl
Raw Normal View History

#!/usr/bin/env perl
2021-07-11 00:00:22 +02:00
# SPDX-FileCopyrightText: 2021 Pragmatic Software <pragma78@gmail.com>
# SPDX-License-Identifier: MIT
License project under MPL2 This patch adds the file LICENSE which is the verbatim copy of the Mozilla Public License Version 2.0 as retreived from https://www.mozilla.org/media/MPL/2.0/index.815ca599c9df.txt on 2017-03-05. This patch also places license headers for the MPL2 type A variant of the license header in the following files: PBot/AntiFlood.pm PBot/BanTracker.pm PBot/BlackList.pm PBot/BotAdminCommands.pm PBot/BotAdmins.pm PBot/ChanOpCommands.pm PBot/ChanOps.pm PBot/Channels.pm PBot/Commands.pm PBot/DualIndexHashObject.pm PBot/EventDispatcher.pm PBot/FactoidCommands.pm PBot/FactoidModuleLauncher.pm PBot/Factoids.pm PBot/HashObject.pm PBot/IRCHandlers.pm PBot/IgnoreList.pm PBot/IgnoreListCommands.pm PBot/Interpreter.pm PBot/LagChecker.pm PBot/Logger.pm PBot/MessageHistory.pm PBot/MessageHistory_SQLite.pm PBot/NickList.pm PBot/PBot.pm PBot/Plugins.pm PBot/Plugins/AntiAway.pm PBot/Plugins/AntiKickAutoRejoin.pm PBot/Plugins/AntiRepeat.pm PBot/Plugins/AntiTwitter.pm PBot/Plugins/AutoRejoin.pm PBot/Plugins/Counter.pm PBot/Plugins/Quotegrabs.pm PBot/Plugins/Quotegrabs/Quotegrabs_Hashtable.pm PBot/Plugins/Quotegrabs/Quotegrabs_SQLite.pm PBot/Plugins/UrlTitles.pm PBot/Plugins/_Example.pm PBot/Refresher.pm PBot/Registerable.pm PBot/Registry.pm PBot/RegistryCommands.pm PBot/SQLiteLogger.pm PBot/SQLiteLoggerLayer.pm PBot/SelectHandler.pm PBot/StdinReader.pm PBot/Timer.pm PBot/Utils/ParseDate.pm PBot/VERSION.pm build/update-version.pl modules/acronym.pl modules/ago.pl modules/c11std.pl modules/c2english.pl modules/c2english/CGrammar.pm modules/c2english/c2eng.pl modules/c99std.pl modules/cdecl.pl modules/cfaq.pl modules/cjeopardy/IRCColors.pm modules/cjeopardy/QStatskeeper.pm modules/cjeopardy/Scorekeeper.pm modules/cjeopardy/cjeopardy.pl modules/cjeopardy/cjeopardy_answer.pl modules/cjeopardy/cjeopardy_filter.pl modules/cjeopardy/cjeopardy_hint.pl modules/cjeopardy/cjeopardy_qstats.pl modules/cjeopardy/cjeopardy_scores.pl modules/cjeopardy/cjeopardy_show.pl modules/codepad.pl modules/compiler_block.pl modules/compiler_client.pl modules/compiler_vm/Diff.pm modules/compiler_vm/cc modules/compiler_vm/compiler_client.pl modules/compiler_vm/compiler_server.pl modules/compiler_vm/compiler_server_vbox_win32.pl modules/compiler_vm/compiler_server_watchdog.pl modules/compiler_vm/compiler_vm_client.pl modules/compiler_vm/compiler_vm_server.pl modules/compiler_vm/compiler_watchdog.pl modules/compiler_vm/languages/_c_base.pm modules/compiler_vm/languages/_default.pm modules/compiler_vm/languages/bash.pm modules/compiler_vm/languages/bc.pm modules/compiler_vm/languages/bf.pm modules/compiler_vm/languages/c11.pm modules/compiler_vm/languages/c89.pm modules/compiler_vm/languages/c99.pm modules/compiler_vm/languages/clang.pm modules/compiler_vm/languages/clang11.pm modules/compiler_vm/languages/clang89.pm modules/compiler_vm/languages/clang99.pm modules/compiler_vm/languages/clangpp.pm modules/compiler_vm/languages/clisp.pm modules/compiler_vm/languages/cpp.pm modules/compiler_vm/languages/freebasic.pm modules/compiler_vm/languages/go.pm modules/compiler_vm/languages/haskell.pm modules/compiler_vm/languages/java.pm modules/compiler_vm/languages/javascript.pm modules/compiler_vm/languages/ksh.pm modules/compiler_vm/languages/lua.pm modules/compiler_vm/languages/perl.pm modules/compiler_vm/languages/python.pm modules/compiler_vm/languages/python3.pm modules/compiler_vm/languages/qbasic.pm modules/compiler_vm/languages/scheme.pm modules/compiler_vm/languages/server/_c_base.pm modules/compiler_vm/languages/server/_default.pm modules/compiler_vm/languages/server/c11.pm modules/compiler_vm/languages/server/c89.pm modules/compiler_vm/languages/server/c99.pm modules/compiler_vm/languages/server/clang.pm modules/compiler_vm/languages/server/clang11.pm modules/compiler_vm/languages/server/clang89.pm modules/compiler_vm/languages/server/clang99.pm modules/compiler_vm/languages/server/cpp.pm modules/compiler_vm/languages/server/freebasic.pm modules/compiler_vm/languages/server/haskell.pm modules/compiler_vm/languages/server/java.pm modules/compiler_vm/languages/server/qbasic.pm modules/compiler_vm/languages/server/tendra.pm modules/compiler_vm/languages/sh.pm modules/compiler_vm/languages/tendra.pm modules/compliment modules/cstd.pl modules/define.pl modules/dice_roll.pl modules/excuse.sh modules/expand_macros.pl modules/fnord.pl modules/funnyish_quote.pl modules/g.pl modules/gdefine.pl modules/gen_cfacts.pl modules/gencstd.pl modules/get_title.pl modules/getcfact.pl modules/google.pl modules/gspy.pl modules/gtop10.pl modules/gtop15.pl modules/headlines.pl modules/horoscope modules/horrorscope modules/ideone.pl modules/insult.pl modules/love_quote.pl modules/man.pl modules/map.pl modules/math.pl modules/prototype.pl modules/qalc.pl modules/random_quote.pl modules/seen.pl modules/urban modules/weather.pl modules/wikipedia.pl pbot.pl pbot.sh It is highly recommended that this list of files is reviewed to ensure that all files are the copyright of the sole maintainer of the repository. If any files with license headers contain the intellectual property of anyone else, it is recommended that a request is made to revise this patch or that the explicit permission of the co-author is gained to allow for the license of the work to be changed. I (Tomasz Kramkowski), the contributor, take no responsibility for any legal action taken against the maintainer of this repository for incorrectly claiming copyright to any work not owned by the maintainer of this repository.
2017-03-05 22:33:31 +01:00
use warnings;
use strict;
use Time::HiRes qw(gettimeofday);
use Time::Duration qw(concise duration);
use lib ".";
use Scorekeeper;
use IRCColors;
my $nick = shift @ARGV;
my $channel = shift @ARGV;
my $command = shift @ARGV;
my $opt = join ' ', @ARGV;
if ($channel !~ /^#/) {
print "Sorry, C Jeopardy must be played in a channel. Feel free to join #cjeopardy.\n";
exit;
}
my $scores = Scorekeeper->new;
$scores->begin;
2015-02-12 05:58:16 +01:00
my $rank_direction = '+';
sub sort_correct {
if ($rank_direction eq '+') {
return $b->{lifetime_correct_answers} <=> $a->{lifetime_correct_answers};
} else {
return $a->{lifetime_correct_answers} <=> $b->{lifetime_correct_answers};
}
}
sub print_correct {
my $player = shift @_;
return undef if $player->{lifetime_correct_answers} == 0;
return "$player->{nick}: $player->{lifetime_correct_answers}";
}
sub sort_wrong {
if ($rank_direction eq '+') {
return $a->{lifetime_wrong_answers} <=> $b->{lifetime_wrong_answers};
} else {
return $b->{lifetime_wrong_answers} <=> $a->{lifetime_wrong_answers};
2015-02-12 05:58:16 +01:00
}
}
sub print_wrong {
my $player = shift @_;
return undef if $player->{lifetime_wrong_answers} == 0 and $player->{lifetime_correct_answers} == 0;
return "$player->{nick}: $player->{lifetime_wrong_answers}";
}
sub sort_ratio {
my $wrong_a = $a->{lifetime_wrong_answers} ? $a->{lifetime_wrong_answers} : 1;
my $wrong_b = $b->{lifetime_wrong_answers} ? $b->{lifetime_wrong_answers} : 1;
2015-02-12 05:58:16 +01:00
if ($rank_direction eq '+') {
return $b->{lifetime_correct_answers} / $wrong_b <=> $a->{lifetime_correct_answers} / $wrong_a;
2015-02-12 05:58:16 +01:00
} else {
return $a->{lifetime_correct_answers} / $wrong_a <=> $b->{lifetime_correct_answers} / $wrong_b;
2015-02-12 05:58:16 +01:00
}
}
sub print_ratio {
my $player = shift @_;
return undef if $player->{lifetime_wrong_answers} + $player->{lifetime_correct_answers} < 50;
my $wrong = $player->{lifetime_wrong_answers} ? $player->{lifetime_wrong_answers} : 1;
my $ratio = $player->{lifetime_correct_answers} / $wrong;
2015-02-12 05:58:16 +01:00
return undef if $ratio == 0;
return sprintf "$player->{nick}: %.2f", $ratio;
}
sub sort_hints {
if ($rank_direction eq '+') {
return $a->{lifetime_hints} <=> $b->{lifetime_hints};
} else {
return $b->{lifetime_hints} <=> $a->{lifetime_hints};
2015-02-12 05:58:16 +01:00
}
}
sub print_hints {
my $player = shift @_;
return undef if $player->{lifetime_hints} == 0 and $player->{lifetime_correct_answers} == 0;
return "$player->{nick}: $player->{lifetime_hints}";
}
sub sort_correctstreak {
if ($rank_direction eq '+') {
return $b->{lifetime_highest_correct_streak} <=> $a->{lifetime_highest_correct_streak};
} else {
return $a->{lifetime_highest_correct_streak} <=> $b->{lifetime_highest_correct_streak};
}
}
sub print_correctstreak {
my $player = shift @_;
return undef if $player->{lifetime_highest_correct_streak} == 0;
return "$player->{nick}: $player->{lifetime_highest_correct_streak}";
}
sub sort_quickeststreak {
my $streak_a = $a->{lifetime_highest_quick_correct_streak} ? $a->{lifetime_highest_quick_correct_streak} : -1000;
my $streak_b = $b->{lifetime_highest_quick_correct_streak} ? $b->{lifetime_highest_quick_correct_streak} : -1000;
if ($rank_direction eq '+') {
return $streak_b - $b->{lifetime_quickest_correct_streak} / ($streak_b / 2) <=> $streak_a - $a->{lifetime_quickest_correct_streak} / ($streak_a / 2);
} else {
return $streak_a - $a->{lifetime_quickest_correct_streak} / ($streak_a / 2) <=> $streak_b - $b->{lifetime_quickest_correct_streak} / ($streak_b / 2);
}
}
sub print_quickeststreak {
my $player = shift @_;
return undef if $player->{lifetime_highest_quick_correct_streak} == 0;
if ($player->{lifetime_quickest_correct_streak} < 60) {
return "$player->{nick}: $player->{lifetime_highest_quick_correct_streak} in " . sprintf("%.2fs", $player->{lifetime_quickest_correct_streak});
} else {
return "$player->{nick}: $player->{lifetime_highest_quick_correct_streak} in " . concise duration $player->{lifetime_quickest_correct_streak};
}
}
2015-02-12 05:58:16 +01:00
sub sort_wrongstreak {
if ($rank_direction eq '+') {
return $a->{lifetime_highest_wrong_streak} <=> $b->{lifetime_highest_wrong_streak};
} else {
return $b->{lifetime_highest_wrong_streak} <=> $a->{lifetime_highest_wrong_streak};
2015-02-12 05:58:16 +01:00
}
}
sub print_wrongstreak {
my $player = shift @_;
return undef if $player->{lifetime_highest_wrong_streak} == 0 and $player->{lifetime_correct_answers} == 0;
return "$player->{nick}: $player->{lifetime_highest_wrong_streak}";
}
sub sort_quickest {
if ($rank_direction eq '+') {
return $a->{quickest_correct} <=> $b->{quickest_correct};
} else {
return $b->{quickest_correct} <=> $a->{quickest_correct};
}
}
sub print_quickest {
my $player = shift @_;
return undef if $player->{quickest_correct} == 0;
my $quickest;
if ($player->{quickest_correct} < 60) {
$quickest = sprintf("%.2fs", $player->{quickest_correct});
2015-02-12 05:58:16 +01:00
} else {
$quickest = concise duration($player->{quickest_correct});
2015-02-12 05:58:16 +01:00
}
return "$player->{nick}: $quickest";
}
if (lc $command eq 'rank') {
my %ranks = (
correct => { sort => \&sort_correct, print => \&print_correct, title => 'correct answers' },
wrong => { sort => \&sort_wrong, print => \&print_wrong, title => 'wrong answers' },
quickest => { sort => \&sort_quickest, print => \&print_quickest, title => 'quickest answer' },
ratio => { sort => \&sort_ratio, print => \&print_ratio, title => 'correct/wrong ratio' },
correctstreak => { sort => \&sort_correctstreak , print => \&print_correctstreak, title => 'correct answer streak' },
quickeststreak => { sort => \&sort_quickeststreak, print => \&print_quickeststreak, title => 'quickest correct answer streak' },
wrongstreak => { sort => \&sort_wrongstreak, print => \&print_wrongstreak, title => 'wrong answer streak' },
hints => { sort => \&sort_hints, print => \&print_hints, title => 'hints used' },
2015-02-12 05:58:16 +01:00
);
if (not $opt) {
print "Usage: rank [-]<keyword> [offset] or rank [-]<nick>; available keywords: ";
2015-02-12 05:58:16 +01:00
print join ', ', sort keys %ranks;
print ".\n";
print "Prefixing the keyword or nick with a dash will invert the sort direction for each category. Specifying an offset will start ranking at that offset.\n";
2015-02-12 05:58:16 +01:00
goto END;
}
$opt = lc $opt;
if ($opt =~ s/^([+-])//) {
$rank_direction = $1;
}
my $offset = 1;
if ($opt =~ s/\s+(\d+)$//) {
$offset = $1;
}
2015-02-12 05:58:16 +01:00
if (not exists $ranks{$opt}) {
my $player_id = $scores->get_player_id($opt, $channel, 1);
my $player_data = $scores->get_player_data($player_id);
if (not defined $player_id) {
print "I don't know anybody named $opt\n";
goto END;
}
my $players = $scores->get_all_players($channel);
my @rankings;
foreach my $key (sort keys %ranks) {
if ($key eq 'ratio' && $player_data->{lifetime_correct_answers} + $player_data->{lifetime_wrong_answers} < 50) {
my $needed_answers = 50 - ($player_data->{lifetime_correct_answers} + $player_data->{lifetime_wrong_answers});
push @rankings, "$ranks{$key}->{title}: ($needed_answers more answers for rank)";
next;
}
my $sort_method = $ranks{$key}->{sort};
@$players = sort $sort_method @$players;
my $rank = 0;
my $stats;
my $last_value = -1;
foreach my $player (@$players) {
next if $player->{nick} eq 'lern2play' and $opt ne 'lern2play';
next if $player->{nick} eq 'keep2play' and $opt ne 'keep2play';
$stats = $ranks{$key}->{print}->($player);
if (defined $stats) {
my ($value) = $stats =~ /[^:]+:\s+(.*)/;
$rank++ if $value ne $last_value;
$last_value = $value;
} else {
$rank++ if lc $player->{nick} eq $opt;
}
last if lc $player->{nick} eq $opt;
}
if (not $rank) {
push @rankings, "$ranks{key}->{title}: N/A";
} else {
if (not $stats) {
push @rankings, "$ranks{$key}->{title}: N/A";
} else {
$stats =~ s/[^:]+:\s+//;
push @rankings, "$ranks{$key}->{title}: #$rank ($stats)";
}
}
}
if (lc $nick ne $opt) {
print "$player_data->{nick}'s rankings: ";
} else {
print "Your rankings: ";
}
print join ', ', @rankings;
print "\n";
2015-02-12 05:58:16 +01:00
goto END;
}
my $players = $scores->get_all_players($channel);
my $sort_method = $ranks{$opt}->{sort};
@$players = sort $sort_method @$players;
2015-02-12 05:58:16 +01:00
my @ranking;
my $rank = 0;
my $last_value = -1;
2015-02-12 05:58:16 +01:00
foreach my $player (@$players) {
next if $player->{nick} eq 'lern2play';
next if $player->{nick} eq 'keep2play';
2015-02-12 05:58:16 +01:00
my $entry = $ranks{$opt}->{print}->($player);
if (defined $entry) {
my ($value) = $entry =~ /[^:]+:\s+(.*)/;
$rank++ if $value ne $last_value;
$last_value = $value;
next if $rank < $offset;
push @ranking, "#$rank $entry" if defined $entry;
last if scalar @ranking >= 15;
}
2015-02-12 05:58:16 +01:00
}
if (not scalar @ranking) {
if ($offset > 1) {
print "No rankings available for $channel at offset #$offset.\n";
} else {
print "No rankings available for $channel yet.\n";
}
2015-02-12 05:58:16 +01:00
} else {
print "Rankings for $ranks{$opt}->{title}: ";
2015-02-12 05:58:16 +01:00
print join ', ', @ranking;
print "\n";
}
goto END;
}
my $player_nick = $nick;
$player_nick = $opt if $opt and lc $command eq 'score';
my $player_id = $scores->get_player_id($player_nick, $channel, 1);
if (not defined $player_id) {
print "I don't know anybody named $player_nick\n";
goto END;
}
my $player_data = $scores->get_player_data($player_id);
if (lc $command eq 'score') {
my $score = "$player_data->{nick}: " unless lc $nick eq lc $player_nick;
$score .= "correct: $player_data->{correct_answers}" . ($player_data->{lifetime_correct_answers} > $player_data->{correct_answers} ? " [$player_data->{lifetime_correct_answers}]" : "") . ", ";
$score .= "current correct streak: $player_data->{correct_streak}, ";
$score .= "highest correct streak: $player_data->{highest_correct_streak}" . ($player_data->{lifetime_highest_correct_streak} > $player_data->{highest_correct_streak} ? " [$player_data->{lifetime_highest_correct_streak}]" : "") . ", ";
$score .= "quickest correct streak: ";
$score .= ($player_data->{highest_quick_correct_streak} > 0 ? "$player_data->{highest_quick_correct_streak} in " . (concise duration $player_data->{quickest_correct_streak}) : "N/A") . ($player_data->{lifetime_highest_quick_correct_streak} > $player_data->{highest_quick_correct_streak} ? " [$player_data->{lifetime_highest_quick_correct_streak} in " . (concise duration $player_data->{lifetime_quickest_correct_streak}) . "]" : "") . ", ";
2019-06-26 18:34:19 +02:00
$score .= "quickest answer: ";
if ($player_data->{quickest_correct} == 0) {
$score .= "N/A";
} elsif ($player_data->{quickest_correct} < 60) {
$score .= sprintf("%.2fs", $player_data->{quickest_correct});
} else {
$score .= concise duration($player_data->{quickest_correct});
}
$score .= ", ";
$score .= "wrong: $player_data->{wrong_answers}" . ($player_data->{lifetime_wrong_answers} > $player_data->{wrong_answers} ? " [$player_data->{lifetime_wrong_answers}]" : "") . ", ";
$score .= "current wrong streak: $player_data->{wrong_streak}, ";
$score .= "highest wrong streak: $player_data->{highest_wrong_streak}" . ($player_data->{lifetime_highest_wrong_streak} > $player_data->{highest_wrong_streak} ? " [$player_data->{lifetime_highest_wrong_streak}]" : "") . ", ";
2015-05-25 20:27:24 +02:00
$score .= "ratio: ";
my $wrong = $player_data->{wrong_answers} ? $player_data->{wrong_answers} : 1;
$score .= sprintf("%.2f", $player_data->{correct_answers} / $wrong);
if ($player_data->{lifetime_correct_answers} > 0) {
$wrong = $player_data->{lifetime_wrong_answers} ? $player_data->{lifetime_wrong_answers} : 1;
$score .= sprintf(" [%.2f]", $player_data->{lifetime_correct_answers} / $wrong);
}
$score .= ", ";
$score .= "hints: $player_data->{hints}" . ($player_data->{lifetime_hints} > $player_data->{hints} ? " [$player_data->{lifetime_hints}]" : "") . "\n";
print $score;
} elsif (lc $command eq 'reset') {
$player_data->{correct_answers} = 0;
$player_data->{wrong_answers} = 0;
$player_data->{correct_streak} = 0;
$player_data->{wrong_streak} = 0;
$player_data->{highest_correct_streak} = 0;
$player_data->{highest_wrong_streak} = 0;
$player_data->{hints} = 0;
$player_data->{correct_streak_timestamp} = 0;
$player_data->{highest_quick_correct_streak} = 0;
$player_data->{quickest_correct_streak} = 0;
$scores->update_player_data($player_id, $player_data);
print "Your scores for this session have been reset.\n";
}
END:
$scores->end;