#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Telegram bot to play UNO in group chats # Copyright (c) 2016 Jannes Höke # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import logging from config import ADMIN_LIST, OPEN_LOBBY, DEFAULT_GAMEMODE, ENABLE_TRANSLATIONS from datetime import datetime import random from deck import Deck import card as c class Game(object): """ This class represents a game of UNO """ current_player = None reversed = False choosing_color = False _randomed_color = False started = False draw_counter = 0 players_won = 0 starter = None mode = DEFAULT_GAMEMODE job = None owner = ADMIN_LIST open = OPEN_LOBBY translate = ENABLE_TRANSLATIONS def __init__(self, chat): self.chat = chat self.last_card = None self.deck = Deck() self.logger = logging.getLogger(__name__) @property def players(self): """Returns a list of all players in this game""" players = list() if not self.current_player: return players current_player = self.current_player itplayer = current_player.next players.append(current_player) while itplayer and itplayer is not current_player: players.append(itplayer) itplayer = itplayer.next return players def start(self): if self.mode == None or self.mode != "wild": self.deck._fill_classic_() else: self.deck._fill_wild_() self._first_card_() self.started = True def set_mode(self, mode): self.mode = mode def reverse(self): """Reverses the direction of game""" self.reversed = not self.reversed def turn(self): """Marks 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 self.current_player.turn_started = datetime.now() self._reset_choosing_color() def _reset_choosing_color(self): """Reset 'choosing_color' flag to false and randomly choose color if it hasn't been done, eg. when player is skipped or leave after playing wildcard or +4 card.""" if self.choosing_color and not self.last_card.color: self.last_card.color = random.choice(c.COLORS) self._randomed_color = True else: self._randomed_color = False self.choosing_color = False def _first_card_(self): # In case that the player did not select a game mode if not self.deck.cards: self.set_mode(DEFAULT_GAMEMODE) # The first card should not be a special card while not self.last_card or self.last_card.special: self.last_card = self.deck.draw() # If the card drawn was special, return it to the deck and loop again if self.last_card.special: self.deck.dismiss(self.last_card) self.play_card(self.last_card) def play_card(self, card): """ Plays a card and triggers its effects. Should be called only from Player.play or on game start to play the first card """ self.deck.dismiss(self.last_card) self.last_card = card self.logger.info("Playing card " + repr(card)) if card.value == c.SKIP: self.turn() elif card.special == c.DRAW_FOUR: self.draw_counter += 4 self.logger.debug("Draw counter increased by 4") elif card.value == c.DRAW_TWO: 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: self.logger.debug("Choosing Color...") 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()