From 1f7466cba525ef516f4d580fce8bdf0918478f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannes=20H=C3=B6ke?= Date: Tue, 8 Mar 2016 02:50:24 +0100 Subject: [PATCH] Add comments everywhere --- bot.py | 22 ++++++++++++++++++---- card.py | 8 ++++++++ deck.py | 15 ++++++++++----- game.py | 19 +++++++------------ game_manager.py | 9 ++++++++- player.py | 25 ++++++++++++++----------- 6 files changed, 65 insertions(+), 33 deletions(-) diff --git a/bot.py b/bot.py index 460a988..3880df1 100644 --- a/bot.py +++ b/bot.py @@ -35,6 +35,7 @@ help_text = "Follow these steps:\n\n" \ def list_subtract(list1, list2): + """ Helper function to subtract two lists and return the sorted result """ list1 = list1.copy() for x in list2: @@ -44,6 +45,7 @@ def list_subtract(list1, list2): def display_name(game): + """ Get the current players name including their username, if possible """ user = game.current_player.user user_name = user.first_name if user.username: @@ -52,6 +54,7 @@ def display_name(game): def display_color(color): + """ Convert a color code to actual color name """ if color == "r": return "Red" if color == "b": @@ -63,10 +66,12 @@ def display_color(color): def error(bot, update, error): + """ Simple error handler """ logger.exception(error) def new_game(bot, update): + """ Handler for the /new command """ chat_id = update.message.chat_id link = gm.generate_invite_link(u.bot.getMe().username, chat_id) bot.sendMessage(chat_id, @@ -74,10 +79,12 @@ def new_game(bot, update): def leave_game(bot, update): + """ Handler for the /leave command """ 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") @@ -87,8 +94,9 @@ def leave_game(bot, update): def start(bot, update, args): + """ Handler for the /start command """ if args: - game_id = args[0] + game_id = args[0] # Contains the game id gm.join_game(game_id, update.message.from_user) game = gm.gameid_game[game_id] groupchat = gm.chatid_gameid[game_id] @@ -99,6 +107,7 @@ def start(bot, update, args): text=update.message.from_user.first_name + " joined the game!") + # Check if user is the first player to join and if, show the first card if game.current_player is game.current_player.next: game.play_card(game.last_card) bot.sendPhoto(groupchat, @@ -111,6 +120,7 @@ def start(bot, update, args): def inline(bot, update): + """ Handler for all inline stuff """ if update.inline_query: reply_to_query(bot, update) else: @@ -118,12 +128,14 @@ def inline(bot, update): def help(bot, update): + """ Handler for the /help command """ bot.sendMessage(update.message.chat_id, text=help_text, parse_mode=ParseMode.HTML) def reply_to_query(bot, update): + """ Builds the result list for inline queries and answers to the client """ user_id = update.inline_query.from_user.id player = gm.userid_player[user_id] game = gm.userid_game[user_id] @@ -241,6 +253,7 @@ def add_gameinfo(game, results): 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 @@ -264,12 +277,13 @@ def add_player(itplayer, players): def process_result(bot, update): + """ Check the players actions and act accordingly """ user = update.chosen_inline_result.from_user game = gm.userid_game[user.id] player = gm.userid_player[user.id] result_id = update.chosen_inline_result.result_id chat_id = gm.chatid_gameid[game] - logger.info("Selected result: " + result_id) + logger.debug("Selected result: " + result_id) if result_id in ('hand', 'gameinfo'): return @@ -284,8 +298,7 @@ def process_result(bot, update): else: do_play_card(bot, chat_id, game, player, result_id, user) - user_name = display_name(game) - bot.sendMessage(chat_id, text="Next player: " + user_name) + bot.sendMessage(chat_id, text="Next player: " + display_name(game)) def do_play_card(bot, chat_id, game, player, result_id, user): @@ -330,6 +343,7 @@ def do_call_bluff(bot, chat_id, game, player): game.turn() +# Add all handlers to the dispatcher and run the bot dp.addTelegramInlineHandler(inline) dp.addTelegramCommandHandler('start', start) dp.addTelegramCommandHandler('new', new_game) diff --git a/card.py b/card.py index 8de8079..7eb0554 100644 --- a/card.py +++ b/card.py @@ -38,6 +38,9 @@ THUMB_PATTERN = 'https://raw.githubusercontent.com/jh0ker/mau_mau_bot/' \ class Card(object): + """ + This class represents a card. + """ def __init__(self, color, value, special=None): self.color = color @@ -54,19 +57,24 @@ class Card(object): return ' '.join([s.capitalize() for s in str(self).split('_')]) def __eq__(self, other): + """ Needed for sorting the cards """ return str(self) == str(other) def __lt__(self, other): + """ Needed for sorting the cards """ return str(self) < str(other) def get_image_link(self): + """ Returns a link to the image of this card """ return IMAGE_PATTERN % str(self) def get_thumb_link(self): + """ Returns a link to the thumbnail-image of this card """ return THUMB_PATTERN % str(self) def from_str(string): + """ Decode a Card object from a string """ if string not in SPECIALS: color, value = string.split('_') return Card(color, value) diff --git a/deck.py b/deck.py index 01f4aa2..8287cef 100644 --- a/deck.py +++ b/deck.py @@ -1,33 +1,37 @@ from random import shuffle -import card +import card as c from card import Card import logging class Deck(object): + """ This class represents a deck of cards """ def __init__(self): self.cards = list() self.graveyard = list() self.logger = logging.getLogger(__name__) - for color in card.COLORS: - for value in card.VALUES: + # Fill deck + for color in c.COLORS: + for value in c.VALUES: self.cards.append(Card(color, value)) - if not value == card.ZERO: + if not value == c.ZERO: self.cards.append(Card(color, value)) - for special in card.SPECIALS * 4: + for special in c.SPECIALS * 4: self.cards.append(Card(None, None, special=special)) self.logger.debug(self.cards) self.shuffle() def shuffle(self): + """ Shuffle the deck """ self.logger.debug("Shuffling Deck") shuffle(self.cards) def draw(self): + """ Draw a card from this deck """ try: card = self.cards.pop() self.logger.debug("Drawing card " + str(card)) @@ -39,4 +43,5 @@ class Deck(object): return self.draw() def dismiss(self, card): + """ All played cards should be returned into the deck """ self.graveyard.append(card) diff --git a/game.py b/game.py index ba6048a..e381dd0 100644 --- a/game.py +++ b/game.py @@ -1,16 +1,11 @@ import logging from deck import Deck -from card import Card import card as c -from player import Player class Game(object): - """ This class represents a game of mau mau - - :type current_player: Player - """ + """ This class represents a game of UNO """ current_player = None reversed = False draw_counter = 0 @@ -22,20 +17,17 @@ class Game(object): self.logger = logging.getLogger(__name__) def reverse(self): + """ Reverse the direction of play """ self.reversed = not self.reversed def turn(self): + """ Mark the turn as over and change the current player """ self.logger.debug("Next Player") self.current_player = self.current_player.next self.current_player.drew = False def play_card(self, card): - """ - - :param card: - :type card: Card - :return: - """ + """ Play a card and trigger its effects """ self.deck.dismiss(self.last_card) self.last_card = card @@ -49,11 +41,13 @@ class Game(object): self.draw_counter += 2 self.logger.debug("Draw counter increased by 2") elif card.value == c.REVERSE: + # Special rule for two players if self.current_player is self.current_player.next.next: self.turn() else: self.reverse() + # Don't turn if the current player has to choose a color if card.special not in (c.CHOOSE, c.DRAW_FOUR): self.turn() else: @@ -61,6 +55,7 @@ class Game(object): self.choosing_color = True def choose_color(self, color): + """ Carries out the color choosing and turns the game """ self.last_card.color = color self.turn() self.choosing_color = False diff --git a/game_manager.py b/game_manager.py index 3a85586..7deca43 100644 --- a/game_manager.py +++ b/game_manager.py @@ -8,15 +8,20 @@ LINK_PATTERN = 'https://telegram.me/%s?start=%s' class GameManager(object): + """ Manages all running games by using a confusing amount of dicts """ def __init__(self): self.gameid_game = dict() self.userid_game = dict() - self.chatid_gameid = dict() + self.chatid_gameid = dict() # Goes both ways self.userid_player = dict() self.logger = logging.getLogger(__name__) def generate_invite_link(self, bot_name, chat_id): + """ + Generate a game join link with a unique ID and connect the game to the + group chat + """ game_id = str(uuid4()) game = Game() @@ -29,6 +34,7 @@ class GameManager(object): return LINK_PATTERN % (bot_name, game_id) def join_game(self, game_id, user): + """ Create a player from the Telegram user and add it to the game """ self.logger.info("Joining game with id " + game_id) game = self.gameid_game[game_id] player = Player(game, user) @@ -36,6 +42,7 @@ class GameManager(object): self.userid_game[user.id] = game def leave_game(self, user): + """ Remove a player from its current game """ player = self.userid_player[user.id] player.leave() diff --git a/player.py b/player.py index 4b328b4..d006d6d 100644 --- a/player.py +++ b/player.py @@ -4,19 +4,20 @@ import card as c class Player(object): + """ + This class represents a player. + It is basically a doubly-linked ring list with the option to reverse the + direction. On initialization, it will connect itself to a game and its + other players by placing itself behind the current player. + """ def __init__(self, game, user): - """ - - :param game: - :type game Game - :return: - """ self.cards = list() self.game = game self.user = user self.logger = logging.getLogger(__name__) + # Check if this player is the first player in this game. if game.current_player: self.next = game.current_player self.prev = game.current_player.prev @@ -34,6 +35,7 @@ class Player(object): self.drew = False def leave(self): + """ Leave the current game """ self.next.prev = self.prev self.prev.next = self.next self.next = None @@ -68,29 +70,30 @@ class Player(object): self._next = player def playable_cards(self): - - if self.game.current_player.user.id is not self.user.id: - self.logger.debug("Player is not current player") - return False + """ Returns a list of the cards this player can play right now """ playable = list() last = self.game.last_card - self.logger.debug("Last card was" + str(last)) + self.logger.debug("Last card was " + str(last)) for card in self.cards: if self.card_playable(card, playable): self.logger.debug("Matching!") playable.append(card) + # You may only play a +4 if it's the only card you can play self.bluffing = bool(len(playable) - 1) + # You may not play a +4 as your last card if len(self.cards) == 1 and self.cards[0].special == c.DRAW_FOUR: return list() return playable def card_playable(self, card, playable): + """ Check a single card if it can be played """ + is_playable = True last = self.game.last_card self.logger.debug("Checking card " + str(card))