class AzGame\r
\r
attr_reader :range, :word\r
+ attr_accessor :tries, :total_tries, :winner\r
def initialize(plugin, lang, word)\r
@plugin = plugin\r
@lang = lang.to_sym\r
@word = word.downcase\r
@range = [AZ_RULES[lang][:first].dup, AZ_RULES[lang][:last].dup]\r
+ @total_tries = 0\r
+ @tries = Hash.new(0)\r
+ @winner = nil\r
def @range.to_s\r
return "%s -- %s" % self\r
end\r
return [:in, @range]\r
end\r
\r
+# TODO scoring: base score is t = ceil(100*exp(-(n-1)^2/50))+p for n attempts\r
+# done by p players; players that didn't win but contributed\r
+# with a attempts will get t*a/n points\r
+\r
+ include Math\r
+\r
+ def score\r
+ n = @total_tries\r
+ p = @tries.keys.length\r
+ t = (100*exp(-(n-1)**2/50**2)).ceil + p\r
+ debug "Total score: #{t}"\r
+ ret = Hash.new\r
+ @tries.each { |k, a|\r
+ ret[k] = [t*a/n, "%d tries" % a]\r
+ }\r
+ if @winner\r
+ debug "replacing winner score of %d with %d" % [ret[@winner].first, t]\r
+ ret[@winner] = [t, "winner"]\r
+ end\r
+ return ret.sort_by { |h| h.last.first }\r
+ end\r
+\r
end\r
\r
class AzGamePlugin < Plugin\r
def listen(m)\r
return unless m.kind_of?(PrivMessage)\r
return if m.channel.nil? or m.address?\r
- k = m.channel.to_s # to_sym?\r
+ k = m.channel.downcase.to_s # to_sym?\r
return unless @games.key?(k)\r
return if m.params\r
word = m.plugin.downcase\r
case isit.first\r
when :bingo\r
m.reply "#{Bold}BINGO!#{Bold}: the word was #{Underline}#{word}#{Underline}. Congrats, #{Bold}#{m.sourcenick}#{Bold}!"\r
+ @games[k].total_tries += 1\r
+ @games[k].tries[m.source] += 1\r
+ @games[k].winner = m.source\r
+ ar = @games[k].score.inject([]) { |res, kv|\r
+ res.push("%s: %d (%s)" % kv.flatten)\r
+ }\r
+ m.reply "The game was won after #{@games[k].total_tries} tries. Scores for this game: #{ar.join('; ')}"\r
@games.delete(k)\r
when :out\r
m.reply "#{word} is not in the range #{Bold}#{isit.last}#{Bold}" if m.address?\r
m.reply "#{word} doesn't exist or is not acceptable for the game"\r
when :in\r
m.reply "close, but no cigar. New range: #{Bold}#{isit.last}#{Bold}"\r
+ @games[k].total_tries += 1\r
+ @games[k].tries[m.source] += 1\r
when :ignore\r
m.reply "#{word} is already one of the range extrema: #{isit.last}" if m.address?\r
else\r
end\r
\r
def manual_word_check(m, params)\r
- k = m.channel.to_s\r
+ k = m.channel.downcase.to_s\r
word = params[:word].downcase\r
if not @games.key?(k)\r
- m.reply "no A-Z game running here, can't check for #{word}, can I?"\r
+ m.reply "no A-Z game running here, can't check if #{word} is valid, can I?"\r
return\r
end\r
if word !~ /^[a-z]+$/\r
\r
def stop_game(m, params)\r
return if m.channel.nil? # Shouldn't happen, but you never know\r
- k = m.channel.to_s # to_sym?\r
+ k = m.channel.downcase.to_s # to_sym?\r
if @games.key?(k)\r
m.reply "the word in #{Bold}#{@games[k].range}#{Bold} was: #{Bold}#{@games[k].word}"\r
+ ar = @games[k].score.inject([]) { |res, kv|\r
+ res.push("%s: %d (%s)" % kv.flatten)\r
+ }\r
+ m.reply "The game was cancelled after #{@games[k].total_tries} tries. Scores for this game would have been: #{ar.join('; ')}"\r
@games.delete(k)\r
else\r
m.reply "no A-Z game running in this channel ..."\r
\r
def start_game(m, params)\r
return if m.channel.nil? # Shouldn't happen, but you never know\r
- k = m.channel.to_s # to_sym?\r
+ k = m.channel.downcase.to_s # to_sym?\r
unless @games.key?(k)\r
lang = (params[:lang] || @bot.config['core.language']).to_sym\r
method = 'random_pick_'+lang.to_s\r
m.reply "got it!"\r
@games[k] = AzGame.new(self, lang, word)\r
end\r
- m.reply "A-Z: #{Bold}#{@games[k].range}#{Bold}"\r
+ tr = @games[k].total_tries\r
+ m.reply "A-Z: #{Bold}#{@games[k].range}#{Bold}" + (tr > 0 ? "(after #{tr} tries)" : "")\r
return\r
end\r
\r
return false\r
end\r
debug p\r
- if p =~ /<span class="hwd">#{word}<\/span>([^\n]+?)<span class="psa">#{rules[:good]}<\/span>/i\r
+ if p =~ /<span class="(?:hwd|srch)">#{word}<\/span>([^\n]+?)<span class="psa">#{rules[:good]}<\/span>/i\r
debug "new word #{word}"\r
wc[word.to_sym] = {:who => :dict}\r
return true\r
lemmi = Array.new\r
good = rules[:good]\r
# We look for a lemma composed by a single word and of length at least two\r
- p.scan(/<span class="hwd">(.*?)<\/span>([^\n]+?)<span class="psa">#{rules[:good]}<\/span>/i) { |prelemma, discard|\r
+ p.scan(/<span class="(?:hwd|srch)">(.*?)<\/span>([^\n]+?)<span class="psa">#{rules[:good]}<\/span>/i) { |prelemma, discard|\r
lemma = prelemma.downcase\r
debug "checking lemma #{lemma} (#{prelemma}) and discarding #{discard}"\r
next if wc.key?(lemma.to_sym)\r