UNPKG

hubot-cah

Version:
331 lines (292 loc) 11.6 kB
# # Description: # Cards Against Humanity game managed by Hubot # https://github.com/coryallegory/hubot-cah # # Dependencies: # None # # Commands: # hubot cah help - List cah commands # hubot cah black - Display a random black card # hubot cah white - Display a random white card # hubot cah play - Add yourself to the game # hubot cah retire - Remove yourself as an active player # hubot cah czar - Display name of the current Card Czar # hubot cah players - List active players # hubot cah leaders - Top five score leaders # hubot cah score - Display your score # hubot cah hand - List cards in your hand # hubot cah submit <#> <#> ... - Indicate white cards to be submitted as an answer, where # indicates card index in hand and amount of white cards submitted corresponds to the amount required by the current black card # hubot cah answers - List the current white card submissions for the current black card (Card Czar only) # hubot cah choose <#> - Choose a winning answer (Card Czar only) # hubot cah status - Display summary of current game # hubot cah skip - Discard current black card and assign a new Card Czar # # Author: # Cory Metcalfe (corymetcalfe@gmail.com) # helpSummary = "_hubot-cah commands:_" helpSummary += "\ncah help - List cah commands" helpSummary += "\ncah black - Display a random black card" helpSummary += "\ncah white - Display a random white card" helpSummary += "\ncah play - Add yourself to the game" helpSummary += "\ncah retire - Remove yourself as an active player" helpSummary += "\ncah czar - Display name of the current Card Czar" helpSummary += "\ncah players - List active players" helpSummary += "\ncah leaders - Top five score leaders" helpSummary += "\ncah score - Display your score" helpSummary += "\ncah hand - List cards in your hand" helpSummary += "\ncah submit <#> <#> ... - Indicate white cards to be submitted as an answer, where # indicates card index in hand and amount of white cards submitted corresponds to the amount required by the current black card" helpSummary += "\ncah answers - List the current white card submissions for the current black card (Card Czar only)" helpSummary += "\ncah choose <#> - Choose a winning answer (Card Czar only)" helpSummary += "\ncah status - Display summary of current game" helpSummary += "\ncah skip - Discard current black card and assign a new Card Czar" blackBlank = "_____" blackCards = require('./blackcards.coffee') whiteCards = require('./whitecards.coffee') # @return black card text string random_black_card = () -> cardIndex = Math.floor(Math.random()*blackCards.length) return blackCards[cardIndex] # @return white card text string random_white_card = () -> cardIndex = Math.floor(Math.random()*whiteCards.length) return whiteCards[cardIndex] db = { scores: {}, # {<name>: <score>, ...} activePlayers: [], # [<player name>, ...] blackCard: random_black_card(), # <card text> czar: null, # <player name> hands: {}, # {<name>: [<card text>, <card text>, ...], ...} answers: [], # [ [<player name>, [<card text>, ...]], ... ] } # prune inactive player hands, ensure everyone has five cards fix_hands = () -> newHands = {} for own name, cardArray of db.hands if name in db.activePlayers while cardArray.length < 5 cardArray.push random_white_card() newHands[name] = cardArray db["hands"] = newHands # add player to active list # fix their hand so it contains five cards # if only player, make them czar # @param playerName: name of player coming into game add_player = (playerName) -> if playerName not in db.activePlayers db.activePlayers.push playerName if !db.scores[playerName]? db.scores[playerName] = 0 cards = [] while cards.length < 5 cards.push random_white_card() db.hands[playerName] = cards if db.activePlayers.length == 1 db.czar = playerName db.blackCard = random_black_card() # remove player from active list # remove any associated hands # if they were czar, assign a new one # @param playerName: name of player leaving the game remove_player = (playerName) -> i = db.activePlayers.indexOf(playerName) if i >= 0 db.activePlayers.splice(i,1) if db.hands[playerName]? delete db.hands[playerName] if db.czar == playerName if db.activePlayers.length == 0 db.czar = null else if i >= db.activePlayers.length db.czar = db.activePlayers[0] else db.czar = db.activePlayers[i] # combine black and white cards into complete phrase # @param blackCard: black card text string # @param whiteCards: array of white card text strings # @return completed string generate_phrase = (blackCard, whiteCards) -> phrase = "" blackBits = blackCard.split blackBlank if blackBits.length == 1 phrase += "#{blackCard} *#{whiteCards[0]}*" else wi = 0 bi = 0 if blackCard.substring(0, blackBlank.length) == blackBlank phrase += "*#{whiteCards[0].match(/(.*[a-zA-Z0-9])[^a-zA-Z0-9]*$/i)[0]}*" wi = 1 while wi < whiteCards.length or bi < blackBits.length if bi < blackBits.length phrase += blackBits[bi] bi++ if wi < whiteCards.length phrase += "*#{whiteCards[wi].match(/(.*[a-zA-Z0-9])[^a-zA-Z0-9]*$/i)[0]}*" wi++ return phrase # remove cards from player hand and add to answers # @param playerName: player submitting answer # @param handIndices: indices of cards in player hand submit_answer = (playerName, handIndices) -> playerHand = db.hands[playerName] cards = [] for i in handIndices cards.push playerHand[i] for card in cards i = playerHand.indexOf(card) playerHand.splice(i,1) db.answers.push [playerName, cards] # specify winning card and reset game for next round # @param answerIndex: if value outside db.answers array range, no winner this round # @return string for public display czar_choose_winner = (answerIndex) -> responseString = "Next round:" if 0 <= answerIndex and answerIndex < db.answers.length responseString = "*#{db.blackCard}*" for answer in db.answers responseString += "\n#{answer[1][0]}" i = 1 while i < answer[1].length responseString += ", #{answer[1][i]}" winner = db.answers[answerIndex][0] cards = db.answers[answerIndex][1] winningPhrase = generate_phrase(db.blackCard, cards) responseString += "\n\n#{winner} earns a point for\n*#{winningPhrase}*" if !db.scores[winner]? db.scores[winner] = 1 else db.scores[winner] = db.scores[winner] + 1 db["answers"] = [] fix_hands() db["blackCard"] = random_black_card() if db.activePlayers.length == 0 db.czar = null else if !db.czar? db.czar = db.activePlayers[0] else czarIndex = db.activePlayers.indexOf db.czar if czarIndex < 0 or czarIndex == db.activePlayers.length-1 db.czar = db.activePlayers[0] else db.czar = db.activePlayers[czarIndex+1] return responseString + "\n\nNext round:\n" + game_state_string() # generate string describing game state # czar, black card, number of submissions game_state_string = () -> if !db.czar? return "Waiting for players." else return "*#{db.blackCard}* [#{db.czar}, #{db.answers.length}/#{db.activePlayers.length-1}]" # @param msg: message object # @return name of message sender sender = (msg) -> return msg.message.user.name.toLowerCase() module.exports = (robot) -> robot.respond /cah help$/i, (msg) -> msg.send helpSummary robot.respond /cah black$/i, (msg) -> msg.send random_black_card() robot.respond /cah white$/i, (msg) -> msg.send random_white_card() robot.respond /cah play$/i, (msg) -> name = sender(msg) add_player(name) robot.messageRoom name, "You are now an active CAH player." robot.respond /cah retire$/i, (msg) -> name = sender(msg) remove_player(name) robot.messageRoom name, "You are no longer a CAH player. Your score will be preserved should you decide to play again." robot.respond /cah czar$/i, (msg) -> if db.czar? msg.send db.czar else msg.send "No Card Czar yet, waiting for players." robot.respond /cah players$/i, (msg) -> if db.activePlayers.length < 1 msg.send "Waiting for players." else responseString = "CAH Players: #{db.activePlayers[0]}" for i in [1...db.activePlayers.length] by 1 responseString += ", #{db.activePlayers[i]}" msg.send responseString robot.respond /cah leaders$/i, (msg) -> scoreTuples = [] for name,score of db.scores scoreTuples.push([name,score]) scoreTuples.sort (a,b) -> a = a[1] b = b[1] return a < b ? -1 : (a > b ? 1 : 0) responseString = "CAH Leaders:" for i in [0...5] by 1 if i >= scoreTuples.length break responseString += "\n#{scoreTuples[i][1]} #{scoreTuples[i][0]}" msg.send responseString robot.respond /cah score$/i, (msg) -> score = db.scores[sender(msg)] if score? msg.reply score else msg.reply "No CAH score on record." robot.respond /cah hand$/i, (msg) -> cards = db.hands[sender(msg)] responseString = "Your white CAH cards:" if cards? for i in [0...cards.length] by 1 responseString += "\n#{i}: #{cards[i]}" robot.messageRoom sender(msg), responseString robot.respond /cah submit(?: ([0-4]+))+$/i, (msg) -> if sender(msg) == db.czar msg.reply "You are currently the Card Czar!" return if db.hands[sender(msg)].length < 5 msg.reply "You have already submitted cards for this round." return numString = msg.match[0].split("submit ")[1] nums = numString.split(" ") expectedCount = (db.blackCard.match(blackBlank) || []).length if expectedCount == 0 expectedCount = 1 if nums.length != expectedCount msg.reply "You submitted #{nums.length} cards, #{expectedCount} expected." else for i in [0...nums.length] by 1 nums[i] = parseInt(nums[i]) if nums[i] >= db.hands[sender(msg)].length msg.reply "#{nums[i]} is not a valid card number." return for i in [0...nums.length] by 1 for j in [i+1...nums.length] by 1 if nums[i] == nums[j] msg.reply "You cannot submit a single card more than once." return submit_answer(sender(msg), nums) msg.reply "Submission accepted." robot.respond /cah answers$/i, (msg) -> if sender(msg) != db.czar msg.reply "Only the Card Czar may see the white card submissions." else answers = db.answers responseString = "White card submissions thus far:" for i in [0...answers.length] by 1 cards = answers[i][1] responseString += "\n#{i}: #{generate_phrase(db.blackCard, cards)}" robot.messageRoom db.czar, responseString robot.respond /cah choose ([0-9]+)$/i, (msg) -> if sender(msg) != db.czar msg.reply "Only the Card Czar may choose a winner." else if db.answers.length == 0 msg.reply "No submissions to choose from yet." else num = parseInt(msg.match[1]) if num < 0 or num >= db.answers.length msg.reply "That is not an valid choice, try again." else msg.send czar_choose_winner num robot.respond /cah status$/i, (msg) -> msg.send game_state_string() robot.respond /cah skip$/i, (msg) -> msg.send czar_choose_winner -1