From 38bf52c8674d98374f014a35473fdb371dc6da53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Sender=C3=A1k?= Date: Thu, 26 Mar 2020 16:37:01 +0100 Subject: [PATCH] Initial commit --- .gitignore | 1 + game.py | 255 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tests.py | 225 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 481 insertions(+) create mode 100644 .gitignore create mode 100644 game.py create mode 100644 tests.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba0430d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ \ No newline at end of file diff --git a/game.py b/game.py new file mode 100644 index 0000000..a2b91a3 --- /dev/null +++ b/game.py @@ -0,0 +1,255 @@ +from enum import Enum +from random import shuffle + +class BridzikException(Exception): + pass + +class RuleException(Exception): + pass + +class Card_colors(Enum): + HEARTS = 'červeň' + LEAVES = 'zeleň' + ACORNS = 'žaluď' + BELLS = 'guľa' + + def __eq__(self, other): + if self.__class__ is other.__class__: + return self.name == other.name + return NotImplemented + + +class Card_values(Enum): + C7 = 1 + C8 = 2 + C9 = 3 + C10 = 4 + LOWER = 5 + UPPER = 6 + KING = 7 + ACE = 8 + + def __ge__(self, other): + if self.__class__ is other.__class__: + return self.value >= other.value + return NotImplemented + def __gt__(self, other): + if self.__class__ is other.__class__: + return self.value > other.value + return NotImplemented + def __le__(self, other): + if self.__class__ is other.__class__: + return self.value <= other.value + return NotImplemented + def __lt__(self, other): + if self.__class__ is other.__class__: + return self.value < other.value + return NotImplemented + def __eq__(self, other): + if self.__class__ is other.__class__: + return self.name == other.name + return NotImplemented + + +class Card(): + def __init__(self, color: Card_colors, value: Card_values): + self.color = color + self.value = value + + def __eq__(self, other): + if self.__class__ is other.__class__: + return self.color == other.color \ + and self.value == other.value + return NotImplemented + + def __str__(self): + return '{}_{}'.format(self.color.name, self.value.name) + +cards = [Card(color, value) for value in Card_values for color in Card_colors] + +# for series_number in range(4): # 4 hry + +# for round_number in range(8): # v kazdej hre 8 serii +# for player_number in range(4): +# rozdaj_karty_na_kolo(series_number, player_number) + +# for player_number in range(4): +# vezmi_tip(series_number, player_number) +# over_ci_je_mozny +# pridaj_tip_do_zoznamu_tipov + +# for game_number in range(series_number+1): # v kazdej serii + +# for turn_number in range(4): +# kopka.pridaj_kartu(hrac, karta) +# vyherca = kopka.vyhodnot_kopku +# hraci[vyherca].pridaj_ziskanu_kopku() + +class Bridzik(): + # self.series = [] + pass + +class Series(): + # self.rounds = [] + pass + +class Round(): + def __init__(self, round_number: int, first_player: int, cards: [], shuffler = shuffle): + # vyrob kopku pre toto kolo a priprav prazdne objekty + if not 0 <= round_number < 8: + raise BridzikException('Round number has to be between 0 and 7.') + self.round_number = round_number + self.first_player = first_player + self.deal_starting_cards(cards, shuffler) + self.guesses = {} + self.stashes = [] + + def add_player_guess(self, player: int, guess: int): + if player not in [0, 1, 2, 3]: + raise BridzikException('Cislo hraca musi byt 0, 1, 2 alebo 3.') + if player in self.guesses: + raise BridzikException('Pre tohto hraca uz bol zadany tip.') + if player != self.get_active_player(): + raise BridzikException('Nie je mozne pridat tip mimo poradia.') + if len(self.guesses) < 3: + self.guesses[player] = guess + elif 8 - self.round_number != guess + sum([self.guesses[player] for player in self.guesses]): + self.guesses[player] = guess + self.start_new_stash() + else: + raise BridzikException('Nie je mozne zadat tip. Sucet sa rovna poctu kopok v hre.') + + def play_card(self, player, card: Card): + if self.is_completed(): + raise BridzikException('Kolo je ukoncene.') + if not self.is_guessing_completed(): + raise BridzikException('Tipovanie nie je ukoncene.') + if self.get_last_stash().get_active_player() != player: + raise BridzikException('Hrac nie je na tahu.') + if card not in self.player_cards[player]: + raise BridzikException('Hrac nema tuto kartu na ruke.') + if self.get_last_stash().get_first_card(): + if [c for c in self.player_cards[player] if c.color == \ + self.get_last_stash().get_first_card().color] == []: # nema farbu + if [c for c in self.player_cards[player] if c.color == \ + Card_colors['HEARTS']] == []: # nema cerven + pass + elif card.color != Card_colors['HEARTS']: # ma cerven ale nezahral + raise BridzikException('Musi zahrat cerven, ktory ma.') + elif card.color != self.get_last_stash().get_first_card().color: # ma farbu ale nezahral + raise BridzikException('Musi zahrat farbu.') + + self.player_cards[player] = [c for c in self.player_cards[player] if c != card] + self.get_last_stash().add_card(card, player) + if self.get_last_stash().is_completed() and not self.is_completed(): + self.start_new_stash() + + def get_highest_guessing_player(self) -> int: + if not self.is_guessing_completed(): + raise BridzikException('Tipovanie nebolo skoncene.') + round_highest_guessing_player = self.first_player + for i in range(1, 4): + if self.guesses[round_highest_guessing_player] < self.guesses[(i+self.first_player) % 4]: + round_highest_guessing_player = (i+self.first_player) % 4 + return round_highest_guessing_player + + def get_active_player(self): + if not self.is_completed(): + if not self.is_guessing_completed(): + return (self.first_player + len(self.guesses)) % 4 + return self.get_last_stash().get_active_player() + raise BridzikException('Kolo je uzavrete') + + def is_completed(self): + """Round completion status""" + return len(self.stashes) == 8 - self.round_number \ + and self.get_last_stash().is_completed() + + def is_guessing_completed(self): + return len(self.guesses) == 4 + + def get_last_stash(self): + return self.stashes[-1] if len(self.stashes) > 0 else None + + def deal_starting_cards(self, cards: [], shuffler = shuffle): + self.round_cards = cards.copy() + shuffler(self.round_cards) + self.round_cards = self.round_cards[(4*self.round_number):] + self.player_cards = {} + for player_number in range(4): + self.player_cards[player_number] = self.round_cards[ + player_number * (8-self.round_number) : + (player_number + 1) * (8-self.round_number) + ] + self.player_starting_cards = self.player_cards.copy() + + def start_new_stash(self): + if self.is_completed(): + raise BridzikException('Kolo je ukoncene.') + if len(self.stashes) == 0: + self.stashes.append(Stash(self.get_highest_guessing_player())) + elif not self.get_last_stash().is_completed(): + raise BridzikException('Predchadzajuca kopka nie je ukoncena.') + else: + self.stashes.append(Stash(self.get_last_stash().get_winner())) + + +class Stash(): + def __init__(self, first_player: int): + if first_player not in [0, 1, 2, 3]: + raise BridzikException('Cislo hraca musi byt 0, 1, 2 alebo 3.') + self._cards = {} + self.first_player = first_player + + def add_card(self, card: Card, player: int): + if player not in [0, 1, 2, 3]: + raise BridzikException('Cislo hraca musi byt 0, 1, 2 alebo 3.') + if player in self._cards: + raise BridzikException('Pre tohto hraca uz bola pridana karta') + self._cards[player] = card + + def get_first_card(self) -> Card: + """Return copy of first card in stash""" + return Card( + self._cards[self.first_player].color, + self._cards[self.first_player].value + ) if len(self._cards) > 0 else None + + def get_winner(self) -> int: + if not self.is_completed(): + raise BridzikException('Nie je mozne urcit vitaza, kopka nie je dohrana.') + winner = None + winning_card = self.get_first_card() + for player_number in range(4): + if self._cards[player_number].color == self.get_first_card().color \ + or self._cards[player_number].color == Card_colors['HEARTS']: + if self._cards[player_number].color == winning_card.color \ + and self._cards[player_number].value >= winning_card.value: + winning_card = self._cards[player_number] + winner = player_number + elif self._cards[player_number].color == Card_colors['HEARTS']: + winning_card = self._cards[player_number] + winner = player_number + return winner + + def get_active_player(self) -> int: + if not self.is_completed(): + return (self.first_player + len(self._cards)) % 4 + else: + raise BridzikException('Kopka je ukoncena.') + + def get_cards(self) -> {}: + return self._cards.copy() + + def is_completed(self) -> bool: + return len(self._cards) == 4 + + + + # pozbieraj tipy + # urci prveho + # zahraj n kopok: + # pozbieraj karty do kopky + # vyhodnot kopku + # pridaj tuto kopku vitazovy + # vyhodnot toto kolo podla tipov a ziskanych kopok diff --git a/tests.py b/tests.py new file mode 100644 index 0000000..4c6ea04 --- /dev/null +++ b/tests.py @@ -0,0 +1,225 @@ +import unittest +from game import Stash, cards, Card_colors, Card_values,\ + RuleException, BridzikException, Card, Round + +class StashCase(unittest.TestCase): + def test_first_card(self): + heart_7 = Card(Card_colors['HEARTS'], Card_values['C7']) + s = Stash(3) + s.add_card(heart_7, 3) + self.assertEqual(s.get_first_card(), heart_7) + + heart_8 = Card(Card_colors['HEARTS'], Card_values['C8']) + s.add_card(heart_8, 1) + self.assertNotEqual(s.get_first_card(), heart_8) + self.assertEqual(s.get_first_card(), heart_7) + + def test_add_cart(self): + heart_7 = Card(Card_colors['HEARTS'], Card_values['C7']) + leaves_7 = Card(Card_colors['LEAVES'], Card_values['C7']) + leaves_ace = Card(Card_colors['LEAVES'], Card_values['ACE']) + bells_10 = Card(Card_colors['BELLS'], Card_values['C10']) + acorns_10 = Card(Card_colors['ACORNS'], Card_values['C10']) + s = Stash(1) + + s.add_card(heart_7, 1) + self.assertEqual(s._cards[1], heart_7) + s.add_card(leaves_7, 2) + self.assertEqual(s._cards[2], leaves_7) + s.add_card(leaves_ace, 3) + self.assertEqual(s._cards[3], leaves_ace) + s.add_card(bells_10, 0) + self.assertEqual(s._cards[0], bells_10) + + self.assertRaises(BridzikException, s.add_card, card=bells_10, player=4) + self.assertRaises(BridzikException, s.add_card, card=acorns_10, player=0) + + def test_get_winner(self): + heart_7 = Card(Card_colors['HEARTS'], Card_values['C7']) + leaves_7 = Card(Card_colors['LEAVES'], Card_values['C7']) + acorns_ace = Card(Card_colors['ACORNS'], Card_values['ACE']) + bells_10 = Card(Card_colors['BELLS'], Card_values['C10']) + + # only heart takes stash + c1 = [leaves_7, heart_7, acorns_ace, bells_10] + s1 = Stash(0) + for i in range(4): + s1.add_card(c1[i], i) + self.assertEqual(s1.get_winner(), 1) + + # highest color takes the stash + leaves_8 = Card(Card_colors['LEAVES'], Card_values['C8']) + leaves_lower = Card(Card_colors['LEAVES'], Card_values['LOWER']) + c2 = [leaves_8, leaves_7, acorns_ace, leaves_lower] + s2 = Stash(0) + for i in range(4): + s2.add_card(c2[i], i) + self.assertEqual(s2.get_winner(), 3) + + # no matching color and no heart in stash + c3 = [bells_10, leaves_lower, acorns_ace, leaves_8] + s3 = Stash(0) + for i in range(4): + s3.add_card(c3[i], i) + self.assertEqual(s3.get_winner(), 0) + + # highest heart takes the stash + heart_upper = Card(Card_colors['HEARTS'], Card_values['UPPER']) + c4 = [leaves_8, leaves_lower, heart_7, heart_upper] + s4 = Stash(0) + for i in range(4): + s4.add_card(c4[i], i) + self.assertEqual(s4.get_winner(), 3) + + # test exceptions + s5 = Stash(3) + self.assertRaises(BridzikException, s5.get_winner) + + def test_get_cards(self): + heart_7 = Card(Card_colors['HEARTS'], Card_values['C7']) + leaves_7 = Card(Card_colors['LEAVES'], Card_values['C7']) + acorns_ace = Card(Card_colors['ACORNS'], Card_values['ACE']) + bells_10 = Card(Card_colors['BELLS'], Card_values['C10']) + s = Stash(0) + self.assertEqual(s.get_cards(), {}) + s.add_card(heart_7, 2) + self.assertEqual(s.get_cards(), {2: heart_7}) + s.add_card(leaves_7, 3) + self.assertEqual(s.get_cards(), {2: heart_7, 3: leaves_7}) + s.add_card(acorns_ace, 0) + self.assertEqual(s.get_cards(), {2: heart_7, 3: leaves_7, 0: acorns_ace}) + s.add_card(bells_10, 1) + self.assertEqual(s.get_cards(), {2: heart_7, 3: leaves_7, 0: acorns_ace, 1: bells_10}) + + def test_is_complete(self): + heart_7 = Card(Card_colors['HEARTS'], Card_values['C7']) + leaves_7 = Card(Card_colors['LEAVES'], Card_values['C7']) + acorns_ace = Card(Card_colors['ACORNS'], Card_values['ACE']) + bells_10 = Card(Card_colors['BELLS'], Card_values['C10']) + s = Stash(0) + c = [heart_7, leaves_7, acorns_ace, bells_10] + for i in range(4): + self.assertFalse(s.is_completed()) + s.add_card(c[0], i) + self.assertTrue(s.is_completed()) + + def test_get_active_player(self): + heart_7 = Card(Card_colors['HEARTS'], Card_values['C7']) + leaves_7 = Card(Card_colors['LEAVES'], Card_values['C7']) + leaves_ace = Card(Card_colors['LEAVES'], Card_values['ACE']) + bells_10 = Card(Card_colors['BELLS'], Card_values['C10']) + s = Stash(1) + + self.assertEqual(s.get_active_player(), 1) + s.add_card(heart_7, 1) + self.assertEqual(s.get_active_player(), 2) + s.add_card(leaves_7, 2) + self.assertEqual(s.get_active_player(), 3) + s.add_card(leaves_ace, 3) + self.assertEqual(s.get_active_player(), 0) + s.add_card(bells_10, 0) + self.assertRaises(BridzikException, s.get_active_player) + + +class RoundCase(unittest.TestCase): + def test_add_player_guess(self): + r = Round(0, 1, cards) + + self.assertRaises(BridzikException, r.add_player_guess, player=2, guess=0) + + r.add_player_guess(1, 1) + self.assertEqual(r.guesses[1], 1) + self.assertRaises(BridzikException, r.add_player_guess, player=1, guess=0) + + r.add_player_guess(2, 0) + self.assertEqual(r.guesses[2], 0) + r.add_player_guess(3, 2) + self.assertEqual(r.guesses[3], 2) + + self.assertEqual(r.stashes, []) + + self.assertRaises(BridzikException, r.add_player_guess, player=0, guess=5) + + r.add_player_guess(0, 4) + self.assertEqual(r.guesses[0], 4) + self.assertEqual(r.stashes[0].first_player, 0) + + def test_get_highest_guessing_player(self): + r1 = Round(0, 0, cards) + r1.add_player_guess(0, 2) + r1.add_player_guess(1, 1) + r1.add_player_guess(2, 3) + self.assertRaises(BridzikException, r1.get_highest_guessing_player) + r1.add_player_guess(3, 4) + self.assertEqual(r1.get_highest_guessing_player(), 3) + + r2 = Round(0, 2, cards) + r2.add_player_guess(2, 5) + r2.add_player_guess(3, 0) + r2.add_player_guess(0, 1) + r2.add_player_guess(1, 1) + self.assertEqual(r2.get_highest_guessing_player(), 2) + + def test_get_active_player(self): + r = Round(6, 1, cards) + self.assertEqual(r.get_active_player(), 1) + r.add_player_guess(1, 1) + self.assertEqual(r.get_active_player(), 2) + r.add_player_guess(2, 0) + self.assertEqual(r.get_active_player(), 3) + r.add_player_guess(3, 2) + self.assertEqual(r.get_active_player(), 0) + r.add_player_guess(0, 4) + self.assertEqual(r.get_active_player(), 0) + + # TODO dokoncit pre prebiehajuce kolo + + def test_play_card(self): + shuffler = lambda list: None + c0 = [ + Card(Card_colors['BELLS'], Card_values['UPPER']), + Card(Card_colors['HEARTS'], Card_values['UPPER']) + ] + c1 = [ + Card(Card_colors['BELLS'], Card_values['C7']), + Card(Card_colors['HEARTS'], Card_values['C10']) + ] + c2 = [ + Card(Card_colors['BELLS'], Card_values['ACE']), + Card(Card_colors['BELLS'], Card_values['C8']) + ] + c3 = [ + Card(Card_colors['LEAVES'], Card_values['C7']), + Card(Card_colors['BELLS'], Card_values['LOWER']) + ] + c = ['dummy']*24 + c0 + c1 + c2 + c3 + r = Round(6, 1, c, shuffler) + r.add_player_guess(1, 0) + r.add_player_guess(2, 0) + r.add_player_guess(3, 1) + self.assertRaises(BridzikException, r.play_card, player=0, card=c0[0]) # neukoncene tipovanie + r.add_player_guess(0, 2) + self.assertRaises(BridzikException, r.play_card, player=1, card=c1[0]) # mimo poradia + + r.play_card(0, c0[0]) + self.assertRaises(BridzikException, r.play_card, player=1, card=c1[1]) # cerven namiesto farby + r.play_card(1, c1[0]) + r.play_card(2, c2[0]) + self.assertRaises(BridzikException, r.play_card, player=3, card=c3[0]) # ina farba + r.play_card(3, c3[1]) + + # print(c2) + # print(r.player_cards[2]) + + r.play_card(2, c2[1]) + r.play_card(3, c3[0]) + self.assertRaises(BridzikException, r.play_card, player=0, card=c0[0]) # uz zahrana karta + r.play_card(0, c0[1]) + r.play_card(1, c1[1]) + + self.assertEqual(len(r.stashes), 2) + + + +if __name__ == '__main__': + unittest.main(verbosity=2)