game.py - Game - сама игра
from player import Cow006Player class Cow006Game(object): def __init__(self, players, rows=4, maxinrow=6, maxhand=10): """ Создает экземпляр игры :param players: список игроков :param rows: количество рядов на столе :param maxinrow: когда "сгорает" карта в ряду :param maxhand: начальное количество карт в руке """ self.players = players self.maxhand = maxhand deck = Cow006Deck() deck.shuffle() # из колоды раздают карты на стол и игрокам, больше она не нужна self.table = Cow006Table(rows, maxinrow) self.table.init_cards(deck) for p in self.players: p.init_hand(deck, maxhand) def run(self): """ Процесс игры до конца :return: None """ # пока не закончатся карты на руке for step in range(self.maxhand): print(f"Begin step {step}") print(self.table) # все игроки кладут закрытые карты choosen_cards = {} for p in self.players: choosen_cards[p.choose_card(self.table)] = p print('Choose cards:'+ ', '.join(['{} ({})'.format(c, p.name) for c, p in choosen_cards.items()])) # карты открываются и выкладываются на стол for card, player in sorted(choosen_cards.items()): print('Resolve {} from {}'.format(card, player)) self.resolve_card(card, player) print(self.table) def end(self): """ Считаем очки, возвращаем список игроков по возрастанию очков Напечатать список, поздравить победителя :return: """ pass def resolve_card(self, card, player): """ Разыгрываем карту card игрока player по правилам :param card: карта :param player: игрок, положивший эту карту :return: """ # ищем в какой ряд подходит эта карта row = self.table.find_row(card) print('Row is', row) # если карту нельзя положить ни в один ряд (она очень маленькая) if row is None: ''' My card is too small''' # игрок выбирает, в какой ряд положить r = player.choose_row(self.table, card) # кладет карту в конец ряда r.add_card(card) # забирает все карты, кроме последней cutted = r.cut() # карты, что забрал игрок, добавляются на его счет player.add_score(cutted) else: # если карту можно положить в ряд, кладем ее в ряд row.add_card(card) # это шестая карта? if row.overflow(): # забираем карты, кроме последней и добавляем их в счет игрока cutted = row.cut() player.add_score(cutted) if __name__ == '__main__': g = Cow006Game([ Cow006Player("Alice"), Cow006Player("Bob"), Cow006Player("Charle") ], rows=3, maxinrow=4, maxhand=5 ) g.run() g.end()
player.py - Player - один игрок
Игрок пока только компьютерныйfrom Card import Cow006Card as Card class Cow006Player(object): def __init__(self, name): self.name = name self.hand = Hand() self._score = 0 def __repr__(self): return '{}: {}'.format(self.name, self.hand) def init_hand(self, deck, maxhand): """ Добавляет карты в руку игрока из колоды, maxhand штук :param deck: колода, из которой добавляем карты :param maxhand: количество карт на руке до начала игры :return: """ if maxhand > len(deck): raise ValueError(f"Need {maxhand} cards, deck has only {len(deck)} cards") def choose_card(self, table=None): """ выбираем какую карту играть (в начале хода)""" # todo: случайную return self.hand.draw() def choose_row(self, table, card): """ выбирает, какой ряд взять со стола и положить в начало ряда карту card""" # todo: случайную return table[0] def score(self): """ считаем очки игрока """ return self._score def add_score(self, card_list): """ добавляем в очки игрока очки из карт card_list """ pass
table.py - Table + Row - стол и ряды стола
from Card import Cow006Card as Card from container import Container class Row(Container): def __init__(self, maxinrow=6): super().__init__() self.maxinrow = maxinrow # какая корова "проваливает" ряд def __lt__(self, other): pass def top(self): """ возвращает последнюю карту в ряду """ pass def overflow(self): """ проверяет, есть 6 коров в ряду (True) или еще нет (False)""" pass def acceptable(self, card): """ эту карту card можно положить в конец этого ряда? """ pass def cut(self): """ Убирает из ряда все карты, кроме последней. Возвращает список убранных карт""" pass class Table: def __init__(self, deck, rows=4, maxinrow=6): self.maxinrow = maxinrow self.rows = [Row(maxinrow) for r in range(rows)] def init_cards(self, deck): for r in self.rows: r.add_card(deck.draw()) def __repr__(self): return '\n'.join(['Row{} : {}'.format(i, r) for i, r in enumerate(self.rows)]) def find_row(self, card): """ ищет, в какие ряды можно положить эту карту, возвращает ряд или None, если карту нельзя положить ни в один ряд """ pass def __getitem__(self, i): return self.rows[i]
class TestRow(TestCase): def create_row(self, card_numbers): row = Row() for n in card_numbers: row.add_card(Card(n)) return row def test_top(self): ar = [13, 25, 86] row = self.create_row(ar) self.assertEqual(row.top(), Card(ar[-1])) row.add_card(Card(100)) self.assertEqual(row.top(), Card(100)) # тест ряда с единственной картой (ряда без карт быть не должно!) ar = [17] row = self.create_row(ar) self.assertEqual(row.top(), Card(ar[0]))
container.py - Container - базовый класс коллекция карт
Заметьте, карты тут не нужны. TODO (мне) - сразу давать с типами.import random # from Card import Cow006Card as Card class Container: def __init__(self): self.cards = [] def __repr__(self): return ' '.join(map(str, self.cards)) def __len__(self): return len(self.cards) def __getitem__(self, i): return self.cards[i] def add_card(self, c): self.cards.append(c) def draw(self): return self.cards.pop() def shuffle(self): random.shuffle(self.cards)
def test_add_card(self): a = Container() ar = [Card(10), Card(5), Card(67)] for c in ar: a.add_card(c) self.assertEqual(str(a), ' '.join(map(str, ar)))
hand.py - Hand - рука 1 игрока
from cow006_container import Container class Hand(Container): pass
deck.py - Deck - колода
from cow006_container import Container from cow006_card import Cow006Card as Card class Cow006Deck(Container): def __init__(self): super().__init__() for c in Card.all_cards(): self.add_card(c)
card.py - Card - 1 карта + делаем набор из всех возможных карт
class Cow006Card: def __init__(self, n): self.n = n self.score = 1 # todo def __repr__(self): return str(self.n) def __lt__(self, other): return self.n < other.n def __eq__(self, other): return self.n == other.n def __hash__(self): return self.n @staticmethod def all_cards(maxsize=104): return [Cow006Card(i + 1) for i in range(maxsize)]
from Card import Cow006Card as Card import unittest class CardTest(unittest.TestCase): def test_print(self): c = Card(10) self.assertEqual(str(c), '10') c = Card(42) self.assertEqual(str(c), '42') def test_eq(self): c1 = Card(10) c2 = Card(10) c3 = Card(30) self.assertEqual(c1, c2) self.assertNotEqual(c1, c3) def test_ls(self): c1 = Card(18) c2 = Card(42) c3 = Card(103) self.assertLess(c1, c2) self.assertLess(c1, c3) self.assertLess(c2, c3) def test_all_cards(self): deck = Card.all_cards(5) self.assertEqual(deck, [Card(1), Card(2), Card(3), Card(4), Card(5)]) if __name__ == '__main__': unittest.main()
Тестирование исключений
То, сообщение исключения ровно такое, как мы ожидаем:
Более пристальное исследование исключения. Сначала получаем исключение в context manager, потом его исследуем
https://docs.python.org/2/library/unittest.html#unittest.TestCase.assertRaises
class DailyEntriesTests(TestCase): def test_cant_have_ip_and_user(self): u = createUser(False) de = createDailyEntry(u, "1.1.1.1", 1) with self.assertRaises(ValidationError) as cm: de.full_clean() # this line bombs - message doesn't exist. I also tried "error_code" like I saw in the documentation, but that doesn't work print(cm.exception.message) self.assertTrue(cm.exception.message.contains("Both"))
with self.assertRaisesRegexp(ValidationError, "Both"): de.full_clean()