4 # :title: Hangman Plugin
6 # Author:: Raine Virta <rane@kapsi.fi>
7 # Copyright:: (C) 2009 Raine Virta
10 # Description:: Hangman game for rbot
12 # TODO:: scoring and stats
13 # some sort of turn-basedness, maybe
16 SITE = "http://coyotecult.com/tools/randomwordgenerator.php"
19 res = Net::HTTP.post_form(URI.parse(SITE), {'numwords' => count})
20 words = res.body.scan(%r{<a.*?\?w=(.*?)\n}).flatten
22 count == 1 ? words.first : words
27 attr_reader :misses, :guesses, :word, :letters
29 STAGES = [' (x_x) ', ' (;_;) ', ' (>_<) ', ' (-_-) ', ' (o_~) ', ' (^_^) ', '\(^o^)/']
30 HEALTH = STAGES.size-1
33 def initialize(word, channel=nil)
42 # array of the letters in the word
43 @word.split(//u).reject { |c| c !~ LETTER }.map { |c| c.downcase }
51 # creates a string that presents the word with unknown letters shown as underscores
52 @word.split(//).map { |c|
53 @guesses.include?(c.downcase) || c !~ LETTER ? c : "_"
61 if str !~ /^#{LETTER}$/u
62 word.downcase == str ? @solved = true : punish
63 else # single letter guess
64 return false if @guesses.include?(str) # letter has been guessed before
66 unless letters.include?(str)
80 (letters - @guesses).empty? || @solved
92 class HangmanPlugin < Plugin
99 def help(plugin, topic="")
102 return "hangman game plugin - topics: play, stop"
104 return "hangman play on <channel> with word <word> => use in private chat with the bot to start a game with custom word\n"+
105 "hangman play random [with [max|min] length [<|>|== <length>]] => hangman with a random word from #{RandomWord::SITE}\n"+
106 "hangman play with wordlist <wordlist> => hangman with random word from <wordlist>"
108 return "hangman stop => quits the current game"
114 params[:word].join(" ")
115 elsif params[:wordlist]
117 wordlist = Wordlist.get(params[:wordlist].join("/"), :spaces => true)
119 raise "no such wordlist"
122 wordlist[rand(wordlist.size)]
123 else # getting a random word
124 words = RandomWord::get(100)
126 if adj = params[:adj]
127 words = words.sort_by { |e| e.size }
134 elsif params[:relation] && params[:size]
135 words = words.select { |w| w.size.send(params[:relation], params[:size].to_i) }
140 m.reply "suitable word not found in the set"
151 word = get_word(params) || return
157 if (params[:channel] || m.public?)
158 target = if m.public?
164 # is the bot on the channel?
165 unless @bot.server.channels.names.include?(target.to_s)
166 m.reply "i'm not on that channel"
170 if @games.has_key?(target)
171 m.reply "there's already a hangman game in progress on the channel"
175 @bot.say target, "#{m.source} has started a hangman -- join the fun!"
177 target = m.source.to_s
180 @games[target] = Hangman.new(word)
181 @settings[target] = params
183 @bot.say target, game_status(@games[target])
187 source = if m.public?
193 if @games.has_key?(source.to_s)
194 @bot.say source, "oh well, the answer would've been #{Bold}#{@games[source.to_s].word}#{Bold}"
195 @games.delete(source.to_s)
200 source = if m.public?
206 if game = @games[source]
207 if m.message =~ /^[^\W0-9_]$/u || m.message =~ prepare_guess_regex(game)
208 return unless game.guess(m.message)
210 m.reply game_status(game)
215 str = "you nailed it!"
217 str = "you've killed the poor guy :("
220 m.reply "#{str} go #{Bold}again#{Bold}?", :nick => true
222 @games.delete(source)
224 elsif @settings[source] && m.message =~ /^(?:again|more!?$)/i
225 start(m, @settings[source])
229 def prepare_guess_regex(game)
230 Regexp.new("^#{game.word.split(//).map { |c|
231 game.guesses.include?(c) || c !~ Hangman::LETTER ? c : '[^\W0-9_]'
235 def game_status(game)
236 "%{word} %{face} %{misses}" % {
237 :word => game.over? ? "#{Bold}#{game.word}#{Bold}" : game.to_s,
239 :misses => game.misses.map { |e| e.upcase }.join(" ")
244 plugin = HangmanPlugin.new
245 plugin.map "hangman [play] with wordlist *wordlist", :action => 'start'
246 plugin.map "hangman [play] on :channel with word *word", :action => 'start'
247 plugin.map "hangman [play] [random] [with [:adj] length [:relation :size]]",
249 :requirements => { :adj => /min|max/, :relation => /<|<=|>=|>|==/, :size => /\d+/ }
251 plugin.map "hangman stop", :action => 'stop'