diff --git a/bot.py b/bot.py index 5ed49da..ab7f7a6 100644 --- a/bot.py +++ b/bot.py @@ -5,6 +5,7 @@ from telegram import Updater, InlineQueryResultPhoto, InlineQueryResultArticle, from game_manager import GameManager import card as c from credentials import TOKEN +from start_bot import start_bot logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', @@ -25,6 +26,29 @@ def list_subtract(list1, list2): return list(sorted(list1)) +def display_name(game): + user = game.current_player.user + user_name = user.first_name + if user.username: + user_name += ' (@' + user.username + ')' + return user_name + + +def display_color(color): + if color == "r": + return "Red" + if color == "b": + return "Blue" + if color == "g": + return "Green" + if color == "y": + return "Yellow" + + +def error(bot, update, error): + logger.exception(error) + + def new_game(bot, update): chat_id = update.message.chat_id link = gm.generate_invite_link(u.bot.getMe().username, chat_id) @@ -32,15 +56,31 @@ def new_game(bot, update): text="Click this link to join the game: %s" % link) +def leave_game(bot, update): + chat_id = update.message.chat_id + game_id = gm.chatid_gameid[chat_id] + game = gm.gameid_game[game_id] + user = update.message.from_user + if game.current_player.user.id == user.id: + bot.sendMessage(chat_id, + text="You can't leave the game if it's your turn") + else: + gm.leave_game(user) + bot.sendMessage(chat_id, text="Okay") + + def start(bot, update, args): if args: game_id = args[0] gm.join_game(game_id, update.message.from_user) game = gm.gameid_game[game_id] groupchat = gm.chatid_gameid[game_id] - bot.sendMessage(update.message.chat_id, text="Joined game!") - bot.sendMessage(groupchat, text=update.message.from_user.first_name + - " joined the game!") + bot.sendMessage(update.message.chat_id, + text="Joined game! Please go back to the group chat " + "and play there, via inline commands.") + bot.sendMessage(groupchat, + text=update.message.from_user.first_name + + " joined the game!") if game.current_player is game.current_player.next: game.play_card(game.last_card) @@ -57,7 +97,7 @@ def inline(bot, update): if update.inline_query: reply_to_query(bot, update) else: - chosen_card(bot, update) + process_result(bot, update) def reply_to_query(bot, update): @@ -67,48 +107,52 @@ def reply_to_query(bot, update): results = list() playable = list() - if game.choosing_color: - choose_color(results) - else: - playable = player.playable_cards() + if user_id == game.current_player.user.id: + if game.choosing_color: + add_choose_color(results) + else: + playable = player.playable_cards() + if playable: + playable = list(sorted(playable)) + if playable: - playable = list(sorted(playable)) + for card in playable: + add_play_card(card, results) - if playable is False: - not_your_turn(game, results) - elif playable: - for card in playable: - play_card(card, results) + if playable is not False and not game.choosing_color and not player.drew: + add_draw(player, results, could_play_card=bool(len(playable))) - if playable is not False and not game.choosing_color and not player.drew: - draw(player, results, could_play_card=bool(len(playable))) + if player.drew and not game.choosing_color: + add_pass(results) - if player.drew: - pass_(results) + if game.last_card.special == c.DRAW_FOUR \ + and not game.choosing_color \ + and game.draw_counter: + add_call_bluff(results) - if game.last_card.special == c.DRAW_FOUR \ - and not game.choosing_color \ - and game.draw_counter: - call_bluff(results) + add_other_cards(playable, player, results) - other_cards(playable, player, results) + add_gameinfo(game, results) bot.answerInlineQuery(update.inline_query.id, results, cache_time=0) -def choose_color(results): +def add_choose_color(results): for color in c.COLORS: results.append( InlineQueryResultArticle( id=color, title="Choose Color", - message_text=color, - description=color.upper() + message_text=display_color(color), + description=display_color(color) ) ) -def other_cards(playable, player, results): +def add_other_cards(playable, player, results): + if not playable: + playable = list() + results.append( InlineQueryResultArticle( "hand", @@ -120,7 +164,7 @@ def other_cards(playable, player, results): ) -def draw(player, results, could_play_card): +def add_draw(player, results, could_play_card): results.append( InlineQueryResultArticle( "draw", @@ -133,7 +177,7 @@ def draw(player, results, could_play_card): ) -def pass_(results): +def add_pass(results): results.append( InlineQueryResultArticle( "pass", @@ -144,7 +188,7 @@ def pass_(results): ) -def call_bluff(results): +def add_call_bluff(results): results.append( InlineQueryResultArticle( "call_bluff", @@ -155,7 +199,7 @@ def call_bluff(results): ) -def play_card(card, results): +def add_play_card(card, results): results.append( InlineQueryResultArticle(str(card), title="Play card", @@ -169,19 +213,34 @@ def play_card(card, results): ) -def not_your_turn(game, results): +def add_gameinfo(game, results): + players = list() + current_player = game.current_player + itplayer = current_player.next + add_player(current_player, players) + while itplayer is not current_player: + add_player(itplayer, players) + itplayer = itplayer.next + results.append( InlineQueryResultArticle( - "not_your_turn", - title="Not your turn", - description="Tap to see the current player", - message_text="Current player: " + - game.current_player.user.first_name + "gameinfo", + title="Show game info", + description="Tap to see the current player, player order, " + "card amounts and last played card", + message_text="Current player: " + display_name(game) + "\n" + + "Last card: " + repr(game.last_card) + "\n" + + "Players: " + " -> ".join(players) ) ) -def chosen_card(bot, update): +def add_player(itplayer, players): + players.append(itplayer.user.first_name + " (%d cards)" + % len(itplayer.cards)) + + +def process_result(bot, update): user = update.chosen_inline_result.from_user game = gm.userid_game[user.id] player = gm.userid_player[user.id] @@ -189,65 +248,70 @@ def chosen_card(bot, update): chat_id = gm.chatid_gameid[game] logger.info("Selected result: " + result_id) - if result_id in ('hand', 'not_your_turn'): + if result_id in ('hand', 'gameinfo'): return elif result_id == 'call_bluff': - if player.prev.bluffing: - bot.sendMessage(chat_id, text="Bluff called! Giving %d cards to %s" - % (game.draw_counter, - player.prev.user.first_name)) - for i in range(game.draw_counter): - player.prev.cards.append(game.deck.draw()) - else: - bot.sendMessage(chat_id, text="%s didn't bluff! Giving %d cards to" - " %s" - % (player.prev.user.first_name, - game.draw_counter + 2, - player.user.first_name)) - for i in range(game.draw_counter + 2): - player.cards.append(game.deck.draw()) - - game.draw_counter = 0 - game.turn() + do_call_bluff(bot, chat_id, game, player) elif result_id == 'draw': - for n in range(game.draw_counter or 1): - player.cards.append(game.deck.draw()) - game.draw_counter = 0 - player.drew = True - - if game.last_card.value == c.DRAW_TWO: - game.turn() + do_draw(game, player) elif result_id == 'pass': game.turn() elif result_id in c.COLORS: game.choose_color(result_id) else: - card = c.from_str(result_id) - game.play_card(card) - player.cards.remove(card) - if game.choosing_color: - bot.sendMessage(chat_id, text="Please choose a color") - elif len(player.cards) == 1: - bot.sendMessage(chat_id, text="Last Card!") - elif len(player.cards) == 0: - gm.leave_game(user) - bot.sendMessage(chat_id, text="Player won!") + do_play_card(bot, chat_id, game, player, result_id, user) - player = game.current_player.user - player_name = player.first_name - if player.user.username: - player_name += ' (@' + player.user.username + ')' - bot.sendMessage(chat_id, text="Next player: " + player_name) + user_name = display_name(game) + bot.sendMessage(chat_id, text="Next player: " + user_name) -def error(bot, update, error): - logger.exception(error) +def do_play_card(bot, chat_id, game, player, result_id, user): + card = c.from_str(result_id) + game.play_card(card) + player.cards.remove(card) + if game.choosing_color: + bot.sendMessage(chat_id, text="Please choose a color") + if len(player.cards) == 1: + bot.sendMessage(chat_id, text="Last Card!") + if len(player.cards) == 0: + gm.leave_game(user) + bot.sendMessage(chat_id, text="Player won!") + + +def do_draw(game, player): + draw_counter_before = game.draw_counter + for n in range(game.draw_counter or 1): + player.cards.append(game.deck.draw()) + game.draw_counter = 0 + player.drew = True + if game.last_card.value == c.DRAW_TWO and draw_counter_before > 0: + game.turn() + + +def do_call_bluff(bot, chat_id, game, player): + if player.prev.bluffing: + bot.sendMessage(chat_id, text="Bluff called! Giving %d cards to %s" + % (game.draw_counter, + player.prev.user.first_name)) + for i in range(game.draw_counter): + player.prev.cards.append(game.deck.draw()) + else: + bot.sendMessage(chat_id, text="%s didn't bluff! Giving %d cards to" + " %s" + % (player.prev.user.first_name, + game.draw_counter + 2, + player.user.first_name)) + for i in range(game.draw_counter + 2): + player.cards.append(game.deck.draw()) + game.draw_counter = 0 + game.turn() dp.addTelegramInlineHandler(inline) dp.addTelegramCommandHandler('start', start) dp.addTelegramCommandHandler('new', new_game) +dp.addTelegramCommandHandler('leave', leave_game) dp.addErrorHandler(error) -u.start_polling() +start_bot() u.idle() diff --git a/game.py b/game.py index 2faef12..ba6048a 100644 --- a/game.py +++ b/game.py @@ -49,7 +49,10 @@ class Game(object): self.draw_counter += 2 self.logger.debug("Draw counter increased by 2") elif card.value == c.REVERSE: - self.reverse() + if self.current_player is self.current_player.next.next: + self.turn() + else: + self.reverse() if card.special not in (c.CHOOSE, c.DRAW_FOUR): self.turn() diff --git a/player.py b/player.py index 1441a3b..4b328b4 100644 --- a/player.py +++ b/player.py @@ -85,6 +85,9 @@ class Player(object): self.bluffing = bool(len(playable) - 1) + if len(self.cards) == 1 and self.cards[0].special == c.DRAW_FOUR: + return list() + return playable def card_playable(self, card, playable): @@ -97,7 +100,6 @@ class Player(object): is_playable = False if last.value == c.DRAW_TWO and not \ (card.value == c.DRAW_TWO or - card.special == c.DRAW_FOUR or not self.game.draw_counter): self.logger.debug("Player has to draw and can't counter") is_playable = False diff --git a/start_bot.py b/start_bot.py new file mode 100644 index 0000000..6cbf15c --- /dev/null +++ b/start_bot.py @@ -0,0 +1,6 @@ +# Modify this file if you want a different startup sequence, for example using +# a Webhook + + +def start_bot(updater): + updater.start_polling()