diff --git a/bot.py b/bot.py index e997a60..0ca27be 100644 --- a/bot.py +++ b/bot.py @@ -694,9 +694,10 @@ def do_call_bluff(bot, player): if player.prev.bluffing: send_async(bot, chat.id, - text=__("Bluff called! Giving 4 cards to {name}", + text=__("Bluff called! Giving {numbers} cards to {name}", multi=game.translate) - .format(name=player.prev.user.first_name)) + .format(name=player.prev.user.first_name, + numbers=game.draw_counter)) try: player.prev.draw() @@ -708,10 +709,11 @@ def do_call_bluff(bot, player): else: game.draw_counter += 2 send_async(bot, chat.id, - text=__("{name1} didn't bluff! Giving 6 cards to {name2}", + text=__("{name1} didn't bluff! Giving {numbers} cards to {name2}", multi=game.translate) .format(name1=player.prev.user.first_name, - name2=player.user.first_name)) + name2=player.user.first_name, + numbers=game.draw_counter)) try: player.draw() except DeckEmptyError: diff --git a/game.py b/game.py index 5308fac..d0ee805 100644 --- a/game.py +++ b/game.py @@ -40,6 +40,7 @@ class Game(object): def __init__(self, chat): self.chat = chat self.last_card = None + self.joined_before = [] #FIXME: Change it as set() while not self.last_card or self.last_card.special: self.deck = Deck() diff --git a/game_manager.py b/game_manager.py index 5c94a22..9db6bf0 100644 --- a/game_manager.py +++ b/game_manager.py @@ -19,6 +19,7 @@ import logging +import random from game import Game from player import Player @@ -77,7 +78,8 @@ class GameManager(object): # Don not re-add a player and remove the player from previous games in # this chat, if he is in one of them for player in players: - if player in game.players: + # Try to pervent someone win or leave then join again. + if player in game.players or user.id in game.joined_before: raise AlreadyJoinedError() else: try: @@ -93,8 +95,14 @@ class GameManager(object): players = self.userid_players[user.id] player = Player(game, user) - - players.append(player) + + # Randomize player position. + game.joined_before.append(user.id) + if len(players) > 2: + players.insert(random.randrange(len(players)), player) + else: + players.append(player) + self.userid_current[user.id] = player def leave_game(self, user, chat): diff --git a/player.py b/player.py index 45954f1..d4f4d1f 100644 --- a/player.py +++ b/player.py @@ -167,17 +167,20 @@ class Player(object): self.logger.debug("Card's color or value doesn't match") is_playable = False elif last.value == c.DRAW_TWO and not \ - card.value == c.DRAW_TWO and self.game.draw_counter: + (card.value == c.DRAW_TWO or card.special == c.DRAW_FOUR) and self.game.draw_counter: self.logger.debug("Player has to draw and can't counter") is_playable = False - elif last.special == c.DRAW_FOUR and self.game.draw_counter: + elif last.special == c.DRAW_FOUR and self.game.draw_counter and not card.special == c.DRAW_FOUR: self.logger.debug("Player has to draw and can't counter") is_playable = False - elif (last.special == c.CHOOSE or last.special == c.DRAW_FOUR) and \ - (card.special == c.CHOOSE or card.special == c.DRAW_FOUR): + elif (last.special == c.CHOOSE) and \ + (card.special == c.CHOOSE or card.special == c.DRAW_FOUR) or \ + (last.special == c.DRAW_FOUR and card.special == c.CHOOSE): self.logger.debug("Can't play colorchooser on another one") is_playable = False - elif not last.color: + # Pervent game locks by choose colors. + # When player is going leave and he didn't selected a color, it cause game locks. + elif not last.color and not (last.special == c.CHOOSE or last.special == c.DRAW_FOUR): self.logger.debug("Last card has no color") is_playable = False diff --git a/test_player.py b/test_player.py new file mode 100644 index 0000000..1f8926e --- /dev/null +++ b/test_player.py @@ -0,0 +1,202 @@ +#!/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 unittest + +import telegram +from game import Game +from player import Player +import card as c + +import logging +logging.basicConfig( + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + level=logging.DEBUG) +logger = logging.getLogger(__name__) + + +class Test(unittest.TestCase): + + game = None + + def setUp(self): + self.game = Game(None) + + def test_insert(self): + p0 = Player(self.game, "Player 0") + p1 = Player(self.game, "Player 1") + p2 = Player(self.game, "Player 2") + + self.assertEqual(p0, p2.next) + self.assertEqual(p1, p0.next) + self.assertEqual(p2, p1.next) + + self.assertEqual(p0.prev, p2) + self.assertEqual(p1.prev, p0) + self.assertEqual(p2.prev, p1) + + def test_reverse(self): + p0 = Player(self.game, "Player 0") + p1 = Player(self.game, "Player 1") + p2 = Player(self.game, "Player 2") + self.game.reverse() + p3 = Player(self.game, "Player 3") + + self.assertEqual(p0, p3.next) + self.assertEqual(p1, p2.next) + self.assertEqual(p2, p0.next) + self.assertEqual(p3, p1.next) + + self.assertEqual(p0, p2.prev) + self.assertEqual(p1, p3.prev) + self.assertEqual(p2, p1.prev) + self.assertEqual(p3, p0.prev) + + def test_leave(self): + p0 = Player(self.game, "Player 0") + p1 = Player(self.game, "Player 1") + p2 = Player(self.game, "Player 2") + + p1.leave() + + self.assertEqual(p0, p2.next) + self.assertEqual(p2, p0.next) + + def test_draw(self): + p = Player(self.game, "Player 0") + + deck_before = len(self.game.deck.cards) + top_card = self.game.deck.cards[-1] + + p.draw() + + self.assertEqual(top_card, p.cards[-1]) + self.assertEqual(deck_before, len(self.game.deck.cards) + 1) + + def test_draw_two(self): + p = Player(self.game, "Player 0") + + deck_before = len(self.game.deck.cards) + self.game.draw_counter = 2 + + p.draw() + + self.assertEqual(deck_before, len(self.game.deck.cards) + 2) + + def test_playable_cards_simple(self): + p = Player(self.game, "Player 0") + + self.game.last_card = c.Card(c.RED, '5') + + p.cards = [c.Card(c.RED, '0'), c.Card(c.RED, '5'), c.Card(c.BLUE, '0'), + c.Card(c.GREEN, '5'), c.Card(c.GREEN, '8')] + + expected = [c.Card(c.RED, '0'), c.Card(c.RED, '5'), + c.Card(c.GREEN, '5')] + + self.assertListEqual(p.playable_cards(), expected) + + def test_playable_cards_on_draw_two(self): + p = Player(self.game, "Player 0") + + self.game.last_card = c.Card(c.RED, c.DRAW_TWO) + self.game.draw_counter = 2 + + p.cards = [c.Card(c.RED, c.DRAW_TWO), c.Card(c.RED, '5'), + c.Card(c.BLUE, '0'), c.Card(c.GREEN, '5'), + c.Card(c.GREEN, c.DRAW_TWO)] + + expected = [c.Card(c.RED, c.DRAW_TWO), c.Card(c.GREEN, c.DRAW_TWO)] + + self.assertListEqual(p.playable_cards(), expected) + + def test_playable_cards_on_draw_two_then_four(self): + p = Player(self.game, "Player 0") + + self.game.last_card = c.Card(c.RED, c.DRAW_TWO) + self.game.draw_counter = 2 + + p.cards = [c.Card(c.RED, c.DRAW_TWO), c.Card(c.RED, '5'), + c.Card(c.BLUE, '0'), c.Card(c.GREEN, '5'), + c.Card(c.GREEN, c.DRAW_TWO), + c.Card(None, None, c.DRAW_FOUR)] + + expected = [c.Card(c.RED, c.DRAW_TWO), c.Card(c.GREEN, c.DRAW_TWO), c.Card(None, None, c.DRAW_FOUR)] + + self.assertListEqual(p.playable_cards(), expected) + + def test_playable_cards_on_draw_four(self): + p = Player(self.game, "Player 0") + + self.game.last_card = c.Card(c.RED, None, c.DRAW_FOUR) + self.game.draw_counter = 4 + + p.cards = [c.Card(c.RED, c.DRAW_TWO), c.Card(c.RED, '5'), + c.Card(c.BLUE, '0'), c.Card(c.GREEN, '5'), + c.Card(c.GREEN, c.DRAW_TWO), + c.Card(None, None, c.DRAW_FOUR), + c.Card(None, None, c.CHOOSE)] + + expected = [c.Card(None, None, c.DRAW_FOUR)] + self.assertListEqual(p.playable_cards(), expected) + +# def test_playable_cards_on_draw_four_then_four(self): +# p = Player(self.game, "Player 0") + +# self.game.last_card = c.Card(c.RED, None, c.DRAW_FOUR) +# self.game.draw_counter = 4 + +# p.cards = [c.Card(c.RED, c.DRAW_TWO), c.Card(c.RED, '5'), +# c.Card(c.BLUE, '0'), c.Card(c.GREEN, '5'), +# c.Card(c.GREEN, c.DRAW_TWO), +# c.Card(None, None, c.DRAW_FOUR)] + +# expected = [c.Card(None, None, c.DRAW_FOUR)] +# self.assertListEqual(p.playable_cards(), expected) + + def test_bluffing(self): + p = Player(self.game, "Player 0") + Player(self.game, "Player 01") + + self.game.last_card = c.Card(c.RED, '1') + + p.cards = [c.Card(c.RED, c.DRAW_TWO), c.Card(c.RED, '5'), + c.Card(c.BLUE, '0'), c.Card(c.GREEN, '5'), + c.Card(c.RED, '5'), c.Card(c.GREEN, c.DRAW_TWO), + c.Card(None, None, c.DRAW_FOUR), + c.Card(None, None, c.CHOOSE)] + + p.playable_cards() + self.assertTrue(p.bluffing) + + p.cards = [c.Card(c.BLUE, '1'), c.Card(c.GREEN, '1'), + c.Card(c.GREEN, c.DRAW_TWO), + c.Card(None, None, c.DRAW_FOUR), + c.Card(None, None, c.CHOOSE)] + + p.playable_cards() + + p.play(c.Card(None, None, c.DRAW_FOUR)) + self.game.choose_color(c.GREEN) + + self.assertFalse(self.game.current_player.prev.bluffing) + +if __name__ == '__main__': + unittest.main()