2 # Lame-o-Nickometer backend
4 # (c) 1998 Adam Spiers <adam.spiers@new.ox.ac.uk>
6 # You may do whatever you want with this code, but give me credit.
22 my $term = (lc $message eq 'me') ? $::who : $message;
24 if ($term =~ /^$::mask{chan}$/) {
25 &::status("Doing nickometer for chan $term.");
27 if (!&::validChan($term)) {
28 &::msg($::who, "error: channel is invalid.");
34 foreach (keys %{ $::channels{lc $term}{''} }) {
37 &WARN("nickometer: nick in chan $term undefined?");
41 my $value = &nickometer($str);
42 $nickometer{$value}{$str} = 1;
46 ### TODO: compact with map?
48 foreach (sort {$b <=> $a} keys %nickometer) {
49 my $str = join(", ", sort keys %{ $nickometer{$_} });
50 push(@list, "$str ($_%)");
53 &::performStrictReply( &::formListReply(0, "Nickometer list for $term ", @list) );
58 my $percentage = &nickometer($term);
60 if ($percentage =~ /NaN/) {
61 $percentage = "off the scale";
63 $percentage = sprintf("%0.4f", $percentage);
64 $percentage =~ s/(\.\d+)0+$/$1/;
68 if ($::msgType eq 'public') {
69 &::say("'$term' is $percentage lame, $::who");
71 &::msg($::who, "the 'lame nick-o-meter' reading for $term is $percentage, $::who");
81 # return unless &loadPerlModule("Getopt::Std");
82 return unless &::loadPerlModule("Math::Trig");
85 &::DEBUG("nickometer: arg == NULL. $text");
89 # Deal with special cases (precede with \ to prevent de-k3wlt0k)
103 '[l1](oo?|u)[sz]er' => 500,
114 foreach my $special (keys %special_cost) {
115 my $special_pattern = $special;
116 my $raw = ($special_pattern =~ s/^\\//);
118 unless (defined $raw) {
119 $nick =~ tr/023457+8/ozeasttb/;
121 &punish($special_cost{$special}, "matched special case /$special_pattern/")
122 if (defined $nick and $nick =~ /$special_pattern/i);
125 # Allow Perl referencing
126 $text =~ s/^\\([A-Za-z])/$1/;
128 # C-- ain't so bad either
131 # Punish consecutive non-alphas
132 $text =~ s/([^A-Za-z0-9]{2,})
133 /my $consecutive = length($1);
134 &punish(&slow_pow(10, $consecutive),
135 "$consecutive total consecutive non-alphas")
140 # Remove balanced brackets (and punish a little bit) and punish for unmatched
141 while ($text =~ s/^([^()]*) (\() (.*) (\)) ([^()]*) $/$1$3$5/x ||
142 $text =~ s/^([^{}]*) (\{) (.*) (\}) ([^{}]*) $/$1$3$5/x ||
143 $text =~ s/^([^\[\]]*) (\[) (.*) (\]) ([^\[\]]*) $/$1$3$5/x)
145 print "Removed $2$4 outside parentheses; nick now $_\n" if $verbose;
146 &punish(15, "brackets");
148 my $parentheses = $text =~ tr/(){}[]/(){}[]/;
149 &punish(&slow_pow(10, $parentheses),
150 "$parentheses unmatched " .
151 ($parentheses == 1 ? 'parenthesis' : 'parentheses'))
155 my @k3wlt0k_weights = (5, 5, 2, 5, 2, 3, 1, 2, 2, 2);
156 for my $digit (0 .. 9) {
157 my $occurrences = $text =~ s/$digit/$digit/g || 0;
158 &punish($k3wlt0k_weights[$digit] * $occurrences * 30,
160 (($occurrences == 1) ? 'occurrence' : 'occurrences') .
165 # An alpha caps is not lame in middle or at end, provided the first
167 my $orig_case = $text;
168 $text =~ s/^([^A-Za-z]*[A-Z].*[a-z].*?)[_-]?([A-Z])/$1\l$2/;
170 # A caps first alpha is sometimes not lame
171 $text =~ s/^([^A-Za-z]*)([A-Z])([a-z])/$1\l$2$3/;
173 # Punish uppercase to lowercase shifts and vice-versa, modulo
175 my $case_shifts = &case_shifts($orig_case);
176 &punish(&slow_pow(9, $case_shifts),
177 $case_shifts . ' case ' .
178 (($case_shifts == 1) ? 'shift' : 'shifts'))
179 if ($case_shifts > 1 && /[A-Z]/);
181 # Punish lame endings (TorgoX, WraithX et al. might kill me for this :-)
182 &punish(50, 'last alpha lame') if $orig_case =~ /[XZ][^a-zA-Z]*$/;
184 # Punish letter to numeric shifts and vice-versa
185 my $number_shifts = &number_shifts($_);
186 &punish(&slow_pow(9, $number_shifts),
187 $number_shifts . ' letter/number ' .
188 (($number_shifts == 1) ? 'shift' : 'shifts'))
189 if $number_shifts > 1;
191 # Punish extraneous caps
192 my $caps = $text =~ tr/A-Z/A-Z/;
193 &punish(&slow_pow(7, $caps), "$caps extraneous caps") if $caps;
195 # One and only one trailing underscore is OK.
198 # Now punish anything that's left
200 $remains =~ tr/a-zA-Z0-9//d;
201 my $remains_length = length($remains);
203 &punish(50 * $remains_length + &slow_pow(9, $remains_length),
204 $remains_length . ' extraneous ' .
205 (($remains_length == 1) ? 'symbol' : 'symbols'))
208 print "\nRaw lameness score is $score\n" if $verbose;
210 # Use an appropriate function to map [0, +inf) to [0, 100)
211 my $percentage = 100 *
212 (1 + &Math::Trig::tanh(($score-400)/400)) *
213 (1 - 1/(1+$score/5)) / 2;
215 my $digits = 2 * (2 - &round_up(log(100 - $percentage) / log(10)));
217 return sprintf "%.${digits}f", $percentage;
220 sub case_shifts ($) {
221 # This is a neat trick suggested by freeside. Thanks freeside!
225 $shifts =~ tr/A-Za-z//cd;
226 $shifts =~ tr/A-Z/U/s;
227 $shifts =~ tr/a-z/l/s;
229 return length($shifts) - 1;
232 sub number_shifts ($) {
235 $shifts =~ tr/A-Za-z0-9//cd;
236 $shifts =~ tr/A-Za-z/l/s;
237 $shifts =~ tr/0-9/n/s;
239 return length($shifts) - 1;
245 return $x ** &slow_exponent($y);
248 sub slow_exponent ($) {
251 return 1.3 * $x * (1 - &Math::Trig::atan($x/6) *2/$pi);
257 return int($float) + ((int($float) == $float) ? 0 : 1);
261 my ($damage, $reason) = @_;
263 return unless $damage;
266 print "$damage lameness points awarded: $reason\n" if $verbose;