This repository has been archived by the owner on Jun 8, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
hangman.coffee
174 lines (140 loc) · 4.62 KB
/
hangman.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# Description:
# The game of Hangman.
# Words and definitions are sourced via the Wordnik API. You'll need an API
# key from http://developer.wordnik.com/
#
# Dependencies:
# None
#
# Configuration:
# WORDNIK_API_KEY
#
# Commands:
# hubot hangman - Display the state of the current game
# hubot hangman <letterOrWord> - Make a guess
#
# Author:
# harukizaemon
class Game
constructor: (word, @definitions) ->
@word = word.toUpperCase()
@wordLetters = @word.split(//)
@answerLetters = ("_" for letter in @wordLetters)
@remainingGuesses = 9
@previousGuesses = []
@message = null
isFinished: ->
this.wasAnswered() or this.wasHung()
wasAnswered: ->
"_" not in @answerLetters
wasHung: ->
@remainingGuesses == 0
guess: (guess) ->
if !guess
this.noGuess()
return
guess = guess.trim().toUpperCase()
if guess in @previousGuesses
this.duplicateGuess(guess)
else
@previousGuesses.push(guess)
switch guess.length
when 1 then this.guessLetter(guess)
when @word.length then this.guessWord(guess)
else this.errantWordGuess(guess)
guessLetter: (guess) ->
indexes = (index for letter, index in @wordLetters when guess == letter)
if indexes.length > 0
@answerLetters[index] = @wordLetters[index] for index in indexes
this.correctGuess("Yes, there #{isOrAre(indexes.length, guess)}")
else
this.incorrectGuess("Sorry, there are no #{guess}'s")
guessWord: (guess) ->
if guess == @word
@answerLetters = @wordLetters
this.correctGuess("Yes, that's correct")
else
this.incorrectGuess("Sorry, the word is not #{guess}")
noGuess: ->
@message = null
errantWordGuess: (guess) ->
@message = "The word #{guess} isn't the correct length so let's pretend that never happened, shall we?"
duplicateGuess: (guess) ->
@message = "You already tried #{guess} so let's pretend that never happened, shall we?"
correctGuess: (message) ->
@message = message
incorrectGuess: (message) ->
@remainingGuesses -= 1 if @remainingGuesses > 0
@message = message
eachMessage: (callback) ->
callback(@message) if @message
if this.isFinished()
if this.wasHung()
callback("You have no remaining guesses")
else if this.wasAnswered()
callback("Congratulations, you still had #{pluralisedGuess(@remainingGuesses)} remaining!")
callback("The #{@wordLetters.length} letter word was: #{@word}")
callback(@definitions)
else
callback("The #{@answerLetters.length} letter word is: #{@answerLetters.join(' ')}")
callback("You have #{pluralisedGuess(@remainingGuesses)} remaining")
module.exports = (robot) ->
gamesByRoom = {}
robot.respond /hangman( .*)?$/i, (msg) ->
if process.env.WORDNIK_API_KEY == undefined
msg.send("Missing WORDNIK_API_KEY env variable.")
return
room = msg.message.user.room
play msg, gamesByRoom[room], (game) ->
gamesByRoom[room] = game
game.guess(msg.match[1])
game.eachMessage (message) -> msg.send(message)
play = (msg, game, callback) ->
if !game or game.isFinished()
generateWord msg, (word, definitions) -> callback new Game(word, definitions)
else
callback(game)
generateWord = (msg, callback) ->
msg.http("http://api.wordnik.com/v4/words.json/randomWord")
.query
hasDictionaryDef: true
minDictionaryCount: 3
minLength: 5
.headers
api_key: process.env.WORDNIK_API_KEY
.get() (err, res, body) ->
result = JSON.parse(body)
word = if result
result.word
else
"hangman"
defineWord(msg, word, callback)
defineWord = (msg, word, callback) ->
msg.http("http://api.wordnik.com/v4/word.json/#{escape(word)}/definitions")
.header("api_key", process.env.WORDNIK_API_KEY)
.get() (err, res, body) ->
definitions = JSON.parse(body)
if definitions.length == 0
callback(word, "No definitions found.")
else
reply = ""
lastSpeechType = null
definitions = definitions.forEach (def) ->
# Show the part of speech (noun, verb, etc.) when it changes
if def.partOfSpeech != lastSpeechType
reply += " (#{def.partOfSpeech})\n" if def.partOfSpeech != undefined
# Track the part of speech
lastSpeechType = def.partOfSpeech
# Add the definition
reply += " - #{def.text}\n"
callback(word, reply)
isOrAre = (count, letter) ->
if count == 1
"is one #{letter}"
else
"are #{count} #{letter}'s"
pluralisedGuess = (count) ->
if count == 1
"one guess"
else
"#{count} guesses"